diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 873c1780f..f61e320c9 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -12,57 +12,53 @@ add a comment to it. You'll see exactly what is sent, the system is 100% transpa
## Issue reporting/feature requests
* Search the [existing issues](https://github.com/TeamNewPipe/NewPipe/issues) first to make sure your issue/feature
-hasn't been reported/requested before
-* Check whether your issue/feature is already fixed/implemented
-* Check if the issue still exists in the latest release/beta version
-* If you are an Android/Java developer, you are always welcome to fix/implement an issue/a feature yourself. PRs welcome!
+hasn't been reported/requested before.
+* Check whether your issue/feature is already fixed/implemented.
+* Check if the issue still exists in the latest release/beta version.
+* If you are an Android/Java developer, you are always welcome to fix an issue or implement a feature yourself. PRs welcome!
* We use English for development. Issues in other languages will be closed and ignored.
* Please only add *one* issue at a time. Do not put multiple issues into one thread.
-* When reporting a bug please give us a context, and a description how to reproduce it.
-* Issues that only contain a generated bug report, but no description might be closed.
+* Follow the template! Issues or feature requests not matching the template might be closed.
## Bug Fixing
* If you want to help NewPipe to become free of bugs (this is our utopic goal for NewPipe), you can send us an email to
-tnp@newpipe.schabi.org to let me know that you intend to help. We'll send you further instructions. You may, on request,
-register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information.
+tnp@newpipe.schabi.org to let us know that you intend to help. We'll send you further instructions. You may, on request,
+register at our [Sentry](https://sentry.schabi.org) instance (see section "Crash reporting" for more information).
## Translation
-* NewPipe can be translated via [Weblate](https://hosted.weblate.org/projects/newpipe/strings/). You can log in there
+* NewPipe is translated via [Weblate](https://hosted.weblate.org/projects/newpipe/strings/). You can log in there
with your GitHub account.
+* If the language you want to translate is not on Weblate, you can add it: see [How to add a new language](https://github.com/TeamNewPipe/NewPipe/wiki/How-to-add-a-new-language-to-NewPipe) in the wiki.
## Code contribution
-* Stick to NewPipe's style conventions (well, just look the other code and then do it the same way :))
-* Do not bring non-free software (e.g., binary blobs) into the project. Also, make sure you do not introduce Google
+* Stick to NewPipe's style conventions: follow [checkStyle](https://github.com/checkstyle/checkstyle). It will run each time you build the project.
+* Do not bring non-free software (e.g. binary blobs) into the project. Also, make sure you do not introduce Google
libraries.
-* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy)
-* Make changes on a separate branch, not on the master branch. This is commonly known as *feature branch workflow*. You
- may then send your changes as a pull request on GitHub. Patches to the email address mentioned in this document might
- not be considered, GitHub is the primary platform. (This only affects you if you are a member of TeamNewPipe)
+* Stick to [F-Droid contribution guidelines](https://f-droid.org/wiki/page/Inclusion_Policy).
+* Make changes on a separate branch with a meaningful name, not on the master neither dev branch. This is commonly known as *feature branch workflow*. You
+ may then send your changes as a pull request (PR) on GitHub.
* When submitting changes, you confirm that your code is licensed under the terms of the
[GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html).
* Please test (compile and run) your code before you submit changes! Ideally, provide test feedback in the PR
description. Untested code will **not** be merged!
* Try to figure out yourself why builds on our CI fail.
* Make sure your PR is up-to-date with the rest of the code. Often, a simple click on "Update branch" will do the job,
- but if not, you are asked to merge the master branch manually and resolve the problems on your own. That will make the
+ but if not, you are asked to rebase the dev branch manually and resolve the problems on your own. You can find help [on the wiki](https://github.com/TeamNewPipe/NewPipe/wiki/How-to-merge-a-PR). That will make the
maintainers' jobs way easier.
* Please show intention to maintain your features and code after you contributed it. Unmaintained code is a hassle for
the core developers, and just adds work. If you do not intend to maintain features you contributed, please think again
about submission, or clearly state that in the description of your PR.
* Respond yourselves if someone requests changes or otherwise raises issues about your PRs.
-* Check if your contributions align with the [fdroid inclusion guidelines](https://f-droid.org/en/docs/Inclusion_Policy/).
-* Check if your submission can be build with the current fdroid build server setup.
* Send PR that only cover one specific issue/solution/bug. Do not send PRs that are huge and consists of multiple
independent solutions.
## Communication
-* WE DO NOW HAVE A MAILING LIST: [newpipe@list.schabi.org](https://list.schabi.org/cgi-bin/mailman/listinfo/newpipe).
* There is an IRC channel on Freenode which is regularly visited by the core team and other developers:
[#newpipe](irc:irc.freenode.net/newpipe). [Click here for Webchat](https://webchat.freenode.net/?channels=newpipe)!
* If you want to get in touch with the core team or one of our other contributors you can send an email to
- tnp(at)schabi.org. Please do not send issue reports, they will be ignored and remain unanswered! Use the GitHub issue
+ tnp@newpipe.schabi.org. Please do not send issue reports, they will be ignored and remain unanswered! Use the GitHub issue
tracker described above!
-* Feel free to post suggestions, changes, ideas etc. on GitHub, IRC or the mailing list!
+* Feel free to post suggestions, changes, ideas etc. on GitHub or IRC!
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 202e8a71a..dbc1c05a5 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -7,38 +7,40 @@ assignees: ''
---
-### Version
-
--
+To make it easier for us to help you please enter detailed information in the template we have provided below. If a section isn't relevant, just delete it, though it would be helpful to still provide as much detail as possible.
+-->
+
+
+
+### Version
+
+-
### Steps to reproduce the bug
-
-Steps to reproduce the behavior:
+
+
+
### Expected behavior
-Tell us what you expected to happen.
+
### Actual behaviour
-Tell us what happens instead.
+
-### Screenshots/Screen records
-If applicable, add screenshots or a screen recording to help explain your problem. GitHub should support uploading them directly in the issue field. If your file is too big, feel free to paste a link from an image/video hoster here instead.
+### Screenshots/Screen recordings
+
### Logs
-If your bug includes a crash, please head over to the [incredible bugreport to markdown converter](https://teamnewpipe.github.io/CrashReportToMarkdown/). Copy the result. Paste it here:
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 89fe58658..90134a204 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -7,22 +7,33 @@ assignees: ''
---
-#### Is your feature request related to a problem? Please describe it
-A clear and concise description of what the problem is.
-Example: *I want to do X, but there is no way to do it.*
-#### Describe the solution you'd like
-A clear and concise description of what you want to happen.
+
+
+#### Describe the feature you want
+
+
+
+
+#### Is your feature request related to a problem? Please describe it
+
+
+
#### Additional context
-Add any other context or screenshots about the feature request here.
-Example: *Here's a photo of my cat!*
+
+
+
#### How will you/everyone benefit from this feature?
-Convince us! How does it change your NewPipe experience and/or your life?
+
+
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 9a1193767..f12eb2fe8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,10 +1,12 @@
#### What is it?
-- [ ] Bug fix
-- [ ] Feature
+- [ ] Bug fix (user facing)
+- [ ] Feature (user facing)
+- [ ] Code base improvement (dev facing)
+- [ ] Meta improvement to the project (dev facing)
-#### Long description of the changes in your PR
+#### Description of the changes in your PR
- record videos
- create clones
diff --git a/README.md b/README.md
index 987327ab8..50eb40594 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,10 @@
-
+
-
+
diff --git a/app/build.gradle b/app/build.gradle
index 49ac0ccfe..baa11bd20 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,7 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
+apply plugin: 'checkstyle'
android {
compileSdkVersion 28
@@ -12,8 +13,8 @@ android {
resValue "string", "app_name", "NewPipe"
minSdkVersion 19
targetSdkVersion 28
- versionCode 870
- versionName "0.18.7"
+ versionCode 920
+ versionName "0.19.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
@@ -46,6 +47,7 @@ android {
} else {
applicationIdSuffix ".debug." + normalizedWorkingBranch
resValue "string", "app_name", "NewPipe " + workingBranch
+ archivesBaseName = 'NewPipe_' + normalizedWorkingBranch
}
}
}
@@ -81,18 +83,60 @@ ext {
icepickLibVersion = '3.2.0'
stethoLibVersion = '1.5.0'
markwonVersion = '4.2.1'
+ checkstyleVersion = '8.31'
}
+checkstyle {
+ configFile rootProject.file('checkstyle.xml')
+ ignoreFailures false
+ showViolations true
+ toolVersion = "${checkstyleVersion}"
+}
+
+task runCheckstyle(type: Checkstyle) {
+ source 'src'
+ include '**/*.java'
+ exclude '**/gen/**'
+ exclude '**/R.java'
+ exclude '**/BuildConfig.java'
+ exclude 'main/java/us/shandian/giga/**'
+
+ // empty classpath
+ classpath = files()
+
+ showViolations true
+
+ reports {
+ xml.enabled true
+ html.enabled true
+ }
+}
+
+tasks.withType(Checkstyle).each {
+ checkstyleTask -> checkstyleTask.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")
+ }
+ }
+ }
+}
+
+preBuild.dependsOn runCheckstyle
+
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "com.puppycrawl.tools:checkstyle:${checkstyleVersion}"
+
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation "android.arch.persistence.room:testing:1.1.1"
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude module: 'support-annotations'
})
- implementation 'com.github.TeamNewPipe:NewPipeExtractor:647e7cd45'
+ implementation 'com.github.TeamNewPipe:NewPipeExtractor:69e0624e3'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'
@@ -164,4 +208,4 @@ static String getGitWorkingBranch() {
// git was not found
return ""
}
-}
\ No newline at end of file
+}
diff --git a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
index 9ecea9f86..917a83bf2 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
+++ b/app/src/androidTest/java/org/schabi/newpipe/database/AppDatabaseTest.kt
@@ -25,10 +25,14 @@ class AppDatabaseTest {
private const val DEFAULT_DURATION = 480L
private const val DEFAULT_UPLOADER_NAME = "Uploader Test"
private const val DEFAULT_THUMBNAIL = "https://example.com/example.jpg"
+
+ private const val DEFAULT_SECOND_SERVICE_ID = 0
+ private const val DEFAULT_SECOND_URL = "https://www.youtube.com/watch?v=ncQU6iBn5Fc"
}
- @get:Rule val testHelper = MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
- AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory());
+ @get:Rule
+ val testHelper = MigrationTestHelper(InstrumentationRegistry.getInstrumentation(),
+ AppDatabase::class.java.canonicalName, FrameworkSQLiteOpenHelperFactory())
@Test
fun migrateDatabaseFrom2to3() {
@@ -45,17 +49,39 @@ class AppDatabaseTest {
put("uploader", DEFAULT_UPLOADER_NAME)
put("thumbnail_url", DEFAULT_THUMBNAIL)
})
+ insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
+ // put("uid", null)
+ put("service_id", DEFAULT_SECOND_SERVICE_ID)
+ put("url", DEFAULT_SECOND_URL)
+ // put("title", null)
+ // put("stream_type", null)
+ // put("duration", null)
+ // put("uploader", null)
+ // put("thumbnail_url", null)
+ })
+ insert("streams", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
+ // put("uid", null)
+ put("service_id", DEFAULT_SERVICE_ID)
+ // put("url", null)
+ // put("title", null)
+ // put("stream_type", null)
+ // put("duration", null)
+ // put("uploader", null)
+ // put("thumbnail_url", null)
+ })
close()
}
testHelper.runMigrationsAndValidate(AppDatabase.DATABASE_NAME, Migrations.DB_VER_3,
- true, Migrations.MIGRATION_2_3);
+ true, Migrations.MIGRATION_2_3)
val migratedDatabaseV3 = getMigratedDatabase()
val listFromDB = migratedDatabaseV3.streamDAO().all.blockingFirst()
- assertEquals(1, listFromDB.size)
- val streamFromMigratedDatabase = listFromDB.first()
+ // Only expect 2, the one with the null url will be ignored
+ assertEquals(2, listFromDB.size)
+
+ val streamFromMigratedDatabase = listFromDB[0]
assertEquals(DEFAULT_SERVICE_ID, streamFromMigratedDatabase.serviceId)
assertEquals(DEFAULT_URL, streamFromMigratedDatabase.url)
assertEquals(DEFAULT_TITLE, streamFromMigratedDatabase.title)
@@ -67,6 +93,20 @@ class AppDatabaseTest {
assertNull(streamFromMigratedDatabase.textualUploadDate)
assertNull(streamFromMigratedDatabase.uploadDate)
assertNull(streamFromMigratedDatabase.isUploadDateApproximation)
+
+ val secondStreamFromMigratedDatabase = listFromDB[1]
+ assertEquals(DEFAULT_SECOND_SERVICE_ID, secondStreamFromMigratedDatabase.serviceId)
+ assertEquals(DEFAULT_SECOND_URL, secondStreamFromMigratedDatabase.url)
+ assertEquals("", secondStreamFromMigratedDatabase.title)
+ // Should fallback to VIDEO_STREAM
+ assertEquals(StreamType.VIDEO_STREAM, secondStreamFromMigratedDatabase.streamType)
+ assertEquals(0, secondStreamFromMigratedDatabase.duration)
+ assertEquals("", secondStreamFromMigratedDatabase.uploader)
+ assertEquals("", secondStreamFromMigratedDatabase.thumbnailUrl)
+ assertNull(secondStreamFromMigratedDatabase.viewCount)
+ assertNull(secondStreamFromMigratedDatabase.textualUploadDate)
+ assertNull(secondStreamFromMigratedDatabase.uploadDate)
+ assertNull(secondStreamFromMigratedDatabase.isUploadDateApproximation)
}
private fun getMigratedDatabase(): AppDatabase {
diff --git a/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java b/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
index 6e51136c0..ab20d2ff3 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
+++ b/app/src/androidTest/java/org/schabi/newpipe/report/ErrorInfoTest.java
@@ -1,8 +1,9 @@
package org.schabi.newpipe.report;
import android.os.Parcel;
-import androidx.test.filters.LargeTest;
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -12,15 +13,16 @@ import org.schabi.newpipe.report.ErrorActivity.ErrorInfo;
import static org.junit.Assert.assertEquals;
/**
- * Instrumented tests for {@link ErrorInfo}
+ * Instrumented tests for {@link ErrorInfo}.
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ErrorInfoTest {
@Test
- public void errorInfo_testParcelable() {
- ErrorInfo info = ErrorInfo.make(UserAction.USER_REPORT, "youtube", "request", R.string.general_error);
+ public void errorInfoTestParcelable() {
+ ErrorInfo info = ErrorInfo.make(UserAction.USER_REPORT, "youtube", "request",
+ R.string.general_error);
// Obtain a Parcel object and write the parcelable object to it:
Parcel parcel = Parcel.obtain();
info.writeToParcel(parcel, 0);
@@ -34,4 +36,4 @@ public class ErrorInfoTest {
parcel.recycle();
}
-}
\ No newline at end of file
+}
diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.java b/app/src/debug/java/org/schabi/newpipe/DebugApp.java
index 66f73d1e9..4d763aeb1 100644
--- a/app/src/debug/java/org/schabi/newpipe/DebugApp.java
+++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.java
@@ -3,6 +3,7 @@ package org.schabi.newpipe;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+
import androidx.annotation.NonNull;
import androidx.multidex.MultiDex;
@@ -26,7 +27,7 @@ public class DebugApp extends App {
private static final String TAG = DebugApp.class.toString();
@Override
- protected void attachBaseContext(Context base) {
+ protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
diff --git a/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java
new file mode 100644
index 000000000..11f457b6c
--- /dev/null
+++ b/app/src/main/java/androidx/fragment/app/FragmentStatePagerAdapterMenuWorkaround.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.fragment.app;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.viewpager.widget.PagerAdapter;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+// TODO: Replace this deprecated class with its ViewPager2 counterpart
+
+/**
+ * This is a copy from {@link androidx.fragment.app.FragmentStatePagerAdapter}.
+ *
+ * It includes a workaround to fix the menu visibility when the adapter is restored.
+ *
+ *
+ * When restoring the state of this adapter, all the fragments' menu visibility were set to false,
+ * effectively disabling the menu from the user until he switched pages or another event
+ * that triggered the menu to be visible again happened.
+ *
+ *
+ * Check out the changes in:
+ *
+ *
+ *
{@link #saveState()}
+ *
{@link #restoreState(Parcelable, ClassLoader)}
+ *
+ */
+@SuppressWarnings("deprecation")
+public abstract class FragmentStatePagerAdapterMenuWorkaround extends PagerAdapter {
+ private static final String TAG = "FragmentStatePagerAdapt";
+ private static final boolean DEBUG = false;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
+ private @interface Behavior { }
+
+ /**
+ * Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
+ * fragment changes.
+ *
+ * @deprecated This behavior relies on the deprecated
+ * {@link Fragment#setUserVisibleHint(boolean)} API. Use
+ * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
+ * {@link FragmentTransaction#setMaxLifecycle}.
+ * @see #FragmentStatePagerAdapterMenuWorkaround(FragmentManager, int)
+ */
+ @Deprecated
+ public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;
+
+ /**
+ * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
+ * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
+ *
+ * @see #FragmentStatePagerAdapterMenuWorkaround(FragmentManager, int)
+ */
+ public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;
+
+ private final FragmentManager mFragmentManager;
+ private final int mBehavior;
+ private FragmentTransaction mCurTransaction = null;
+
+ private ArrayList mSavedState = new ArrayList();
+ private ArrayList mFragments = new ArrayList();
+ private Fragment mCurrentPrimaryItem = null;
+
+ /**
+ * Constructor for {@link FragmentStatePagerAdapterMenuWorkaround}
+ * that sets the fragment manager for the adapter. This is the equivalent of calling
+ * {@link #FragmentStatePagerAdapterMenuWorkaround(FragmentManager, int)} and passing in
+ * {@link #BEHAVIOR_SET_USER_VISIBLE_HINT}.
+ *
+ *
Fragments will have {@link Fragment#setUserVisibleHint(boolean)} called whenever the
+ * current Fragment changes.
+ *
+ * @param fm fragment manager that will interact with this adapter
+ * @deprecated use {@link #FragmentStatePagerAdapterMenuWorkaround(FragmentManager, int)} with
+ * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT}
+ */
+ @Deprecated
+ public FragmentStatePagerAdapterMenuWorkaround(@NonNull final FragmentManager fm) {
+ this(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
+ }
+
+ /**
+ * Constructor for {@link FragmentStatePagerAdapterMenuWorkaround}.
+ *
+ * If {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} is passed in, then only the current
+ * Fragment is in the {@link Lifecycle.State#RESUMED} state, while all other fragments are
+ * capped at {@link Lifecycle.State#STARTED}. If {@link #BEHAVIOR_SET_USER_VISIBLE_HINT} is
+ * passed, all fragments are in the {@link Lifecycle.State#RESUMED} state and there will be
+ * callbacks to {@link Fragment#setUserVisibleHint(boolean)}.
+ *
+ * @param fm fragment manager that will interact with this adapter
+ * @param behavior determines if only current fragments are in a resumed state
+ */
+ public FragmentStatePagerAdapterMenuWorkaround(@NonNull final FragmentManager fm,
+ @Behavior final int behavior) {
+ mFragmentManager = fm;
+ mBehavior = behavior;
+ }
+
+ /**
+ * @param position the position of the item you want
+ * @return the {@link Fragment} associated with a specified position
+ */
+ @NonNull
+ public abstract Fragment getItem(int position);
+
+ @Override
+ public void startUpdate(@NonNull final ViewGroup container) {
+ if (container.getId() == View.NO_ID) {
+ throw new IllegalStateException("ViewPager with adapter " + this
+ + " requires a view id");
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull final ViewGroup container, final int position) {
+ // If we already have this item instantiated, there is nothing
+ // to do. This can happen when we are restoring the entire pager
+ // from its saved state, where the fragment manager has already
+ // taken care of restoring the fragments we previously had instantiated.
+ if (mFragments.size() > position) {
+ Fragment f = mFragments.get(position);
+ if (f != null) {
+ return f;
+ }
+ }
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ Fragment fragment = getItem(position);
+ if (DEBUG) {
+ Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
+ }
+ if (mSavedState.size() > position) {
+ Fragment.SavedState fss = mSavedState.get(position);
+ if (fss != null) {
+ fragment.setInitialSavedState(fss);
+ }
+ }
+ while (mFragments.size() <= position) {
+ mFragments.add(null);
+ }
+ fragment.setMenuVisibility(false);
+ if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
+ fragment.setUserVisibleHint(false);
+ }
+
+ mFragments.set(position, fragment);
+ mCurTransaction.add(container.getId(), fragment);
+
+ if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
+ mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
+ }
+
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(@NonNull final ViewGroup container, final int position,
+ @NonNull final Object object) {
+ Fragment fragment = (Fragment) object;
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ if (DEBUG) {
+ Log.v(TAG, "Removing item #" + position + ": f=" + object
+ + " v=" + ((Fragment) object).getView());
+ }
+ while (mSavedState.size() <= position) {
+ mSavedState.add(null);
+ }
+ mSavedState.set(position, fragment.isAdded()
+ ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
+ mFragments.set(position, null);
+
+ mCurTransaction.remove(fragment);
+ if (fragment == mCurrentPrimaryItem) {
+ mCurrentPrimaryItem = null;
+ }
+ }
+
+ @Override
+ @SuppressWarnings({"ReferenceEquality", "deprecation"})
+ public void setPrimaryItem(@NonNull final ViewGroup container, final int position,
+ @NonNull final Object object) {
+ Fragment fragment = (Fragment) object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ mCurTransaction.setMaxLifecycle(mCurrentPrimaryItem, Lifecycle.State.STARTED);
+ } else {
+ mCurrentPrimaryItem.setUserVisibleHint(false);
+ }
+ }
+ fragment.setMenuVisibility(true);
+ if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.RESUMED);
+ } else {
+ fragment.setUserVisibleHint(true);
+ }
+
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ @Override
+ public void finishUpdate(@NonNull final ViewGroup container) {
+ if (mCurTransaction != null) {
+ mCurTransaction.commitNowAllowingStateLoss();
+ mCurTransaction = null;
+ }
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull final View view, @NonNull final Object object) {
+ return ((Fragment) object).getView() == view;
+ }
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ private final String selectedFragment = "selected_fragment";
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ @Override
+ @Nullable
+ public Parcelable saveState() {
+ Bundle state = null;
+ if (mSavedState.size() > 0) {
+ state = new Bundle();
+ Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
+ mSavedState.toArray(fss);
+ state.putParcelableArray("states", fss);
+ }
+ for (int i = 0; i < mFragments.size(); i++) {
+ Fragment f = mFragments.get(i);
+ if (f != null && f.isAdded()) {
+ if (state == null) {
+ state = new Bundle();
+ }
+ String key = "f" + i;
+ mFragmentManager.putFragment(state, key, f);
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // Check if it's the same fragment instance
+ if (f == mCurrentPrimaryItem) {
+ state.putString(selectedFragment, key);
+ }
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ }
+ }
+ return state;
+ }
+
+ @Override
+ public void restoreState(@Nullable final Parcelable state, @Nullable final ClassLoader loader) {
+ if (state != null) {
+ Bundle bundle = (Bundle) state;
+ bundle.setClassLoader(loader);
+ Parcelable[] fss = bundle.getParcelableArray("states");
+ mSavedState.clear();
+ mFragments.clear();
+ if (fss != null) {
+ for (int i = 0; i < fss.length; i++) {
+ mSavedState.add((Fragment.SavedState) fss[i]);
+ }
+ }
+ Iterable keys = bundle.keySet();
+ for (String key: keys) {
+ if (key.startsWith("f")) {
+ int index = Integer.parseInt(key.substring(1));
+ Fragment f = mFragmentManager.getFragment(bundle, key);
+ if (f != null) {
+ while (mFragments.size() <= index) {
+ mFragments.add(null);
+ }
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ final boolean wasSelected = bundle.getString(selectedFragment, "")
+ .equals(key);
+ f.setMenuVisibility(wasSelected);
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ mFragments.set(index, f);
+ } else {
+ Log.w(TAG, "Bad fragment at key " + key);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
index 4a2662f53..78da9678b 100644
--- a/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
+++ b/app/src/main/java/com/google/android/material/appbar/FlingBehavior.java
@@ -10,15 +10,15 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout;
import java.lang.reflect.Field;
-// check this https://stackoverflow.com/questions/56849221/recyclerview-fling-causes-laggy-while-appbarlayout-is-scrolling/57997489#57997489
+// See https://stackoverflow.com/questions/56849221#57997489
public final class FlingBehavior extends AppBarLayout.Behavior {
-
- public FlingBehavior(Context context, AttributeSet attrs) {
+ public FlingBehavior(final Context context, final AttributeSet attrs) {
super(context, attrs);
}
@Override
- public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
+ public boolean onInterceptTouchEvent(final CoordinatorLayout parent, final AppBarLayout child,
+ final MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// remove reference to old nested scrolling child
@@ -35,7 +35,8 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
@Nullable
private OverScroller getScrollerField() {
try {
- Class> headerBehaviorType = this.getClass().getSuperclass().getSuperclass().getSuperclass();
+ Class> headerBehaviorType = this.getClass()
+ .getSuperclass().getSuperclass().getSuperclass();
if (headerBehaviorType != null) {
Field field = headerBehaviorType.getDeclaredField("scroller");
field.setAccessible(true);
@@ -62,12 +63,14 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
return null;
}
- private void resetNestedScrollingChild(){
+ private void resetNestedScrollingChild() {
Field field = getLastNestedScrollingChildRefField();
- if(field != null){
+ if (field != null) {
try {
Object value = field.get(this);
- if(value != null) field.set(this, null);
+ if (value != null) {
+ field.set(this, null);
+ }
} catch (IllegalAccessException e) {
// ?
}
@@ -76,7 +79,8 @@ public final class FlingBehavior extends AppBarLayout.Behavior {
private void stopAppBarLayoutFling() {
OverScroller scroller = getScrollerField();
- if (scroller != null) scroller.forceFinished(true);
+ if (scroller != null) {
+ scroller.forceFinished(true);
+ }
}
-
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java
index da601a42f..9321b3071 100644
--- a/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java
+++ b/app/src/main/java/org/schabi/newpipe/ActivityCommunicator.java
@@ -23,17 +23,25 @@ package org.schabi.newpipe;
/**
* Singleton:
* Used to send data between certain Activity/Services within the same process.
- * This can be considered as an ugly hack inside the Android universe. **/
+ * This can be considered as an ugly hack inside the Android universe.
+ **/
public class ActivityCommunicator {
private static ActivityCommunicator activityCommunicator;
+ private volatile Class returnActivity;
public static ActivityCommunicator getCommunicator() {
- if(activityCommunicator == null) {
+ if (activityCommunicator == null) {
activityCommunicator = new ActivityCommunicator();
}
return activityCommunicator;
}
- public volatile Class returnActivity;
+ public Class getReturnActivity() {
+ return returnActivity;
+ }
+
+ public void setReturnActivity(final Class returnActivity) {
+ this.returnActivity = returnActivity;
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java
index dae143b6c..f9b3abfb1 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -66,15 +66,24 @@ import io.reactivex.plugins.RxJavaPlugins;
public class App extends Application {
protected static final String TAG = App.class.toString();
- private RefWatcher refWatcher;
- private static App app;
-
@SuppressWarnings("unchecked")
private static final Class extends ReportSenderFactory>[]
- reportSenderFactoryClasses = new Class[]{AcraReportSenderFactory.class};
+ REPORT_SENDER_FACTORY_CLASSES = new Class[]{AcraReportSenderFactory.class};
+ 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() {
+ return app;
+ }
@Override
- protected void attachBaseContext(Context base) {
+ protected void attachBaseContext(final Context base) {
super.attachBaseContext(base);
initACRA();
@@ -123,24 +132,30 @@ public class App extends Application {
// https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0#error-handling
RxJavaPlugins.setErrorHandler(new Consumer() {
@Override
- public void accept(@NonNull Throwable throwable) {
- Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : " +
- "throwable = [" + throwable.getClass().getName() + "]");
+ public void accept(@NonNull final Throwable throwable) {
+ Log.e(TAG, "RxJavaPlugins.ErrorHandler called with -> : "
+ + "throwable = [" + throwable.getClass().getName() + "]");
+ final Throwable actualThrowable;
if (throwable instanceof UndeliverableException) {
- // As UndeliverableException is a wrapper, get the cause of it to get the "real" exception
- throwable = throwable.getCause();
+ // As UndeliverableException is a wrapper,
+ // get the cause of it to get the "real" exception
+ actualThrowable = throwable.getCause();
+ } else {
+ actualThrowable = throwable;
}
final List errors;
- if (throwable instanceof CompositeException) {
- errors = ((CompositeException) throwable).getExceptions();
+ if (actualThrowable instanceof CompositeException) {
+ errors = ((CompositeException) actualThrowable).getExceptions();
} else {
- errors = Collections.singletonList(throwable);
+ errors = Collections.singletonList(actualThrowable);
}
for (final Throwable error : errors) {
- if (isThrowableIgnored(error)) return;
+ if (isThrowableIgnored(error)) {
+ return;
+ }
if (isThrowableCritical(error)) {
reportException(error);
return;
@@ -150,17 +165,19 @@ public class App extends Application {
// Out-of-lifecycle exceptions should only be reported if a debug user wishes so,
// When exception is not reported, log it
if (isDisposedRxExceptionsReported()) {
- reportException(throwable);
+ reportException(actualThrowable);
} else {
- Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", throwable);
+ Log.e(TAG, "RxJavaPlugin: Undeliverable Exception received: ", actualThrowable);
}
}
private boolean isThrowableIgnored(@NonNull final Throwable throwable) {
// Don't crash the application over a simple network problem
return ExtractorHelper.hasAssignableCauseThrowable(throwable,
- IOException.class, SocketException.class, // network api cancellation
- InterruptedException.class, InterruptedIOException.class); // blocking code disposed
+ // network api cancellation
+ IOException.class, SocketException.class,
+ // blocking code disposed
+ InterruptedException.class, InterruptedIOException.class);
}
private boolean isThrowableCritical(@NonNull final Throwable throwable) {
@@ -191,7 +208,7 @@ public class App extends Application {
private void initACRA() {
try {
final ACRAConfiguration acraConfig = new ConfigurationBuilder(this)
- .setReportSenderFactoryClasses(reportSenderFactoryClasses)
+ .setReportSenderFactoryClasses(REPORT_SENDER_FACTORY_CLASSES)
.setBuildConfigClass(BuildConfig.class)
.build();
ACRA.init(this, acraConfig);
@@ -202,7 +219,7 @@ public class App extends Application {
null,
null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
- "Could not initialize ACRA crash report", R.string.app_ui_crash));
+ "Could not initialize ACRA crash report", R.string.app_ui_crash));
}
}
@@ -230,11 +247,11 @@ public class App extends Application {
/**
* Set up notification channel for app update.
+ *
* @param importance
*/
@TargetApi(Build.VERSION_CODES.O)
- private void setUpUpdateNotificationChannel(int importance) {
-
+ private void setUpUpdateNotificationChannel(final int importance) {
final String appUpdateId
= getString(R.string.app_update_notification_channel_id);
final CharSequence appUpdateName
@@ -251,12 +268,6 @@ public class App extends Application {
appUpdateNotificationManager.createNotificationChannel(appUpdateChannel);
}
- @Nullable
- public static RefWatcher getRefWatcher(Context context) {
- final App application = (App) context.getApplicationContext();
- return application.refWatcher;
- }
-
protected RefWatcher installLeakCanary() {
return RefWatcher.DISABLED;
}
@@ -264,8 +275,4 @@ public class App extends Application {
protected boolean isDisposedRxExceptionsReported() {
return false;
}
-
- public static App getApp() {
- return app;
- }
}
diff --git a/app/src/main/java/org/schabi/newpipe/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/BaseFragment.java
index d4795cde2..9a86fd5ad 100644
--- a/app/src/main/java/org/schabi/newpipe/BaseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/BaseFragment.java
@@ -2,13 +2,14 @@ package org.schabi.newpipe;
import android.content.Context;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+
import com.nostra13.universalimageloader.core.ImageLoader;
import com.squareup.leakcanary.RefWatcher;
@@ -16,18 +17,16 @@ import icepick.Icepick;
import icepick.State;
public abstract class BaseFragment extends Fragment {
+ public static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance();
protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode());
protected final boolean DEBUG = MainActivity.DEBUG;
-
protected AppCompatActivity activity;
- public static final ImageLoader imageLoader = ImageLoader.getInstance();
-
- //These values are used for controlling framgents when they are part of the frontpage
+ //These values are used for controlling fragments when they are part of the frontpage
@State
protected boolean useAsFrontPage = false;
- protected boolean mIsVisibleToUser = false;
+ private boolean mIsVisibleToUser = false;
- public void useAsFrontPage(boolean value) {
+ public void useAsFrontPage(final boolean value) {
useAsFrontPage = value;
}
@@ -36,7 +35,7 @@ public abstract class BaseFragment extends Fragment {
//////////////////////////////////////////////////////////////////////////*/
@Override
- public void onAttach(Context context) {
+ public void onAttach(final Context context) {
super.onAttach(context);
activity = (AppCompatActivity) context;
}
@@ -48,43 +47,51 @@ public abstract class BaseFragment extends Fragment {
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
+ public void onCreate(final Bundle savedInstanceState) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreate() called with: "
+ + "savedInstanceState = [" + savedInstanceState + "]");
+ }
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
- if (savedInstanceState != null) onRestoreInstanceState(savedInstanceState);
+ if (savedInstanceState != null) {
+ onRestoreInstanceState(savedInstanceState);
+ }
}
@Override
- public void onViewCreated(View rootView, Bundle savedInstanceState) {
+ public void onViewCreated(final View rootView, final Bundle savedInstanceState) {
super.onViewCreated(rootView, savedInstanceState);
if (DEBUG) {
- Log.d(TAG, "onViewCreated() called with: rootView = [" + rootView + "], savedInstanceState = [" + savedInstanceState + "]");
+ Log.d(TAG, "onViewCreated() called with: "
+ + "rootView = [" + rootView + "], "
+ + "savedInstanceState = [" + savedInstanceState + "]");
}
initViews(rootView, savedInstanceState);
initListeners();
}
@Override
- public void onSaveInstanceState(Bundle outState) {
+ public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
- protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
- }
+ protected void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) { }
@Override
public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = App.getRefWatcher(getActivity());
- if (refWatcher != null) refWatcher.watch(this);
+ if (refWatcher != null) {
+ refWatcher.watch(this);
+ }
}
@Override
- public void setUserVisibleHint(boolean isVisibleToUser) {
+ public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
mIsVisibleToUser = isVisibleToUser;
}
@@ -93,20 +100,20 @@ public abstract class BaseFragment extends Fragment {
// Init
//////////////////////////////////////////////////////////////////////////*/
- protected void initViews(View rootView, Bundle savedInstanceState) {
- }
+ protected void initViews(final View rootView, final Bundle savedInstanceState) { }
- protected void initListeners() {
- }
+ protected void initListeners() { }
/*//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////*/
- public void setTitle(String title) {
- if (DEBUG) Log.d(TAG, "setTitle() called with: title = [" + title + "]");
- if((!useAsFrontPage || mIsVisibleToUser)
- && (activity != null && activity.getSupportActionBar() != null)) {
+ public void setTitle(final String title) {
+ if (DEBUG) {
+ Log.d(TAG, "setTitle() called with: title = [" + title + "]");
+ }
+ if ((!useAsFrontPage || mIsVisibleToUser)
+ && (activity != null && activity.getSupportActionBar() != null)) {
activity.getSupportActionBar().setDisplayShowTitleEnabled(true);
activity.getSupportActionBar().setTitle(title);
}
diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
index 22f7bc558..625f514e9 100644
--- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
+++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersionTask.java
@@ -12,12 +12,16 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
-import androidx.core.app.NotificationCompat;
-import androidx.core.app.NotificationManagerCompat;
import android.util.Log;
-import org.json.JSONException;
-import org.json.JSONObject;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import com.grack.nanojson.JsonObject;
+import com.grack.nanojson.JsonParser;
+import com.grack.nanojson.JsonParserException;
+
+import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@@ -30,11 +34,6 @@ import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-import java.util.concurrent.TimeUnit;
-
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.Response;
/**
* AsyncTask to check if there is a newer version of the NewPipe github apk available or not.
@@ -42,149 +41,44 @@ import okhttp3.Response;
* the notification, the user will be directed to the download link.
*/
public class CheckForNewAppVersionTask extends AsyncTask {
-
private static final boolean DEBUG = MainActivity.DEBUG;
private static final String TAG = CheckForNewAppVersionTask.class.getSimpleName();
- private static final Application app = App.getApp();
- private static final String GITHUB_APK_SHA1 = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
- private static final String newPipeApiUrl = "https://newpipe.schabi.org/api/data.json";
- private static final int timeoutPeriod = 30;
- private SharedPreferences mPrefs;
- private OkHttpClient client;
-
- @Override
- protected void onPreExecute() {
-
- mPrefs = PreferenceManager.getDefaultSharedPreferences(app);
-
- // Check if user has enabled/ disabled update checking
- // and if the current apk is a github one or not.
- if (!mPrefs.getBoolean(app.getString(R.string.update_app_key), true)
- || !isGithubApk()) {
- this.cancel(true);
- }
- }
-
- @Override
- protected String doInBackground(Void... voids) {
-
- if(isCancelled() || !isConnected()) return null;
-
- // Make a network request to get latest NewPipe data.
- if (client == null) {
-
- client = new OkHttpClient
- .Builder()
- .readTimeout(timeoutPeriod, TimeUnit.SECONDS)
- .build();
- }
-
- Request request = new Request.Builder()
- .url(newPipeApiUrl)
- .build();
-
- try {
- Response response = client.newCall(request).execute();
- return response.body().string();
- } catch (IOException ex) {
- // connectivity problems, do not alarm user and fail silently
- if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
- }
-
- return null;
- }
-
- @Override
- protected void onPostExecute(String response) {
-
- // Parse the json from the response.
- if (response != null) {
-
- try {
- JSONObject mainObject = new JSONObject(response);
- JSONObject flavoursObject = mainObject.getJSONObject("flavors");
- JSONObject githubObject = flavoursObject.getJSONObject("github");
- JSONObject githubStableObject = githubObject.getJSONObject("stable");
-
- String versionName = githubStableObject.getString("version");
- String versionCode = githubStableObject.getString("version_code");
- String apkLocationUrl = githubStableObject.getString("apk");
-
- compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
-
- } catch (JSONException ex) {
- // connectivity problems, do not alarm user and fail silently
- if (DEBUG) Log.w(TAG, Log.getStackTraceString(ex));
- }
- }
- }
+ private static final Application APP = App.getApp();
+ private static final String GITHUB_APK_SHA1
+ = "B0:2E:90:7C:1C:D6:FC:57:C3:35:F0:88:D0:8F:50:5F:94:E4:D2:15";
+ private static final String NEWPIPE_API_URL = "https://newpipe.schabi.org/api/data.json";
/**
- * Method to compare the current and latest available app version.
- * If a newer version is available, we show the update notification.
- * @param versionName
- * @param apkLocationUrl
- */
- private void compareAppVersionAndShowNotification(String versionName,
- String apkLocationUrl,
- String versionCode) {
-
- int NOTIFICATION_ID = 2000;
-
- if (BuildConfig.VERSION_CODE < Integer.valueOf(versionCode)) {
-
- // A pending intent to open the apk location url in the browser.
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
- PendingIntent pendingIntent
- = PendingIntent.getActivity(app, 0, intent, 0);
-
- NotificationCompat.Builder notificationBuilder = new NotificationCompat
- .Builder(app, app.getString(R.string.app_update_notification_channel_id))
- .setSmallIcon(R.drawable.ic_newpipe_update)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setContentIntent(pendingIntent)
- .setAutoCancel(true)
- .setContentTitle(app.getString(R.string.app_update_notification_content_title))
- .setContentText(app.getString(R.string.app_update_notification_content_text)
- + " " + versionName);
-
- NotificationManagerCompat notificationManager = NotificationManagerCompat.from(app);
- notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
- }
- }
-
- /**
- * Method to get the apk's SHA1 key.
- * https://stackoverflow.com/questions/9293019/get-certificate-fingerprint-from-android-app#22506133
+ * Method to get the apk's SHA1 key. See https://stackoverflow.com/questions/9293019/#22506133.
+ *
+ * @return String with the apk's SHA1 fingeprint in hexadecimal
*/
private static String getCertificateSHA1Fingerprint() {
-
- PackageManager pm = app.getPackageManager();
- String packageName = app.getPackageName();
- int flags = PackageManager.GET_SIGNATURES;
+ final PackageManager pm = APP.getPackageManager();
+ final String packageName = APP.getPackageName();
+ final int flags = PackageManager.GET_SIGNATURES;
PackageInfo packageInfo = null;
try {
packageInfo = pm.getPackageInfo(packageName, flags);
- } catch (PackageManager.NameNotFoundException ex) {
- ErrorActivity.reportError(app, ex, null, null,
+ } catch (PackageManager.NameNotFoundException e) {
+ ErrorActivity.reportError(APP, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not find package info", R.string.app_ui_crash));
}
- Signature[] signatures = packageInfo.signatures;
- byte[] cert = signatures[0].toByteArray();
- InputStream input = new ByteArrayInputStream(cert);
+ final Signature[] signatures = packageInfo.signatures;
+ final byte[] cert = signatures[0].toByteArray();
+ final InputStream input = new ByteArrayInputStream(cert);
- CertificateFactory cf = null;
X509Certificate c = null;
try {
- cf = CertificateFactory.getInstance("X509");
+ final CertificateFactory cf = CertificateFactory.getInstance("X509");
c = (X509Certificate) cf.generateCertificate(input);
- } catch (CertificateException ex) {
- ErrorActivity.reportError(app, ex, null, null,
+ } catch (CertificateException e) {
+ ErrorActivity.reportError(APP, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Certificate error", R.string.app_ui_crash));
}
@@ -193,14 +87,10 @@ public class CheckForNewAppVersionTask extends AsyncTask {
try {
MessageDigest md = MessageDigest.getInstance("SHA1");
- byte[] publicKey = md.digest(c.getEncoded());
+ final byte[] publicKey = md.digest(c.getEncoded());
hexString = byte2HexFormatted(publicKey);
- } catch (NoSuchAlgorithmException ex1) {
- ErrorActivity.reportError(app, ex1, null, null,
- ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
- "Could not retrieve SHA1 key", R.string.app_ui_crash));
- } catch (CertificateEncodingException ex2) {
- ErrorActivity.reportError(app, ex2, null, null,
+ } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+ ErrorActivity.reportError(APP, e, null, null,
ErrorActivity.ErrorInfo.make(UserAction.SOMETHING_ELSE, "none",
"Could not retrieve SHA1 key", R.string.app_ui_crash));
}
@@ -208,31 +98,124 @@ public class CheckForNewAppVersionTask extends AsyncTask {
return hexString;
}
- private static String byte2HexFormatted(byte[] arr) {
-
- StringBuilder str = new StringBuilder(arr.length * 2);
+ private static String byte2HexFormatted(final byte[] arr) {
+ final StringBuilder str = new StringBuilder(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
String h = Integer.toHexString(arr[i]);
- int l = h.length();
- if (l == 1) h = "0" + h;
- if (l > 2) h = h.substring(l - 2, l);
+ final int l = h.length();
+ if (l == 1) {
+ h = "0" + h;
+ }
+ if (l > 2) {
+ h = h.substring(l - 2, l);
+ }
str.append(h.toUpperCase());
- if (i < (arr.length - 1)) str.append(':');
+ if (i < (arr.length - 1)) {
+ str.append(':');
+ }
}
return str.toString();
}
public static boolean isGithubApk() {
-
return getCertificateSHA1Fingerprint().equals(GITHUB_APK_SHA1);
}
-
+
+ @Override
+ protected void onPreExecute() {
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(APP);
+
+ // Check if user has enabled/disabled update checking
+ // and if the current apk is a github one or not.
+ if (!prefs.getBoolean(APP.getString(R.string.update_app_key), true) || !isGithubApk()) {
+ this.cancel(true);
+ }
+ }
+
+ @Override
+ protected String doInBackground(final Void... voids) {
+ if (isCancelled() || !isConnected()) {
+ return null;
+ }
+
+ // Make a network request to get latest NewPipe data.
+ try {
+ return DownloaderImpl.getInstance().get(NEWPIPE_API_URL).responseBody();
+ } catch (IOException | ReCaptchaException e) {
+ // connectivity problems, do not alarm user and fail silently
+ if (DEBUG) {
+ Log.w(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(final String response) {
+ // Parse the json from the response.
+ if (response != null) {
+
+ try {
+ final JsonObject githubStableObject = JsonParser.object().from(response)
+ .getObject("flavors").getObject("github").getObject("stable");
+
+ final String versionName = githubStableObject.getString("version");
+ final int versionCode = githubStableObject.getInt("version_code");
+ final String apkLocationUrl = githubStableObject.getString("apk");
+
+ compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode);
+
+ } catch (JsonParserException e) {
+ // connectivity problems, do not alarm user and fail silently
+ if (DEBUG) {
+ Log.w(TAG, Log.getStackTraceString(e));
+ }
+ }
+ }
+ }
+
+ /**
+ * Method to compare the current and latest available app version.
+ * If a newer version is available, we show the update notification.
+ *
+ * @param versionName Name of new version
+ * @param apkLocationUrl Url with the new apk
+ * @param versionCode Code of new version
+ */
+ private void compareAppVersionAndShowNotification(final String versionName,
+ final String apkLocationUrl,
+ final int versionCode) {
+ int notificationId = 2000;
+
+ if (BuildConfig.VERSION_CODE < versionCode) {
+
+ // A pending intent to open the apk location url in the browser.
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(apkLocationUrl));
+ final PendingIntent pendingIntent
+ = PendingIntent.getActivity(APP, 0, intent, 0);
+
+ final NotificationCompat.Builder notificationBuilder = new NotificationCompat
+ .Builder(APP, APP.getString(R.string.app_update_notification_channel_id))
+ .setSmallIcon(R.drawable.ic_newpipe_update)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setContentIntent(pendingIntent)
+ .setAutoCancel(true)
+ .setContentTitle(APP.getString(R.string.app_update_notification_content_title))
+ .setContentText(APP.getString(R.string.app_update_notification_content_text)
+ + " " + versionName);
+
+ final NotificationManagerCompat notificationManager
+ = NotificationManagerCompat.from(APP);
+ notificationManager.notify(notificationId, notificationBuilder.build());
+ }
+ }
+
private boolean isConnected() {
-
- ConnectivityManager cm =
- (ConnectivityManager) app.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final ConnectivityManager cm =
+ (ConnectivityManager) APP.getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().isConnected();
+ && cm.getActiveNetworkInfo().isConnected();
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
index 8c551d2a7..ed517f160 100644
--- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
@@ -3,6 +3,9 @@ package org.schabi.newpipe;
import android.os.Build;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Request;
import org.schabi.newpipe.extractor.downloader.Response;
@@ -26,9 +29,6 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
@@ -37,20 +37,22 @@ import okhttp3.ResponseBody;
import static org.schabi.newpipe.MainActivity.DEBUG;
-public class DownloaderImpl extends Downloader {
- public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0";
+public final class DownloaderImpl extends Downloader {
+ public static final String USER_AGENT
+ = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:68.0) Gecko/20100101 Firefox/68.0";
private static DownloaderImpl instance;
private String mCookies;
private OkHttpClient client;
- private DownloaderImpl(OkHttpClient.Builder builder) {
+ private DownloaderImpl(final OkHttpClient.Builder builder) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
enableModernTLS(builder);
}
this.client = builder
.readTimeout(30, TimeUnit.SECONDS)
- //.cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"), 16 * 1024 * 1024))
+// .cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"),
+// 16 * 1024 * 1024))
.build();
}
@@ -58,20 +60,72 @@ public class DownloaderImpl extends Downloader {
* It's recommended to call exactly once in the entire lifetime of the application.
*
* @param builder if null, default builder will be used
+ * @return a new instance of {@link DownloaderImpl}
*/
- public static DownloaderImpl init(@Nullable OkHttpClient.Builder builder) {
- return instance = new DownloaderImpl(builder != null ? builder : new OkHttpClient.Builder());
+ public static DownloaderImpl init(@Nullable final OkHttpClient.Builder builder) {
+ instance = new DownloaderImpl(
+ builder != null ? builder : new OkHttpClient.Builder());
+ return instance;
}
public static DownloaderImpl getInstance() {
return instance;
}
+ /**
+ * Enable TLS 1.2 and 1.1 on Android Kitkat. This function is mostly taken
+ * from the documentation of OkHttpClient.Builder.sslSocketFactory(_,_).
+ *
+ * If there is an error, the function will safely fall back to doing nothing
+ * and printing the error to the console.
+ *
+ *
+ * @param builder The HTTPClient Builder on which TLS is enabled on (will be modified in-place)
+ */
+ private static void enableModernTLS(final OkHttpClient.Builder builder) {
+ try {
+ // get the default TrustManager
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
+ TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init((KeyStore) null);
+ TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+ if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
+ throw new IllegalStateException("Unexpected default trust managers:"
+ + Arrays.toString(trustManagers));
+ }
+ X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
+
+ // insert our own TLSSocketFactory
+ SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance();
+
+ builder.sslSocketFactory(sslSocketFactory, trustManager);
+
+ // This will try to enable all modern CipherSuites(+2 more)
+ // that are supported on the device.
+ // Necessary because some servers (e.g. Framatube.org)
+ // don't support the old cipher suites.
+ // https://github.com/square/okhttp/issues/4053#issuecomment-402579554
+ List cipherSuites = new ArrayList<>();
+ cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
+ cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
+ ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
+ .build();
+
+ builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT));
+ } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
+ if (DEBUG) {
+ e.printStackTrace();
+ }
+ }
+ }
+
public String getCookies() {
return mCookies;
}
- public void setCookies(String cookies) {
+ public void setCookies(final String cookies) {
mCookies = cookies;
}
@@ -81,7 +135,7 @@ public class DownloaderImpl extends Downloader {
* @param url an url pointing to the content
* @return the size of the content, in bytes
*/
- public long getContentLength(String url) throws IOException {
+ public long getContentLength(final String url) throws IOException {
try {
final Response response = head(url);
return Long.parseLong(response.getHeader("Content-Length"));
@@ -92,7 +146,7 @@ public class DownloaderImpl extends Downloader {
}
}
- public InputStream stream(String siteUrl) throws IOException {
+ public InputStream stream(final String siteUrl) throws IOException {
try {
final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
.method("GET", null).url(siteUrl)
@@ -122,7 +176,8 @@ public class DownloaderImpl extends Downloader {
}
@Override
- public Response execute(@NonNull Request request) throws IOException, ReCaptchaException {
+ public Response execute(@NonNull final Request request)
+ throws IOException, ReCaptchaException {
final String httpMethod = request.httpMethod();
final String url = request.url();
final Map> headers = request.headers();
@@ -172,49 +227,7 @@ public class DownloaderImpl extends Downloader {
}
final String latestUrl = response.request().url().toString();
- return new Response(response.code(), response.message(), response.headers().toMultimap(), responseBodyToReturn, latestUrl);
- }
-
- /**
- * Enable TLS 1.2 and 1.1 on Android Kitkat. This function is mostly taken from the documentation of
- * OkHttpClient.Builder.sslSocketFactory(_,_)
- *
- * If there is an error, the function will safely fall back to doing nothing and printing the error to the console.
- *
- * @param builder The HTTPClient Builder on which TLS is enabled on (will be modified in-place)
- */
- private static void enableModernTLS(OkHttpClient.Builder builder) {
- try {
- // get the default TrustManager
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
- TrustManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init((KeyStore) null);
- TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
- if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
- throw new IllegalStateException("Unexpected default trust managers:"
- + Arrays.toString(trustManagers));
- }
- X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
-
- // insert our own TLSSocketFactory
- SSLSocketFactory sslSocketFactory = TLSSocketFactoryCompat.getInstance();
-
- builder.sslSocketFactory(sslSocketFactory, trustManager);
-
- // This will try to enable all modern CipherSuites(+2 more) that are supported on the device.
- // Necessary because some servers (e.g. Framatube.org) don't support the old cipher suites.
- // https://github.com/square/okhttp/issues/4053#issuecomment-402579554
- List cipherSuites = new ArrayList<>();
- cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
- cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
- cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
- ConnectionSpec legacyTLS = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
- .cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
- .build();
-
- builder.connectionSpecs(Arrays.asList(legacyTLS, ConnectionSpec.CLEARTEXT));
- } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
- if (DEBUG) e.printStackTrace();
- }
+ return new Response(response.code(), response.message(), response.headers().toMultimap(),
+ responseBodyToReturn, latestUrl);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/ExitActivity.java b/app/src/main/java/org/schabi/newpipe/ExitActivity.java
index 1ea3abe34..94eff9560 100644
--- a/app/src/main/java/org/schabi/newpipe/ExitActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/ExitActivity.java
@@ -1,4 +1,3 @@
-
package org.schabi.newpipe;
import android.annotation.SuppressLint;
@@ -27,9 +26,20 @@ import android.os.Bundle;
public class ExitActivity extends Activity {
+ public static void exitAndRemoveFromRecentApps(final Activity activity) {
+ Intent intent = new Intent(activity, ExitActivity.class);
+
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NO_ANIMATION);
+
+ activity.startActivity(intent);
+ }
+
@SuppressLint("NewApi")
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 21) {
@@ -40,15 +50,4 @@ public class ExitActivity extends Activity {
System.exit(0);
}
-
- public static void exitAndRemoveFromRecentApps(Activity activity) {
- Intent intent = new Intent(activity, ExitActivity.class);
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NO_ANIMATION);
-
- activity.startActivity(intent);
- }
}
diff --git a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
index dfb7d3276..ca61c9655 100644
--- a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
+++ b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java
@@ -18,7 +18,7 @@ public class ImageDownloader extends BaseImageDownloader {
private final SharedPreferences preferences;
private final String downloadThumbnailKey;
- public ImageDownloader(Context context) {
+ public ImageDownloader(final Context context) {
super(context);
this.resources = context.getResources();
this.preferences = PreferenceManager.getDefaultSharedPreferences(context);
@@ -31,7 +31,7 @@ public class ImageDownloader extends BaseImageDownloader {
@SuppressLint("ResourceType")
@Override
- public InputStream getStream(String imageUri, Object extra) throws IOException {
+ public InputStream getStream(final String imageUri, final Object extra) throws IOException {
if (isDownloadingThumbnail()) {
return super.getStream(imageUri, extra);
} else {
@@ -39,7 +39,8 @@ public class ImageDownloader extends BaseImageDownloader {
}
}
- protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
+ protected InputStream getStreamFromNetwork(final String imageUri, final Object extra)
+ throws IOException {
final DownloaderImpl downloader = (DownloaderImpl) NewPipe.getDownloader();
return downloader.stream(imageUri);
}
diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java
index 1d7a930ba..c004eae4a 100644
--- a/app/src/main/java/org/schabi/newpipe/MainActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java
@@ -83,20 +83,21 @@ public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release");
- private ActionBarDrawerToggle toggle = null;
- private DrawerLayout drawer = null;
- private NavigationView drawerItems = null;
- private TextView headerServiceView = null;
- private Button toggleServiceButton = null;
+ private ActionBarDrawerToggle toggle;
+ private DrawerLayout drawer;
+ private NavigationView drawerItems;
+ private ImageView headerServiceIcon;
+ private TextView headerServiceView;
+ private Button toggleServiceButton;
private boolean servicesShown = false;
private ImageView serviceArrow;
- private static final int ITEM_ID_SUBSCRIPTIONS = - 1;
- private static final int ITEM_ID_FEED = - 2;
- private static final int ITEM_ID_BOOKMARKS = - 3;
- private static final int ITEM_ID_DOWNLOADS = - 4;
- private static final int ITEM_ID_HISTORY = - 5;
+ private static final int ITEM_ID_SUBSCRIPTIONS = -1;
+ private static final int ITEM_ID_FEED = -2;
+ private static final int ITEM_ID_BOOKMARKS = -3;
+ private static final int ITEM_ID_DOWNLOADS = -4;
+ private static final int ITEM_ID_HISTORY = -5;
private static final int ITEM_ID_SETTINGS = 0;
private static final int ITEM_ID_ABOUT = 1;
@@ -107,8 +108,11 @@ public class MainActivity extends AppCompatActivity {
//////////////////////////////////////////////////////////////////////////*/
@Override
- protected void onCreate(Bundle savedInstanceState) {
- if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");
+ protected void onCreate(final Bundle savedInstanceState) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreate() called with: "
+ + "savedInstanceState = [" + savedInstanceState + "]");
+ }
// enable TLS1.1/1.2 for kitkat devices, to fix download and play for mediaCCC sources
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
@@ -122,10 +126,12 @@ public class MainActivity extends AppCompatActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window w = getWindow();
- w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ w.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
+ WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
- if (getSupportFragmentManager() != null && getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ if (getSupportFragmentManager() != null
+ && getSupportFragmentManager().getBackStackEntryCount() == 0) {
initFragments();
}
@@ -150,13 +156,15 @@ public class MainActivity extends AppCompatActivity {
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
- .add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator.getTranslatedKioskName(ks, this))
+ .add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator
+ .getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
- kioskId ++;
+ kioskId++;
}
drawerItems.getMenu()
- .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions)
+ .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER,
+ R.string.tab_subscriptions)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.ic_channel));
drawerItems.getMenu()
.add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title)
@@ -179,20 +187,21 @@ public class MainActivity extends AppCompatActivity {
.add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about)
.setIcon(ThemeHelper.resolveResourceIdFromAttr(this, R.attr.info));
- toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close);
+ toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open,
+ R.string.drawer_close);
toggle.syncState();
drawer.addDrawerListener(toggle);
drawer.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
private int lastService;
@Override
- public void onDrawerOpened(View drawerView) {
+ public void onDrawerOpened(final View drawerView) {
lastService = ServiceHelper.getSelectedServiceId(MainActivity.this);
}
@Override
- public void onDrawerClosed(View drawerView) {
- if(servicesShown) {
+ public void onDrawerClosed(final View drawerView) {
+ if (servicesShown) {
toggleServices();
}
if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) {
@@ -205,7 +214,7 @@ public class MainActivity extends AppCompatActivity {
setupDrawerHeader();
}
- private boolean drawerItemSelected(MenuItem item) {
+ private boolean drawerItemSelected(final MenuItem item) {
switch (item.getGroupId()) {
case R.id.menu_services_group:
changeService(item);
@@ -228,14 +237,16 @@ public class MainActivity extends AppCompatActivity {
return true;
}
- private void changeService(MenuItem item) {
- drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(false);
+ private void changeService(final MenuItem item) {
+ drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this))
+ .setChecked(false);
ServiceHelper.setSelectedServiceId(this, item.getItemId());
- drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
+ drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this))
+ .setChecked(true);
}
- private void tabSelected(MenuItem item) throws ExtractionException {
- switch(item.getItemId()) {
+ private void tabSelected(final MenuItem item) throws ExtractionException {
+ switch (item.getItemId()) {
case ITEM_ID_SUBSCRIPTIONS:
NavigationHelper.openSubscriptionFragment(getSupportFragmentManager());
break;
@@ -258,19 +269,20 @@ public class MainActivity extends AppCompatActivity {
int kioskId = 0;
for (final String ks : service.getKioskList().getAvailableKiosks()) {
- if(kioskId == item.getItemId()) {
+ if (kioskId == item.getItemId()) {
serviceName = ks;
}
- kioskId ++;
+ kioskId++;
}
- NavigationHelper.openKioskFragment(getSupportFragmentManager(), currentServiceId, serviceName);
+ NavigationHelper.openKioskFragment(getSupportFragmentManager(), currentServiceId,
+ serviceName);
break;
}
}
- private void optionsAboutSelected(MenuItem item) {
- switch(item.getItemId()) {
+ private void optionsAboutSelected(final MenuItem item) {
+ switch (item.getItemId()) {
case ITEM_ID_SETTINGS:
NavigationHelper.openSettings(this);
break;
@@ -282,14 +294,13 @@ public class MainActivity extends AppCompatActivity {
private void setupDrawerHeader() {
NavigationView navigationView = findViewById(R.id.navigation);
- View hView = navigationView.getHeaderView(0);
+ View hView = navigationView.getHeaderView(0);
serviceArrow = hView.findViewById(R.id.drawer_arrow);
+ headerServiceIcon = hView.findViewById(R.id.drawer_header_service_icon);
headerServiceView = hView.findViewById(R.id.drawer_header_service_view);
toggleServiceButton = hView.findViewById(R.id.drawer_header_action_button);
- toggleServiceButton.setOnClickListener(view -> {
- toggleServices();
- });
+ toggleServiceButton.setOnClickListener(view -> toggleServices());
}
private void toggleServices() {
@@ -299,8 +310,7 @@ public class MainActivity extends AppCompatActivity {
drawerItems.getMenu().removeGroup(R.id.menu_tabs_group);
drawerItems.getMenu().removeGroup(R.id.menu_options_about_group);
-
- if(servicesShown) {
+ if (servicesShown) {
showServices();
} else {
try {
@@ -312,57 +322,64 @@ public class MainActivity extends AppCompatActivity {
}
private void showServices() {
- serviceArrow.setImageResource(R.drawable.ic_arrow_up_white);
+ serviceArrow.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp);
- for(StreamingService s : NewPipe.getServices()) {
- final String title = s.getServiceInfo().getName() +
- (ServiceHelper.isBeta(s) ? " (beta)" : "");
+ for (StreamingService s : NewPipe.getServices()) {
+ final String title = s.getServiceInfo().getName()
+ + (ServiceHelper.isBeta(s) ? " (beta)" : "");
MenuItem menuItem = drawerItems.getMenu()
.add(R.id.menu_services_group, s.getServiceId(), ORDER, title)
.setIcon(ServiceHelper.getIcon(s.getServiceId()));
// peertube specifics
- if(s.getServiceId() == 3){
+ if (s.getServiceId() == 3) {
enhancePeertubeMenu(s, menuItem);
}
}
- drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this)).setChecked(true);
+ drawerItems.getMenu().getItem(ServiceHelper.getSelectedServiceId(this))
+ .setChecked(true);
}
- private void enhancePeertubeMenu(StreamingService s, MenuItem menuItem) {
+ private void enhancePeertubeMenu(final StreamingService s, final MenuItem menuItem) {
PeertubeInstance currentInstace = PeertubeHelper.getCurrentInstance();
menuItem.setTitle(currentInstace.getName() + (ServiceHelper.isBeta(s) ? " (beta)" : ""));
- Spinner spinner = (Spinner) LayoutInflater.from(this).inflate(R.layout.instance_spinner_layout, null);
+ Spinner spinner = (Spinner) LayoutInflater.from(this)
+ .inflate(R.layout.instance_spinner_layout, null);
List instances = PeertubeHelper.getInstanceList(this);
List items = new ArrayList<>();
int defaultSelect = 0;
- for(PeertubeInstance instance: instances){
+ for (PeertubeInstance instance : instances) {
items.add(instance.getName());
- if(instance.getUrl().equals(currentInstace.getUrl())){
- defaultSelect = items.size()-1;
+ if (instance.getUrl().equals(currentInstace.getUrl())) {
+ defaultSelect = items.size() - 1;
}
}
- ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.instance_spinner_item, items);
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.instance_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
spinner.setSelection(defaultSelect, false);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
- public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ public void onItemSelected(final AdapterView> parent, final View view,
+ final int position, final long id) {
PeertubeInstance newInstance = instances.get(position);
- if(newInstance.getUrl().equals(PeertubeHelper.getCurrentInstance().getUrl())) return;
+ if (newInstance.getUrl().equals(PeertubeHelper.getCurrentInstance().getUrl())) {
+ return;
+ }
PeertubeHelper.selectInstance(newInstance, getApplicationContext());
changeService(menuItem);
drawer.closeDrawers();
new Handler(Looper.getMainLooper()).postDelayed(() -> {
- getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ getSupportFragmentManager().popBackStack(null,
+ FragmentManager.POP_BACK_STACK_INCLUSIVE);
recreate();
}, 300);
}
@Override
- public void onNothingSelected(AdapterView> parent) {
+ public void onNothingSelected(final AdapterView> parent) {
}
});
@@ -370,7 +387,7 @@ public class MainActivity extends AppCompatActivity {
}
private void showTabs() throws ExtractionException {
- serviceArrow.setImageResource(R.drawable.ic_arrow_down_white);
+ serviceArrow.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp);
//Tabs
int currentServiceId = ServiceHelper.getSelectedServiceId(this);
@@ -380,9 +397,10 @@ public class MainActivity extends AppCompatActivity {
for (final String ks : service.getKioskList().getAvailableKiosks()) {
drawerItems.getMenu()
- .add(R.id.menu_tabs_group, kioskId, ORDER, KioskTranslator.getTranslatedKioskName(ks, this))
+ .add(R.id.menu_tabs_group, kioskId, ORDER,
+ KioskTranslator.getTranslatedKioskName(ks, this))
.setIcon(KioskTranslator.getKioskIcons(ks, this));
- kioskId ++;
+ kioskId++;
}
drawerItems.getMenu()
@@ -421,16 +439,20 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onResume() {
assureCorrectAppLanguage(this);
- Localization.init(getApplicationContext()); //change the date format to match the selected language on resume
+ // Change the date format to match the selected language on resume
+ Localization.init(getApplicationContext());
super.onResume();
- // close drawer on return, and don't show animation, so its looks like the drawer isn't open
- // when the user returns to MainActivity
+ // Close drawer on return, and don't show animation,
+ // so it looks like the drawer isn't open when the user returns to MainActivity
drawer.closeDrawer(GravityCompat.START, false);
try {
- String selectedServiceName = NewPipe.getService(
- ServiceHelper.getSelectedServiceId(this)).getServiceInfo().getName();
+ final int selectedServiceId = ServiceHelper.getSelectedServiceId(this);
+ final String selectedServiceName = NewPipe.getService(selectedServiceId)
+ .getServiceInfo().getName();
headerServiceView.setText(selectedServiceName);
+ headerServiceIcon.setImageResource(ServiceHelper.getIcon(selectedServiceId));
+
headerServiceView.post(() -> headerServiceView.setSelected(true));
toggleServiceButton.setContentDescription(
getString(R.string.drawer_header_description) + selectedServiceName);
@@ -440,15 +462,20 @@ public class MainActivity extends AppCompatActivity {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
if (sharedPreferences.getBoolean(Constants.KEY_THEME_CHANGE, false)) {
- if (DEBUG) Log.d(TAG, "Theme has changed, recreating activity...");
+ if (DEBUG) {
+ Log.d(TAG, "Theme has changed, recreating activity...");
+ }
sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply();
- // https://stackoverflow.com/questions/10844112/runtimeexception-performing-pause-of-activity-that-is-not-resumed
- // Briefly, let the activity resume properly posting the recreate call to end of the message queue
+ // https://stackoverflow.com/questions/10844112/
+ // Briefly, let the activity resume
+ // properly posting the recreate call to end of the message queue
new Handler(Looper.getMainLooper()).post(MainActivity.this::recreate);
}
if (sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) {
- if (DEBUG) Log.d(TAG, "main page has changed, recreating main fragment...");
+ if (DEBUG) {
+ Log.d(TAG, "main page has changed, recreating main fragment...");
+ }
sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply();
NavigationHelper.openMainActivity(this);
}
@@ -459,13 +486,18 @@ public class MainActivity extends AppCompatActivity {
}
@Override
- protected void onNewIntent(Intent intent) {
- if (DEBUG) Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
+ protected void onNewIntent(final Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]");
+ }
if (intent != null) {
// Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...)
// to not destroy the already created backstack
String action = intent.getAction();
- if ((action != null && action.equals(Intent.ACTION_MAIN)) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) return;
+ if ((action != null && action.equals(Intent.ACTION_MAIN))
+ && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
+ return;
+ }
}
super.onNewIntent(intent);
@@ -475,24 +507,32 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
- if (DEBUG) Log.d(TAG, "onBackPressed() called");
-
- Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
- // If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it
- if (fragment instanceof BackPressable) {
- if (((BackPressable) fragment).onBackPressed()) return;
+ if (DEBUG) {
+ Log.d(TAG, "onBackPressed() called");
}
+ Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
+ // If current fragment implements BackPressable (i.e. can/wanna handle back press)
+ // delegate the back press to it
+ if (fragment instanceof BackPressable) {
+ if (((BackPressable) fragment).onBackPressed()) {
+ return;
+ }
+ }
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
finish();
- } else super.onBackPressed();
+ } else {
+ super.onBackPressed();
+ }
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- for (int i: grantResults){
- if (i == PackageManager.PERMISSION_DENIED){
+ public void onRequestPermissionsResult(final int requestCode,
+ @NonNull final String[] permissions,
+ @NonNull final int[] grantResults) {
+ for (int i : grantResults) {
+ if (i == PackageManager.PERMISSION_DENIED) {
return;
}
}
@@ -501,7 +541,8 @@ public class MainActivity extends AppCompatActivity {
NavigationHelper.openDownloads(this);
break;
case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE:
- Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
+ Fragment fragment = getSupportFragmentManager()
+ .findFragmentById(R.id.fragment_holder);
if (fragment instanceof VideoDetailFragment) {
((VideoDetailFragment) fragment).openDownloadDialog();
}
@@ -546,8 +587,10 @@ public class MainActivity extends AppCompatActivity {
//////////////////////////////////////////////////////////////////////////*/
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "]");
+ public boolean onCreateOptionsMenu(final Menu menu) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "]");
+ }
super.onCreateOptionsMenu(menu);
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
@@ -556,8 +599,8 @@ public class MainActivity extends AppCompatActivity {
}
if (!(fragment instanceof SearchFragment)) {
- findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container).setVisibility(View.GONE);
-
+ findViewById(R.id.toolbar).findViewById(R.id.toolbar_search_container)
+ .setVisibility(View.GONE);
}
ActionBar actionBar = getSupportActionBar();
@@ -571,8 +614,10 @@ public class MainActivity extends AppCompatActivity {
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ if (DEBUG) {
+ Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]");
+ }
int id = item.getItemId();
switch (id) {
@@ -589,11 +634,15 @@ public class MainActivity extends AppCompatActivity {
//////////////////////////////////////////////////////////////////////////*/
private void initFragments() {
- if (DEBUG) Log.d(TAG, "initFragments() called");
+ if (DEBUG) {
+ Log.d(TAG, "initFragments() called");
+ }
StateSaver.clearStateFiles();
if (getIntent() != null && getIntent().hasExtra(Constants.KEY_LINK_TYPE)) {
handleIntent(getIntent());
- } else NavigationHelper.gotoMainFragment(getSupportFragmentManager());
+ } else {
+ NavigationHelper.gotoMainFragment(getSupportFragmentManager());
+ }
}
/*//////////////////////////////////////////////////////////////////////////
@@ -601,12 +650,14 @@ public class MainActivity extends AppCompatActivity {
//////////////////////////////////////////////////////////////////////////*/
private void updateDrawerNavigation() {
- if (getSupportActionBar() == null) return;
+ if (getSupportActionBar() == null) {
+ return;
+ }
final Toolbar toolbar = findViewById(R.id.toolbar);
- final DrawerLayout drawer = findViewById(R.id.drawer_layout);
- final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder);
+ final Fragment fragment = getSupportFragmentManager()
+ .findFragmentById(R.id.fragment_holder);
if (fragment instanceof MainFragment) {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
if (toggle != null) {
@@ -621,26 +672,23 @@ public class MainActivity extends AppCompatActivity {
}
}
- private void updateDrawerHeaderString(String content) {
- NavigationView navigationView = findViewById(R.id.navigation);
- View hView = navigationView.getHeaderView(0);
- Button action = hView.findViewById(R.id.drawer_header_action_button);
-
- action.setContentDescription(content);
- }
-
- private void handleIntent(Intent intent) {
+ private void handleIntent(final Intent intent) {
try {
- if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
+ if (DEBUG) {
+ Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]");
+ }
if (intent.hasExtra(Constants.KEY_LINK_TYPE)) {
String url = intent.getStringExtra(Constants.KEY_URL);
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
String title = intent.getStringExtra(Constants.KEY_TITLE);
- switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) {
+ switch (((StreamingService.LinkType) intent
+ .getSerializableExtra(Constants.KEY_LINK_TYPE))) {
case STREAM:
- boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
- NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay);
+ boolean autoPlay = intent
+ .getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false);
+ NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(),
+ serviceId, url, title, autoPlay);
break;
case CHANNEL:
NavigationHelper.openChannelFragment(getSupportFragmentManager(),
@@ -657,7 +705,9 @@ public class MainActivity extends AppCompatActivity {
}
} else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) {
String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING);
- if (searchString == null) searchString = "";
+ if (searchString == null) {
+ searchString = "";
+ }
int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0);
NavigationHelper.openSearchFragment(
getSupportFragmentManager(),
diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
index 81b5dd72f..c59c48367 100644
--- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
+++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java
@@ -13,14 +13,13 @@ import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2;
import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3;
public final class NewPipeDatabase {
-
private static volatile AppDatabase databaseInstance;
private NewPipeDatabase() {
//no instance
}
- private static AppDatabase getDatabase(Context context) {
+ private static AppDatabase getDatabase(final Context context) {
return Room
.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DATABASE_NAME)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
@@ -28,13 +27,14 @@ public final class NewPipeDatabase {
}
@NonNull
- public static AppDatabase getInstance(@NonNull Context context) {
+ public static AppDatabase getInstance(@NonNull final Context context) {
AppDatabase result = databaseInstance;
if (result == null) {
synchronized (NewPipeDatabase.class) {
result = databaseInstance;
if (result == null) {
- databaseInstance = (result = getDatabase(context));
+ databaseInstance = getDatabase(context);
+ result = databaseInstance;
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java b/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
index 4118070d5..2e1abd598 100644
--- a/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/PanicResponderActivity.java
@@ -1,4 +1,3 @@
-
package org.schabi.newpipe;
import android.annotation.SuppressLint;
@@ -26,17 +25,18 @@ import android.os.Bundle;
*/
public class PanicResponderActivity extends Activity {
-
public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
@SuppressLint("NewApi")
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
- // TODO explicitly clear the search results once they are restored when the app restarts
- // or if the app reloads the current video after being killed, that should be cleared also
+ // TODO: Explicitly clear the search results
+ // once they are restored when the app restarts
+ // or if the app reloads the current video after being killed,
+ // that should be cleared also
ExitActivity.exitAndRemoveFromRecentApps(this);
}
diff --git a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
index 4219638d6..a8a83e13e 100644
--- a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java
@@ -3,11 +3,6 @@ package org.schabi.newpipe;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
-import androidx.core.app.NavUtils;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -16,9 +11,13 @@ import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import org.schabi.newpipe.util.ThemeHelper;
-
import androidx.annotation.NonNull;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.core.app.NavUtils;
+
+import org.schabi.newpipe.util.ThemeHelper;
/*
* Created by beneth on 06.12.16.
@@ -49,7 +48,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
private String foundCookies = "";
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recaptcha);
@@ -73,7 +72,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
webView.setWebViewClient(new WebViewClient() {
@Override
- public void onPageFinished(WebView view, String url) {
+ public void onPageFinished(final WebView view, final String url) {
super.onPageFinished(view, url);
handleCookies(url);
}
@@ -84,7 +83,8 @@ public class ReCaptchaActivity extends AppCompatActivity {
webView.clearHistory();
android.webkit.CookieManager cookieManager = CookieManager.getInstance();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- cookieManager.removeAllCookies(aBoolean -> {});
+ cookieManager.removeAllCookies(aBoolean -> {
+ });
} else {
cookieManager.removeAllCookie();
}
@@ -93,7 +93,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu_recaptcha, menu);
ActionBar actionBar = getSupportActionBar();
@@ -112,7 +112,7 @@ public class ReCaptchaActivity extends AppCompatActivity {
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(final MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.menu_item_done:
@@ -137,24 +137,29 @@ public class ReCaptchaActivity extends AppCompatActivity {
}
-
- private void handleCookies(String url) {
+ private void handleCookies(final String url) {
String cookies = CookieManager.getInstance().getCookie(url);
- if (MainActivity.DEBUG) Log.d(TAG, "handleCookies: url=" + url + "; cookies=" + (cookies == null ? "null" : cookies));
- if (cookies == null) return;
+ if (MainActivity.DEBUG) {
+ Log.d(TAG, "handleCookies: "
+ + "url=" + url + "; cookies=" + (cookies == null ? "null" : cookies));
+ }
+ if (cookies == null) {
+ return;
+ }
addYoutubeCookies(cookies);
// add other methods to extract cookies here
}
- private void addYoutubeCookies(@NonNull String cookies) {
- if (cookies.contains("s_gl=") || cookies.contains("goojf=") || cookies.contains("VISITOR_INFO1_LIVE=")) {
+ private void addYoutubeCookies(@NonNull final String cookies) {
+ if (cookies.contains("s_gl=") || cookies.contains("goojf=")
+ || cookies.contains("VISITOR_INFO1_LIVE=")) {
// youtube seems to also need the other cookies:
addCookie(cookies);
}
}
- private void addCookie(String cookie) {
+ private void addCookie(final String cookie) {
if (foundCookies.contains(cookie)) {
return;
}
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index 1be6e096a..bb24c9681 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -9,12 +9,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.app.NotificationCompat;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -26,6 +20,12 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.NotificationCompat;
import androidx.fragment.app.FragmentManager;
import org.schabi.newpipe.download.DownloadDialog;
@@ -49,6 +49,7 @@ import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.NavigationHelper;
import org.schabi.newpipe.util.PermissionHelper;
import org.schabi.newpipe.util.ThemeHelper;
+import org.schabi.newpipe.util.urlfinder.UrlFinder;
import java.io.Serializable;
import java.util.ArrayList;
@@ -72,29 +73,31 @@ import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCap
import static org.schabi.newpipe.util.ThemeHelper.resolveResourceIdFromAttr;
/**
- * Get the url from the intent and open it in the chosen preferred player
+ * Get the url from the intent and open it in the chosen preferred player.
*/
public class RouterActivity extends AppCompatActivity {
-
+ public static final String INTERNAL_ROUTE_KEY = "internalRoute";
+ /**
+ * Removes invisible separators (\p{Z}) and punctuation characters including
+ * brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
+ * more details.
+ */
+ private static final String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
+ protected final CompositeDisposable disposables = new CompositeDisposable();
@State
protected int currentServiceId = -1;
- private StreamingService currentService;
@State
protected LinkType currentLinkType;
@State
protected int selectedRadioPosition = -1;
protected int selectedPreviously = -1;
-
protected String currentUrl;
protected boolean internalRoute = false;
- protected final CompositeDisposable disposables = new CompositeDisposable();
-
+ private StreamingService currentService;
private boolean selectionIsDownload = false;
- public static final String internalRouteKey = "internalRoute";
-
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
@@ -107,14 +110,14 @@ public class RouterActivity extends AppCompatActivity {
}
}
- internalRoute = getIntent().getBooleanExtra(internalRouteKey, false);
+ internalRoute = getIntent().getBooleanExtra(INTERNAL_ROUTE_KEY, false);
setTheme(ThemeHelper.isLightThemeSelected(this)
? R.style.RouterActivityThemeLight : R.style.RouterActivityThemeDark);
}
@Override
- protected void onSaveInstanceState(Bundle outState) {
+ protected void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
@@ -133,7 +136,7 @@ public class RouterActivity extends AppCompatActivity {
disposables.clear();
}
- private void handleUrl(String url) {
+ private void handleUrl(final String url) {
disposables.add(Observable
.fromCallable(() -> {
if (currentServiceId == -1) {
@@ -158,13 +161,14 @@ public class RouterActivity extends AppCompatActivity {
}, this::handleError));
}
- private void handleError(Throwable error) {
+ private void handleError(final Throwable error) {
error.printStackTrace();
if (error instanceof ExtractionException) {
Toast.makeText(this, R.string.url_not_supported_toast, Toast.LENGTH_LONG).show();
} else {
- ExtractorHelper.handleGeneralException(this, -1, null, error, UserAction.SOMETHING_ELSE, null);
+ ExtractorHelper.handleGeneralException(this, -1, null, error,
+ UserAction.SOMETHING_ELSE, null);
}
finish();
@@ -176,8 +180,11 @@ public class RouterActivity extends AppCompatActivity {
}
protected void onSuccess() {
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- final String selectedChoiceKey = preferences.getString(getString(R.string.preferred_open_action_key), getString(R.string.preferred_open_action_default));
+ final SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ final String selectedChoiceKey = preferences
+ .getString(getString(R.string.preferred_open_action_key),
+ getString(R.string.preferred_open_action_default));
final String showInfoKey = getString(R.string.show_info_key);
final String videoPlayerKey = getString(R.string.video_player_key);
@@ -187,7 +194,8 @@ public class RouterActivity extends AppCompatActivity {
final String alwaysAskKey = getString(R.string.always_ask_open_action_key);
if (selectedChoiceKey.equals(alwaysAskKey)) {
- final List choices = getChoicesForService(currentService, currentLinkType);
+ final List choices
+ = getChoicesForService(currentService, currentLinkType);
switch (choices.size()) {
case 1:
@@ -205,20 +213,26 @@ public class RouterActivity extends AppCompatActivity {
} else if (selectedChoiceKey.equals(downloadKey)) {
handleChoice(downloadKey);
} else {
- final boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
- final boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
- final boolean isVideoPlayerSelected = selectedChoiceKey.equals(videoPlayerKey) || selectedChoiceKey.equals(popupPlayerKey);
+ final boolean isExtVideoEnabled = preferences.getBoolean(
+ getString(R.string.use_external_video_player_key), false);
+ final boolean isExtAudioEnabled = preferences.getBoolean(
+ getString(R.string.use_external_audio_player_key), false);
+ final boolean isVideoPlayerSelected = selectedChoiceKey.equals(videoPlayerKey)
+ || selectedChoiceKey.equals(popupPlayerKey);
final boolean isAudioPlayerSelected = selectedChoiceKey.equals(backgroundPlayerKey);
if (currentLinkType != LinkType.STREAM) {
- if (isExtAudioEnabled && isAudioPlayerSelected || isExtVideoEnabled && isVideoPlayerSelected) {
- Toast.makeText(this, R.string.external_player_unsupported_link_type, Toast.LENGTH_LONG).show();
+ if (isExtAudioEnabled && isAudioPlayerSelected
+ || isExtVideoEnabled && isVideoPlayerSelected) {
+ Toast.makeText(this, R.string.external_player_unsupported_link_type,
+ Toast.LENGTH_LONG).show();
handleChoice(showInfoKey);
return;
}
}
- final List capabilities = currentService.getServiceInfo().getMediaCapabilities();
+ final List capabilities
+ = currentService.getServiceInfo().getMediaCapabilities();
boolean serviceSupportsChoice = false;
if (isVideoPlayerSelected) {
@@ -240,7 +254,8 @@ public class RouterActivity extends AppCompatActivity {
final Context themeWrapperContext = getThemeWrapperContext();
final LayoutInflater inflater = LayoutInflater.from(themeWrapperContext);
- final LinearLayout rootLayout = (LinearLayout) inflater.inflate(R.layout.preferred_player_dialog_view, null, false);
+ final LinearLayout rootLayout = (LinearLayout) inflater.inflate(
+ R.layout.preferred_player_dialog_view, null, false);
final RadioGroup radioGroup = rootLayout.findViewById(android.R.id.list);
final DialogInterface.OnClickListener dialogButtonsClickListener = (dialog, which) -> {
@@ -251,7 +266,9 @@ public class RouterActivity extends AppCompatActivity {
handleChoice(choice.key);
if (which == DialogInterface.BUTTON_POSITIVE) {
- preferences.edit().putString(getString(R.string.preferred_open_action_key), choice.key).apply();
+ preferences.edit()
+ .putString(getString(R.string.preferred_open_action_key), choice.key)
+ .apply();
}
};
@@ -262,7 +279,9 @@ public class RouterActivity extends AppCompatActivity {
.setNegativeButton(R.string.just_once, dialogButtonsClickListener)
.setPositiveButton(R.string.always, dialogButtonsClickListener)
.setOnDismissListener((dialog) -> {
- if (!selectionIsDownload) finish();
+ if (!selectionIsDownload) {
+ finish();
+ }
})
.create();
@@ -271,10 +290,13 @@ public class RouterActivity extends AppCompatActivity {
setDialogButtonsState(alertDialog, radioGroup.getCheckedRadioButtonId() != -1);
});
- radioGroup.setOnCheckedChangeListener((group, checkedId) -> setDialogButtonsState(alertDialog, true));
+ radioGroup.setOnCheckedChangeListener((group, checkedId) ->
+ setDialogButtonsState(alertDialog, true));
final View.OnClickListener radioButtonsClickListener = v -> {
final int indexOfChild = radioGroup.indexOfChild(v);
- if (indexOfChild == -1) return;
+ if (indexOfChild == -1) {
+ return;
+ }
selectedPreviously = selectedRadioPosition;
selectedRadioPosition = indexOfChild;
@@ -286,18 +308,21 @@ public class RouterActivity extends AppCompatActivity {
int id = 12345;
for (AdapterChoiceItem item : choices) {
- final RadioButton radioButton = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
+ final RadioButton radioButton
+ = (RadioButton) inflater.inflate(R.layout.list_radio_icon_item, null);
radioButton.setText(item.description);
radioButton.setCompoundDrawablesWithIntrinsicBounds(item.icon, 0, 0, 0);
radioButton.setChecked(false);
radioButton.setId(id++);
- radioButton.setLayoutParams(new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ radioButton.setLayoutParams(new RadioGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
radioButton.setOnClickListener(radioButtonsClickListener);
radioGroup.addView(radioButton);
}
if (selectedRadioPosition == -1) {
- final String lastSelectedPlayer = preferences.getString(getString(R.string.preferred_open_action_last_selected_key), null);
+ final String lastSelectedPlayer = preferences.getString(
+ getString(R.string.preferred_open_action_last_selected_key), null);
if (!TextUtils.isEmpty(lastSelectedPlayer)) {
for (int i = 0; i < choices.size(); i++) {
AdapterChoiceItem c = choices.get(i);
@@ -318,46 +343,58 @@ public class RouterActivity extends AppCompatActivity {
alertDialog.show();
}
- private List getChoicesForService(StreamingService service, LinkType linkType) {
+ private List getChoicesForService(final StreamingService service,
+ final LinkType linkType) {
final Context context = getThemeWrapperContext();
final List returnList = new ArrayList<>();
- final List capabilities = service.getServiceInfo().getMediaCapabilities();
+ final List capabilities
+ = service.getServiceInfo().getMediaCapabilities();
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
- boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
+ final SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ boolean isExtVideoEnabled = preferences.getBoolean(
+ getString(R.string.use_external_video_player_key), false);
+ boolean isExtAudioEnabled = preferences.getBoolean(
+ getString(R.string.use_external_audio_player_key), false);
- returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key), getString(R.string.show_info),
+ returnList.add(new AdapterChoiceItem(getString(R.string.show_info_key),
+ getString(R.string.show_info),
resolveResourceIdFromAttr(context, R.attr.info)));
if (capabilities.contains(VIDEO) && !(isExtVideoEnabled && linkType != LinkType.STREAM)) {
- returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key), getString(R.string.video_player),
+ returnList.add(new AdapterChoiceItem(getString(R.string.video_player_key),
+ getString(R.string.video_player),
resolveResourceIdFromAttr(context, R.attr.play)));
- returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key), getString(R.string.popup_player),
+ returnList.add(new AdapterChoiceItem(getString(R.string.popup_player_key),
+ getString(R.string.popup_player),
resolveResourceIdFromAttr(context, R.attr.popup)));
}
if (capabilities.contains(AUDIO) && !(isExtAudioEnabled && linkType != LinkType.STREAM)) {
- returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key), getString(R.string.background_player),
+ returnList.add(new AdapterChoiceItem(getString(R.string.background_player_key),
+ getString(R.string.background_player),
resolveResourceIdFromAttr(context, R.attr.audio)));
}
- returnList.add(new AdapterChoiceItem(getString(R.string.download_key), getString(R.string.download),
+ returnList.add(new AdapterChoiceItem(getString(R.string.download_key),
+ getString(R.string.download),
resolveResourceIdFromAttr(context, R.attr.download)));
return returnList;
}
private Context getThemeWrapperContext() {
- return new ContextThemeWrapper(this,
- ThemeHelper.isLightThemeSelected(this) ? R.style.LightTheme : R.style.DarkTheme);
+ return new ContextThemeWrapper(this, ThemeHelper.isLightThemeSelected(this)
+ ? R.style.LightTheme : R.style.DarkTheme);
}
- private void setDialogButtonsState(AlertDialog dialog, boolean state) {
+ private void setDialogButtonsState(final AlertDialog dialog, final boolean state) {
final Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
final Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
- if (negativeButton == null || positiveButton == null) return;
+ if (negativeButton == null || positiveButton == null) {
+ return;
+ }
negativeButton.setEnabled(state);
positiveButton.setEnabled(state);
@@ -373,21 +410,25 @@ public class RouterActivity extends AppCompatActivity {
}
private void handleChoice(final String selectedChoiceKey) {
- final List validChoicesList = Arrays.asList(getResources().getStringArray(R.array.preferred_open_action_values_list));
+ final List validChoicesList = Arrays.asList(getResources()
+ .getStringArray(R.array.preferred_open_action_values_list));
if (validChoicesList.contains(selectedChoiceKey)) {
PreferenceManager.getDefaultSharedPreferences(this).edit()
- .putString(getString(R.string.preferred_open_action_last_selected_key), selectedChoiceKey)
+ .putString(getString(
+ R.string.preferred_open_action_last_selected_key), selectedChoiceKey)
.apply();
}
- if (selectedChoiceKey.equals(getString(R.string.popup_player_key)) && !PermissionHelper.isPopupEnabled(this)) {
+ if (selectedChoiceKey.equals(getString(R.string.popup_player_key))
+ && !PermissionHelper.isPopupEnabled(this)) {
PermissionHelper.showPopupEnablementToast(this);
finish();
return;
}
if (selectedChoiceKey.equals(getString(R.string.download_key))) {
- if (PermissionHelper.checkStoragePermissions(this, PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
+ if (PermissionHelper.checkStoragePermissions(this,
+ PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE)) {
selectionIsDownload = true;
openDownloadDialog();
}
@@ -415,7 +456,8 @@ public class RouterActivity extends AppCompatActivity {
}
final Intent intent = new Intent(this, FetcherService.class);
- final Choice choice = new Choice(currentService.getServiceId(), currentLinkType, currentUrl, selectedChoiceKey);
+ final Choice choice = new Choice(currentService.getServiceId(), currentLinkType,
+ currentUrl, selectedChoiceKey);
intent.putExtra(FetcherService.KEY_CHOICE, choice);
startService(intent);
@@ -428,12 +470,11 @@ public class RouterActivity extends AppCompatActivity {
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((@NonNull StreamInfo result) -> {
- List sortedVideoStreams = ListHelper.getSortedStreamVideosList(this,
- result.getVideoStreams(),
- result.getVideoOnlyStreams(),
- false);
- int selectedVideoStreamIndex = ListHelper.getDefaultResolutionIndex(this,
- sortedVideoStreams);
+ List sortedVideoStreams = ListHelper
+ .getSortedStreamVideosList(this, result.getVideoStreams(),
+ result.getVideoOnlyStreams(), false);
+ int selectedVideoStreamIndex = ListHelper
+ .getDefaultResolutionIndex(this, sortedVideoStreams);
FragmentManager fm = getSupportFragmentManager();
DownloadDialog downloadDialog = DownloadDialog.newInstance(result);
@@ -451,7 +492,9 @@ public class RouterActivity extends AppCompatActivity {
}
@Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(final int requestCode,
+ @NonNull final String[] permissions,
+ @NonNull final int[] grantResults) {
for (int i : grantResults) {
if (i == PackageManager.PERMISSION_DENIED) {
finish();
@@ -463,191 +506,10 @@ public class RouterActivity extends AppCompatActivity {
}
}
- private static class AdapterChoiceItem {
- final String description, key;
- @DrawableRes
- final int icon;
-
- AdapterChoiceItem(String key, String description, int icon) {
- this.description = description;
- this.key = key;
- this.icon = icon;
- }
- }
-
- private static class Choice implements Serializable {
- final int serviceId;
- final String url, playerChoice;
- final LinkType linkType;
-
- Choice(int serviceId, LinkType linkType, String url, String playerChoice) {
- this.serviceId = serviceId;
- this.linkType = linkType;
- this.url = url;
- this.playerChoice = playerChoice;
- }
-
- @Override
- public String toString() {
- return serviceId + ":" + url + " > " + linkType + " ::: " + playerChoice;
- }
- }
-
/*//////////////////////////////////////////////////////////////////////////
// Service Fetcher
//////////////////////////////////////////////////////////////////////////*/
- public static class FetcherService extends IntentService {
-
- private static final int ID = 456;
- public static final String KEY_CHOICE = "key_choice";
- private Disposable fetcher;
-
- public FetcherService() {
- super(FetcherService.class.getSimpleName());
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- startForeground(ID, createNotification().build());
- }
-
- @Override
- protected void onHandleIntent(@Nullable Intent intent) {
- if (intent == null) return;
-
- final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
- if (!(serializable instanceof Choice)) return;
- Choice playerChoice = (Choice) serializable;
- handleChoice(playerChoice);
- }
-
- public void handleChoice(Choice choice) {
- Single extends Info> single = null;
- UserAction userAction = UserAction.SOMETHING_ELSE;
-
- switch (choice.linkType) {
- case STREAM:
- single = ExtractorHelper.getStreamInfo(choice.serviceId, choice.url, false);
- userAction = UserAction.REQUESTED_STREAM;
- break;
- case CHANNEL:
- single = ExtractorHelper.getChannelInfo(choice.serviceId, choice.url, false);
- userAction = UserAction.REQUESTED_CHANNEL;
- break;
- case PLAYLIST:
- single = ExtractorHelper.getPlaylistInfo(choice.serviceId, choice.url, false);
- userAction = UserAction.REQUESTED_PLAYLIST;
- break;
- }
-
-
- if (single != null) {
- final UserAction finalUserAction = userAction;
- final Consumer resultHandler = getResultHandler(choice);
- fetcher = single
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(info -> {
- resultHandler.accept(info);
- if (fetcher != null) fetcher.dispose();
- }, throwable -> ExtractorHelper.handleGeneralException(this,
- choice.serviceId, choice.url, throwable, finalUserAction, ", opened with " + choice.playerChoice));
- }
- }
-
- public Consumer getResultHandler(Choice choice) {
- return info -> {
- final String videoPlayerKey = getString(R.string.video_player_key);
- final String backgroundPlayerKey = getString(R.string.background_player_key);
- final String popupPlayerKey = getString(R.string.popup_player_key);
-
- final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
- boolean isExtVideoEnabled = preferences.getBoolean(getString(R.string.use_external_video_player_key), false);
- boolean isExtAudioEnabled = preferences.getBoolean(getString(R.string.use_external_audio_player_key), false);
- ;
-
- PlayQueue playQueue;
- String playerChoice = choice.playerChoice;
-
- if (info instanceof StreamInfo) {
- if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
- NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
-
- } else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
- NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
-
- } else {
- playQueue = new SinglePlayQueue((StreamInfo) info);
-
- if (playerChoice.equals(videoPlayerKey)) {
- NavigationHelper.playOnMainPlayer(this, playQueue, true);
- } else if (playerChoice.equals(backgroundPlayerKey)) {
- NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
- } else if (playerChoice.equals(popupPlayerKey)) {
- NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
- }
- }
- }
-
- if (info instanceof ChannelInfo || info instanceof PlaylistInfo) {
- playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info);
-
- if (playerChoice.equals(videoPlayerKey)) {
- NavigationHelper.playOnMainPlayer(this, playQueue, true);
- } else if (playerChoice.equals(backgroundPlayerKey)) {
- NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
- } else if (playerChoice.equals(popupPlayerKey)) {
- NavigationHelper.playOnPopupPlayer(this, playQueue, true);
- }
- }
- };
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- stopForeground(true);
- if (fetcher != null) fetcher.dispose();
- }
-
- private NotificationCompat.Builder createNotification() {
- return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
- .setOngoing(true)
- .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
- .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
- .setContentTitle(getString(R.string.preferred_player_fetcher_notification_title))
- .setContentText(getString(R.string.preferred_player_fetcher_notification_message));
- }
- }
-
- /*//////////////////////////////////////////////////////////////////////////
- // Utils
- //////////////////////////////////////////////////////////////////////////*/
-
- /**
- * Removes invisible separators (\p{Z}) and punctuation characters including
- * brackets (\p{P}). See http://www.regular-expressions.info/unicode.html for
- * more details.
- */
- private final static String REGEX_REMOVE_FROM_URL = "[\\p{Z}\\p{P}]";
-
- private String getUrl(Intent intent) {
- // first gather data and find service
- String videoUrl = null;
- if (intent.getData() != null) {
- // this means the video was called though another app
- videoUrl = intent.getData().toString();
- } else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
- //this means that vidoe was called through share menu
- String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
- final String[] uris = getUris(extraText);
- videoUrl = uris.length > 0 ? uris[0] : null;
- }
-
- return videoUrl;
- }
-
private String removeHeadingGibberish(final String input) {
int start = 0;
for (int i = input.indexOf("://") - 1; i >= 0; i--) {
@@ -656,9 +518,13 @@ public class RouterActivity extends AppCompatActivity {
break;
}
}
- return input.substring(start, input.length());
+ return input.substring(start);
}
+ /*//////////////////////////////////////////////////////////////////////////
+ // Utils
+ //////////////////////////////////////////////////////////////////////////*/
+
private String trim(final String input) {
if (input == null || input.length() < 1) {
return input;
@@ -668,7 +534,7 @@ public class RouterActivity extends AppCompatActivity {
output = output.substring(1);
}
while (output.length() > 0
- && output.substring(output.length() - 1, output.length()).matches(REGEX_REMOVE_FROM_URL)) {
+ && output.substring(output.length() - 1).matches(REGEX_REMOVE_FROM_URL)) {
output = output.substring(0, output.length() - 1);
}
return output;
@@ -699,4 +565,195 @@ public class RouterActivity extends AppCompatActivity {
}
return result.toArray(new String[result.size()]);
}
+
+ private static class AdapterChoiceItem {
+ final String description;
+ final String key;
+ @DrawableRes
+ final int icon;
+
+ AdapterChoiceItem(final String key, final String description, final int icon) {
+ this.description = description;
+ this.key = key;
+ this.icon = icon;
+ }
+ }
+
+ private static class Choice implements Serializable {
+ final int serviceId;
+ final String url;
+ final String playerChoice;
+ final LinkType linkType;
+
+ Choice(final int serviceId, final LinkType linkType,
+ final String url, final String playerChoice) {
+ this.serviceId = serviceId;
+ this.linkType = linkType;
+ this.url = url;
+ this.playerChoice = playerChoice;
+ }
+
+ @Override
+ public String toString() {
+ return serviceId + ":" + url + " > " + linkType + " ::: " + playerChoice;
+ }
+ }
+
+ public static class FetcherService extends IntentService {
+
+ public static final String KEY_CHOICE = "key_choice";
+ private static final int ID = 456;
+ private Disposable fetcher;
+
+ public FetcherService() {
+ super(FetcherService.class.getSimpleName());
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ startForeground(ID, createNotification().build());
+ }
+
+ @Override
+ protected void onHandleIntent(@Nullable final Intent intent) {
+ if (intent == null) {
+ return;
+ }
+
+ final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE);
+ if (!(serializable instanceof Choice)) {
+ return;
+ }
+ Choice playerChoice = (Choice) serializable;
+ handleChoice(playerChoice);
+ }
+
+ public void handleChoice(final Choice choice) {
+ Single extends Info> single = null;
+ UserAction userAction = UserAction.SOMETHING_ELSE;
+
+ switch (choice.linkType) {
+ case STREAM:
+ single = ExtractorHelper.getStreamInfo(choice.serviceId, choice.url, false);
+ userAction = UserAction.REQUESTED_STREAM;
+ break;
+ case CHANNEL:
+ single = ExtractorHelper.getChannelInfo(choice.serviceId, choice.url, false);
+ userAction = UserAction.REQUESTED_CHANNEL;
+ break;
+ case PLAYLIST:
+ single = ExtractorHelper.getPlaylistInfo(choice.serviceId, choice.url, false);
+ userAction = UserAction.REQUESTED_PLAYLIST;
+ break;
+ }
+
+
+ if (single != null) {
+ final UserAction finalUserAction = userAction;
+ final Consumer resultHandler = getResultHandler(choice);
+ fetcher = single
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(info -> {
+ resultHandler.accept(info);
+ if (fetcher != null) {
+ fetcher.dispose();
+ }
+ }, throwable -> ExtractorHelper.handleGeneralException(this,
+ choice.serviceId, choice.url, throwable, finalUserAction,
+ ", opened with " + choice.playerChoice));
+ }
+ }
+
+ public Consumer getResultHandler(final Choice choice) {
+ return info -> {
+ final String videoPlayerKey = getString(R.string.video_player_key);
+ final String backgroundPlayerKey = getString(R.string.background_player_key);
+ final String popupPlayerKey = getString(R.string.popup_player_key);
+
+ final SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(this);
+ boolean isExtVideoEnabled = preferences.getBoolean(
+ getString(R.string.use_external_video_player_key), false);
+ boolean isExtAudioEnabled = preferences.getBoolean(
+ getString(R.string.use_external_audio_player_key), false);
+
+ PlayQueue playQueue;
+ String playerChoice = choice.playerChoice;
+
+ if (info instanceof StreamInfo) {
+ if (playerChoice.equals(backgroundPlayerKey) && isExtAudioEnabled) {
+ NavigationHelper.playOnExternalAudioPlayer(this, (StreamInfo) info);
+
+ } else if (playerChoice.equals(videoPlayerKey) && isExtVideoEnabled) {
+ NavigationHelper.playOnExternalVideoPlayer(this, (StreamInfo) info);
+
+ } else {
+ playQueue = new SinglePlayQueue((StreamInfo) info);
+
+ if (playerChoice.equals(videoPlayerKey)) {
+ NavigationHelper.playOnMainPlayer(this, playQueue, true);
+ } else if (playerChoice.equals(backgroundPlayerKey)) {
+ NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true);
+ } else if (playerChoice.equals(popupPlayerKey)) {
+ NavigationHelper.enqueueOnPopupPlayer(this, playQueue, true);
+ }
+ }
+ }
+
+ if (info instanceof ChannelInfo || info instanceof PlaylistInfo) {
+ playQueue = info instanceof ChannelInfo
+ ? new ChannelPlayQueue((ChannelInfo) info)
+ : new PlaylistPlayQueue((PlaylistInfo) info);
+
+ if (playerChoice.equals(videoPlayerKey)) {
+ NavigationHelper.playOnMainPlayer(this, playQueue, true);
+ } else if (playerChoice.equals(backgroundPlayerKey)) {
+ NavigationHelper.playOnBackgroundPlayer(this, playQueue, true);
+ } else if (playerChoice.equals(popupPlayerKey)) {
+ NavigationHelper.playOnPopupPlayer(this, playQueue, true);
+ }
+ }
+ };
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopForeground(true);
+ if (fetcher != null) {
+ fetcher.dispose();
+ }
+ }
+
+ private NotificationCompat.Builder createNotification() {
+ return new NotificationCompat.Builder(this, getString(R.string.notification_channel_id))
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_newpipe_triangle_white)
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setContentTitle(
+ getString(R.string.preferred_player_fetcher_notification_title))
+ .setContentText(
+ getString(R.string.preferred_player_fetcher_notification_message));
+ }
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Utils
+ //////////////////////////////////////////////////////////////////////////*/
+
+ @Nullable
+ private String getUrl(final Intent intent) {
+ String foundUrl = null;
+ if (intent.getData() != null) {
+ // Called from another app
+ foundUrl = intent.getData().toString();
+ } else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
+ // Called from the share menu
+ final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT);
+ foundUrl = UrlFinder.firstUrlFromInput(extraText);
+ }
+
+ return foundUrl;
+ }
}
diff --git a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
index 0a4e9e865..2fb8ac7f7 100644
--- a/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/about/AboutActivity.java
@@ -4,21 +4,22 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import com.google.android.material.tabs.TabLayout;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
+
+import com.google.android.material.tabs.TabLayout;
import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.R;
@@ -27,26 +28,41 @@ import org.schabi.newpipe.util.ThemeHelper;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class AboutActivity extends AppCompatActivity {
-
/**
- * List of all software components
+ * List of all software components.
*/
private static final SoftwareComponent[] SOFTWARE_COMPONENTS = new SoftwareComponent[]{
- new SoftwareComponent("Giga Get", "2014 - 2015", "Peter Cai", "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2),
- new SoftwareComponent("NewPipe Extractor", "2017 - 2020", "Christian Schabesberger", "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3),
- new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley", "https://github.com/jhy/jsoup", StandardLicenses.MIT),
- new SoftwareComponent("Rhino", "2015", "Mozilla", "https://www.mozilla.org/rhino/", StandardLicenses.MPL2),
- new SoftwareComponent("ACRA", "2013", "Kevin Gaudin", "http://www.acra.ch", StandardLicenses.APACHE2),
- new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich", "https://github.com/nostra13/Android-Universal-Image-Loader", StandardLicenses.APACHE2),
- new SoftwareComponent("CircleImageView", "2014 - 2020", "Henning Dodenhof", "https://github.com/hdodenhof/CircleImageView", StandardLicenses.APACHE2),
- new SoftwareComponent("NoNonsense-FilePicker", "2016", "Jonas Kalderstam", "https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2),
- new SoftwareComponent("ExoPlayer", "2014 - 2020", "Google Inc", "https://github.com/google/ExoPlayer", StandardLicenses.APACHE2),
- new SoftwareComponent("RxAndroid", "2015 - 2018", "The RxAndroid authors", "https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2),
- new SoftwareComponent("RxJava", "2016 - 2020", "RxJava Contributors", "https://github.com/ReactiveX/RxJava", StandardLicenses.APACHE2),
- new SoftwareComponent("RxBinding", "2015 - 2018", "Jake Wharton", "https://github.com/JakeWharton/RxBinding", StandardLicenses.APACHE2),
- new SoftwareComponent("PrettyTime", "2012 - 2020", "Lincoln Baxter, III", "https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2),
- new SoftwareComponent("Markwon", "2017 - 2020", "Noties", "https://github.com/noties/Markwon", StandardLicenses.APACHE2),
- new SoftwareComponent("Groupie", "2016", "Lisa Wray", "https://github.com/lisawray/groupie", StandardLicenses.MIT)
+ new SoftwareComponent("Giga Get", "2014 - 2015", "Peter Cai",
+ "https://github.com/PaperAirplane-Dev-Team/GigaGet", StandardLicenses.GPL2),
+ new SoftwareComponent("NewPipe Extractor", "2017 - 2020", "Christian Schabesberger",
+ "https://github.com/TeamNewPipe/NewPipeExtractor", StandardLicenses.GPL3),
+ new SoftwareComponent("Jsoup", "2017", "Jonathan Hedley",
+ "https://github.com/jhy/jsoup", StandardLicenses.MIT),
+ new SoftwareComponent("Rhino", "2015", "Mozilla",
+ "https://www.mozilla.org/rhino/", StandardLicenses.MPL2),
+ new SoftwareComponent("ACRA", "2013", "Kevin Gaudin",
+ "http://www.acra.ch", StandardLicenses.APACHE2),
+ new SoftwareComponent("Universal Image Loader", "2011 - 2015", "Sergey Tarasevich",
+ "https://github.com/nostra13/Android-Universal-Image-Loader",
+ StandardLicenses.APACHE2),
+ new SoftwareComponent("CircleImageView", "2014 - 2020", "Henning Dodenhof",
+ "https://github.com/hdodenhof/CircleImageView", StandardLicenses.APACHE2),
+ new SoftwareComponent("NoNonsense-FilePicker", "2016", "Jonas Kalderstam",
+ "https://github.com/spacecowboy/NoNonsense-FilePicker", StandardLicenses.MPL2),
+ new SoftwareComponent("ExoPlayer", "2014 - 2020", "Google Inc",
+ "https://github.com/google/ExoPlayer", StandardLicenses.APACHE2),
+ new SoftwareComponent("RxAndroid", "2015 - 2018", "The RxAndroid authors",
+ "https://github.com/ReactiveX/RxAndroid", StandardLicenses.APACHE2),
+ new SoftwareComponent("RxJava", "2016 - 2020", "RxJava Contributors",
+ "https://github.com/ReactiveX/RxJava", StandardLicenses.APACHE2),
+ new SoftwareComponent("RxBinding", "2015 - 2018", "Jake Wharton",
+ "https://github.com/JakeWharton/RxBinding", StandardLicenses.APACHE2),
+ new SoftwareComponent("PrettyTime", "2012 - 2020", "Lincoln Baxter, III",
+ "https://github.com/ocpsoft/prettytime", StandardLicenses.APACHE2),
+ new SoftwareComponent("Markwon", "2017 - 2020", "Noties",
+ "https://github.com/noties/Markwon", StandardLicenses.APACHE2),
+ new SoftwareComponent("Groupie", "2016", "Lisa Wray",
+ "https://github.com/lisawray/groupie", StandardLicenses.MIT)
};
/**
@@ -65,7 +81,7 @@ public class AboutActivity extends AppCompatActivity {
private ViewPager mViewPager;
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ protected void onCreate(final Bundle savedInstanceState) {
assureCorrectAppLanguage(this);
super.onCreate(savedInstanceState);
ThemeHelper.setTheme(this);
@@ -88,10 +104,8 @@ public class AboutActivity extends AppCompatActivity {
tabLayout.setupWithViewPager(mViewPager);
}
-
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
+ public boolean onOptionsItemSelected(final MenuItem item) {
int id = item.getItemId();
switch (id) {
@@ -107,21 +121,20 @@ public class AboutActivity extends AppCompatActivity {
* A placeholder fragment containing a simple view.
*/
public static class AboutFragment extends Fragment {
-
- public AboutFragment() {
- }
+ public AboutFragment() { }
/**
- * Returns a new instance of this fragment for the given section
- * number.
+ * Created a new instance of this fragment for the given section number.
+ *
+ * @return New instance of {@link AboutFragment}
*/
public static AboutFragment newInstance() {
return new AboutFragment();
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
+ public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
+ final Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_about, container, false);
Context context = this.getContext();
@@ -129,40 +142,42 @@ public class AboutActivity extends AppCompatActivity {
version.setText(BuildConfig.VERSION_NAME);
View githubLink = rootView.findViewById(R.id.github_link);
- githubLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.github_url), context));
+ githubLink.setOnClickListener(nv ->
+ openWebsite(context.getString(R.string.github_url), context));
View donationLink = rootView.findViewById(R.id.donation_link);
- donationLink.setOnClickListener(v -> openWebsite(context.getString(R.string.donation_url), context));
+ donationLink.setOnClickListener(v ->
+ openWebsite(context.getString(R.string.donation_url), context));
View websiteLink = rootView.findViewById(R.id.website_link);
- websiteLink.setOnClickListener(nv -> openWebsite(context.getString(R.string.website_url), context));
+ websiteLink.setOnClickListener(nv ->
+ openWebsite(context.getString(R.string.website_url), context));
View privacyPolicyLink = rootView.findViewById(R.id.privacy_policy_link);
- privacyPolicyLink.setOnClickListener(v -> openWebsite(context.getString(R.string.privacy_policy_url), context));
+ privacyPolicyLink.setOnClickListener(v ->
+ openWebsite(context.getString(R.string.privacy_policy_url), context));
return rootView;
}
- private void openWebsite(String url, Context context) {
+ private void openWebsite(final String url, final Context context) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(intent);
}
}
-
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
-
- public SectionsPagerAdapter(FragmentManager fm) {
+ public SectionsPagerAdapter(final FragmentManager fm) {
super(fm);
}
@Override
- public Fragment getItem(int position) {
+ public Fragment getItem(final int position) {
switch (position) {
case 0:
return AboutFragment.newInstance();
@@ -179,7 +194,7 @@ public class AboutActivity extends AppCompatActivity {
}
@Override
- public CharSequence getPageTitle(int position) {
+ public CharSequence getPageTitle(final int position) {
switch (position) {
case 0:
return getString(R.string.tab_about);
diff --git a/app/src/main/java/org/schabi/newpipe/about/License.java b/app/src/main/java/org/schabi/newpipe/about/License.java
index e51e1d0f1..370009860 100644
--- a/app/src/main/java/org/schabi/newpipe/about/License.java
+++ b/app/src/main/java/org/schabi/newpipe/about/License.java
@@ -5,18 +5,17 @@ import android.os.Parcel;
import android.os.Parcelable;
/**
- * A software license
+ * Class for storing information about a software license.
*/
public class License implements Parcelable {
-
public static final Creator CREATOR = new Creator() {
@Override
- public License createFromParcel(Parcel source) {
+ public License createFromParcel(final Parcel source) {
return new License(source);
}
@Override
- public License[] newArray(int size) {
+ public License[] newArray(final int size) {
return new License[size];
}
};
@@ -24,16 +23,22 @@ public class License implements Parcelable {
private final String name;
private String filename;
- public License(String name, String abbreviation, String filename) {
- if(name == null) throw new NullPointerException("name is null");
- if(abbreviation == null) throw new NullPointerException("abbreviation is null");
- if(filename == null) throw new NullPointerException("filename is null");
+ public License(final String name, final String abbreviation, final String filename) {
+ if (name == null) {
+ throw new NullPointerException("name is null");
+ }
+ if (abbreviation == null) {
+ throw new NullPointerException("abbreviation is null");
+ }
+ if (filename == null) {
+ throw new NullPointerException("filename is null");
+ }
this.name = name;
this.filename = filename;
this.abbreviation = abbreviation;
}
- protected License(Parcel in) {
+ protected License(final Parcel in) {
this.filename = in.readString();
this.abbreviation = in.readString();
this.name = in.readString();
@@ -50,7 +55,7 @@ public class License implements Parcelable {
public String getAbbreviation() {
return abbreviation;
}
-
+
public String getFilename() {
return filename;
}
@@ -61,7 +66,7 @@ public class License implements Parcelable {
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(final Parcel dest, final int flags) {
dest.writeString(this.filename);
dest.writeString(this.abbreviation);
dest.writeString(this.name);
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
index fe78ff9f1..0bda79fee 100644
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java
@@ -5,26 +5,32 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
-import android.view.*;
-import android.widget.TextView;
+
import org.schabi.newpipe.R;
import java.util.Arrays;
import java.util.Comparator;
/**
- * Fragment containing the software licenses
+ * Fragment containing the software licenses.
*/
public class LicenseFragment extends Fragment {
-
private static final String ARG_COMPONENTS = "components";
private SoftwareComponent[] softwareComponents;
private SoftwareComponent mComponentForContextMenu;
- public static LicenseFragment newInstance(SoftwareComponent[] softwareComponents) {
- if(softwareComponents == null) {
+ public static LicenseFragment newInstance(final SoftwareComponent[] softwareComponents) {
+ if (softwareComponents == null) {
throw new NullPointerException("softwareComponents is null");
}
LicenseFragment fragment = new LicenseFragment();
@@ -35,23 +41,25 @@ public class LicenseFragment extends Fragment {
}
/**
- * Shows a popup containing the license
+ * Shows a popup containing the license.
+ *
* @param context the context to use
* @param license the license to show
*/
- public static void showLicense(Context context, License license) {
+ public static void showLicense(final Context context, final License license) {
new LicenseFragmentHelper((Activity) context).execute(license);
}
@Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
+ public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- softwareComponents = (SoftwareComponent[]) getArguments().getParcelableArray(ARG_COMPONENTS);
+ softwareComponents = (SoftwareComponent[]) getArguments()
+ .getParcelableArray(ARG_COMPONENTS);
// Sort components by name
Arrays.sort(softwareComponents, new Comparator() {
@Override
- public int compare(SoftwareComponent o1, SoftwareComponent o2) {
+ public int compare(final SoftwareComponent o1, final SoftwareComponent o2) {
return o1.getName().compareTo(o2.getName());
}
});
@@ -59,7 +67,8 @@ public class LicenseFragment extends Fragment {
@Nullable
@Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
+ @Nullable final Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_licenses, container, false);
ViewGroup softwareComponentsView = rootView.findViewById(R.id.software_components);
@@ -67,7 +76,8 @@ public class LicenseFragment extends Fragment {
licenseLink.setOnClickListener(new OnReadFullLicenseClickListener());
for (final SoftwareComponent component : softwareComponents) {
- View componentView = inflater.inflate(R.layout.item_software_component, container, false);
+ View componentView = inflater
+ .inflate(R.layout.item_software_component, container, false);
TextView softwareName = componentView.findViewById(R.id.name);
TextView copyright = componentView.findViewById(R.id.copyright);
softwareName.setText(component.getName());
@@ -79,7 +89,7 @@ public class LicenseFragment extends Fragment {
componentView.setTag(component);
componentView.setOnClickListener(new View.OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(final View v) {
Context context = v.getContext();
if (context != null) {
showLicense(context, component.getLicense());
@@ -93,7 +103,8 @@ public class LicenseFragment extends Fragment {
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ public void onCreateContextMenu(final ContextMenu menu, final View v,
+ final ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = getActivity().getMenuInflater();
SoftwareComponent component = (SoftwareComponent) v.getTag();
menu.setHeaderTitle(component.getName());
@@ -103,7 +114,7 @@ public class LicenseFragment extends Fragment {
}
@Override
- public boolean onContextItemSelected(MenuItem item) {
+ public boolean onContextItemSelected(final MenuItem item) {
// item.getMenuInfo() is null so we use the tag of the view
final SoftwareComponent component = mComponentForContextMenu;
if (component == null) {
@@ -119,14 +130,14 @@ public class LicenseFragment extends Fragment {
return false;
}
- private void openWebsite(String componentLink) {
+ 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(View v) {
+ public void onClick(final View v) {
LicenseFragment.showLicense(v.getContext(), StandardLicenses.GPL3);
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
index 9a11b19cc..94a1532f5 100644
--- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java
@@ -2,30 +2,103 @@ package org.schabi.newpipe.about;
import android.app.Activity;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
import android.os.AsyncTask;
+import android.webkit.WebView;
+
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
-import android.webkit.WebView;
+
import org.schabi.newpipe.R;
import org.schabi.newpipe.util.ThemeHelper;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
public class LicenseFragmentHelper extends AsyncTask