From 74bda719a6224cfae1c307ce860bdafc51634e61 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Fri, 22 Sep 2017 11:07:10 +0200 Subject: [PATCH 001/154] add themes to license dialog --- app/src/main/assets/gpl_2.html | 6 +- app/src/main/assets/mpl2.html | 1 + .../org/schabi/newpipe/about/License.java | 4 ++ .../schabi/newpipe/about/LicenseFragment.java | 57 ++++++++++++++++--- app/src/main/res/values/colors.xml | 8 ++- 5 files changed, 64 insertions(+), 12 deletions(-) diff --git a/app/src/main/assets/gpl_2.html b/app/src/main/assets/gpl_2.html index 37d578a69..0e1b8827e 100644 --- a/app/src/main/assets/gpl_2.html +++ b/app/src/main/assets/gpl_2.html @@ -15,9 +15,9 @@ Version 2, June 1991

-Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
-51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
-
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
diff --git a/app/src/main/assets/mpl2.html b/app/src/main/assets/mpl2.html index 5009391a0..5e988a70c 100644 --- a/app/src/main/assets/mpl2.html +++ b/app/src/main/assets/mpl2.html @@ -4,6 +4,7 @@ Mozilla Public License, version 2.0 +

Mozilla Public License
Version 2.0

1. Definitions

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 312ad5087..e51e1d0f1 100644 --- a/app/src/main/java/org/schabi/newpipe/about/License.java +++ b/app/src/main/java/org/schabi/newpipe/about/License.java @@ -50,6 +50,10 @@ public class License implements Parcelable { public String getAbbreviation() { return abbreviation; } + + public String getFilename() { + return filename; + } @Override public int describeContents() { 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 8b0e67d18..42e886d30 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java @@ -8,17 +8,15 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; -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.view.*; import android.webkit.WebView; import android.widget.TextView; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.ThemeHelper; +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; @@ -58,7 +56,26 @@ public class LicenseFragment extends Fragment { alert.setTitle(license.getName()); WebView wv = new WebView(context); - wv.loadUrl(license.getContentUri().toString()); + String licenseContent = ""; + String webViewData; + try { + BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8")); + String str; + while ((str = in.readLine()) != null) { + licenseContent += str; + } + in.close(); + + // split the HTML file and insert the stylesheet into the HEAD of the file + String[] insert = licenseContent.split(""); + webViewData = insert[0] + "" + + insert[1]; + } catch (Exception e) { + throw new NullPointerException("could not get license file:" + getLicenseStylesheet(context)); + } + wv.loadData(webViewData, "text/html", "utf-8"); + alert.setView(wv); alert.setNegativeButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override @@ -69,6 +86,32 @@ public class LicenseFragment extends Fragment { alert.show(); } + public static String getLicenseStylesheet(Context context) { + return "body{padding:12px 15px;margin:0;background:#" + + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) + ? R.color.light_license_background_color + : R.color.dark_license_background_color) + + ";color:#" + + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) + ? R.color.light_license_text_color + : R.color.dark_license_text_color) + ";}" + + "a[href]{color:#" + + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) + ? R.color.light_youtube_primary_color + : R.color.dark_youtube_primary_color) + ";}" + + "pre{white-space: pre-wrap;}"; + } + + /** + * Cast R.color to a hexadecimal color value + * @param context the context to use + * @param color the color number from R.color + * @return a six characters long String with hexadecimal RGB values + */ + public static String getHexRGBColor(Context context, int color) { + return context.getResources().getString(color).substring(3); + } + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9f8a41bc5..3008f68ee 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -10,6 +10,8 @@ #48868686 #1fa6a6a6 #5a000000 + #ffffff + #212121 #222222 @@ -20,6 +22,8 @@ #48ffffff #1f717171 #82000000 + #424242 + #ffffff #000 @@ -37,8 +41,8 @@ #e53935 #fff - #d6d6d6d - #717171d + #d6d6d6 + #717171 #607D8B From 0d2296917a791d26845ffdd7c4040480c867a901 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sat, 23 Sep 2017 17:39:04 +0200 Subject: [PATCH 002/154] creating first prototype of kiosk page --- app/build.gradle | 2 +- .../newpipe/fragments/MainFragment.java | 17 +- .../fragments/list/kisok/KioskFragment.java | 148 ++++++++++++++++++ .../schabi/newpipe/util/ExtractorHelper.java | 19 +++ app/src/main/res/layout/fragment_kiosk.xml | 64 ++++++++ app/src/main/res/layout/kiosk_header.xml | 23 +++ app/src/main/res/values/dimens.xml | 2 + 7 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java create mode 100644 app/src/main/res/layout/fragment_kiosk.xml create mode 100644 app/src/main/res/layout/kiosk_header.xml diff --git a/app/build.gradle b/app/build.gradle index 941dc72d7..742b00d4b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,7 +45,7 @@ dependencies { exclude module: 'support-annotations' } - compile 'com.github.TeamNewPipe:NewPipeExtractor:7ae274b' + compile 'com.github.TeamNewPipe:NewPipeExtractor:7fffef5' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 236f95968..6f313aee0 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -18,11 +18,16 @@ import android.view.ViewGroup; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; +import org.schabi.newpipe.fragments.list.kisok.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; import org.schabi.newpipe.util.NavigationHelper; public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { private ViewPager viewPager; + private boolean showBlankTab = false; + + //todo: FIX THIS URGENTLY + public int currentServiceId = 0; //for youtube /*////////////////////////////////////////////////////////////////////////// // Fragment's LifeCycle @@ -115,7 +120,17 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte case 1: return new SubscriptionFragment(); default: - return new BlankFragment(); + if(showBlankTab) { + return new BlankFragment(); + } else { + try { + return KioskFragment.getInstance(currentServiceId); + } catch (Exception e) { + //todo: replace this with propper error reporting + e.printStackTrace(); + return new BlankFragment(); + } + } } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java new file mode 100644 index 000000000..a1e5a960f --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java @@ -0,0 +1,148 @@ +package org.schabi.newpipe.fragments.list.kisok; + +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.ListExtractor; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.UrlIdHandler; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.kiosk.KioskInfo; +import org.schabi.newpipe.extractor.kiosk.KioskList; +import org.schabi.newpipe.extractor.playlist.PlaylistInfo; +import org.schabi.newpipe.fragments.list.BaseListInfoFragment; +import org.schabi.newpipe.fragments.list.channel.ChannelFragment; +import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.NavigationHelper; + +import io.reactivex.Single; + +import static org.schabi.newpipe.util.AnimationUtils.animateView; + +/** + * Created by Christian Schabesberger on 23.09.17. + * + * Copyright (C) Christian Schabesberger 2017 + * KioskFragment.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenHitboxStreams. If not, see . + */ + +public class KioskFragment extends BaseListInfoFragment { + + + /*////////////////////////////////////////////////////////////////////////// + // Views + //////////////////////////////////////////////////////////////////////////*/ + + private View headerRootLayout; + private TextView headerTitleView; + + public static KioskFragment getInstance(int serviceId) + throws ExtractionException { + KioskFragment instance = new KioskFragment(); + StreamingService service = NewPipe.getService(serviceId); + String defaultKioskType = service.getKioskList().getDefaultKioskType(); + UrlIdHandler defaultKioskTypeUrlIdHandler = service.getKioskList() + .getUrlIdHandlerByType(defaultKioskType); + instance.setInitialData(serviceId, + defaultKioskTypeUrlIdHandler.getUrl(defaultKioskType), + defaultKioskType); + return instance; + } + + /*////////////////////////////////////////////////////////////////////////// + // LifeCycle + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_kiosk, container, false); + } + + /*////////////////////////////////////////////////////////////////////////// + // Init + //////////////////////////////////////////////////////////////////////////*/ + + protected View getListHeader() { + headerRootLayout = activity.getLayoutInflater().inflate(R.layout.kiosk_header, itemsList, false); + headerTitleView = headerRootLayout.findViewById(R.id.kiosk_title_view); + + return headerRootLayout; + } + + /*////////////////////////////////////////////////////////////////////////// + // Load and handle + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public Single loadResult(boolean forceReload) { + String contentCountry = PreferenceManager + .getDefaultSharedPreferences(activity) + .getString(getString(R.string.search_language_key), + getString(R.string.default_language_value)); + return ExtractorHelper.getKioskInfo(serviceId, url, contentCountry, forceReload); + } + + @Override + public Single loadMoreItemsLogic() { + return ExtractorHelper.getMoreKisokItems(serviceId, url, currentNextItemsUrl); + } + + /*////////////////////////////////////////////////////////////////////////// + // Contract + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void showLoading() { + super.showLoading(); + animateView(headerRootLayout, false, 200); + animateView(itemsList, false, 100); + } + + @Override + public void handleResult(@NonNull final KioskInfo result) { + super.handleResult(result); + + animateView(headerRootLayout, true, 100); + headerTitleView.setText(result.type); + + if (!result.errors.isEmpty()) { + showSnackBarError(result.errors, + UserAction.REQUESTED_PLAYLIST, + NewPipe.getNameOfService(result.service_id), result.url, 0); + } + } + + @Override + public void handleNextItems(ListExtractor.NextItemsResult result) { + super.handleNextItems(result); + + if (!result.errors.isEmpty()) { + showSnackBarError(result.errors, + UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(serviceId) + , "Get next page of: " + url, 0); + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 5cf9f057e..856769da8 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -26,6 +26,7 @@ import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.ListExtractor.NextItemsResult; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.channel.ChannelInfo; +import org.schabi.newpipe.extractor.kiosk.KioskInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.search.SearchEngine; import org.schabi.newpipe.extractor.search.SearchResult; @@ -124,6 +125,24 @@ public final class ExtractorHelper { }); } + public static Single getKioskInfo(final int serviceId, final String url, final String contentCountry, boolean forceLoad) { + return checkCache(forceLoad, serviceId, url, Single.fromCallable(new Callable() { + @Override + public KioskInfo call() throws Exception { + return KioskInfo.getInfo(NewPipe.getService(serviceId), url, contentCountry); + } + })); + } + + public static Single getMoreKisokItems(final int serviceId, final String url, final String nextStreamsUrl) { + return Single.fromCallable(new Callable() { + @Override + public NextItemsResult call() throws Exception { + return KioskInfo.getMoreItems(NewPipe.getService(serviceId), url, nextStreamsUrl); + } + }); + } + /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/res/layout/fragment_kiosk.xml b/app/src/main/res/layout/fragment_kiosk.xml new file mode 100644 index 000000000..a85c30cb1 --- /dev/null +++ b/app/src/main/res/layout/fragment_kiosk.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/kiosk_header.xml b/app/src/main/res/layout/kiosk_header.xml new file mode 100644 index 000000000..c161ca83d --- /dev/null +++ b/app/src/main/res/layout/kiosk_header.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 32e300ca1..1c5265b8b 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -67,4 +67,6 @@ 24dp 28dp + + 30sp From f6c624b59a56904ddbeba1bad2a19bfa5d024c49 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 25 Sep 2017 12:49:14 +0200 Subject: [PATCH 003/154] make name translatable, fix tab on items, remove back button s/kisok/kiosk/g --- app/build.gradle | 2 +- .../newpipe/fragments/MainFragment.java | 2 +- .../list/{kisok => kiosk}/KioskFragment.java | 52 +++++++++++++++---- .../schabi/newpipe/util/ExtractorHelper.java | 2 +- 4 files changed, 44 insertions(+), 14 deletions(-) rename app/src/main/java/org/schabi/newpipe/fragments/list/{kisok => kiosk}/KioskFragment.java (73%) diff --git a/app/build.gradle b/app/build.gradle index 8bb0d19b2..a3ad71b5c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ dependencies { exclude module: 'support-annotations' } - compile 'com.github.TeamNewPipe:NewPipeExtractor:7fffef5' + compile 'com.github.TeamNewPipe:NewPipeExtractor:466d87c' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 6f313aee0..3ca14f552 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -18,7 +18,7 @@ import android.view.ViewGroup; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; -import org.schabi.newpipe.fragments.list.kisok.KioskFragment; +import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; import org.schabi.newpipe.util.NavigationHelper; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java similarity index 73% rename from app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java rename to app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index a1e5a960f..932cf2a44 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kisok/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -1,11 +1,13 @@ -package org.schabi.newpipe.fragments.list.kisok; +package org.schabi.newpipe.fragments.list.kiosk; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.text.TextUtils; +import android.support.v7.app.ActionBar; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @@ -17,10 +19,9 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.UrlIdHandler; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.kiosk.KioskInfo; -import org.schabi.newpipe.extractor.kiosk.KioskList; -import org.schabi.newpipe.extractor.playlist.PlaylistInfo; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; -import org.schabi.newpipe.fragments.list.channel.ChannelFragment; +import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.NavigationHelper; @@ -63,12 +64,12 @@ public class KioskFragment extends BaseListInfoFragment { throws ExtractionException { KioskFragment instance = new KioskFragment(); StreamingService service = NewPipe.getService(serviceId); - String defaultKioskType = service.getKioskList().getDefaultKioskType(); + String defaultKioskId = service.getKioskList().getDefaultKioskId(); UrlIdHandler defaultKioskTypeUrlIdHandler = service.getKioskList() - .getUrlIdHandlerByType(defaultKioskType); + .getUrlIdHandlerByType(defaultKioskId); instance.setInitialData(serviceId, - defaultKioskTypeUrlIdHandler.getUrl(defaultKioskType), - defaultKioskType); + defaultKioskTypeUrlIdHandler.getUrl(defaultKioskId), + defaultKioskId); return instance; } @@ -81,6 +82,20 @@ public class KioskFragment extends BaseListInfoFragment { return inflater.inflate(R.layout.fragment_kiosk, container, false); } + /*////////////////////////////////////////////////////////////////////////// + // Menu + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + ActionBar supportActionBar = activity.getSupportActionBar(); + if (supportActionBar != null) { + supportActionBar.setDisplayShowTitleEnabled(false); + supportActionBar.setDisplayHomeAsUpEnabled(false); + } + } + /*////////////////////////////////////////////////////////////////////////// // Init //////////////////////////////////////////////////////////////////////////*/ @@ -92,6 +107,21 @@ public class KioskFragment extends BaseListInfoFragment { return headerRootLayout; } + @Override + public void initListeners() { + // We have to override this because the default implementation of this function calls + // openVideoDetailFragment on getFragmentManager() but what we want here is + // getParentFragment().getFragmentManager() + infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + @Override + public void selected(StreamInfoItem selectedItem) { + onItemSelected(selectedItem); + NavigationHelper.openVideoDetailFragment(getParentFragment().getFragmentManager(), + selectedItem.service_id, selectedItem.url, selectedItem.name); + } + }); + } + /*////////////////////////////////////////////////////////////////////////// // Load and handle //////////////////////////////////////////////////////////////////////////*/ @@ -107,7 +137,7 @@ public class KioskFragment extends BaseListInfoFragment { @Override public Single loadMoreItemsLogic() { - return ExtractorHelper.getMoreKisokItems(serviceId, url, currentNextItemsUrl); + return ExtractorHelper.getMoreKioskItems(serviceId, url, currentNextItemsUrl); } /*////////////////////////////////////////////////////////////////////////// @@ -126,7 +156,7 @@ public class KioskFragment extends BaseListInfoFragment { super.handleResult(result); animateView(headerRootLayout, true, 100); - headerTitleView.setText(result.type); + headerTitleView.setText("★★ " +result.name+ " ★★"); if (!result.errors.isEmpty()) { showSnackBarError(result.errors, diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index 856769da8..b1ed2e901 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -134,7 +134,7 @@ public final class ExtractorHelper { })); } - public static Single getMoreKisokItems(final int serviceId, final String url, final String nextStreamsUrl) { + public static Single getMoreKioskItems(final int serviceId, final String url, final String nextStreamsUrl) { return Single.fromCallable(new Callable() { @Override public NextItemsResult call() throws Exception { From 5ebde97352c28fd540ce6ee1577bb2e0e2bc7145 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 25 Sep 2017 13:05:54 +0200 Subject: [PATCH 004/154] fix error report and add setting for current_service --- .../schabi/newpipe/fragments/MainFragment.java | 15 +++++++++++---- app/src/main/res/values/settings_keys.xml | 3 +++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 3ca14f552..a92287089 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -8,6 +8,7 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; +import android.support.v7.preference.PreferenceManager; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -20,14 +21,15 @@ import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { private ViewPager viewPager; private boolean showBlankTab = false; - //todo: FIX THIS URGENTLY - public int currentServiceId = 0; //for youtube + public int currentServiceId = -1; /*////////////////////////////////////////////////////////////////////////// // Fragment's LifeCycle @@ -41,6 +43,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + currentServiceId = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getString(getString(R.string.current_service_key), "0")); return inflater.inflate(R.layout.fragment_main, container, false); } @@ -126,8 +130,11 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte try { return KioskFragment.getInstance(currentServiceId); } catch (Exception e) { - //todo: replace this with propper error reporting - e.printStackTrace(); + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); return new BlankFragment(); } } diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 76cd10681..f5cef09a0 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -1,5 +1,8 @@ + + current_service + download_path download_path_audio From 65c8b6e66a14144c59173cc085108526feda12d5 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 26 Sep 2017 17:29:38 +0200 Subject: [PATCH 005/154] add selection for front page --- .../org/schabi/newpipe/RouterActivity.java | 10 +- .../newpipe/fragments/MainFragment.java | 49 +++-- .../settings/ContentSettingsFragment.java | 49 +++++ .../settings/SelectChannelFragment.java | 184 ++++++++++++++++++ .../res/layout/select_channel_fragment.xml | 42 ++++ .../main/res/layout/select_channel_item.xml | 38 ++++ app/src/main/res/values/settings_keys.xml | 16 ++ app/src/main/res/values/strings.xml | 11 ++ app/src/main/res/xml/content_settings.xml | 7 + 9 files changed, 387 insertions(+), 19 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java create mode 100644 app/src/main/res/layout/select_channel_fragment.xml create mode 100644 app/src/main/res/layout/select_channel_item.xml diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index ce9c3802f..41e557b52 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -10,22 +10,22 @@ import org.schabi.newpipe.util.NavigationHelper; import java.util.Collection; import java.util.HashSet; -/* +/** * Copyright (C) Christian Schabesberger 2017 - * RouterActivity .java is part of NewPipe. + * RouterActivity.java is part of NewPipe. * - * OpenHitboxStreams is free software: you can redistribute it and/or modify + * NewPipe is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * OpenHitboxStreams is distributed in the hope that it will be useful, + * NewPipe is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with OpenHitboxStreams. If not, see . + * along with NewPipe. If not, see . */ /** diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index a92287089..0a8e9b655 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -25,6 +25,8 @@ import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.NavigationHelper; +import java.util.concurrent.ExecutionException; + public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { private ViewPager viewPager; private boolean showBlankTab = false; @@ -124,20 +126,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte case 1: return new SubscriptionFragment(); default: - if(showBlankTab) { - return new BlankFragment(); - } else { - try { - return KioskFragment.getInstance(currentServiceId); - } catch (Exception e) { - ErrorActivity.reportError(activity, e, - activity.getClass(), - null, - ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, - "none", "", R.string.app_ui_crash)); - return new BlankFragment(); - } - } + return getMainPageFramgent(); } } @@ -151,4 +140,36 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte return this.tabTitles.length; } } + + /*////////////////////////////////////////////////////////////////////////// + // Main page content + //////////////////////////////////////////////////////////////////////////*/ + + private Fragment getMainPageFramgent() { + try { + final String set_main_page = PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getString(getString(R.string.main_page_content_key), + getString(R.string.main_page_selectd_kiosk_id)); + if(set_main_page.equals(getString(R.string.blank_page_key))) { + return new BlankFragment(); + } else if(set_main_page.equals(getString(R.string.kiosk_page_key))) { + return KioskFragment.getInstance(currentServiceId); + } else if(set_main_page.equals(getString(R.string.feed_page_key))) { + return new BlankFragment(); + } else if(set_main_page.equals(getString(R.string.channel_page_key))) { + return new BlankFragment(); + } else { + return new BlankFragment(); + } + + + } catch (Exception e) { + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + return new BlankFragment(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 6021b40fd..0164f584e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -1,6 +1,9 @@ package org.schabi.newpipe.settings; import android.os.Bundle; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.util.Log; import org.schabi.newpipe.R; @@ -8,5 +11,51 @@ public class ContentSettingsFragment extends BasePreferenceFragment { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.content_settings); + + findPreference(getString(R.string.main_page_content_key)) + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValueO) { + final String newValue = newValueO.toString(); + + if(newValue.equals(getString(R.string.kiosk_page_key))) { + //todo on multyservice support show a kiosk an service selector here + } else if(newValue.equals(getString(R.string.channel_page_key))) { + SelectChannelFragment selectChannelFragment = new SelectChannelFragment(); + selectChannelFragment.setOnSelectedLisener(new SelectChannelFragment.OnSelectedLisener() { + @Override + public void onChannelSelected(String url, String name, int service) { + defaultPreferences.edit() + .putInt(getString(R.string.main_page_selected_service), service).apply(); + defaultPreferences.edit() + .putString(getString(R.string.main_page_selected_channel_url), url).apply(); + defaultPreferences.edit() + .putString(getString(R.string.main_page_selected_channel_name), name).apply(); + + //change summery + Preference pref = findPreference(getString(R.string.main_page_content_key)); + pref.setSummary(name); + + } + }); + selectChannelFragment.show(getFragmentManager(), "select_channel"); + } + + return true; + } + }); + } + + @Override + public void onResume() { + super.onResume(); + + final String mainPageContentKey = getString(R.string.main_page_content_key); + if(defaultPreferences.getString(mainPageContentKey, + getString(R.string.blank_page_key)) + .equals(getString(R.string.channel_page_key))) { + Preference pref = findPreference(getString(R.string.main_page_content_key)); + pref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error")); + } } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java new file mode 100644 index 000000000..5cc7070f9 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -0,0 +1,184 @@ +package org.schabi.newpipe.settings; + +import android.app.Activity; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.subscription.SubscriptionEntity; +import org.schabi.newpipe.fragments.subscription.SubscriptionService; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; + +import java.util.List; +import java.util.Vector; + +import de.hdodenhof.circleimageview.CircleImageView; +import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + + +/** + * Created by Christian Schabesberger on 26.09.17. + * SelectChannelFragment.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class SelectChannelFragment extends DialogFragment { + private SelectChannelAdapter channelAdapter; + private SubscriptionService subscriptionService; + + private ProgressBar progressBar; + private TextView emptyView; + private RecyclerView recyclerView; + + private List subscriptions = new Vector<>(); + + public interface OnSelectedLisener { + public void onChannelSelected(String url, String name, int service); + } + + OnSelectedLisener onSelectedLisener; + public void setOnSelectedLisener(OnSelectedLisener listener) { + onSelectedLisener = listener; + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.select_channel_fragment, container, false); + recyclerView = (RecyclerView) v.findViewById(R.id.items_list); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + channelAdapter = new SelectChannelAdapter(); + recyclerView.setAdapter(channelAdapter); + + progressBar = v.findViewById(R.id.progressBar); + emptyView = v.findViewById(R.id.empty_state_view); + progressBar.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + emptyView.setVisibility(View.GONE); + + + subscriptionService = SubscriptionService.getInstance(); + subscriptionService.getSubscription().toObservable() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getSubscriptionObserver()); + + return v; + } + + private void displayChannels(List subscriptions) { + this.subscriptions = subscriptions; + progressBar.setVisibility(View.GONE); + if(subscriptions.isEmpty()) { + emptyView.setVisibility(View.VISIBLE); + return; + } + recyclerView.setVisibility(View.VISIBLE); + + } + + private void clickedItem(int position) { + if(onSelectedLisener != null) { + SubscriptionEntity entry = subscriptions.get(position); + onSelectedLisener.onChannelSelected(entry.getUrl(), entry.getName(), entry.getServiceId()); + } + dismiss(); + } + + private Observer> getSubscriptionObserver() { + return new Observer>() { + @Override + public void onSubscribe(Disposable d) { + } + + @Override + public void onNext(List subscriptions) { + displayChannels(subscriptions); + } + + @Override + public void onError(Throwable exception) { + onError(exception); + } + + @Override + public void onComplete() { + } + }; + } + + private class SelectChannelAdapter extends + RecyclerView.Adapter { + + @Override + public SelectChannelItemHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View item = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.select_channel_item, parent, false); + return new SelectChannelItemHolder(item); + } + + @Override + public void onBindViewHolder(SelectChannelItemHolder holder, final int position) { + SubscriptionEntity entry = subscriptions.get(position); + holder.titleView.setText(entry.getName()); + holder.view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + clickedItem(position); + } + }); + } + + @Override + public int getItemCount() { + return subscriptions.size(); + } + + public class SelectChannelItemHolder extends RecyclerView.ViewHolder { + public SelectChannelItemHolder(View v) { + super(v); + this.view = v; + thumbnailView = v.findViewById(R.id.itemThumbnailView); + titleView = v.findViewById(R.id.itemTitleView); + } + public View view; + public CircleImageView thumbnailView; + public TextView titleView; + } + } + + protected boolean onError(Throwable e) { + final Activity activity = getActivity(); + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + return true; + } +} diff --git a/app/src/main/res/layout/select_channel_fragment.xml b/app/src/main/res/layout/select_channel_fragment.xml new file mode 100644 index 000000000..9c8e79779 --- /dev/null +++ b/app/src/main/res/layout/select_channel_fragment.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/select_channel_item.xml b/app/src/main/res/layout/select_channel_item.xml new file mode 100644 index 000000000..903894aeb --- /dev/null +++ b/app/src/main/res/layout/select_channel_item.xml @@ -0,0 +1,38 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index f5cef09a0..878d51741 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -92,6 +92,7 @@ @string/black_theme_title + show_search_suggestions show_play_with_kodi show_next_video @@ -101,6 +102,21 @@ use_tor enable_search_history enable_watch_history + main_page_content + blank_page + feed_page + kiosk_page + channel_page + + @string/blank_page_key + @string/kiosk_page_key + @string/feed_page_key + @string/channel_page_key + + main_page_selected_service + main_page_selected_channel_name + main_page_selected_channel_url + main_page_selectd_kiosk_id file_rename diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a5b3993d8..e16bfca16 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -265,4 +265,15 @@ The history is empty History cleared Item deleted + + + Content of main page + + Blank Page + Trending Page + Feed Page + Channel Page + + Select a channel + No channel subscribed yet diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index 63cd3cd01..11672671e 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -21,5 +21,12 @@ android:key="@string/show_search_suggestions_key" android:summary="@string/show_search_suggestions_summary" android:title="@string/show_search_suggestions_title"/> + From 3490273b498156ccd56ee29b63d0db09fa2cb169 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Tue, 26 Sep 2017 18:16:39 +0200 Subject: [PATCH 006/154] made fragments frontpagable --- .../newpipe/fragments/BaseStateFragment.java | 6 +++++ .../newpipe/fragments/MainFragment.java | 22 +++++++++++++++---- .../fragments/list/BaseListFragment.java | 12 +++++++--- .../list/channel/ChannelFragment.java | 19 +++++++++++----- .../fragments/list/feed/FeedFragment.java | 7 ++++-- .../fragments/list/kiosk/KioskFragment.java | 18 +-------------- .../settings/ContentSettingsFragment.java | 2 ++ .../settings/SelectChannelFragment.java | 1 - 8 files changed, 54 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index 5a8d8dd52..80f05585b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -50,6 +50,8 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC protected Button errorButtonRetry; protected TextView errorTextView; + protected boolean useAsFrontPage = false; + @Override public void onViewCreated(View rootView, Bundle savedInstanceState) { super.onViewCreated(rootView, savedInstanceState); @@ -62,6 +64,10 @@ public abstract class BaseStateFragment extends BaseFragment implements ViewC wasLoading.set(isLoading.get()); } + public void useAsFrontPage(boolean value) { + useAsFrontPage = value; + } + /*////////////////////////////////////////////////////////////////////////// // Init //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 0a8e9b655..aa28c54fb 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.fragments; +import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; @@ -19,6 +20,8 @@ import android.view.ViewGroup; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; +import org.schabi.newpipe.fragments.list.channel.ChannelFragment; +import org.schabi.newpipe.fragments.list.feed.FeedFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; import org.schabi.newpipe.report.ErrorActivity; @@ -153,16 +156,27 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte if(set_main_page.equals(getString(R.string.blank_page_key))) { return new BlankFragment(); } else if(set_main_page.equals(getString(R.string.kiosk_page_key))) { - return KioskFragment.getInstance(currentServiceId); + KioskFragment fragment = KioskFragment.getInstance(currentServiceId); + fragment.useAsFrontPage(true); + return fragment; } else if(set_main_page.equals(getString(R.string.feed_page_key))) { - return new BlankFragment(); + FeedFragment fragment = new FeedFragment(); + fragment.useAsFrontPage(true); + return fragment; } else if(set_main_page.equals(getString(R.string.channel_page_key))) { - return new BlankFragment(); + SharedPreferences preferences = + PreferenceManager.getDefaultSharedPreferences(getActivity()); + int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); + String url = preferences.getString(getString(R.string.main_page_selected_channel_url), + "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"); + String name = preferences.getString(getString(R.string.main_page_selected_channel_name), "Music"); + ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name); + fragment.useAsFrontPage(true); + return fragment; } else { return new BlankFragment(); } - } catch (Exception e) { ErrorActivity.reportError(activity, e, activity.getClass(), diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 4501ab859..48661969f 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -135,7 +135,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem @Override public void selected(StreamInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); + NavigationHelper.openVideoDetailFragment( + useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), + selectedItem.service_id, selectedItem.url, selectedItem.name); } }); @@ -143,7 +145,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem @Override public void selected(ChannelInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openChannelFragment(getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); + NavigationHelper.openChannelFragment( + useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), + selectedItem.service_id, selectedItem.url, selectedItem.name); } }); @@ -151,7 +155,9 @@ public abstract class BaseListFragment extends BaseStateFragment implem @Override public void selected(PlaylistInfoItem selectedItem) { onItemSelected(selectedItem); - NavigationHelper.openPlaylistFragment(getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); + NavigationHelper.openPlaylistFragment( + useAsFrontPage?getParentFragment().getFragmentManager():getFragmentManager(), + selectedItem.service_id, selectedItem.url, selectedItem.name); } }); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index b7148fe63..5a722ec4c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -7,6 +7,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -118,15 +119,21 @@ public class ChannelFragment extends BaseListInfoFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.menu_channel, menu); + ActionBar supportActionBar = activity.getSupportActionBar(); + if(useAsFrontPage) { + supportActionBar.setDisplayHomeAsUpEnabled(false); + supportActionBar.setDisplayShowTitleEnabled(false); + } else { + inflater.inflate(R.menu.menu_channel, menu); + + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); + menuRssButton = menu.findItem(R.id.menu_item_rss); + if (currentInfo != null) { + menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.feed_url)); + } - menuRssButton = menu.findItem(R.id.menu_item_rss); - if (currentInfo != null) { - menuRssButton.setVisible(!TextUtils.isEmpty(currentInfo.feed_url)); } - } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java index 2af9a9270..1a8f8e625 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java @@ -36,10 +36,8 @@ import io.reactivex.MaybeObserver; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; -import io.reactivex.functions.Action; import io.reactivex.functions.Consumer; import io.reactivex.functions.Predicate; -import io.reactivex.schedulers.Schedulers; public class FeedFragment extends BaseListFragment, Void> { @@ -121,6 +119,11 @@ public class FeedFragment extends BaseListFragment, Voi if (supportActionBar != null) { supportActionBar.setTitle(R.string.fragment_whats_new); } + + if(useAsFrontPage) { + supportActionBar.setDisplayHomeAsUpEnabled(false); + supportActionBar.setDisplayShowTitleEnabled(false); + } } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 932cf2a44..3d019e6cb 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -52,7 +52,6 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; public class KioskFragment extends BaseListInfoFragment { - /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ @@ -90,7 +89,7 @@ public class KioskFragment extends BaseListInfoFragment { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); ActionBar supportActionBar = activity.getSupportActionBar(); - if (supportActionBar != null) { + if (supportActionBar != null && useAsFrontPage) { supportActionBar.setDisplayShowTitleEnabled(false); supportActionBar.setDisplayHomeAsUpEnabled(false); } @@ -107,21 +106,6 @@ public class KioskFragment extends BaseListInfoFragment { return headerRootLayout; } - @Override - public void initListeners() { - // We have to override this because the default implementation of this function calls - // openVideoDetailFragment on getFragmentManager() but what we want here is - // getParentFragment().getFragmentManager() - infoListAdapter.setOnStreamSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { - @Override - public void selected(StreamInfoItem selectedItem) { - onItemSelected(selectedItem); - NavigationHelper.openVideoDetailFragment(getParentFragment().getFragmentManager(), - selectedItem.service_id, selectedItem.url, selectedItem.name); - } - }); - } - /*////////////////////////////////////////////////////////////////////////// // Load and handle //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 0164f584e..9b3ffb5ae 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -6,6 +6,7 @@ import android.support.v7.preference.Preference; import android.util.Log; import org.schabi.newpipe.R; +import org.schabi.newpipe.util.Constants; public class ContentSettingsFragment extends BasePreferenceFragment { @Override @@ -36,6 +37,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { Preference pref = findPreference(getString(R.string.main_page_content_key)); pref.setSummary(name); + } }); selectChannelFragment.show(getFragmentManager(), "select_channel"); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 5cc7070f9..16b68196a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -66,7 +66,6 @@ public class SelectChannelFragment extends DialogFragment { onSelectedLisener = listener; } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.select_channel_fragment, container, false); From 29fee28d1d2f7e83ecacade996912a129151acfc Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 28 Sep 2017 15:36:15 +0200 Subject: [PATCH 007/154] fix channel selection --- .../settings/BasePreferenceFragment.java | 2 +- .../settings/ContentSettingsFragment.java | 55 +++++++++++-- .../settings/SelectChannelFragment.java | 79 ++++++++++++++++--- app/src/main/res/values/strings.xml | 12 ++- 4 files changed, 123 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java index a16f7dd79..e3c52cdad 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java @@ -19,8 +19,8 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { @Override public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); defaultPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); + super.onCreate(savedInstanceState); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 9b3ffb5ae..02038f9fa 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -3,22 +3,27 @@ package org.schabi.newpipe.settings; import android.os.Bundle; import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; -import android.util.Log; import org.schabi.newpipe.R; -import org.schabi.newpipe.util.Constants; public class ContentSettingsFragment extends BasePreferenceFragment { + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.content_settings); - findPreference(getString(R.string.main_page_content_key)) - .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key)); + + mainPageContentPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValueO) { final String newValue = newValueO.toString(); + final String mainPrefOldValue = + defaultPreferences.getString(getString(R.string.main_page_content_key), "blank_page"); + final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref); + if(newValue.equals(getString(R.string.kiosk_page_key))) { //todo on multyservice support show a kiosk an service selector here } else if(newValue.equals(getString(R.string.channel_page_key))) { @@ -34,15 +39,25 @@ public class ContentSettingsFragment extends BasePreferenceFragment { .putString(getString(R.string.main_page_selected_channel_name), name).apply(); //change summery - Preference pref = findPreference(getString(R.string.main_page_content_key)); - pref.setSummary(name); - - + mainPageContentPref.setSummary(name); + } + }); + selectChannelFragment.setOnCancelListener(new SelectChannelFragment.OnCancelListener() { + @Override + public void onCancel() { + //defaultPreferences.edit() + // .putString(getString(R.string.main_page_content_key), mainPrefOldValue).apply(); + mainPageContentPref.setSummary(mainPrefOldSummary); + mainPageContentPref.setValue(mainPrefOldValue); } }); selectChannelFragment.show(getFragmentManager(), "select_channel"); } + if(!newValue.equals(getString(R.string.channel_page_key))) { + mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue)); + } + return true; } }); @@ -60,4 +75,28 @@ public class ContentSettingsFragment extends BasePreferenceFragment { pref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error")); } } + + /*////////////////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////////////////*/ + private String getMainPagePrefSummery(final String mainPrefOldValue, final ListPreference mainPageContentPref) { + if(mainPrefOldValue.equals(getString(R.string.channel_page_key))) { + return defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error"); + } else { + return mainPageContentPref.getSummary().toString(); + } + } + + private int getMainPageSummeryByKey(final String key) { + if(key.equals(getString(R.string.blank_page_key))) { + return R.string.blank_page_summary; + } else if(key.equals(getString(R.string.kiosk_page_key))) { + return R.string.kiosk_page_summary; + } else if(key.equals(getString(R.string.feed_page_key))) { + return R.string.feed_page_summary; + } else if(key.equals(getString(R.string.channel_page_key))) { + return R.string.channel_page_summary; + } + return R.string.blank_page_summary; + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 16b68196a..97af11f1b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -13,6 +13,9 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; + import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.fragments.subscription.SubscriptionService; @@ -50,6 +53,7 @@ import io.reactivex.schedulers.Schedulers; public class SelectChannelFragment extends DialogFragment { private SelectChannelAdapter channelAdapter; private SubscriptionService subscriptionService; + private ImageLoader imageLoader = ImageLoader.getInstance(); private ProgressBar progressBar; private TextView emptyView; @@ -57,15 +61,31 @@ public class SelectChannelFragment extends DialogFragment { private List subscriptions = new Vector<>(); - public interface OnSelectedLisener { - public void onChannelSelected(String url, String name, int service); - } + /*////////////////////////////////////////////////////////////////////////// + // Interfaces + //////////////////////////////////////////////////////////////////////////*/ - OnSelectedLisener onSelectedLisener; + public interface OnSelectedLisener { + void onChannelSelected(String url, String name, int service); + } + OnSelectedLisener onSelectedLisener = null; public void setOnSelectedLisener(OnSelectedLisener listener) { onSelectedLisener = listener; } + public interface OnCancelListener { + void onCancel(); + } + OnCancelListener onCancelListener = null; + public void setOnCancelListener(OnCancelListener listener) { + onCancelListener = listener; + } + + /*////////////////////////////////////////////////////////////////////////// + // Init + //////////////////////////////////////////////////////////////////////////*/ + + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.select_channel_fragment, container, false); @@ -90,6 +110,31 @@ public class SelectChannelFragment extends DialogFragment { return v; } + + /*////////////////////////////////////////////////////////////////////////// + // Handle actions + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCancel(final DialogInterface dialogInterface) { + super.onCancel(dialogInterface); + if(onCancelListener != null) { + onCancelListener.onCancel(); + } + } + + private void clickedItem(int position) { + if(onSelectedLisener != null) { + SubscriptionEntity entry = subscriptions.get(position); + onSelectedLisener.onChannelSelected(entry.getUrl(), entry.getName(), entry.getServiceId()); + } + dismiss(); + } + + /*////////////////////////////////////////////////////////////////////////// + // Item handling + //////////////////////////////////////////////////////////////////////////*/ + private void displayChannels(List subscriptions) { this.subscriptions = subscriptions; progressBar.setVisibility(View.GONE); @@ -101,14 +146,6 @@ public class SelectChannelFragment extends DialogFragment { } - private void clickedItem(int position) { - if(onSelectedLisener != null) { - SubscriptionEntity entry = subscriptions.get(position); - onSelectedLisener.onChannelSelected(entry.getUrl(), entry.getName(), entry.getServiceId()); - } - dismiss(); - } - private Observer> getSubscriptionObserver() { return new Observer>() { @Override @@ -151,6 +188,7 @@ public class SelectChannelFragment extends DialogFragment { clickedItem(position); } }); + imageLoader.displayImage(entry.getAvatarUrl(), holder.thumbnailView, DISPLAY_IMAGE_OPTIONS); } @Override @@ -171,6 +209,10 @@ public class SelectChannelFragment extends DialogFragment { } } + /*////////////////////////////////////////////////////////////////////////// + // Error + //////////////////////////////////////////////////////////////////////////*/ + protected boolean onError(Throwable e) { final Activity activity = getActivity(); ErrorActivity.reportError(activity, e, @@ -180,4 +222,17 @@ public class SelectChannelFragment extends DialogFragment { "none", "", R.string.app_ui_crash)); return true; } + + + /*////////////////////////////////////////////////////////////////////////// + // ImageLoaderOptions + //////////////////////////////////////////////////////////////////////////*/ + + /** + * Base display options + */ + public static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS = + new DisplayImageOptions.Builder() + .cacheInMemory(true) + .build(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e16bfca16..b71bfa7db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -268,11 +268,15 @@ Content of main page + Blank Page + Kiosk Page + Feed Page + Channel Page - Blank Page - Trending Page - Feed Page - Channel Page + @string/blank_page_summary + @string/kiosk_page_summary + @string/feed_page_summary + @string/channel_page_summary Select a channel No channel subscribed yet From 46fa9a9366cf74e6381384f794027ee6c1034a54 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 28 Sep 2017 16:15:09 +0200 Subject: [PATCH 008/154] translate kiosk name using resources fix CDATA fuu --- .../newpipe/fragments/MainFragment.java | 24 +++++++++++- .../list/channel/ChannelFragment.java | 6 ++- .../fragments/list/feed/FeedFragment.java | 2 +- .../fragments/list/kiosk/KioskFragment.java | 5 ++- .../subscription/SubscriptionFragment.java | 2 + .../settings/ContentSettingsFragment.java | 2 + .../schabi/newpipe/util/KioskTranslator.java | 38 +++++++++++++++++++ app/src/main/res/menu/main_menu.xml | 2 +- app/src/main/res/values/settings_keys.xml | 2 + app/src/main/res/values/strings.xml | 7 ++++ 10 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index aa28c54fb..c6d3f07f9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -126,10 +126,18 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte @Override public Fragment getItem(int position) { switch (position) { + case 0: + if(PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key)) + .equals(getString(R.string.subscription_page_key))) { + return new SubscriptionFragment(); + } else { + return getMainPageFramgent(); + } case 1: return new SubscriptionFragment(); default: - return getMainPageFramgent(); + return new BlankFragment(); } } @@ -140,7 +148,13 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte @Override public int getCount() { - return this.tabTitles.length; + if(PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key)) + .equals(getString(R.string.subscription_page_key))) { + return 1; + } else { + return 2; + } } } @@ -186,4 +200,10 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte return new BlankFragment(); } } + + /*////////////////////////////////////////////////////////////////////////// + // Main page content + //////////////////////////////////////////////////////////////////////////*/ + + } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 5a722ec4c..daa1b62ed 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -89,7 +89,8 @@ public class ChannelFragment extends BaseListInfoFragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_channel, container, false); + View v = inflater.inflate(R.layout.fragment_channel, container, false); + return v; } @Override @@ -110,6 +111,7 @@ public class ChannelFragment extends BaseListInfoFragment { headerTitleView = headerRootLayout.findViewById(R.id.channel_title_view); headerSubscribersTextView = headerRootLayout.findViewById(R.id.channel_subscriber_view); headerSubscribeButton = headerRootLayout.findViewById(R.id.channel_subscribe_button); + return headerRootLayout; } @@ -123,7 +125,7 @@ public class ChannelFragment extends BaseListInfoFragment { ActionBar supportActionBar = activity.getSupportActionBar(); if(useAsFrontPage) { supportActionBar.setDisplayHomeAsUpEnabled(false); - supportActionBar.setDisplayShowTitleEnabled(false); + //supportActionBar.setDisplayShowTitleEnabled(false); } else { inflater.inflate(R.menu.menu_channel, menu); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java index 1a8f8e625..379ec591a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java @@ -122,7 +122,7 @@ public class FeedFragment extends BaseListFragment, Voi if(useAsFrontPage) { supportActionBar.setDisplayHomeAsUpEnabled(false); - supportActionBar.setDisplayShowTitleEnabled(false); + //supportActionBar.setDisplayShowTitleEnabled(false); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 3d019e6cb..451ade674 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -24,6 +24,7 @@ import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; import io.reactivex.Single; @@ -90,7 +91,7 @@ public class KioskFragment extends BaseListInfoFragment { super.onCreateOptionsMenu(menu, inflater); ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null && useAsFrontPage) { - supportActionBar.setDisplayShowTitleEnabled(false); + //supportActionBar.setDisplayShowTitleEnabled(false); supportActionBar.setDisplayHomeAsUpEnabled(false); } } @@ -140,7 +141,7 @@ public class KioskFragment extends BaseListInfoFragment { super.handleResult(result); animateView(headerRootLayout, true, 100); - headerTitleView.setText("★★ " +result.name+ " ★★"); + headerTitleView.setText("★★ " + KioskTranslator.getTranslatedKioskName(result.id, getActivity())+ " ★★"); if (!result.errors.isEmpty()) { showSnackBarError(result.errors, diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index 646fe597e..afb2f078e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -62,6 +62,8 @@ public class SubscriptionFragment extends BaseStateFragment. + */ + +public class KioskTranslator { + public static String getTranslatedKioskName(String kioskId, Context c) { + switch(kioskId) { + case "Trending": + return c.getString(R.string.trending); + case "Top 50": + return c.getString(R.string.top_50); + case "New & hot": + return c.getString(R.string.new_and_hot); + default: + return kioskId; + } + } +} diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml index be3548532..02402e2e8 100644 --- a/app/src/main/res/menu/main_menu.xml +++ b/app/src/main/res/menu/main_menu.xml @@ -20,6 +20,6 @@ + android:title="@string/action_about"/> \ No newline at end of file diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index 878d51741..dabbfb40d 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -105,12 +105,14 @@ main_page_content blank_page feed_page + subscription_page_key kiosk_page channel_page @string/blank_page_key @string/kiosk_page_key @string/feed_page_key + @string/subscription_page_key @string/channel_page_key main_page_selected_service diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b71bfa7db..bd8bdc048 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -270,14 +270,21 @@ Content of main page Blank Page Kiosk Page + Subscription Page Feed Page Channel Page @string/blank_page_summary @string/kiosk_page_summary @string/feed_page_summary + @string/subscription_page_summary @string/channel_page_summary Select a channel No channel subscribed yet + + + Trending + Top 50 + New & hot From 6d8a361c9af75938ed6480f30c6fd3265aa538bb Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 5 Oct 2017 14:57:19 +0200 Subject: [PATCH 009/154] add menu to select kiosk of current service --- .../newpipe/fragments/MainFragment.java | 50 ++++++++++++++++++- .../fragments/list/kiosk/KioskFragment.java | 16 ++++-- .../schabi/newpipe/util/NavigationHelper.java | 10 ++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index c6d3f07f9..4ba648402 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -15,17 +15,22 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.kiosk.KioskList; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.feed.FeedFragment; import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; import java.util.concurrent.ExecutionException; @@ -36,6 +41,12 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte public int currentServiceId = -1; + /*////////////////////////////////////////////////////////////////////////// + // Konst + //////////////////////////////////////////////////////////////////////////*/ + + private static final int KIOSK_MENU_OFFSETT = 2000; + /*////////////////////////////////////////////////////////////////////////// // Fragment's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @@ -77,6 +88,16 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte super.onCreateOptionsMenu(menu, inflater); if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); inflater.inflate(R.menu.main_fragment_menu, menu); + SubMenu kioskMenu = menu.addSubMenu(getString(R.string.kiosk)); + try { + createKioskMenu(kioskMenu, inflater); + } catch (Exception e) { + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + } ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null) { @@ -202,8 +223,33 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte } /*////////////////////////////////////////////////////////////////////////// - // Main page content + // Select Kiosk //////////////////////////////////////////////////////////////////////////*/ - + private void createKioskMenu(Menu menu, MenuInflater menuInflater) + throws Exception { + StreamingService service = NewPipe.getService(currentServiceId); + KioskList kl = service.getKioskList(); + int i = 0; + for(final String ks : kl.getAvailableKisoks()) { + menu.add(0, KIOSK_MENU_OFFSETT + i, Menu.NONE, + KioskTranslator.getTranslatedKioskName(ks, getContext())) + .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + try { + NavigationHelper.openKioskFragment(getFragmentManager(), currentServiceId, ks); + } catch (Exception e) { + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + } + return true; + } + }); + i++; + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 451ade674..8e1acb572 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -62,14 +62,20 @@ public class KioskFragment extends BaseListInfoFragment { public static KioskFragment getInstance(int serviceId) throws ExtractionException { + return getInstance(serviceId, NewPipe.getService(serviceId) + .getKioskList() + .getDefaultKioskId()); + } + + public static KioskFragment getInstance(int serviceId, String kioskId) + throws ExtractionException { KioskFragment instance = new KioskFragment(); StreamingService service = NewPipe.getService(serviceId); - String defaultKioskId = service.getKioskList().getDefaultKioskId(); - UrlIdHandler defaultKioskTypeUrlIdHandler = service.getKioskList() - .getUrlIdHandlerByType(defaultKioskId); + UrlIdHandler kioskTypeUrlIdHandler = service.getKioskList() + .getUrlIdHandlerByType(kioskId); instance.setInitialData(serviceId, - defaultKioskTypeUrlIdHandler.getUrl(defaultKioskId), - defaultKioskId); + kioskTypeUrlIdHandler.getUrl(kioskId), + kioskId); return instance; } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 538675685..b30407315 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -23,6 +23,7 @@ import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; import org.schabi.newpipe.fragments.list.feed.FeedFragment; +import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment; import org.schabi.newpipe.fragments.list.search.SearchFragment; import org.schabi.newpipe.history.HistoryActivity; @@ -163,6 +164,15 @@ public class NavigationHelper { .commit(); } + public static void openKioskFragment(FragmentManager fragmentManager, int serviceId, String kioskId) + throws ExtractionException { + fragmentManager.beginTransaction() + .setCustomAnimations(R.animator.custom_fade_in, R.animator.custom_fade_out, R.animator.custom_fade_in, R.animator.custom_fade_out) + .replace(R.id.fragment_holder, KioskFragment.getInstance(serviceId, kioskId)) + .addToBackStack(null) + .commit(); + } + /*////////////////////////////////////////////////////////////////////////// // Through Intents //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bd8bdc048..e11483ac0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,6 +284,7 @@ No channel subscribed yet + Kisok Trending Top 50 New & hot From cbfc359a99c18e37b45c86c98fbeadba20fdb821 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 9 Oct 2017 12:22:01 +0200 Subject: [PATCH 010/154] add service icons --- app/src/main/res/drawable-nodpi/soud_cloud.png | Bin 0 -> 12259 bytes app/src/main/res/drawable-nodpi/youtube.png | Bin 0 -> 6182 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/src/main/res/drawable-nodpi/soud_cloud.png create mode 100644 app/src/main/res/drawable-nodpi/youtube.png diff --git a/app/src/main/res/drawable-nodpi/soud_cloud.png b/app/src/main/res/drawable-nodpi/soud_cloud.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa6045d5e89b438b5b2b15eb61a44bd878797ad GIT binary patch literal 12259 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 Date: Mon, 9 Oct 2017 14:22:27 +0200 Subject: [PATCH 011/154] add selector for kiosk --- .../settings/ContentSettingsFragment.java | 85 ++++++-- .../newpipe/settings/SelectKioskFragment.java | 185 ++++++++++++++++++ .../newpipe/util/ServiceIconMapper.java | 35 ++++ app/src/main/res/drawable-nodpi/service.png | Bin 0 -> 5765 bytes .../main/res/layout/select_kiosk_fragment.xml | 26 +++ app/src/main/res/layout/select_kiosk_item.xml | 37 ++++ app/src/main/res/values/strings.xml | 1 + assets/service.svg | 109 +++++++++++ 8 files changed, 466 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java create mode 100644 app/src/main/java/org/schabi/newpipe/util/ServiceIconMapper.java create mode 100644 app/src/main/res/drawable-nodpi/service.png create mode 100644 app/src/main/res/layout/select_kiosk_fragment.xml create mode 100644 app/src/main/res/layout/select_kiosk_item.xml create mode 100644 assets/service.svg diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 4c0c7aec3..381c20ff1 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -1,10 +1,17 @@ package org.schabi.newpipe.settings; +import android.app.Activity; import android.os.Bundle; import android.support.v7.preference.ListPreference; import android.support.v7.preference.Preference; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.KioskTranslator; public class ContentSettingsFragment extends BasePreferenceFragment { @@ -14,7 +21,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { addPreferencesFromResource(R.xml.content_settings); final ListPreference mainPageContentPref = (ListPreference) findPreference(getString(R.string.main_page_content_key)); - mainPageContentPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValueO) { @@ -25,7 +31,36 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final String mainPrefOldSummary = getMainPagePrefSummery(mainPrefOldValue, mainPageContentPref); if(newValue.equals(getString(R.string.kiosk_page_key))) { - //todo on multyservice support show a kiosk an service selector here + SelectKioskFragment selectKioskFragment = new SelectKioskFragment(); + selectKioskFragment.setOnSelectedLisener(new SelectKioskFragment.OnSelectedLisener() { + @Override + public void onKioskSelected(String kioskId, int service_id) { + defaultPreferences.edit() + .putInt(getString(R.string.main_page_selected_service), service_id).apply(); + defaultPreferences.edit() + .putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply(); + String summary = ""; + try { + summary += NewPipe.getService(service_id).getServiceInfo().name; + } catch (ExtractionException e) { + onError(e); + } + summary += "/"; + summary += KioskTranslator.getTranslatedKioskName(kioskId, getContext()); + + + + mainPageContentPref.setSummary(summary); + } + }); + selectKioskFragment.setOnCancelListener(new SelectKioskFragment.OnCancelListener() { + @Override + public void onCancel() { + mainPageContentPref.setSummary(mainPrefOldSummary); + mainPageContentPref.setValue(mainPrefOldValue); + } + }); + selectKioskFragment.show(getFragmentManager(), "select_kiosk"); } else if(newValue.equals(getString(R.string.channel_page_key))) { SelectChannelFragment selectChannelFragment = new SelectChannelFragment(); selectChannelFragment.setOnSelectedLisener(new SelectChannelFragment.OnSelectedLisener() { @@ -38,23 +73,18 @@ public class ContentSettingsFragment extends BasePreferenceFragment { defaultPreferences.edit() .putString(getString(R.string.main_page_selected_channel_name), name).apply(); - //change summery mainPageContentPref.setSummary(name); } }); selectChannelFragment.setOnCancelListener(new SelectChannelFragment.OnCancelListener() { @Override public void onCancel() { - //defaultPreferences.edit() - // .putString(getString(R.string.main_page_content_key), mainPrefOldValue).apply(); mainPageContentPref.setSummary(mainPrefOldSummary); mainPageContentPref.setValue(mainPrefOldValue); } }); selectChannelFragment.show(getFragmentManager(), "select_channel"); - } - - if(!newValue.equals(getString(R.string.channel_page_key))) { + } else { mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue)); } @@ -68,11 +98,28 @@ public class ContentSettingsFragment extends BasePreferenceFragment { super.onResume(); final String mainPageContentKey = getString(R.string.main_page_content_key); - if(defaultPreferences.getString(mainPageContentKey, - getString(R.string.blank_page_key)) + final Preference mainPagePref = findPreference(getString(R.string.main_page_content_key)); + final String bpk = getString(R.string.blank_page_key); + if(defaultPreferences.getString(mainPageContentKey, bpk) .equals(getString(R.string.channel_page_key))) { - Preference pref = findPreference(getString(R.string.main_page_content_key)); - pref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error")); + mainPagePref.setSummary(defaultPreferences.getString(getString(R.string.main_page_selected_channel_name), "error")); + } else if(defaultPreferences.getString(mainPageContentKey, bpk) + .equals(getString(R.string.kiosk_page_key))) { + try { + StreamingService service = NewPipe.getService( + defaultPreferences.getInt( + getString(R.string.main_page_selected_service), 0)); + String summary = ""; + summary += service.getServiceInfo().name; + summary += "/"; + summary += KioskTranslator.getTranslatedKioskName( + defaultPreferences.getString( + getString(R.string.main_page_selectd_kiosk_id), "Trending"), + getContext()); + mainPagePref.setSummary(summary); + } catch (Exception e) { + onError(e); + } } } @@ -101,4 +148,18 @@ public class ContentSettingsFragment extends BasePreferenceFragment { } return R.string.blank_page_summary; } + + /*////////////////////////////////////////////////////////////////////////// + // Error + //////////////////////////////////////////////////////////////////////////*/ + + protected boolean onError(Throwable e) { + final Activity activity = getActivity(); + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + return true; + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java new file mode 100644 index 000000000..6e7ba191e --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -0,0 +1,185 @@ +package org.schabi.newpipe.settings; + +import android.app.Activity; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.subscription.SubscriptionEntity; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.fragments.subscription.SubscriptionService; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.KioskTranslator; +import org.schabi.newpipe.util.ServiceIconMapper; + +import java.util.List; +import java.util.Vector; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; + +/** + * Created by Christian Schabesberger on 09.10.17. + * SelectKioskFragment.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class SelectKioskFragment extends DialogFragment { + + RecyclerView recyclerView = null; + SelectKioskAdapter selectKioskAdapter = null; + + /*////////////////////////////////////////////////////////////////////////// + // Interfaces + //////////////////////////////////////////////////////////////////////////*/ + + public interface OnSelectedLisener { + void onKioskSelected(String kioskId, int service_id); + } + + OnSelectedLisener onSelectedLisener = null; + public void setOnSelectedLisener(OnSelectedLisener listener) { + onSelectedLisener = listener; + } + + public interface OnCancelListener { + void onCancel(); + } + OnCancelListener onCancelListener = null; + public void setOnCancelListener(OnCancelListener listener) { + onCancelListener = listener; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.select_kiosk_fragment, container, false); + recyclerView = (RecyclerView) v.findViewById(R.id.items_list); + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + try { + selectKioskAdapter = new SelectKioskAdapter(); + } catch (Exception e) { + onError(e); + } + recyclerView.setAdapter(selectKioskAdapter); + + return v; + } + + /*////////////////////////////////////////////////////////////////////////// + // Handle actions + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCancel(final DialogInterface dialogInterface) { + super.onCancel(dialogInterface); + if(onCancelListener != null) { + onCancelListener.onCancel(); + } + } + + private void clickedItem(SelectKioskAdapter.Entry entry) { + if(onSelectedLisener != null) { + onSelectedLisener.onKioskSelected(entry.kioskId, entry.serviceId); + } + dismiss(); + } + + private class SelectKioskAdapter + extends RecyclerView.Adapter { + public class Entry { + public Entry (int i, int si, String ki, String kn){ + icon = i; serviceId=si; kioskId=ki; kioskName = kn; + } + int icon; + int serviceId; + String kioskId; + String kioskName; + }; + + private List kioskList = new Vector<>(); + + public SelectKioskAdapter() + throws Exception { + for(StreamingService service : NewPipe.getServices()) { + for(String kioskId : service.getKioskList().getAvailableKisoks()) { + kioskList.add(new Entry( + ServiceIconMapper.getIconResource(service.getServiceId()), + service.getServiceId(), + kioskId, + KioskTranslator.getTranslatedKioskName(kioskId, getContext()))); + } + } + } + + public int getItemCount() { + return kioskList.size(); + } + + public SelectKioskItemHolder onCreateViewHolder(ViewGroup parent, int type) { + View item = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.select_kiosk_item, parent, false); + return new SelectKioskItemHolder(item); + } + + public class SelectKioskItemHolder extends RecyclerView.ViewHolder { + public SelectKioskItemHolder(View v) { + super(v); + this.view = v; + thumbnailView = v.findViewById(R.id.itemThumbnailView); + titleView = v.findViewById(R.id.itemTitleView); + } + public View view; + public ImageView thumbnailView; + public TextView titleView; + } + + public void onBindViewHolder(SelectKioskItemHolder holder, final int position) { + final Entry entry = kioskList.get(position); + holder.titleView.setText(entry.kioskName); + holder.thumbnailView.setImageDrawable(ContextCompat.getDrawable(getContext(), entry.icon)); + holder.view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + clickedItem(entry); + } + }); + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Error + //////////////////////////////////////////////////////////////////////////*/ + + protected boolean onError(Throwable e) { + final Activity activity = getActivity(); + ErrorActivity.reportError(activity, e, + activity.getClass(), + null, + ErrorActivity.ErrorInfo.make(UserAction.UI_ERROR, + "none", "", R.string.app_ui_crash)); + return true; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/util/ServiceIconMapper.java b/app/src/main/java/org/schabi/newpipe/util/ServiceIconMapper.java new file mode 100644 index 000000000..060013dd2 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/ServiceIconMapper.java @@ -0,0 +1,35 @@ +package org.schabi.newpipe.util; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.NewPipe; + +/** + * Created by Chrsitian Schabesberger on 09.10.17. + * ServiceIconMapper.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class ServiceIconMapper { + public static int getIconResource(int service_id) { + switch(service_id) { + case 0: + return R.drawable.youtube; + case 1: + return R.drawable.soud_cloud; + default: + return R.drawable.service; + } + } +} diff --git a/app/src/main/res/drawable-nodpi/service.png b/app/src/main/res/drawable-nodpi/service.png new file mode 100644 index 0000000000000000000000000000000000000000..cfaff19e2df8b3954bae3630c1d68bd811a1e279 GIT binary patch literal 5765 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout/select_kiosk_fragment.xml b/app/src/main/res/layout/select_kiosk_fragment.xml new file mode 100644 index 000000000..7be96754a --- /dev/null +++ b/app/src/main/res/layout/select_kiosk_fragment.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/select_kiosk_item.xml b/app/src/main/res/layout/select_kiosk_item.xml new file mode 100644 index 000000000..818380849 --- /dev/null +++ b/app/src/main/res/layout/select_kiosk_item.xml @@ -0,0 +1,37 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e11483ac0..371ad1688 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,6 +282,7 @@ Select a channel No channel subscribed yet + Select a kiosk Kisok diff --git a/assets/service.svg b/assets/service.svg new file mode 100644 index 000000000..172cc106f --- /dev/null +++ b/assets/service.svg @@ -0,0 +1,109 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + From 7fcc07805a20d0eb350e3efd77b3ca18777243c1 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 9 Oct 2017 14:34:10 +0200 Subject: [PATCH 012/154] make kiosk selector signle service again --- .../org/schabi/newpipe/fragments/MainFragment.java | 12 +++++++----- .../schabi/newpipe/settings/SelectKioskFragment.java | 12 +++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 4ba648402..48f2d150e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -185,13 +185,17 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte private Fragment getMainPageFramgent() { try { - final String set_main_page = PreferenceManager.getDefaultSharedPreferences(getActivity()) - .getString(getString(R.string.main_page_content_key), + SharedPreferences preferences = + PreferenceManager.getDefaultSharedPreferences(getActivity()); + final String set_main_page = preferences.getString(getString(R.string.main_page_content_key), getString(R.string.main_page_selectd_kiosk_id)); if(set_main_page.equals(getString(R.string.blank_page_key))) { return new BlankFragment(); } else if(set_main_page.equals(getString(R.string.kiosk_page_key))) { - KioskFragment fragment = KioskFragment.getInstance(currentServiceId); + int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); + String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id), "Trending"); + KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId + ); fragment.useAsFrontPage(true); return fragment; } else if(set_main_page.equals(getString(R.string.feed_page_key))) { @@ -199,8 +203,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte fragment.useAsFrontPage(true); return fragment; } else if(set_main_page.equals(getString(R.string.channel_page_key))) { - SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(getActivity()); int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); String url = preferences.getString(getString(R.string.main_page_selected_channel_url), "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 6e7ba191e..204b35905 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -117,25 +117,31 @@ public class SelectKioskFragment extends DialogFragment { int serviceId; String kioskId; String kioskName; - }; + } private List kioskList = new Vector<>(); public SelectKioskAdapter() throws Exception { + for(StreamingService service : NewPipe.getServices()) { for(String kioskId : service.getKioskList().getAvailableKisoks()) { + String name = service.getServiceInfo().name; + name += "/"; + name += KioskTranslator.getTranslatedKioskName(kioskId, getContext()); kioskList.add(new Entry( ServiceIconMapper.getIconResource(service.getServiceId()), service.getServiceId(), kioskId, - KioskTranslator.getTranslatedKioskName(kioskId, getContext()))); + name)); } } } public int getItemCount() { - return kioskList.size(); + //todo: uncommend this line on multyservice support + //return kioskList.size(); + return 1; } public SelectKioskItemHolder onCreateViewHolder(ViewGroup parent, int type) { From 0951f0f824fb55e4a2d4e28487ef1a9ddeb3dfae Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 11 Oct 2017 15:24:57 +0200 Subject: [PATCH 013/154] small fixes small fixes --- .../schabi/newpipe/fragments/list/kiosk/KioskFragment.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index 8e1acb572..c308fe1ec 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -48,7 +48,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with OpenHitboxStreams. If not, see . + * along with NewPipe. If not, see . */ public class KioskFragment extends BaseListInfoFragment { @@ -147,7 +147,10 @@ public class KioskFragment extends BaseListInfoFragment { super.handleResult(result); animateView(headerRootLayout, true, 100); - headerTitleView.setText("★★ " + KioskTranslator.getTranslatedKioskName(result.id, getActivity())+ " ★★"); + String title = KioskTranslator.getTranslatedKioskName(result.id, getActivity()); + headerTitleView.setText("★★ " + title + " ★★"); + ActionBar supportActionBar = activity.getSupportActionBar(); + supportActionBar.setTitle(title); if (!result.errors.isEmpty()) { showSnackBarError(result.errors, From 8e8d74b5b7bb0c3d00b3fbcacd20461ffdc96222 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 13 Oct 2017 13:01:16 +0200 Subject: [PATCH 014/154] remove header front kiosk --- .../fragments/list/kiosk/KioskFragment.java | 14 ----------- app/src/main/res/layout/kiosk_header.xml | 23 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 app/src/main/res/layout/kiosk_header.xml diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index c308fe1ec..b1387b1ad 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -102,17 +102,6 @@ public class KioskFragment extends BaseListInfoFragment { } } - /*////////////////////////////////////////////////////////////////////////// - // Init - //////////////////////////////////////////////////////////////////////////*/ - - protected View getListHeader() { - headerRootLayout = activity.getLayoutInflater().inflate(R.layout.kiosk_header, itemsList, false); - headerTitleView = headerRootLayout.findViewById(R.id.kiosk_title_view); - - return headerRootLayout; - } - /*////////////////////////////////////////////////////////////////////////// // Load and handle //////////////////////////////////////////////////////////////////////////*/ @@ -138,7 +127,6 @@ public class KioskFragment extends BaseListInfoFragment { @Override public void showLoading() { super.showLoading(); - animateView(headerRootLayout, false, 200); animateView(itemsList, false, 100); } @@ -146,9 +134,7 @@ public class KioskFragment extends BaseListInfoFragment { public void handleResult(@NonNull final KioskInfo result) { super.handleResult(result); - animateView(headerRootLayout, true, 100); String title = KioskTranslator.getTranslatedKioskName(result.id, getActivity()); - headerTitleView.setText("★★ " + title + " ★★"); ActionBar supportActionBar = activity.getSupportActionBar(); supportActionBar.setTitle(title); diff --git a/app/src/main/res/layout/kiosk_header.xml b/app/src/main/res/layout/kiosk_header.xml deleted file mode 100644 index c161ca83d..000000000 --- a/app/src/main/res/layout/kiosk_header.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - \ No newline at end of file From 23eeb4353dc96504f1fa6a8fbc8a5e1b5256c757 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 13 Oct 2017 13:49:31 +0200 Subject: [PATCH 015/154] make main page changes display emediatly --- app/src/main/java/org/schabi/newpipe/MainActivity.java | 6 ++++++ .../java/org/schabi/newpipe/fragments/MainFragment.java | 1 + .../schabi/newpipe/settings/ContentSettingsFragment.java | 3 +++ app/src/main/java/org/schabi/newpipe/util/Constants.java | 1 + .../main/java/org/schabi/newpipe/util/NavigationHelper.java | 2 +- 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 67689d541..3ff55cb76 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -119,6 +119,12 @@ public class MainActivity extends AppCompatActivity implements HistoryListener { }); } + if(sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) { + if (DEBUG) Log.d(TAG, "main page has changed, recreating main fragment..."); + sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply(); + NavigationHelper.openMainActivity(this); + } + } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 48f2d150e..ba09f4001 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -30,6 +30,7 @@ import org.schabi.newpipe.fragments.list.kiosk.KioskFragment; import org.schabi.newpipe.fragments.subscription.SubscriptionFragment; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 381c20ff1..aeba78cbd 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -11,6 +11,7 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.KioskTranslator; public class ContentSettingsFragment extends BasePreferenceFragment { @@ -88,6 +89,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment { mainPageContentPref.setSummary(getMainPageSummeryByKey(newValue)); } + defaultPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, true).apply(); + return true; } }); diff --git a/app/src/main/java/org/schabi/newpipe/util/Constants.java b/app/src/main/java/org/schabi/newpipe/util/Constants.java index f9329b0be..08e64aceb 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Constants.java +++ b/app/src/main/java/org/schabi/newpipe/util/Constants.java @@ -9,4 +9,5 @@ public class Constants { public static final String KEY_QUERY = "key_query"; public static final String KEY_THEME_CHANGE = "key_theme_change"; + public static final String KEY_MAIN_PAGE_CHANGE = "key_main_page_change"; } diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index b30407315..b08251436 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -94,7 +94,7 @@ public class NavigationHelper { if (!popped) openMainFragment(fragmentManager); } - private static void openMainFragment(FragmentManager fragmentManager) { + public static void openMainFragment(FragmentManager fragmentManager) { InfoCache.getInstance().trimCache(); fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); From d928f5759fd358be5e48a309922a8c74b2eb604a Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 13 Oct 2017 13:55:55 +0200 Subject: [PATCH 016/154] try to fix margin / padding --- app/src/main/res/layout/select_channel_fragment.xml | 6 ++++-- app/src/main/res/layout/select_channel_item.xml | 2 -- app/src/main/res/layout/select_kiosk_fragment.xml | 3 ++- app/src/main/res/layout/select_kiosk_item.xml | 2 -- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/layout/select_channel_fragment.xml b/app/src/main/res/layout/select_channel_fragment.xml index 9c8e79779..11c723b4b 100644 --- a/app/src/main/res/layout/select_channel_fragment.xml +++ b/app/src/main/res/layout/select_channel_fragment.xml @@ -3,7 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:padding="13dp"> + android:layout_marginEnd="5dp" + android:layout_marginBottom="10dp"/> + android:layout_height="match_parent" + android:padding="13dp"> Date: Fri, 20 Oct 2017 23:41:30 +0200 Subject: [PATCH 017/154] Fix charset issue. Move Java I/O related methods to separate thread. --- .../schabi/newpipe/about/LicenseFragment.java | 73 +----------- .../newpipe/about/LicenseFragmentHelper.java | 111 ++++++++++++++++++ 2 files changed, 112 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java 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 42e886d30..4400cac53 100644 --- a/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragment.java @@ -1,22 +1,15 @@ package org.schabi.newpipe.about; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; -import android.support.v7.app.AlertDialog; import android.view.*; -import android.webkit.WebView; import android.widget.TextView; - import org.schabi.newpipe.R; -import org.schabi.newpipe.util.ThemeHelper; -import java.io.BufferedReader; -import java.io.InputStreamReader; import java.util.Arrays; import java.util.Comparator; @@ -46,70 +39,7 @@ public class LicenseFragment extends Fragment { * @param license the license to show */ public static void showLicense(Context context, License license) { - if(context == null) { - throw new NullPointerException("context is null"); - } - if(license == null) { - throw new NullPointerException("license is null"); - } - AlertDialog.Builder alert = new AlertDialog.Builder(context); - alert.setTitle(license.getName()); - - WebView wv = new WebView(context); - String licenseContent = ""; - String webViewData; - try { - BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8")); - String str; - while ((str = in.readLine()) != null) { - licenseContent += str; - } - in.close(); - - // split the HTML file and insert the stylesheet into the HEAD of the file - String[] insert = licenseContent.split(""); - webViewData = insert[0] + "" - + insert[1]; - } catch (Exception e) { - throw new NullPointerException("could not get license file:" + getLicenseStylesheet(context)); - } - wv.loadData(webViewData, "text/html", "utf-8"); - - alert.setView(wv); - alert.setNegativeButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - alert.show(); - } - - public static String getLicenseStylesheet(Context context) { - return "body{padding:12px 15px;margin:0;background:#" - + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) - ? R.color.light_license_background_color - : R.color.dark_license_background_color) - + ";color:#" - + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) - ? R.color.light_license_text_color - : R.color.dark_license_text_color) + ";}" - + "a[href]{color:#" - + getHexRGBColor(context,(ThemeHelper.isLightThemeSelected(context)) - ? R.color.light_youtube_primary_color - : R.color.dark_youtube_primary_color) + ";}" - + "pre{white-space: pre-wrap;}"; - } - - /** - * Cast R.color to a hexadecimal color value - * @param context the context to use - * @param color the color number from R.color - * @return a six characters long String with hexadecimal RGB values - */ - public static String getHexRGBColor(Context context, int color) { - return context.getResources().getString(color).substring(3); + new LicenseFragmentHelper().execute(context, license); } @Override @@ -154,7 +84,6 @@ public class LicenseFragment extends Fragment { }); softwareComponentsView.addView(componentView); registerForContextMenu(componentView); - } return rootView; } diff --git a/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java new file mode 100644 index 000000000..726e97ec2 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/about/LicenseFragmentHelper.java @@ -0,0 +1,111 @@ +package org.schabi.newpipe.about; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.support.v7.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; + +public class LicenseFragmentHelper extends AsyncTask { + + private Context context; + private License license; + + @Override + protected Integer doInBackground(Object... objects) { + context = (Context) objects[0]; + license = (License) objects[1]; + return 1; + } + + @Override + protected void onPostExecute(Integer result){ + String webViewData = getFormattedLicense(context, license); + AlertDialog.Builder alert = new AlertDialog.Builder(context); + alert.setTitle(license.getName()); + + WebView wv = new WebView(context); + wv.loadData(webViewData, "text/html; charset=UTF-8", null); + + alert.setView(wv); + alert.setNegativeButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + alert.show(); + } + + /** + * @param context the context to use + * @param license the license + * @return String which contains a HTML formatted license page styled according to the context's theme + */ + public static String getFormattedLicense(Context context, License license) { + if(context == null) { + throw new NullPointerException("context is null"); + } + if(license == null) { + throw new NullPointerException("license is null"); + } + + String licenseContent = ""; + String webViewData; + try { + BufferedReader in = new BufferedReader(new InputStreamReader(context.getAssets().open(license.getFilename()), "UTF-8")); + String str; + while ((str = in.readLine()) != null) { + licenseContent += str; + } + in.close(); + + // split the HTML file and insert the stylesheet into the HEAD of the file + String[] insert = licenseContent.split(""); + webViewData = insert[0] + "" + + insert[1]; + } catch (Exception e) { + throw new NullPointerException("could not get license file:" + getLicenseStylesheet(context)); + } + return webViewData; + } + + /** + * + * @param context + * @return String which is a CSS stylesheet according to the context's theme + */ + public static String getLicenseStylesheet(Context context) { + boolean isLightTheme = ThemeHelper.isLightThemeSelected(context); + return "body{padding:12px 15px;margin:0;background:#" + + getHexRGBColor(context, isLightTheme + ? R.color.light_license_background_color + : R.color.dark_license_background_color) + + ";color:#" + + getHexRGBColor(context, isLightTheme + ? R.color.light_license_text_color + : R.color.dark_license_text_color) + ";}" + + "a[href]{color:#" + + getHexRGBColor(context, isLightTheme + ? R.color.light_youtube_primary_color + : R.color.dark_youtube_primary_color) + ";}" + + "pre{white-space: pre-wrap;}"; + } + + /** + * Cast R.color to a hexadecimal color value + * @param context the context to use + * @param color the color number from R.color + * @return a six characters long String with hexadecimal RGB values + */ + public static String getHexRGBColor(Context context, int color) { + return context.getResources().getString(color).substring(3); + } + +} From c3f04ea67d4b5d491ac98195b5a6c6ab160bce19 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 22 Oct 2017 21:46:50 +0200 Subject: [PATCH 018/154] fix Kisok spelling error --- app/build.gradle | 2 +- .../main/java/org/schabi/newpipe/fragments/MainFragment.java | 2 +- .../java/org/schabi/newpipe/settings/SelectKioskFragment.java | 2 +- app/src/main/res/values/strings.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2f1dd4005..529d04425 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ dependencies { exclude module: 'support-annotations' } - compile 'com.github.TeamNewPipe:NewPipeExtractor:1df3f67' + compile 'com.github.TeamNewPipe:NewPipeExtractor:7899cd1' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index ba09f4001..f26f9739e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -234,7 +234,7 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte StreamingService service = NewPipe.getService(currentServiceId); KioskList kl = service.getKioskList(); int i = 0; - for(final String ks : kl.getAvailableKisoks()) { + for(final String ks : kl.getAvailableKiosks()) { menu.add(0, KIOSK_MENU_OFFSETT + i, Menu.NONE, KioskTranslator.getTranslatedKioskName(ks, getContext())) .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 204b35905..3f84f9006 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -125,7 +125,7 @@ public class SelectKioskFragment extends DialogFragment { throws Exception { for(StreamingService service : NewPipe.getServices()) { - for(String kioskId : service.getKioskList().getAvailableKisoks()) { + for(String kioskId : service.getKioskList().getAvailableKiosks()) { String name = service.getServiceInfo().name; name += "/"; name += KioskTranslator.getTranslatedKioskName(kioskId, getContext()); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e552177c5..035e94418 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -286,7 +286,7 @@ Select a kiosk - Kisok + Kiosk Trending Top 50 New & hot From d1f446aae241357b0294f5bf64829c2e27e6ed3c Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 22 Oct 2017 22:06:25 +0200 Subject: [PATCH 019/154] make summary a dynamic string --- .../settings/ContentSettingsFragment.java | 25 ++++++++++++------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index aeba78cbd..4f6c2d1ae 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -40,16 +40,19 @@ public class ContentSettingsFragment extends BasePreferenceFragment { .putInt(getString(R.string.main_page_selected_service), service_id).apply(); defaultPreferences.edit() .putString(getString(R.string.main_page_selectd_kiosk_id), kioskId).apply(); - String summary = ""; + String serviceName = ""; try { - summary += NewPipe.getService(service_id).getServiceInfo().name; + serviceName = NewPipe.getService(service_id).getServiceInfo().name; } catch (ExtractionException e) { onError(e); } - summary += "/"; - summary += KioskTranslator.getTranslatedKioskName(kioskId, getContext()); - + String kioskName = KioskTranslator.getTranslatedKioskName(kioskId, + getContext()); + String summary = + String.format(getString(R.string.service_kosk_string), + serviceName, + kioskName); mainPageContentPref.setSummary(summary); } @@ -112,13 +115,17 @@ public class ContentSettingsFragment extends BasePreferenceFragment { StreamingService service = NewPipe.getService( defaultPreferences.getInt( getString(R.string.main_page_selected_service), 0)); - String summary = ""; - summary += service.getServiceInfo().name; - summary += "/"; - summary += KioskTranslator.getTranslatedKioskName( + + String kioskName = KioskTranslator.getTranslatedKioskName( defaultPreferences.getString( getString(R.string.main_page_selectd_kiosk_id), "Trending"), getContext()); + + String summary = + String.format(getString(R.string.service_kosk_string), + service.getServiceInfo().name, + kioskName); + mainPagePref.setSummary(summary); } catch (Exception e) { onError(e); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 035e94418..94b185988 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -290,4 +290,5 @@ Trending Top 50 New & hot + %1$s/%2$s From fd899a2e9505211d301492e5a2e542a940292bd0 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 22 Oct 2017 22:10:48 +0200 Subject: [PATCH 020/154] fix cammel case --- .../org/schabi/newpipe/fragments/MainFragment.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index f26f9739e..a1dd34467 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -188,22 +188,22 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte try { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - final String set_main_page = preferences.getString(getString(R.string.main_page_content_key), + final String setMainPage = preferences.getString(getString(R.string.main_page_content_key), getString(R.string.main_page_selectd_kiosk_id)); - if(set_main_page.equals(getString(R.string.blank_page_key))) { + if(setMainPage.equals(getString(R.string.blank_page_key))) { return new BlankFragment(); - } else if(set_main_page.equals(getString(R.string.kiosk_page_key))) { + } else if(setMainPage.equals(getString(R.string.kiosk_page_key))) { int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id), "Trending"); KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId ); fragment.useAsFrontPage(true); return fragment; - } else if(set_main_page.equals(getString(R.string.feed_page_key))) { + } else if(setMainPage.equals(getString(R.string.feed_page_key))) { FeedFragment fragment = new FeedFragment(); fragment.useAsFrontPage(true); return fragment; - } else if(set_main_page.equals(getString(R.string.channel_page_key))) { + } else if(setMainPage.equals(getString(R.string.channel_page_key))) { int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); String url = preferences.getString(getString(R.string.main_page_selected_channel_url), "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"); From 54cdfc0c1650750d5a6c2b98cf4a6b6847903d02 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 22 Oct 2017 22:53:27 +0200 Subject: [PATCH 021/154] deactivate icon --- .../org/schabi/newpipe/settings/SelectKioskFragment.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 3f84f9006..0a8f3bfdc 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -126,11 +126,12 @@ public class SelectKioskFragment extends DialogFragment { for(StreamingService service : NewPipe.getServices()) { for(String kioskId : service.getKioskList().getAvailableKiosks()) { - String name = service.getServiceInfo().name; - name += "/"; - name += KioskTranslator.getTranslatedKioskName(kioskId, getContext()); + String name = String.format(getString(R.string.service_kosk_string), + service.getServiceInfo().name, + KioskTranslator.getTranslatedKioskName(kioskId, getContext())); kioskList.add(new Entry( - ServiceIconMapper.getIconResource(service.getServiceId()), + //ServiceIconMapper.getIconResource(service.getServiceId()), + 0, service.getServiceId(), kioskId, name)); From ccd26b4146de9d32b622f8577dd28c08db3973e0 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 22 Oct 2017 23:56:39 +0200 Subject: [PATCH 022/154] fix kiosk service/icon --- .../java/org/schabi/newpipe/settings/SelectKioskFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 0a8f3bfdc..a9f0a53ed 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -131,7 +131,7 @@ public class SelectKioskFragment extends DialogFragment { KioskTranslator.getTranslatedKioskName(kioskId, getContext())); kioskList.add(new Entry( //ServiceIconMapper.getIconResource(service.getServiceId()), - 0, + ServiceIconMapper.getIconResource(-1), service.getServiceId(), kioskId, name)); From 7d296ee65056d783253e5b26808da01d29a97588 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 23 Oct 2017 00:26:20 +0200 Subject: [PATCH 023/154] remove hardcoded strings form MainFragment --- .../newpipe/fragments/MainFragment.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index a1dd34467..317630faa 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -40,6 +40,12 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte private ViewPager viewPager; private boolean showBlankTab = false; + private static final int FALLBACK_SERVICE_ID = 0; // Youtbe + private static final String FALLBACK_CHANNEL_URL = + "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"; + private static final String FALLBACK_CHANNEL_NAME = "Music"; + private static final String FALLBACK_KIOSK_ID = "Trending"; + public int currentServiceId = -1; /*////////////////////////////////////////////////////////////////////////// @@ -193,8 +199,10 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte if(setMainPage.equals(getString(R.string.blank_page_key))) { return new BlankFragment(); } else if(setMainPage.equals(getString(R.string.kiosk_page_key))) { - int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); - String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id), "Trending"); + int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), + FALLBACK_SERVICE_ID); + String kioskId = preferences.getString(getString(R.string.main_page_selectd_kiosk_id), + FALLBACK_KIOSK_ID); KioskFragment fragment = KioskFragment.getInstance(serviceId, kioskId ); fragment.useAsFrontPage(true); @@ -204,10 +212,12 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte fragment.useAsFrontPage(true); return fragment; } else if(setMainPage.equals(getString(R.string.channel_page_key))) { - int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), 0); + int serviceId = preferences.getInt(getString(R.string.main_page_selected_service), + FALLBACK_SERVICE_ID); String url = preferences.getString(getString(R.string.main_page_selected_channel_url), - "https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ"); - String name = preferences.getString(getString(R.string.main_page_selected_channel_name), "Music"); + FALLBACK_CHANNEL_URL); + String name = preferences.getString(getString(R.string.main_page_selected_channel_name), + FALLBACK_CHANNEL_NAME); ChannelFragment fragment = ChannelFragment.getInstance(serviceId, url, name); fragment.useAsFrontPage(true); return fragment; From 3ac3cedc19a81393ad3d431ce96940910c9345db Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 23 Oct 2017 15:16:34 +0200 Subject: [PATCH 024/154] add fdroid build server node to contribution.md --- .github/CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index a003040c8..4a3cc8254 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -34,6 +34,8 @@ Do not report crashes in the GitHub issue tracker. NewPipe has an automated cras * 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 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 sumission, 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. ## Communication From fdfd94b9d0331fa4ae15b486782dec0135bde475 Mon Sep 17 00:00:00 2001 From: Filip Sebastian Date: Tue, 24 Oct 2017 21:55:11 +0000 Subject: [PATCH 025/154] Translated using Weblate (Romanian) Currently translated at 100.0% (206 of 206 strings) --- app/src/main/res/values-ro/strings.xml | 83 +++++++++++++++++++++----- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 46b0b0584..9243d7ebf 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -27,7 +27,7 @@ Rezoluție implicită Redați folosind Kodi - Aplicația Kore nu a fost găsită. Instalați Kore? + Aplicația Kore nu a fost găsită. Doriți să o instalați? Arată opțiunea \"Redați folosind Kodi\" Arătați o opțiune de redare a videoclipurilor via centrului media Kodi Audio @@ -60,13 +60,13 @@ Nu s-a putut analiza website-ul Conținut indisponibil Blocat de către GEMA - Nu s-a putut seta meniul de descărcare. - Acesta este un LIVE STREAM. Acestea nu sunt încă suportate. + Imposibil de inițializat meniul pentru descărcări + Acesta este un LIVE STREAM, care încă nu este suportat. Thumbnail de previzualizare al videoclipului Thumbnail de previzualizare al videoclipului - Thumbnail-ul pentru userpicul persoanei care a încărcat videoclipul + Thumbnail-ul autorului Au apreciat Nu au apreciat Folosiți Tor @@ -103,15 +103,15 @@ Start Pauză - Vezi + Redă Șterge Suma de control Misiune nouă - Ok + OK - Nume fișier + Numele fișierului Thread-uri Eroare Server nesuportat @@ -120,8 +120,8 @@ NewPipe Descarcă Apasă pentru detalii Vă rugăm așteptați… - Copiat în clipboard. - Vă rugăm selectați un director de descărcare disponibil. + Copiat în clipboard + Vă rugăm alegeți un folder pentru descărcări Deschide in mod pop-up Aceasta permisiune este necesara @@ -131,7 +131,7 @@ pentru a deschide în mod pop-up reCAPTCHA noua reCAPTCHA noua ceruta - NewPipe mod Pop-up + NewPipe mod pop-up Rezoluție inițială pop-up Arată rezoluție mai mare @@ -150,12 +150,12 @@ pentru a deschide în mod pop-up App/UI eroare What:\\nRequest:\\nContent Lang:\\nService:\\nGMT Time:\\nPackage:\\nVersion:\\nOS version:\\nGlob. IP clasa: Folosește player vechi - Versiune veche în Mediaframework player. + Player Mediaframework implicit K - M - B + mil. + mld. Anumite rezoluții NU vor avea sunet atunci când această opțiune este activată Fundal @@ -204,4 +204,59 @@ pentru a deschide în mod pop-up Fără rezultate Nimic Aici Doar Greieri - + Niciun abonat + + %s abonat + %s abonați + %s abonați + + + Nicio vizionare + + %s vizionare + %s vizionări + %s vizionări + + + Niciun videoclip + + %s videoclip + %s videoclipuri + %s videoclipuri + + + Descarcă + Caractere permise în numele fișierelor + Caracterele invalide sunt înlocuite cu această valoare + Caracter înlocuitor + + Litere și cifre + Caracterele cele mai speciale + + Despre NewPipe + Setări + Despre + Licențe terță-parte + © %1$s de %2$s sub %3$s + Nu s-a putut încărca licența + Accesează site-ul + Despre + Contributori + Licențe + Un player Youtube lightweight gratuit, pentru Android. + Vedeți pe GitHub + Licența NewPipe + Dacă aveți idei, doriți să traduceți, să schimbați design-ul, să curățați codul sau să schimbați codul, ajutorul este mereu bine primit. Cu cât mai mult, cu atât mai bine! + Citiți licența + Contribuție + + Istoric + Căutat + Vizionat + Istoricul este dezactivat + Istoric + Istoricul este gol + Istoric curățat + Element șters + Doriți să ștergeți acest element din istoricul de căutare? + From 89a87693994883cca9d170958c1b3cc18fedbb21 Mon Sep 17 00:00:00 2001 From: Joona Mattila Date: Tue, 24 Oct 2017 14:57:26 +0000 Subject: [PATCH 026/154] Translated using Weblate (Finnish) Currently translated at 99.0% (204 of 206 strings) --- app/src/main/res/values-fi/strings.xml | 46 ++++++++++++++------------ 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 33b4a6cd5..5b01fcdd4 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -2,7 +2,7 @@ Napauta hakua aloittaaksesi %1$s näyttökertaa Julkaistu %1$s - Ei löytynyt suoratoisto soitinta. Haluatko asentaa VLC:n? + Ei löytynyt suoratoistosoitinta. Haluatko asentaa VLC:n? Asenna Peruuta Avaa selaimessa @@ -15,9 +15,9 @@ Jaa Valitse selain kierto - Käytä ulkoista videotoistinta + Käytä ulkoista videosoitinta Joillain resoluutioilla EI ole ääntä, kun tämä on valittuna - Käytä ulkoista äänitoistinta + Käytä ulkoista äänisoitinta NewPipe ponnahdusikkuna Tilaa Tilattu @@ -31,13 +31,13 @@ Uusimmat Taustatoisto - Ponnahdusikkuna + Ikkuna Videon latauksen sijainti Sijainti ladatuille videoille Aseta sijainti minne videot tallennetaan - Audio latauksen sijainti + Audion latauksen sijainti Sijainti ladatuille audiotiedostoille Aseta sijainti minne audiotiedostot tallennetaan @@ -73,10 +73,10 @@ Jatka toistoa keskeytysten jälkeen (esim. puhelut) Lataa Seuraava video - Näytä seuraava ja samankaltaisia videoita + Näytä seuraava video ja samankaltaisia videoita URL ei tueta - Oletus-sisällon kieli - Toistin + Oletus sisällon kieli + Soitin Käyttäytyminen Video & Audio Historia @@ -124,10 +124,10 @@ Sovellus/UI kaatui Pahoittelut, noin ei olisi pitänyt käydä. Raportoi virhe sähköpostin kautta - Pahoittelit, joitain virheitä tapahtui. + Pahoittelut, joitain virheitä tapahtui. RAPORTTI Mitä tapahtui: - Sinun viesti (Englanniksi): + Sinun viesti (englanniksi): Yksityiskohdat: @@ -150,29 +150,29 @@ Audio Toista uudelleen Oikeus tallennustilan hallintaan evätty - Käytä vanhaa toistinta - Käytä vanhaa sisäänrakennettua Mediaframework toistinta + Käytä vanhaa soitinta + Käytä vanhaa sisäänrakennettua Mediaframework soitinta t. - Milj. - Bilj. + milj. + bilj. Ei tilaajia %s tilaaja - %s tilaajia + %s tilaajaa Ei katselukertoja %s katselukerta - %s katselukertoja + %s katselukertaa Ei videoita %s video - %s videoita + %s videota Aloita @@ -185,7 +185,7 @@ OK Tiedostonimi - Säikeet + Viestiketjut Virhe Serveriä ei tueta Tiedosto on jo olemassa @@ -202,9 +202,9 @@ reCAPTCHA Haaste pyydetty Lataus - Sallittuja merkkejä tiedostonimissä + Sallitut merkit tiedostonimissä Epäkelvot merkit korvataan tällä arvolla - Korvaus merkki + Korvaava merkki Kirjaimia ja numeroita Suurin osa erikoismerkeistä @@ -221,7 +221,7 @@ Vapaa kevyt YouTube frontend Android:lle. Näytä GitHub:ssa NewPipe:n Lisenssi - Olkoon sinulla ideoita; käännöksistä, design muutoksista, koodin siivoamisesta tai raskaista koodimuutoksista—apu on aina tervetullutta. Mitä enemmän saadaan tehtyä sen paremmaksi se tulee! + Olkoon sinulla ideoita; käännöksistä, design muutoksista, koodin siivoamisesta tai raskaista koodimuutoksista—apu on aina tervetullutta. Mitä enemmän saadaan tehtyä, sen paremmaksi sovellus tulee! Lue lisenssi Lahjoitus @@ -234,4 +234,6 @@ Historia pyyhitty Poistettu Haluatko poistaa tämän hakuhistoriasta? - +Jatka toistoa + Info: + From d5d9ed7200f6413093b70a60cbf45d709a058f35 Mon Sep 17 00:00:00 2001 From: Filip Sebastian Date: Tue, 24 Oct 2017 22:00:20 +0000 Subject: [PATCH 027/154] Translated using Weblate (Romanian) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-ro/strings.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 9243d7ebf..2cc912900 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -259,4 +259,19 @@ pentru a deschide în mod pop-up Istoric curățat Element șters Doriți să ștergeți acest element din istoricul de căutare? +Conținutul paginii principale + Pagină Goală + Pagină Chioșc + Pagină Abonări + Pagină Feed + Pagină Canale + Alegeți un canal + Nu v-ați abonat la niciun canal deocamdată + Alegeți un chioșc + + Chioșc + Trenduri + Top 50 + Tendințe + %1$s/%2$s From 4db3bd3270476d62b186fbd1088f6f5e8e1d8777 Mon Sep 17 00:00:00 2001 From: Filip Sebastian Date: Tue, 24 Oct 2017 22:01:06 +0000 Subject: [PATCH 028/154] Translated using Weblate (Romanian) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-ro/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 2cc912900..bbac3946c 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -85,7 +85,7 @@ Nu s-a putut prelua niciun stream Scuze, asta n-ar fi trebui să se întâmple. Raportează eroarea prin e-mail - Scuze, câteva erori au apărut. + Ne scuzați, au apărut câteva erori. RAPORTAȚI Informații: Ce s-a întâmplat: From 9a073713bb715e13d4bdf706048354bb62f37e23 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 25 Oct 2017 15:20:57 +0200 Subject: [PATCH 029/154] put tabs on top --- .../fragments/list/feed/FeedFragment.java | 2 +- .../subscription/SubscriptionFragment.java | 1 + app/src/main/res/layout/fragment_blank.xml | 6 + app/src/main/res/layout/fragment_channel.xml | 6 + app/src/main/res/layout/fragment_feed.xml | 6 + app/src/main/res/layout/fragment_kiosk.xml | 7 + app/src/main/res/layout/fragment_main.xml | 5 +- app/src/main/res/layout/fragment_playlist.xml | 6 + app/src/main/res/layout/fragment_search.xml | 6 + .../main/res/layout/fragment_subscription.xml | 6 + .../main/res/layout/fragment_video_detail.xml | 682 +++++++++--------- app/src/main/res/layout/toolbar_layout.xml | 5 - 12 files changed, 395 insertions(+), 343 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java index 379ec591a..835647eec 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/feed/FeedFragment.java @@ -121,7 +121,7 @@ public class FeedFragment extends BaseListFragment, Voi } if(useAsFrontPage) { - supportActionBar.setDisplayHomeAsUpEnabled(false); + supportActionBar.setDisplayShowTitleEnabled(true); //supportActionBar.setDisplayShowTitleEnabled(false); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index afb2f078e..3ff56a163 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -62,6 +62,7 @@ public class SubscriptionFragment extends BaseStateFragment + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_channel.xml b/app/src/main/res/layout/fragment_channel.xml index 460f95a7a..67691fc81 100644 --- a/app/src/main/res/layout/fragment_channel.xml +++ b/app/src/main/res/layout/fragment_channel.xml @@ -62,4 +62,10 @@ android:visibility="gone" tools:visibility="visible"/> + + diff --git a/app/src/main/res/layout/fragment_feed.xml b/app/src/main/res/layout/fragment_feed.xml index 0f2d0b675..0868d8233 100644 --- a/app/src/main/res/layout/fragment_feed.xml +++ b/app/src/main/res/layout/fragment_feed.xml @@ -34,4 +34,10 @@ android:visibility="gone" tools:visibility="visible"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_kiosk.xml b/app/src/main/res/layout/fragment_kiosk.xml index a85c30cb1..4730e66c8 100644 --- a/app/src/main/res/layout/fragment_kiosk.xml +++ b/app/src/main/res/layout/fragment_kiosk.xml @@ -4,6 +4,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index dac5478ff..b1dd3e20b 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -10,13 +10,14 @@ android:id="@+id/main_tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_alignParentBottom="true" + android:layout_alignParentTop="true" + android:background="@color/dark_youtube_primary_color" app:tabGravity="fill"/> + android:layout_below="@id/main_tab_layout"/> diff --git a/app/src/main/res/layout/fragment_playlist.xml b/app/src/main/res/layout/fragment_playlist.xml index d5ef26a63..37c609fa4 100644 --- a/app/src/main/res/layout/fragment_playlist.xml +++ b/app/src/main/res/layout/fragment_playlist.xml @@ -62,4 +62,10 @@ android:visibility="gone" tools:visibility="visible"/> + + diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 04b10347c..d49d23175 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -81,4 +81,10 @@ android:visibility="gone" tools:visibility="visible"/> + + diff --git a/app/src/main/res/layout/fragment_subscription.xml b/app/src/main/res/layout/fragment_subscription.xml index 35cfbfeb7..e0d0348dc 100644 --- a/app/src/main/res/layout/fragment_subscription.xml +++ b/app/src/main/res/layout/fragment_subscription.xml @@ -35,4 +35,10 @@ android:visibility="gone" tools:visibility="visible"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 0d87809fc..b067db7bc 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -1,345 +1,308 @@ - - - + + android:focusableInTouchMode="true"> - - + android:layout_height="match_parent" + android:visibility="visible" + app:parallax_factor="1.9"> - - + + android:orientation="vertical"> - - - - - - - - - - + - - + android:foreground="?attr/selectableItemBackground"> + android:id="@+id/detail_thumbnail_image_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:color/transparent" + android:contentDescription="@string/detail_thumbnail_view_description" + android:scaleType="centerCrop" + tools:ignore="RtlHardcoded" + tools:layout_height="200dp" + tools:src="@drawable/dummy_thumbnail"/> + + - - - - - - - - + + android:background="?android:windowBackground"> - - - - - - - - - - - - - - - - - - - - - - - - + - - - + android:paddingRight="12dp"> + tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed a ultricies ex. Integer sit amet sodales risus. Duis non mi et urna pretium bibendum. Nunc eleifend est quis ipsum porttitor egestas. Sed facilisis, nisl quis eleifend pellentesque, orci metus egestas dolor, at accumsan eros metus quis libero."/> - - + android:layout_marginLeft="5dp" + android:src="@drawable/arrow_down" + tools:ignore="ContentDescription,RtlHardcoded"/> - + - - + + + + + + + - + + android:layout_marginTop="6dp" + android:baselineAligned="false" + android:orientation="horizontal"> - + + + + + + + + + + + + + + + + + + + + + android:background="?attr/selectableItemBackground" + android:gravity="center_vertical" + android:orientation="horizontal" + android:paddingBottom="8dp" + android:paddingLeft="12dp" + android:paddingRight="12dp" + android:paddingTop="8dp"> + + + + + + + + - - - - - - - + - - + android:layout_marginTop="5dp" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + diff --git a/app/src/main/res/layout/toolbar_layout.xml b/app/src/main/res/layout/toolbar_layout.xml index 36ab6454e..096974c03 100644 --- a/app/src/main/res/layout/toolbar_layout.xml +++ b/app/src/main/res/layout/toolbar_layout.xml @@ -36,9 +36,4 @@ - - \ No newline at end of file From 4c4cfb49b47cf6eb84b486b43c30ec1935348c34 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 25 Oct 2017 16:09:26 +0200 Subject: [PATCH 030/154] make tabs contain icons instead of title --- .../schabi/newpipe/fragments/MainFragment.java | 12 +++++++++++- .../subscription/SubscriptionFragment.java | 1 - .../res/drawable-hdpi/ic_channel_white_24dp.png | Bin 0 -> 398 bytes .../res/drawable-hdpi/ic_whatshot_white_24dp.png | Bin 0 -> 468 bytes .../res/drawable-mdpi/ic_channel_white_24dp.png | Bin 0 -> 304 bytes .../res/drawable-mdpi/ic_whatshot_white_24dp.png | Bin 0 -> 334 bytes .../res/drawable-xhdpi/ic_channel_white_24dp.png | Bin 0 -> 468 bytes .../drawable-xhdpi/ic_whatshot_white_24dp.png | Bin 0 -> 595 bytes .../drawable-xxhdpi/ic_channel_white_24dp.png | Bin 0 -> 739 bytes .../drawable-xxhdpi/ic_whatshot_white_24dp.png | Bin 0 -> 866 bytes .../drawable-xxxhdpi/ic_channel_white_24dp.png | Bin 0 -> 633 bytes .../drawable-xxxhdpi/ic_whatshot_white_24dp.png | Bin 0 -> 1129 bytes 12 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_channel_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_whatshot_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_channel_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_whatshot_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_channel_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_whatshot_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_channel_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_whatshot_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_channel_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_whatshot_white_24dp.png diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 317630faa..42f52431e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -84,6 +84,15 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte viewPager.setOffscreenPageLimit(adapter.getCount()); tabLayout.setupWithViewPager(viewPager); + + if(PreferenceManager.getDefaultSharedPreferences(getActivity()) + .getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key)) + .equals(getString(R.string.subscription_page_key))) { + tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_white_24dp); + } else { + tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_white_24dp); + tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_white_24dp); + } } /*////////////////////////////////////////////////////////////////////////// @@ -171,7 +180,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte @Override public CharSequence getPageTitle(int position) { - return getString(this.tabTitles[position]); + //return getString(this.tabTitles[position]); + return ""; } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index 3ff56a163..afb2f078e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -62,7 +62,6 @@ public class SubscriptionFragment extends BaseStateFragmentn;!$1&*;dh)(f!siq0+~ZlBH#{)`w)j?Q1Ejfej4sTiHHIe@%p1MjzWkQdlw-D z(^$5p$K%0+6RWZtRsmP%lj zsm_cJbt|u!sCB1n+a literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_whatshot_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_whatshot_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..46ed1f8b67db8c34ee5ec652fab79b36ef42a684 GIT binary patch literal 468 zcmV;_0W1EAP)5Q+*74bf0x&=Nt=AV`E`P;rr% zjXDH^f>SO>Q6R;g4&L4KeM_!?|GRhZ|K5wMtQ@v7FkaE7-?&cDjH2)q>kD@n<{Vd9q~507XyO~ue0DK<<-#Cw zCU}8yO)lIf$`%=kNsK3Q;Uz9x+@)PVaRFmVE^JV5g&8i=q+d9W@lh^(r`87s6hI22 zAQwthTc=ea@Tu_RLWyc)3PB2^C>K6q`LrkmXEAbe;Vo8Kp}2>Ul?ykic2adXN*-fM zF7&X6^*{kMvxMQ1mK)EpJjPUoW4yzlnj?B;F2e!iUQ07sBGET*?k!xiBhgQF7;_ zt=D(V3|Lkn8~1QgbnsNeRQ3b2B&YAn+7hJYof5_7X|_XU=^;)Qi_=_;*MF9LG(5Cm zWxP+&4wdcRQj?TrYOC2r(`AdU+*5S4xO15Mt#f>#%8DJA9SS;b2e&jDED~?tC;#ia vey!RkT_w&;2fdy;+f2DTzqbCAddycI&vNIBYtmVuXBa$P{an^LB{Ts5C^vIp literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_whatshot_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_whatshot_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..4cf6f85f83b9b993773a899637777e081a897e0b GIT binary patch literal 334 zcmV-U0kQsxP)PLcWj2anrF;5%c2NSX}hp$76Tfz#mu|gK@oGFANh9etScs3QnAx2X+cJOQ~ zfN9<_O0tp1#kgl&J&bXV;gVB7s(2dAGQuPUZt#>9g%rm`IU=cOq^RN&)VR$jk6L2xJaZ4ns%n0LBXC)U?y|U z+~2uyz#oSmm2ce7H}7wwzf>W0Qz)0T)Wtp$x7`vo{nfrs>W~OT51_m65;gsGs8HXx zC2IPcklECbh(HgZhe|@Zq{5qC0}VA%D9rS%L7RT8&=@x)G*k?X01QAA`Vsof%=%vf zs#eSbB}9WSiQO*$iGWOzEf5IBchN2)iRa&^I`b27ssp8z2m_2GQs!2;)iw|991>Z51df-u$x84 zRV}Qg=!mL?ITq#UQgx7_XqI)V25#}o1T|WIT&CohmV$aVD-uJLd}h7kW0*cg;u5B- zii{AFj`GhG)tK2YzpT`0D(7-A3_zlPvjXWplF8f(cEzRs;koN>lk&rvu z$P&jaEsBQ@vbdUMlj5U|XSkT6MN2~i=gCt^jsX(-6SR@WBaFugty&JO(@+%0@$YfnCcp+Oup_~S#0D#fH>lKsg#m<+U}HdmSD`O}#Q!&dQiM=c2^LmF z2c!=9N8%qtY}rm+;>5A91wI)ZC6Vt(=iGDeO#)3c(L}96Pt;lJ^Bp}dlk-%P@{`n& zBt0sNv#EDdep1Mhp35tX)Xq;zaJI@UT3c`PkSgMYq&JI56>{>BD&{OAIp6@OrPo;` zN1TxKYL?`X6OvvgNsc)o=|w~;fuj?8s81TG${1*057f@$oY#R|MOWt{heg?8p9>C; z5nV3Z5Fpz2?mFebMeagZg$d2~zxyS%nM7+_)Y!iw4wy9D#)D zmm!&~UL_Nfsb+IXm^SMGGAvx>#YAUyX6CN(qw+2@0)Aj>D}F`c)l0ya-BaSoIeknh zzS{KqaXyQ-+_)=luE(h-9C5lnn`?k*^OIdiU6xUffB=uHyyyDLH(kAChDVUB-h?qj z2J{&)dId7` zw>`w=UbfzYwO_1Y>p$iDtfOY`LFN5G(tMR}K>r*l>at<{qo?FV(obio>pV5xG-%g3 z=vn$a)MAfI4mqE>VxvTcY{(71ION=%a?<-{OZT;>UZwv7_4Gmaoc-a~L=*pI`~fV* V9>c$)0PO$(002ovPDHLkV1jjtSHA!N literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_whatshot_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_whatshot_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..8eaf3755da5295bbe800e7022c2db30d17f680f6 GIT binary patch literal 866 zcmV-o1D*VdP)7r0tpIELMaN&@wqXVwGqKn-Kqw3Fc(-sVUA4Js=7Sr{7>1#2m?Fj^D^ zEyVXdHt#D6J|?bjNi&Wqt0)*Io-@=*oWQ15p}2u~j&ZI2K|40v6pGzMH_9IUhwa!r zrcgXjG*fhNz5YiUn@)w|J;MFMbEI_zMltm$6kiZxkWTJknJoAk(^m?`5BT_Qg9Q$K_=O;5zb{x)h3LT%J_` z_F&VbP&DB3k^=B0rXqC;$0_W3d=hUbu=uY%+?1HB4gHEH89plcQGA z@EUemR;nzX!mdM6v5p*eM^zHbIEdW@4T_F-Tn@8b4y>mSmo~*ginF-%vqc8dyvPJD zr>RjqY+?eJ0w;KgO{^qEn%l^5oQt@P)2R5^MS&#b*`Z|6N)lR>99k(5N1hfXi=F&M zRO9SW^4Q2XL~)u%Eeui|AV-J^+NjY&VlACa;v+`~4O$GW;c-q<#HGjp8EUn-sOLet s=wpZxrkQ4hA^PZ|i8?J=vZQML4Wq^#!k7$FxBvhE07*qoM6N<$f@DjJI{*Lx literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_channel_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_channel_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..9384592d6aada1745b649ff14a9bd93b13119605 GIT binary patch literal 633 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgn3BBRT^Rni_n+Ah4nJ za0`PlBg3pY5H=O_J>@Oyb2bFjdNpwnz%e&978H@y}jk{cgR74^#l9A=H76F zygNd|jwxxmyo!fc~Ula*WC4&INvL)2weL1 z*XnZFGLe9lAzD{;!Vl&aZgXJa)8??d&nFlY*RFC>;p-4@uS@u(V6862Gn>+2;RtL%O8y^$I zD!bX@+T*(>%J=Q<;D7T-!$>{t#`pJoW}Xy&eK=V$r;&Mmp6eU+-2r!3@7Da9A};mh z-I1Tcya8Rz8P&%%n(OS1;xB%;E@t9TXkdU4H++n2*aKoc7#LXu91ujqmTavb_n5v& zKlr<(?p*l+Be7+LUK?&dk$U#w-+zX`{%g2r0u3;@-SBh0fBr`Hc{5($X-?t(vgg*v zyDATEX*;M}PFeB5uKaNO$HQG*a*uUCG!*B5`~T9wZl7&c%puh>+m8C?y@Bk8AG+Ai z71e!H^jq}N@X^QLT;BqZYfUREfAnTi^XCnpdzfE%>402n;N8G~wop%c^5vIyz=Xo! M>FVdQ&MBb@06@0`-T(jq literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_whatshot_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_whatshot_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5d86873cce687891517d459d778b4b840d38c4 GIT binary patch literal 1129 zcmV-v1eW`WP)Z*MU8R+!PP3YYb2E=S+pnxf>h`>!&wJ)e=ene<{}H+?-gt{5#Tl!Q-KusDXz%pqBQdqR~+J^79LeRv5{5_6h|zgRWq{{KitlLnwYA%p%<5G;AX`Q zr?}J!1}I+mgv;%ttKx)pTxp%+gy(2$zT$(qwDkug6&FmQJyql=9vDUYmMR_?O#7M` zqfBC;0-+Zsdr2r$=)(>LLJ!PJl^G=2&3*+!FU(GpuT0y7{e{;stCkH^W8bIr)8CkZS&B{yi!P+F zE6p-;^e+bCq)1^g3cD&9qsvf-+1(0@nb=dq47I_>m@QIREWw5rKI9$}Y6ojDTdA;E zPgn3qU=?l#DIofj!mM02a2sx> zD@s6eJUPvNYQ-tvtCPU2*-Y~fnY;%tR{q8pVsIYSTG!^=2p z;uiTpH+JEyOg7Pz(>VKqzVe7dDslD;d9sT~aC3m`%riBd*Q@fbJdM@Cuo+3qqxp>jcZXRbmy~*Jk`clkX)^Y@|W+^(XCTiu1 z5=pibr7a{BEppjORNkYDqDL; Date: Tue, 24 Oct 2017 23:34:58 +0000 Subject: [PATCH 031/154] Translated using Weblate (French) Currently translated at 94.0% (207 of 220 strings) --- app/src/main/res/values-fr/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f8de02050..9f89e07d8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -257,4 +257,5 @@ Objet effacé Voulez-vous supprimer cet élément de l\'historique de recherche ? +%1$s/%2$s From fb71ba3b7cea12ca9cb30da161ca1e78ede3d79a Mon Sep 17 00:00:00 2001 From: Anton Shestakov Date: Wed, 25 Oct 2017 04:25:24 +0000 Subject: [PATCH 032/154] Translated using Weblate (Russian) Currently translated at 98.1% (216 of 220 strings) --- app/src/main/res/values-ru/strings.xml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e60ec9917..b5a5dcc74 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -258,4 +258,17 @@ Элемент удалён Удалить этот элемент из истории поиска? - +Контент главной страницы + Пустая страница + Страница киоска + Страница подписки + Страница ленты + Страница канала + Выберите канал + Выберите киоск + + Киоск + В тренде + Топ 50 + Новое и горячее + From f6353cfb473d7b519362100d07c0b430d47999a2 Mon Sep 17 00:00:00 2001 From: John Zhen Mo Date: Wed, 25 Oct 2017 22:00:58 -0700 Subject: [PATCH 033/154] -Added fling mechanic for popup player shutdown. -Changed long click to fill screen for popup player. -Added 2-finger resizing for popup player. -Removed long click resize mechanic. --- .../newpipe/player/PopupVideoPlayer.java | 78 +++++++++++++------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java index b022cd003..a4087a942 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java @@ -92,6 +92,7 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; public class PopupVideoPlayer extends Service { private static final String TAG = ".PopupVideoPlayer"; private static final boolean DEBUG = BasePlayer.DEBUG; + private static final int SHUTDOWN_FLING_VELOCITY = 10000; private static final int NOTIFICATION_ID = 40028922; public static final String ACTION_CLOSE = "org.schabi.newpipe.player.PopupVideoPlayer.CLOSE"; @@ -303,7 +304,6 @@ public class PopupVideoPlayer extends Service { public void onVideoClose() { if (DEBUG) Log.d(TAG, "onVideoClose() called"); - savePositionAndSize(); stopSelf(); } @@ -574,9 +574,7 @@ public class PopupVideoPlayer extends Service { private int initialPopupX, initialPopupY; private boolean isMoving; - private int onDownPopupWidth = 0; private boolean isResizing; - private boolean isResizingRightSide; @Override public boolean onDoubleTap(MotionEvent e) { @@ -603,27 +601,20 @@ public class PopupVideoPlayer extends Service { initialPopupY = windowLayoutParams.y; popupWidth = windowLayoutParams.width; popupHeight = windowLayoutParams.height; - onDownPopupWidth = windowLayoutParams.width; - return false; + return super.onDown(e); } @Override public void onLongPress(MotionEvent e) { if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); - playerImpl.showAndAnimateControl(-1, true); - playerImpl.getLoadingPanel().setVisibility(View.GONE); - - playerImpl.hideControls(0, 0); - animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); - animateView(playerImpl.getResizingIndicator(), true, 200, 0); - - isResizing = true; - isResizingRightSide = e.getRawX() > windowLayoutParams.x + (windowLayoutParams.width / 2f); + updateScreenSize(); + checkPositionBounds(); + updatePopupSize((int) screenWidth, -1); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (isResizing) return false; + if (isResizing) return super.onScroll(e1, e2, distanceX, distanceY); if (playerImpl.getCurrentState() != BasePlayer.STATE_BUFFERING && (!isMoving || playerImpl.getControlsRoot().getAlpha() != 1f)) playerImpl.showControls(0); @@ -659,19 +650,33 @@ public class PopupVideoPlayer extends Service { } } + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (Math.abs(velocityX) > SHUTDOWN_FLING_VELOCITY) { + if (DEBUG) Log.d(TAG, "Popup close fling velocity= " + velocityX); + onVideoClose(); + return true; + } + return false; + } + @Override public boolean onTouch(View v, MotionEvent event) { gestureDetector.onTouchEvent(event); - if (event.getAction() == MotionEvent.ACTION_MOVE && isResizing && !isMoving) { - //if (DEBUG) Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); - int width; - if (isResizingRightSide) width = (int) event.getRawX() - windowLayoutParams.x; - else { - width = (int) (windowLayoutParams.width + (windowLayoutParams.x - event.getRawX())); - if (width > minimumWidth) windowLayoutParams.x = initialPopupX - (width - onDownPopupWidth); - } - if (width <= maximumWidth && width >= minimumWidth) updatePopupSize(width, -1); - return true; + if (event.getPointerCount() == 2 && !isResizing) { + if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing."); + playerImpl.showAndAnimateControl(-1, true); + playerImpl.getLoadingPanel().setVisibility(View.GONE); + + playerImpl.hideControls(0, 0); + animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); + animateView(playerImpl.getResizingIndicator(), true, 200, 0); + isResizing = true; + } + + if (event.getAction() == MotionEvent.ACTION_MOVE && !isMoving && isResizing) { + if (DEBUG) Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "], e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); + return handleMultiDrag(event); } if (event.getAction() == MotionEvent.ACTION_UP) { @@ -692,6 +697,29 @@ public class PopupVideoPlayer extends Service { return true; } + private boolean handleMultiDrag(final MotionEvent event) { + if (event.getPointerCount() != 2) return false; + + final float firstPointerX = event.getX(0); + final float secondPointerX = event.getX(1); + + final float diff = Math.abs(firstPointerX - secondPointerX); + if (firstPointerX > secondPointerX) { + // second pointer is the anchor (the leftmost pointer) + windowLayoutParams.x = (int) (event.getRawX() - diff); + } else { + // first pointer is the anchor + windowLayoutParams.x = (int) event.getRawX(); + } + + checkPositionBounds(); + updateScreenSize(); + + final int width = (int) Math.min(screenWidth, diff); + updatePopupSize(width, -1); + + return true; + } } /** From 0a2aa54508747e2e2dd9164629594d77515fd2c0 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Thu, 26 Oct 2017 09:23:43 +0200 Subject: [PATCH 034/154] fix typo --- .../org/schabi/newpipe/settings/ContentSettingsFragment.java | 4 ++-- .../java/org/schabi/newpipe/settings/SelectKioskFragment.java | 2 +- app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values/strings.xml | 2 +- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 4f6c2d1ae..2cda95987 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -50,7 +50,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { getContext()); String summary = - String.format(getString(R.string.service_kosk_string), + String.format(getString(R.string.service_kiosk_string), serviceName, kioskName); @@ -122,7 +122,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { getContext()); String summary = - String.format(getString(R.string.service_kosk_string), + String.format(getString(R.string.service_kiosk_string), service.getServiceInfo().name, kioskName); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index a9f0a53ed..9e5420b6e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -126,7 +126,7 @@ public class SelectKioskFragment extends DialogFragment { for(StreamingService service : NewPipe.getServices()) { for(String kioskId : service.getKioskList().getAvailableKiosks()) { - String name = String.format(getString(R.string.service_kosk_string), + String name = String.format(getString(R.string.service_kiosk_string), service.getServiceInfo().name, KioskTranslator.getTranslatedKioskName(kioskId, getContext())); kioskList.add(new Entry( diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9f89e07d8..f8de02050 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -257,5 +257,4 @@ Objet effacé Voulez-vous supprimer cet élément de l\'historique de recherche ? -%1$s/%2$s diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index bbac3946c..acbc9891a 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -273,5 +273,4 @@ pentru a deschide în mod pop-up Trenduri Top 50 Tendințe - %1$s/%2$s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94b185988..4f06dc4c4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -290,5 +290,5 @@ Trending Top 50 New & hot - %1$s/%2$s + %1$s/%2$s From 0d6662b558b0fa456de9d21bcc1ef62d2570ef4c Mon Sep 17 00:00:00 2001 From: Tobias Groza Date: Wed, 25 Oct 2017 09:27:37 +0000 Subject: [PATCH 035/154] Translated using Weblate (German) Currently translated at 95.0% (209 of 220 strings) --- app/src/main/res/values-de/strings.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 0e183f2f8..ef5547f5b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -259,4 +259,8 @@ Nichts hier außer Grillen Möchten Sie dieses Element aus dem Suchverlauf löschen? - +Leere Seite + Wähle einen Kanal aus + Noch kein Kanal abonniert + Trends + From 65e83e8fb6c261eaa650d35e179a65715117f468 Mon Sep 17 00:00:00 2001 From: Eduardo Caron Date: Thu, 26 Oct 2017 12:53:00 +0000 Subject: [PATCH 036/154] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-pt-rBR/strings.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 9123f868c..7ab7475eb 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -242,4 +242,19 @@ abrir em modo popup Não há nada aqui Deseja apagar este item do seu histórico de busca? +Conteúdo + Página em Branco + Página de Quiosque + Página de Inscrição + Página de Atualizações + Página de Canais + Selecione um canal + Nenhuma inscrição ainda + Selecione um quiosque + + Quiosque + Em Alta + Top 50 + Novos e tendências + %1$s/%2$s From be3d6adf77aea9ffbbf988ba36bf3e72c95a8621 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Wed, 25 Oct 2017 19:05:45 +0000 Subject: [PATCH 037/154] Translated using Weblate (Arabic) Currently translated at 30.9% (68 of 220 strings) --- app/src/main/res/values-ar/strings.xml | 150 ++++++++++++++----------- 1 file changed, 87 insertions(+), 63 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index fc3706bba..44599b136 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,63 +1,87 @@ - - - جاري التشغيل في الخلفية - إلغاء - إختر متصفح - مظلم - صيغة الصوت الإفتراضية - الدقة الإفتراضية - عدم الإعجاب - الإعجابات - صور معاينة الفيديو - "Uploader's userpic thumbnail" - هل تقصد: - تنزيل - تنزيل - أدخل مسار التنزيل للملفات الصوتية. - مسار حفظ التنزيلات الصوتية في. - مسار الصوتيات المحفوظة - أدخل مسار التنزيل لملفات الفيديو - مسار حفظ تنزيلات الفيديو في. - مسار الفيديوهات المحفوظة - "لا يمكن إنشاء مجلد للتنزيلات في '%1$s'" - "تم إنشاء مجلد تنزيلات في '%1$s'" - تثبيت - تطبيق Kore غير موجود. هل تريد تثبيته؟ - مضيء - صور معاينة الفيديو - m4a — جودة أفضل - خطأ في الشبكة - الفيديو التالي - لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟ - فتح في المتصفح - صوت - تشغيل - تشغيل بواسطة Kodi - تدوير - بحث - لغة المحتوى المفضل - الإعدادات - المظهر - تعريب JetSub مدونة درويديات - الفيديو والصوتيات - مشاركة - مشاركة بواسطة - عرض التالي والفيديوهات المشابهة - عرض خيار لتشغيل الفيديو بواسطة Kodi Media Center. - عرض خيار التشغيل بواسطة Kodi. - الثيم - تم الرفع في %1$s - الرابط غير مدعوم - استخدام مشغل صوتيات خارجي - استخدام مشغل فيديو خارجي - إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ) - استخدام Tor - %1$s المشاهدات - WebM - Blocked by GEMA. - المحتوى غير متاح. - لم يتمكن من تحميل كل صور المعاينة - خطأ - لا يمكن تحليل الموقع. - لا يمكن فك تشفير توقيع رابط الفيديو. - + + + جاري التشغيل في الخلفية + إلغاء + إختر متصفح + مظلم + صيغة الصوت الإفتراضية + الدقة الإفتراضية + عدم الإعجاب + الإعجابات + صور معاينة الفيديو + "Uploader's userpic thumbnail" + هل تقصد : %1$s ؟ + تنزيل + تنزيل + أدخل مسار التنزيل للملفات الصوتية. + مسار حفظ التنزيلات الصوتية في. + مسار الصوتيات المحفوظة + أدخل مسار التنزيل لملفات الفيديو + مسار حفظ تنزيلات الفيديو في + مسار الفيديوهات المحفوظة + "لا يمكن إنشاء مجلد للتنزيلات في '%1$s'" + "تم إنشاء مجلد تنزيلات في '%1$s'" + تثبيت + تطبيق Kore غير موجود. هل تريد تثبيته؟ + مضيء + صور معاينة الفيديو + m4a — جودة أفضل + خطأ في الشبكة + الفيديو التالي + لا يوجد مشغل فيديو. هل تريد تثبيت VLC ؟ + فتح في المتصفح + صوت + تشغيل + تشغيل بواسطة Kodi + تدوير + بحث + لغة المحتوى المفضل + الإعدادات + المظهر + تعريب JetSub مدونة درويديات + الفيديو والصوتيات + مشاركة + مشاركة بواسطة + عرض التالي والفيديوهات المشابهة + عرض خيار لتشغيل الفيديو بواسطة Kodi Media Center. + عرض خيار التشغيل بواسطة Kodi. + السمة + تم نشرها في %1$s + الرابط غير مدعوم + استخدام مشغل صوتيات خارجي + استخدام مشغل فيديو خارجي + إجراء التنزيلات من خلال استخدام بروكسي Tor لزيادة الخصوصية ( تشغيل الفيديو المباشر غير مدعوم حتى الأن ) + استخدام Tor + %1$s المشاهدات + WebM + Blocked by GEMA. + المحتوى غير متاح. + لم يتمكن من تحميل كل صور المعاينة + خطأ + لا يمكن تحليل الموقع. + لا يمكن فك تشفير توقيع رابط الفيديو. +إضغط على البحث للمواصلة + إشترك + مشترك + الرئيسية + الإشتراكات + + ما الجديد + + الخلفية + التشغيل التلقائي + أسود + التأريخ + التأريخ + المحتوى + التنزيلات + التنزيلات + الكل + قناة + فيديو + التنزيل + عن التطبيق + عن التطبيق + التأريخ + التأريخ + From 66de4cbeadc4be8aeb384e3f28c8adf51baa0216 Mon Sep 17 00:00:00 2001 From: ButterflyOfFire Date: Wed, 25 Oct 2017 18:43:53 +0000 Subject: [PATCH 038/154] Translated using Weblate (French) Currently translated at 98.1% (216 of 220 strings) --- app/src/main/res/values-fr/strings.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9f89e07d8..aea2e91d2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -258,4 +258,13 @@ Objet effacé Voulez-vous supprimer cet élément de l\'historique de recherche ? %1$s/%2$s - +Contentu + Page vide + Page de souscription + Page de Flux + Page de la chaîne + Sélectionnez une chaîne + Populaires + Top 50 + Nouveau & populaire + From d5b3f650767cdb021a3f6cc34d93c116306e7559 Mon Sep 17 00:00:00 2001 From: Filip Sebastian Date: Wed, 25 Oct 2017 14:21:08 +0000 Subject: [PATCH 039/154] Translated using Weblate (Romanian) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-ro/strings.xml | 36 +++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index bbac3946c..a809e81dc 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1,8 +1,8 @@ - %1$s (de) vizionări + %1$s vizionări Încărcat pe %1$s - Nici un player pentru stream-uri găsit. Instalați VLC? + Niciun player pentru stream-uri găsit. Instalați VLC? Instalare Anulare Deschidere în browser @@ -17,13 +17,13 @@ Folosește un player video extern Folosește un player audio extern - Locul în care se vor descărca videoclipurile - Locul în care se vor stoca videoclipurile descărcate - Introduceți locul în care se vor descărca videoclipurile + Locația videoclipurilor descărcate + Locul în care se vor descărca videoclipurile + Introduceți locația în care se vor descărca videoclipurile - Calea de descărcare a sunetelor - Locul în care se vor stoca fișierele audio descărcate - Introduceți calea de descărcare pentru fișierele audio + Locația audio-ului descărcat + Locul în care se va descărca audio-ul + Introduceți locația în care se va descărca audio-ul Rezoluție implicită Redați folosind Kodi @@ -75,7 +75,7 @@ Nu s-a putut crea directorul de descărcare \'%1$s\' Apăsați căutare pentru a începe Redă automat - Redă automat un video când NewPipe este chemat de altă aplicație + Redă automat un videoclip atunci când NewPipe este deschis din altă aplicație în direct Descărcări Descărcări @@ -123,7 +123,7 @@ Copiat în clipboard Vă rugăm alegeți un folder pentru descărcări - Deschide in mod pop-up + Deschide in modul popup Aceasta permisiune este necesara pentru a deschide în mod pop-up @@ -134,8 +134,8 @@ pentru a deschide în mod pop-up NewPipe mod pop-up Rezoluție inițială pop-up - Arată rezoluție mai mare - Doar anumite dispozitive suportă afișare video în mod 2K/4K + Afișează rezoluții mai mari + Doar anumite dispozitive suportă redarea videoclipurilor 2K/4K Format video preferat Negru @@ -173,18 +173,18 @@ pentru a deschide în mod pop-up Reîmprospătare Șterge Redimensionare - Cea mai bună rezoluție + Rezoluția optimă Abonează-te Abonat Canal dezabonat - Nu s-a putut modifica abonarea - Nu s-a putut actualiza abonarea + Nu s-a putut modifica abonamentul + Nu s-a putut actualiza abonamentul Principal - Abonări + Abonamente - Ce e nou + Ce este nou Istoric de căutare Stochează local căutările @@ -202,7 +202,7 @@ pentru a deschide în mod pop-up Notificări pentru NewPipe Background și Popup Players Fără rezultate - Nimic Aici Doar Greieri + Nimic aici în afară de sunetul greierilor Niciun abonat From dd64bf2af712e7b8cb07dd967bb14e10d1b93181 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 26 Oct 2017 15:16:35 +0200 Subject: [PATCH 040/154] make title contain current tab --- .../java/org/schabi/newpipe/MainActivity.java | 1 - .../newpipe/fragments/BlankFragment.java | 19 +++++++++++++++++++ .../newpipe/fragments/MainFragment.java | 1 - .../list/channel/ChannelFragment.java | 2 +- .../fragments/list/kiosk/KioskFragment.java | 17 ++++++++++++++++- .../subscription/SubscriptionFragment.java | 16 ++++++++++++++++ 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 3ff55cb76..03b856d31 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -181,7 +181,6 @@ public class MainActivity extends AppCompatActivity implements HistoryListener { ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { - actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayHomeAsUpEnabled(false); } return true; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java index e9e50dd69..e81645202 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.fragments; import android.os.Bundle; import android.support.annotation.Nullable; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,6 +14,24 @@ public class BlankFragment extends BaseFragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + if(activity != null && activity.getSupportActionBar() != null) { + activity.getSupportActionBar() + .setTitle("NewPipe"); + } return inflater.inflate(R.layout.fragment_blank, container, false); } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if(isVisibleToUser) { + if(activity != null && activity.getSupportActionBar() != null) { + activity.getSupportActionBar() + .setTitle("NewPipe"); + } + // leave this inline. Will make it harder for copy cats. + // If you are a Copy cat FUCK YOU. + // I WILL FIND YOU, AND I WILL ... + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 42f52431e..190361f0e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -117,7 +117,6 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null) { - supportActionBar.setDisplayShowTitleEnabled(false); supportActionBar.setDisplayHomeAsUpEnabled(false); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index daa1b62ed..f98f8c36a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -33,6 +33,7 @@ import org.schabi.newpipe.fragments.subscription.SubscriptionService; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.AnimationUtils; import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; import java.util.List; @@ -125,7 +126,6 @@ public class ChannelFragment extends BaseListInfoFragment { ActionBar supportActionBar = activity.getSupportActionBar(); if(useAsFrontPage) { supportActionBar.setDisplayHomeAsUpEnabled(false); - //supportActionBar.setDisplayShowTitleEnabled(false); } else { inflater.inflate(R.menu.menu_channel, menu); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java index b1387b1ad..a9d1cda76 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/kiosk/KioskFragment.java @@ -53,6 +53,9 @@ import static org.schabi.newpipe.util.AnimationUtils.animateView; public class KioskFragment extends BaseListInfoFragment { + private String kioskId = ""; + + /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ @@ -76,6 +79,7 @@ public class KioskFragment extends BaseListInfoFragment { instance.setInitialData(serviceId, kioskTypeUrlIdHandler.getUrl(kioskId), kioskId); + instance.kioskId = kioskId; return instance; } @@ -83,6 +87,18 @@ public class KioskFragment extends BaseListInfoFragment { // LifeCycle //////////////////////////////////////////////////////////////////////////*/ + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if(useAsFrontPage && isVisibleToUser) { + try { + activity.getSupportActionBar().setTitle(KioskTranslator.getTranslatedKioskName(kioskId, getActivity())); + } catch (Exception e) { + onError(e); + } + } + } + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_kiosk, container, false); @@ -97,7 +113,6 @@ public class KioskFragment extends BaseListInfoFragment { super.onCreateOptionsMenu(menu, inflater); ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null && useAsFrontPage) { - //supportActionBar.setDisplayShowTitleEnabled(false); supportActionBar.setDisplayHomeAsUpEnabled(false); } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index afb2f078e..c4dfbc50d 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -7,6 +7,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -19,6 +20,7 @@ import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; import java.util.ArrayList; @@ -52,6 +54,17 @@ public class SubscriptionFragment extends BaseStateFragment Date: Thu, 26 Oct 2017 20:45:16 +0200 Subject: [PATCH 041/154] fix channel title for new tabs --- .../fragments/list/channel/ChannelFragment.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index f98f8c36a..61a97e847 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -26,6 +26,7 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.UrlIdHandler; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; @@ -82,6 +83,20 @@ public class ChannelFragment extends BaseListInfoFragment { // LifeCycle //////////////////////////////////////////////////////////////////////////*/ + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if(activity != null + && useAsFrontPage + && isVisibleToUser) { + try { + activity.getSupportActionBar().setTitle(currentInfo.name); + } catch (Exception e) { + onError(e); + } + } + } + @Override public void onAttach(Context context) { super.onAttach(context); From 8d5a59e7d51c601190d410bbef77ccad79823da7 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 26 Oct 2017 23:56:02 +0200 Subject: [PATCH 042/154] fix light theme for main page --- .../newpipe/fragments/MainFragment.java | 20 +++++++++++++++--- .../drawable-hdpi/ic_channel_black_24dp.png | Bin 0 -> 396 bytes .../drawable-hdpi/ic_whatshot_black_24dp.png | Bin 0 -> 462 bytes .../drawable-mdpi/ic_channel_black_24dp.png | Bin 0 -> 290 bytes .../drawable-mdpi/ic_whatshot_black_24dp.png | Bin 0 -> 330 bytes .../drawable-xhdpi/ic_channel_black_24dp.png | Bin 0 -> 423 bytes .../drawable-xhdpi/ic_whatshot_black_24dp.png | Bin 0 -> 601 bytes .../drawable-xxhdpi/ic_channel_black_24dp.png | Bin 0 -> 483 bytes .../ic_whatshot_black_24dp.png | Bin 0 -> 874 bytes .../ic_channel_black_24dp.png | Bin 0 -> 592 bytes .../ic_whatshot_black_24dp.png | Bin 0 -> 1134 bytes 11 files changed, 17 insertions(+), 3 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_channel_black_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_whatshot_black_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_channel_black_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_whatshot_black_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_channel_black_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_whatshot_black_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_channel_black_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_whatshot_black_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_channel_black_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_whatshot_black_24dp.png diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 190361f0e..7992f88f0 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -33,6 +33,7 @@ import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.ThemeHelper; import java.util.concurrent.ExecutionException; @@ -85,13 +86,26 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte tabLayout.setupWithViewPager(viewPager); + if(ThemeHelper.isLightThemeSelected(getActivity())) { + tabLayout.setBackgroundColor(getResources().getColor(R.color.light_youtube_primary_color)); + } + if(PreferenceManager.getDefaultSharedPreferences(getActivity()) .getString(getString(R.string.main_page_content_key), getString(R.string.blank_page_key)) .equals(getString(R.string.subscription_page_key))) { - tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_white_24dp); + if(ThemeHelper.isLightThemeSelected(getActivity())) { + tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_black_24dp); + } else{ + tabLayout.getTabAt(0).setIcon(R.drawable.ic_channel_white_24dp); + } } else { - tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_white_24dp); - tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_white_24dp); + if(ThemeHelper.isLightThemeSelected(getActivity())) { + tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_black_24dp); + tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_black_24dp); + } else { + tabLayout.getTabAt(0).setIcon(R.drawable.ic_whatshot_white_24dp); + tabLayout.getTabAt(1).setIcon(R.drawable.ic_channel_white_24dp); + } } } diff --git a/app/src/main/res/drawable-hdpi/ic_channel_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_channel_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ac66a3b860064d933a365f3e7454a8d83af3835d GIT binary patch literal 396 zcmV;70dxL|P) zI*6odGjzi@RZUF~tZfua)~`6_VZnNz9*^m~aF5boz$n+vDs|lbpaLUJn|9pJQAQ?E zH&ZHunWd7M99tt9>gI;4|6 zIUA9^cNAzbT19J0MJxKcgo+eFQ7RNc7~3n-Inc#UECy`?LV-hJLKT+Q9H97_`nH_h qW?ueZ?A0|L^J4rRd7e39z2h5Zpw|+kS*+s#0000 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_whatshot_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_whatshot_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b2db5994cecf747f3dc2276b20cf40f45e3162bb GIT binary patch literal 462 zcmV;<0WtoGP)7Bih!S+{oXiJ2yCR>=~#3lD{-D!F3ZVspI^sl7^VGbJ35waS!84J-9X z2(NXS?YfxN7KQrK;zP?MPKZo{3f&T^PcGVTr(=diHlqr3_$mr(`I+9ma_tr+opwLH zkIQvI6!b+u&ZN&b<$6$X<66Oo$I-sQAWm+PF6vdLVljivd3TtPIRF3v07*qoM6N<$ Ef?reB(f|Me literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_channel_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_channel_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..984ff498ea08633941f05f9e06203a72c76ed406 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zVGw3ym^BBap1s7=*OmPtmz1c!)I{&xK%mfOPZ!4!i_?22d2=0d5O9gNTU6~>@c2^o ziy#5}=N|-$jrTSz;C`Q=B^6 zTRfGVb*xZNG4r0wa>Z;0%jDiRcauy(-mjAyH?IqAzSTbO)Iq>Klv+FMfI-kMQ)z4*}Q$iB}166B7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_whatshot_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_whatshot_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..31b1981f0abb4c0d99e9ab77fbd93150c9be23f0 GIT binary patch literal 330 zcmV-Q0k!^#P)SX$|J@^;bm4$-i?4V4+;hX%KMZtq$t|4>g_}D0tZ_vLYvGI2D!|A{+e!#a6*%mb ztJ*$@?3@bMgy(tyE(@8ZO3z#Q(GXm;6EeS?R9p};ziqu2?MxJRMa@Ish44tRD#zd4 zaL6+uyjFZ_oqqM$bp!Aqm!0B|keNFMfIISCiXS3-0p*9i;*H2|YvW0-8^wK*-P3l) z&s?X9vl=0MbOOL}FLP^*RTd&!JMV~7Cfxv^m)s_V`bXd4W3ABlm%rV(T zf1z1Td4z#Hz|9JgB4qXD2Mn1aN@LDFgQEOIO?*8jdBjj%UJynA4uC0rgElj#zXr(a zlT(t;=|$Gv^1lF%h?@e!Om?|{OiWGy*R00E0EZ+*#KgF|u7#y&kAyc~07t}>_w|UGn)r;nqI?%W8ShfUA6ud7 RF;f5l002ovPDHLkV1hPjsssQ4 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_whatshot_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_whatshot_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e9ae826708be97f118730111e3975486ba9ef25d GIT binary patch literal 601 zcmV-f0;c_mP)2x%>qIMq5TA4+W z&g1M1sx(DgWL*;|rVljXB*OwSdc`3c`K6b~D51SL$)iZ`xyv{=c!`puh?5CaXLC4c z#z)RQKxTAel|#~+<8Q-YxZ0NMC`cbGBl4W>{jPh8uMTWYaWP_oRW^pi% z%(zC9t)b10;iNnC*b@7Q)5}L>6wryI5oCtX0hVjwWaP#S;jEQsDAXPHvzkWQ*}*XH zVyj?#t&8WxTqt|PUU9}|iB zvy9Vx8r!Mk01}BLZbpvnPqB+fhxQeA@Dz`K1_GgS;1CEWu{bMu06CDVw)G{lSftW{ z^v79^Ip#{-FGWhk(WJYDq=cMhNQpU%NDLeRC~(_LV&Z7hjYVSQXwtPoV&-Vl)h{Um zXAMV~plN?N&C%cmhba3v+xSdM=^izp)I4YF>bu>hJ&n002ovPDHLkV1jgi#R>ob literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_whatshot_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_whatshot_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a14dcd695f0de6b126efd3745d359c7b78fcb771 GIT binary patch literal 874 zcmV-w1C{)VP)vfAJuvc8F$0Vcf zJu#$RM%@NsXseB~E@5bA+-OuS7n$}OuW~oZ>wY2I6DoGMx8!xNknNNX6`J91AIWQ( zNVHXjz7Y1wYpqCh&Kwn4DTK4Gmd_56>|>Saa7tY0L3y+|DUzKxTLs<`7uze3rDE8& zYS?^(;!=x^WP(Ft*v^=&K|95ej+v>xXGPtf(vW#-!q5)uFuePm6LmYRA=`v(zqw7? zN&{lV<|}QIevxR5{W)k%BP~ie$b{@$kwBjW1^wH(~dnN(G-QDC% z=y8R;C`xt4M%Qymn@F@tVIPRX{q6;AvQ7xub}H;EQJ^C>xzji)-wN5jQP|HSM}OJk z0qyG9Rn3QgLX6O#9@S!GZzrF9VP{3%cAG4p*M)3-3L6x4+GT>g)+gtFVgHF?`_(n_ zdr8PPps`fuvtBSbJc_G_o zg)JA-J+FurBGEF1O%u|+qKL1BY&GplI3SYkl;1*;XrEG6i)1IY%I7LaM4~RGOx54u ze`u>n)Mtv4-Vn)pOf<4*M6!)ao1sr6+pfNG){A7PP1AsNBAqS9OJk;Pk~}X zv)9eCX!EkuBAtU;G-z&};|J~Wu(>8`)#i3xcGq1Ay=JSx5`&_G&Re7+t3(BLs?aKf z4c< zku?3j-uqr3F9jHeVHhTT`fxK{?`FI{W3C~P3tZSzO#Z#{Ds2X4hkXuOc(xc3aasc zS5S`+02hf0Ch!5E6RWU^f2MtZ+QIvlf+_r{Lh1M?@bCG^884tm5Oqkbu9M z$f1Bl%A_j^d^PJQYZRaW1t_pD0*-Y7sYW8-77{q9+nxi00QE8}!MzZdQ3_Ci0u-PC z1t>s)RNKg0wu_C*@WplogmZubY5)1_ei{o|fg(yG)3Q)jW!oASP)(20bn%j?7;MLTFe~Ai= zO$ppZe`+Bx^x5TcoRw!rU=UxHCMG8E$2d&ttR_6=jTB&zjK7)-w|K-{9r_#(xQXw5 ehG7`y|KA*ZU8cIrkjqdl)YN zD@FZ?xwlCXYp>AEG-7NfBF#O-RyjgsSV(LgMu<)xA*ME?t`-wZOHo%H%p-{zcoR}Dia@Nj73Tn9ND9dUe2?}NqJBg-0@~nmeNwS9n)^|XhrksL!9&wClu0cM% zN&!@nL3B_`6t^Ou&NCdx`tmJ8h$%$zDDv$s99xOHdX6ZzBHuEM!l5ywQCE%Ruap)P z&^jF2g)V9a`I?UcJ4Z#K?lE;F$VY+$C`8L~q``3XEJC3=K`$J*8)enX^@KH&zfj0# z;lS4@M@^&%qm2D1bRS{eH004qst8iSS16A%RAAXQlI1{mNJXUPqoaxVnRy_XR@$ zfH}Ssz8A*&kVaO^u!0ikG6MOO!q|9ZwK^tpsb=Kaofx|hS*wvbWcv(xwzQ`Vu}(hW zE)wLj4tcg3V;hlShj@_`-X2GuZNb>b20xVVvV>6tsYafCim`7{p0)D<3mHaOQ+#*N z*fEqx7g)nki+}evVeAYFT|HyTUpexu8Dr;B$hOg!D0(B$S~1pvLbaO`2cMBC)B;X0 zfauaL7jO=R=zgM0x;;WCQD8?&5?vYc>^R2uqrg^TVgmB49%DODVAGvl7q?>UB@|dW zCe|U(YB4q&1(e3j5#(7l#)hGQS}-vQ`BaGoKOo;SB#3T3@~jan)}Vk!5Yg zaWe{NHc@RtK264o1V19*HWSTl$fv_t^Ca@Eje+E^B4Eld!*WZnNtHr7ToJPL=%wWQ*qz?J^2W43HFbe1(R}seToI(NBU|o`ZD4-^$ zlWxJ!N--iN8vf9DfPf}3xX+@Sg%Urs_Xud=i z+DnRVlH?(pkwJe_LxOHKfR(hM2%VvpGJ1qEp5O?|qr)ttKRrr`a7cP@RBX-4|iU0rr07*qoM6N<$f+o2K A_y7O^ literal 0 HcmV?d00001 From 7f5b5d6f03ecf4c2c192fce761fd4a21330e79b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Freddy=20Mor=C3=A1n=20Jr?= Date: Thu, 26 Oct 2017 23:14:25 +0000 Subject: [PATCH 043/154] Translated using Weblate (Spanish) Currently translated at 99.5% (219 of 220 strings) --- app/src/main/res/values-es/strings.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7d09d85b9..2be3a3763 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -260,4 +260,18 @@ abrir en modo popup Elemento eliminado ¿Desea eliminar este elemento del historial de búsqueda? +Contenido de la página principal + Página en blanco + Página del kiosco + Página de suscripción + Página de feed + Página del canal + Seleccione un canal + No hay canal suscrito todavía + Seleccione un kiosco + + Kiosco + Tendencias + Top 50 + %1$s/%2$s From bd7a520316dee7f86d2b317ca6cfdd388c754ac7 Mon Sep 17 00:00:00 2001 From: Emanuele Petriglia Date: Sat, 28 Oct 2017 19:35:51 +0000 Subject: [PATCH 044/154] Translated using Weblate (Italian) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-it/strings.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 0081c9844..a4cbc8ef8 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -265,4 +265,19 @@ Nulla da mostrare Vuoi eliminare questo elemento dalla cronologia? +Contenuto della pagina principale + Pagina vuota + Edicola + Pagina iscrizione + Pagina del feed + Pagina del canale + Seleziona un canale + Non ancora iscritto a nessun canale + Seleziona un\'edicola + + Edicola + In tendenza + Primi 50 + Nuovi e caldi + %1$s/%2$s From 76f3e170d58001e7a11f849a1e65116bccc904ba Mon Sep 17 00:00:00 2001 From: Joona Mattila Date: Sun, 29 Oct 2017 18:04:06 +0000 Subject: [PATCH 045/154] Translated using Weblate (Finnish) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-fi/strings.xml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 5b01fcdd4..790cd1676 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -236,4 +236,21 @@ Haluatko poistaa tämän hakuhistoriasta? Jatka toistoa Info: - + Mikä:\\nPyyntö:\\nSisällön kieli:\\nPalvelu:\\nGMT Aika:\\nPaketti:\\nVersio:\\nOS versio:\\nGlob. IP väli: + © %1$s %2$s %3$s alla + Pääsivun sisältö + Tyhjä sivu + Kioski sivu + Tilaukset sivu + Syötteet sivu + Kanavat sivu + Valitse kanava + Ei yhtään tilattua kanavaa vielä + Valitse kioski + + Kioski + Nousussa + Top 50 + Uudet & kuumat + %1$s/%2$s + From e95854523047a191eadb172ed61eabb7d24f218c Mon Sep 17 00:00:00 2001 From: Emanuele Petriglia Date: Sat, 28 Oct 2017 19:41:41 +0000 Subject: [PATCH 046/154] Translated using Weblate (Italian) Currently translated at 100.0% (220 of 220 strings) --- app/src/main/res/values-it/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a4cbc8ef8..0bed4dee6 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -149,7 +149,7 @@ Apri in modalità popup - NewPipe in modalità popup + NewPipe modalità a comparsa Riproduzione in modalità popup Disattivato @@ -240,7 +240,7 @@ Annulla Notifiche NewPipe - Notifiche per NewPipe in background e Popup Player + Notifiche per NewPipe in background e per il lettore a comparsa Nessun risultato Nessun iscritto @@ -257,8 +257,8 @@ Nessun video - %s video - %s video + %s filmato + %s filmati Elemento eliminato From 59b3362715f1f08dc3e6c3c36e780b1465b3ef4a Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Sun, 29 Oct 2017 21:34:52 +0100 Subject: [PATCH 047/154] put bountysource bounty lable on top of readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a127b10a5..add5aed9a 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ +


ScreenshotsDescriptionFeaturesContributionDonateLicense

From 7e005549fecd3da18bb8a4f2be6104933865bbd8 Mon Sep 17 00:00:00 2001 From: SpajicM Date: Mon, 30 Oct 2017 11:53:44 +0100 Subject: [PATCH 048/154] Fix history showing even when disabled --- .../main/java/org/schabi/newpipe/history/HistoryFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java b/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java index 03657d264..b8641c92b 100644 --- a/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java +++ b/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java @@ -241,6 +241,7 @@ public abstract class HistoryFragment extends BaseFragme if (mHistoryIsEnabled) { mRecyclerView.setVisibility(View.VISIBLE); } else { + mRecyclerView.setVisibility(View.GONE); mDisabledView.setVisibility(View.VISIBLE); } From 640396da6420e3a7cc15b04cebdc09170d4103f9 Mon Sep 17 00:00:00 2001 From: Joona Mattila Date: Sun, 29 Oct 2017 18:26:04 +0000 Subject: [PATCH 049/154] Translated using Weblate (Finnish) Currently translated at 100.0% (219 of 219 strings) --- app/src/main/res/values-fi/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 790cd1676..9326c9a87 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -89,7 +89,7 @@ Sisältö Näytä ikärajoitettua sisältöä Ikärajoitettu video. Ikärajoituksen voi sallia asetuksista. - live + suora Lataukset Lataukset Virheraportti From 65a6488e445e1559c85bb2d26ab962dafe77b635 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 30 Oct 2017 22:04:58 +0100 Subject: [PATCH 050/154] dont show search history in suggestion when disabled --- .../fragments/list/search/SearchFragment.java | 6 ++++++ .../list/search/SuggestionListAdapter.java | 16 +++++++++++++++- app/src/main/res/layout/activity_history.xml | 1 - 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java index 90d4d9741..fae97bb7b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java @@ -113,6 +113,7 @@ public class SearchFragment extends BaseListFragment suggestionPublisher = PublishSubject.create(); private Disposable searchDisposable; @@ -160,7 +161,12 @@ public class SearchFragment extends BaseListFragment items = new ArrayList<>(); private final Context context; private OnSuggestionItemSelected listener; + private boolean showSugestinHistory = true; public interface OnSuggestionItemSelected { void onSuggestionItemSelected(SuggestionItem item); @@ -31,7 +32,16 @@ public class SuggestionListAdapter extends RecyclerView.Adapter items) { this.items.clear(); - this.items.addAll(items); + if (showSugestinHistory) { + this.items.addAll(items); + } else { + // remove history items if history is disabled + for (SuggestionItem item : items) { + if (!item.fromHistory) { + this.items.add(item); + } + } + } notifyDataSetChanged(); } @@ -39,6 +49,10 @@ public class SuggestionListAdapter extends RecyclerView.Adapter From 02d986fc8952b3307a02e57949e7884a5b8f106d Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 30 Oct 2017 22:46:55 +0100 Subject: [PATCH 051/154] fix multidefined swipe problem in history page --- .../org/schabi/newpipe/history/HistoryFragment.java | 12 ++++++------ .../newpipe/history/SearchHistoryFragment.java | 11 +++++++++++ .../newpipe/history/WatchedHistoryFragment.java | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java b/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java index b8641c92b..c64689775 100644 --- a/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java +++ b/app/src/main/java/org/schabi/newpipe/history/HistoryFragment.java @@ -55,7 +55,7 @@ public abstract class HistoryFragment extends BaseFragme private RecyclerView mRecyclerView; private HistoryEntryAdapter mHistoryAdapter; private ItemTouchHelper.SimpleCallback mHistoryItemSwipeCallback; - private int allowedSwipeToDeleteDirections = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; + // private int allowedSwipeToDeleteDirections = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; private HistoryDAO mHistoryDataSource; private PublishSubject> mHistoryEntryDeleteSubject; @@ -99,7 +99,11 @@ public abstract class HistoryFragment extends BaseFragme } }); - mHistoryItemSwipeCallback = new ItemTouchHelper.SimpleCallback(0, allowedSwipeToDeleteDirections) { + + } + + protected void historyItemSwipeCallback(int swipeDirection) { + mHistoryItemSwipeCallback = new ItemTouchHelper.SimpleCallback(0, swipeDirection) { @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return false; @@ -265,10 +269,6 @@ public abstract class HistoryFragment extends BaseFragme mRecyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState(); } - public void setAllowedSwipeToDeleteDirections(int allowedSwipeToDeleteDirections) { - this.allowedSwipeToDeleteDirections = allowedSwipeToDeleteDirections; - } - /** * Called when history enabled flag is changed. * diff --git a/app/src/main/java/org/schabi/newpipe/history/SearchHistoryFragment.java b/app/src/main/java/org/schabi/newpipe/history/SearchHistoryFragment.java index 888086a83..91e2cecff 100644 --- a/app/src/main/java/org/schabi/newpipe/history/SearchHistoryFragment.java +++ b/app/src/main/java/org/schabi/newpipe/history/SearchHistoryFragment.java @@ -1,9 +1,12 @@ package org.schabi.newpipe.history; import android.content.Context; +import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -17,11 +20,19 @@ import org.schabi.newpipe.util.NavigationHelper; public class SearchHistoryFragment extends HistoryFragment { + private static int allowedSwipeToDeleteDirections = ItemTouchHelper.RIGHT; + @NonNull public static SearchHistoryFragment newInstance() { return new SearchHistoryFragment(); } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + historyItemSwipeCallback(allowedSwipeToDeleteDirections); + } + @NonNull @Override protected SearchHistoryAdapter createAdapter() { diff --git a/app/src/main/java/org/schabi/newpipe/history/WatchedHistoryFragment.java b/app/src/main/java/org/schabi/newpipe/history/WatchedHistoryFragment.java index 086528af7..d898bf353 100644 --- a/app/src/main/java/org/schabi/newpipe/history/WatchedHistoryFragment.java +++ b/app/src/main/java/org/schabi/newpipe/history/WatchedHistoryFragment.java @@ -7,6 +7,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.StringRes; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -26,6 +27,8 @@ import org.schabi.newpipe.util.NavigationHelper; public class WatchedHistoryFragment extends HistoryFragment { + private static int allowedSwipeToDeleteDirections = ItemTouchHelper.LEFT; + @NonNull public static WatchedHistoryFragment newInstance() { return new WatchedHistoryFragment(); @@ -34,7 +37,7 @@ public class WatchedHistoryFragment extends HistoryFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - + historyItemSwipeCallback(allowedSwipeToDeleteDirections); } @StringRes From 391d3e7fc7b13846a49176955dcddc47a788c07c Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Mon, 30 Oct 2017 23:03:18 +0100 Subject: [PATCH 052/154] fix back button for feed on main page --- .../org/schabi/newpipe/fragments/list/BaseListFragment.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 48661969f..35f6a08d3 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -187,7 +187,11 @@ public abstract class BaseListFragment extends BaseStateFragment implem ActionBar supportActionBar = activity.getSupportActionBar(); if (supportActionBar != null) { supportActionBar.setDisplayShowTitleEnabled(true); - supportActionBar.setDisplayHomeAsUpEnabled(true); + if(useAsFrontPage) { + supportActionBar.setDisplayHomeAsUpEnabled(false); + } else { + supportActionBar.setDisplayHomeAsUpEnabled(true); + } } } From e70dcdc6426e8219184d5babc250ef4535db9f2e Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Fri, 25 Aug 2017 15:10:54 -0700 Subject: [PATCH 053/154] -Added rudimentary playlist fragment. -Added schema for stream storage. --- .../schabi/newpipe/database/Converters.java | 17 + .../database/playlist/PlaylistEntity.java | 70 +++ .../newpipe/database/stream/StreamDAO.java | 24 + .../newpipe/database/stream/StreamEntity.java | 207 ++++++++ .../fragments/playlist/PlaylistFragment.java | 445 ++++++++++++++++ .../fragments/search/PlaylistService.java | 4 + .../fragments/subscription/FeedFragment.java | 496 ++++++++++++++++++ .../subscription/SubscriptionEngine.java | 170 ++++++ .../subscription/SubscriptionFragment.java | 244 +++++---- .../info_list/PlaylistInfoItemHolder.java | 49 ++ app/src/main/res/layout/playlist_item.xml | 51 ++ 11 files changed, 1673 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/database/Converters.java create mode 100644 app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistEntity.java create mode 100644 app/src/main/java/org/schabi/newpipe/database/stream/StreamDAO.java create mode 100644 app/src/main/java/org/schabi/newpipe/database/stream/StreamEntity.java create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/subscription/FeedFragment.java create mode 100644 app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionEngine.java create mode 100644 app/src/main/java/org/schabi/newpipe/info_list/PlaylistInfoItemHolder.java create mode 100644 app/src/main/res/layout/playlist_item.xml diff --git a/app/src/main/java/org/schabi/newpipe/database/Converters.java b/app/src/main/java/org/schabi/newpipe/database/Converters.java new file mode 100644 index 000000000..3203e2b3c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/database/Converters.java @@ -0,0 +1,17 @@ +package org.schabi.newpipe.database; + +import android.arch.persistence.room.TypeConverter; + +import java.util.Date; + +public class Converters { + @TypeConverter + public static Date fromTimestamp(Long value) { + return value == null ? null : new Date(value); + } + + @TypeConverter + public static Long dateToTimestamp(Date date) { + return date == null ? null : date.getTime(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistEntity.java new file mode 100644 index 000000000..76cef7ef5 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistEntity.java @@ -0,0 +1,70 @@ +package org.schabi.newpipe.database.playlist; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.Ignore; +import android.arch.persistence.room.PrimaryKey; + +import static org.schabi.newpipe.database.playlist.PlaylistEntity.PLAYLIST_TABLE; + +@Entity(tableName = PLAYLIST_TABLE) +public class PlaylistEntity { + + final static String PLAYLIST_TABLE = "playlists"; + final static String PLAYLIST_URL = "url"; + final static String PLAYLIST_TITLE = "title"; + final static String PLAYLIST_THUMBNAIL_URL = "thumbnail_url"; + + @PrimaryKey(autoGenerate = true) + private long uid = 0; + + @ColumnInfo(name = PLAYLIST_TITLE) + private String title; + + /* This is used as a reference to the source, should this playlist be dynamic */ + @ColumnInfo(name = PLAYLIST_URL) + private String url; + + @ColumnInfo(name = PLAYLIST_THUMBNAIL_URL) + private String thumbnailUrl; + + public long getUid() { + return uid; + } + + /* Keep this package-private since UID should always be auto generated by Room impl */ + void setUid(long uid) { + this.uid = uid; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public void setThumbnailUrl(String thumbnailUrl) { + this.thumbnailUrl = thumbnailUrl; + } + + @Ignore + public void setData(final String title, + final String thumbnailUrl) { + this.setTitle(title); + this.setThumbnailUrl(thumbnailUrl); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/StreamDAO.java new file mode 100644 index 000000000..31e156cc8 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamDAO.java @@ -0,0 +1,24 @@ +package org.schabi.newpipe.database.stream; + +import android.arch.persistence.room.Dao; +import android.arch.persistence.room.Query; + +import org.schabi.newpipe.database.BasicDAO; + +import java.util.List; + +import io.reactivex.Flowable; + +import static org.schabi.newpipe.database.stream.StreamEntity.STREAM_SERVICE_ID; +import static org.schabi.newpipe.database.stream.StreamEntity.STREAM_TABLE; + +@Dao +public interface StreamDAO extends BasicDAO { + @Override + @Query("SELECT * FROM " + STREAM_TABLE) + Flowable> findAll(); + + @Override + @Query("SELECT * FROM " + STREAM_TABLE + " WHERE " + STREAM_SERVICE_ID + " = :serviceId") + Flowable> listByService(int serviceId); +} diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/StreamEntity.java b/app/src/main/java/org/schabi/newpipe/database/stream/StreamEntity.java new file mode 100644 index 000000000..20eddb38a --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/database/stream/StreamEntity.java @@ -0,0 +1,207 @@ +package org.schabi.newpipe.database.stream; + +import android.arch.persistence.room.ColumnInfo; +import android.arch.persistence.room.Entity; +import android.arch.persistence.room.Ignore; +import android.arch.persistence.room.Index; +import android.arch.persistence.room.PrimaryKey; + +import org.schabi.newpipe.extractor.AbstractStreamInfo; +import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; + +import java.util.Date; + +import static org.schabi.newpipe.database.stream.StreamEntity.STREAM_SERVICE_ID; +import static org.schabi.newpipe.database.stream.StreamEntity.STREAM_TABLE; +import static org.schabi.newpipe.database.stream.StreamEntity.STREAM_URL; + +@Entity(tableName = STREAM_TABLE, + indices = {@Index(value = {STREAM_SERVICE_ID, STREAM_URL}, unique = true)}) +public class StreamEntity { + public final static String STREAM_UID = "uid"; + + final static String STREAM_TABLE = "streams"; + final static String STREAM_ID = "id"; + final static String STREAM_TYPE = "type"; + final static String STREAM_SERVICE_ID = "service_id"; + final static String STREAM_URL = "url"; + final static String STREAM_TITLE = "title"; + final static String STREAM_THUMBNAIL_URL = "thumbnail_url"; + final static String STREAM_VIEW_COUNT = "view_count"; + final static String STREAM_UPLOADER = "uploader"; + final static String STREAM_UPLOAD_DATE = "upload_date"; + final static String STREAM_DURATION = "duration"; + + @PrimaryKey(autoGenerate = true) + private long uid = 0; + + @ColumnInfo(name = STREAM_SERVICE_ID) + private int serviceId = -1; + + @ColumnInfo(name = STREAM_ID) + private String id; + + @ColumnInfo(name = STREAM_TYPE) + private String type; + + @ColumnInfo(name = STREAM_URL) + private String url; + + @ColumnInfo(name = STREAM_TITLE) + private String title; + + @ColumnInfo(name = STREAM_THUMBNAIL_URL) + private String thumbnailUrl; + + @ColumnInfo(name = STREAM_VIEW_COUNT) + private Long viewCount; + + @ColumnInfo(name = STREAM_UPLOADER) + private String uploader; + + @ColumnInfo(name = STREAM_UPLOAD_DATE) + private long uploadDate; + + @ColumnInfo(name = STREAM_DURATION) + private int duration; + + @Ignore + public StreamInfoItem toStreamInfoItem() { + StreamInfoItem item = new StreamInfoItem(); + + item.stream_type = AbstractStreamInfo.StreamType.valueOf( this.getType() ); + + item.service_id = this.getServiceId(); + item.id = this.getId(); + item.webpage_url = this.getUrl(); + item.title = this.getTitle(); + item.thumbnail_url = this.getThumbnailUrl(); + item.view_count = this.getViewCount(); + item.uploader = this.getUploader(); + + // TODO: temporary until upload date parsing is fleshed out + item.upload_date = "Unknown"; + item.duration = this.getDuration(); + + return item; + } + + @Ignore + public StreamEntity(final StreamInfoItem item) { + setData(item); + } + + @Ignore + public void setData(final StreamInfoItem item) { + // Do not store ordinals into db since they may change in the future + this.type = item.stream_type.name(); + + this.serviceId = item.service_id; + this.id = item.id; + this.url = item.webpage_url; + this.title = item.title; + this.thumbnailUrl = item.thumbnail_url; + this.viewCount = item.view_count; + this.uploader = item.uploader; + + // TODO: temporary until upload date parsing is fleshed out + this.uploadDate = new Date().getTime(); + this.duration = item.duration; + } + + @Ignore + public boolean is(final StreamInfoItem item) { + return this.type.equals( item.stream_type.name() ) && + this.serviceId == item.service_id && + this.id.equals( item.id ) && + this.url.equals( item.webpage_url ); + } + + public long getUid() { + return uid; + } + + void setUid(long uid) { + this.uid = uid; + } + + public int getServiceId() { + return serviceId; + } + + public void setServiceId(int serviceId) { + this.serviceId = serviceId; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getThumbnailUrl() { + return thumbnailUrl; + } + + public void setThumbnailUrl(String thumbnailUrl) { + this.thumbnailUrl = thumbnailUrl; + } + + public Long getViewCount() { + return viewCount; + } + + public void setViewCount(Long viewCount) { + this.viewCount = viewCount; + } + + public String getUploader() { + return uploader; + } + + public void setUploader(String uploader) { + this.uploader = uploader; + } + + public long getUploadDate() { + return uploadDate; + } + + public void setUploadDate(long uploadDate) { + this.uploadDate = uploadDate; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java new file mode 100644 index 000000000..0493fd525 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java @@ -0,0 +1,445 @@ +package org.schabi.newpipe.fragments.playlist; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.schabi.newpipe.ImageErrorLoadingListener; +import org.schabi.newpipe.MainActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.playlist.PlayListExtractor; +import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.fragments.BaseFragment; +import org.schabi.newpipe.fragments.search.OnScrollBelowItemsListener; +import org.schabi.newpipe.info_list.InfoItemBuilder; +import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.report.UserAction; +import org.schabi.newpipe.util.Constants; +import org.schabi.newpipe.util.NavigationHelper; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.concurrent.Callable; + +import io.reactivex.Observable; +import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.annotations.NonNull; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import static org.schabi.newpipe.util.AnimationUtils.animateView; + +public class PlaylistFragment extends BaseFragment { + private final String TAG = "PlaylistFragment@" + Integer.toHexString(hashCode()); + + private static final String INFO_LIST_KEY = "info_list_key"; + private static final String PLAYLIST_INFO_KEY = "playlist_info_key"; + private static final String PAGE_NUMBER_KEY = "page_number_key"; + + private InfoListAdapter infoListAdapter; + + private PlayListInfo currentPlaylistInfo; + private int serviceId = -1; + private String playlistTitle = ""; + private String playlistUrl = ""; + private int pageNumber = 0; + private boolean hasNextPage = true; + + /*////////////////////////////////////////////////////////////////////////// + // Views + //////////////////////////////////////////////////////////////////////////*/ + + private RecyclerView playlistStreams; + + private View headerRootLayout; + private ImageView headerBannerView; + private ImageView headerAvatarView; + private TextView headerTitleView; + + /*////////////////////////////////////////////////////////////////////////*/ + + public PlaylistFragment() { + } + + public static Fragment getInstance(int serviceId, String playlistUrl, String title) { + PlaylistFragment instance = new PlaylistFragment(); + instance.setPlaylist(serviceId, playlistUrl, title); + return instance; + } + + /*////////////////////////////////////////////////////////////////////////// + // Fragment's LifeCycle + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void onCreate(Bundle savedInstanceState) { + if (DEBUG) Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]"); + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + if (savedInstanceState != null) { + playlistUrl = savedInstanceState.getString(Constants.KEY_URL); + playlistTitle = savedInstanceState.getString(Constants.KEY_TITLE); + serviceId = savedInstanceState.getInt(Constants.KEY_SERVICE_ID, -1); + + pageNumber = savedInstanceState.getInt(PAGE_NUMBER_KEY, 0); + Serializable serializable = savedInstanceState.getSerializable(PLAYLIST_INFO_KEY); + if (serializable instanceof PlayListInfo) currentPlaylistInfo = (PlayListInfo) serializable; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + if (DEBUG) Log.d(TAG, "onCreateView() called with: inflater = [" + inflater + "], container = [" + container + "], savedInstanceState = [" + savedInstanceState + "]"); + return inflater.inflate(R.layout.fragment_channel, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + if (currentPlaylistInfo == null) loadPage(0); + else handlePlayListInfo(currentPlaylistInfo, false, false); + } + + @Override + public void onDestroyView() { + if (DEBUG) Log.d(TAG, "onDestroyView() called"); + headerAvatarView.setImageBitmap(null); + headerBannerView.setImageBitmap(null); + playlistStreams.removeAllViews(); + + playlistStreams = null; + headerRootLayout = null; + headerBannerView = null; + headerAvatarView = null; + headerTitleView = null; + + super.onDestroyView(); + } + + @Override + public void onResume() { + if (DEBUG) Log.d(TAG, "onResume() called"); + super.onResume(); + if (wasLoading.getAndSet(false)) { + loadPage(pageNumber); + } + } + + @Override + public void onStop() { + if (DEBUG) Log.d(TAG, "onStop() called"); + + disposable.dispose(); + disposable = null; + + super.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + if (DEBUG) Log.d(TAG, "onSaveInstanceState() called with: outState = [" + outState + "]"); + super.onSaveInstanceState(outState); + outState.putString(Constants.KEY_URL, playlistUrl); + outState.putString(Constants.KEY_TITLE, playlistTitle); + outState.putInt(Constants.KEY_SERVICE_ID, serviceId); + + outState.putSerializable(INFO_LIST_KEY, infoListAdapter.getItemsList()); + outState.putSerializable(PLAYLIST_INFO_KEY, currentPlaylistInfo); + outState.putInt(PAGE_NUMBER_KEY, pageNumber); + } + + /*////////////////////////////////////////////////////////////////////////// + // Menu + //////////////////////////////////////////////////////////////////////////*/ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_channel, menu); + + ActionBar supportActionBar = activity.getSupportActionBar(); + if (supportActionBar != null) { + supportActionBar.setDisplayShowTitleEnabled(true); + supportActionBar.setDisplayHomeAsUpEnabled(true); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (DEBUG) Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]"); + super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case R.id.menu_item_openInBrowser: { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse(playlistUrl)); + startActivity(Intent.createChooser(intent, getString(R.string.choose_browser))); + return true; + } + case R.id.menu_item_share: { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, playlistUrl); + intent.setType("text/plain"); + startActivity(Intent.createChooser(intent, getString(R.string.share_dialog_title))); + return true; + } + default: + return super.onOptionsItemSelected(item); + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Init's + //////////////////////////////////////////////////////////////////////////*/ + + @Override + protected void initViews(View rootView, Bundle savedInstanceState) { + super.initViews(rootView, savedInstanceState); + + playlistStreams = (RecyclerView) rootView.findViewById(R.id.channel_streams_view); + + playlistStreams.setLayoutManager(new LinearLayoutManager(activity)); + if (infoListAdapter == null) { + infoListAdapter = new InfoListAdapter(activity); + if (savedInstanceState != null) { + //noinspection unchecked + ArrayList serializable = (ArrayList) savedInstanceState.getSerializable(INFO_LIST_KEY); + infoListAdapter.addInfoItemList(serializable); + } + } + + playlistStreams.setAdapter(infoListAdapter); + headerRootLayout = activity.getLayoutInflater().inflate(R.layout.playlist_header, playlistStreams, false); + infoListAdapter.setHeader(headerRootLayout); + infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, playlistStreams, false)); + + headerBannerView = (ImageView) headerRootLayout.findViewById(R.id.playlist_banner_image); + headerAvatarView = (ImageView) headerRootLayout.findViewById(R.id.playlist_avatar_view); + headerTitleView = (TextView) headerRootLayout.findViewById(R.id.playlist_title_view); + } + + protected void initListeners() { + super.initListeners(); + + infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + @Override + public void selected(int serviceId, String url, String title) { + if (DEBUG) Log.d(TAG, "selected() called with: serviceId = [" + serviceId + "], url = [" + url + "], title = [" + title + "]"); + NavigationHelper.openVideoDetailFragment(getFragmentManager(), serviceId, url, title); + } + }); + + playlistStreams.clearOnScrollListeners(); + playlistStreams.addOnScrollListener(new OnScrollBelowItemsListener() { + @Override + public void onScrolledDown(RecyclerView recyclerView) { + loadMore(true); + } + }); + } + + + @Override + protected void reloadContent() { + if (DEBUG) Log.d(TAG, "reloadContent() called"); + currentPlaylistInfo = null; + infoListAdapter.clearStreamItemList(); + loadPage(0); + } + + /*////////////////////////////////////////////////////////////////////////// + // Playlist Loader + //////////////////////////////////////////////////////////////////////////*/ + + private StreamingService getService(final int serviceId) throws ExtractionException { + return NewPipe.getService(serviceId); + } + + Disposable disposable; + + private void loadMore(final boolean onlyVideos) { + final Callable task = new Callable() { + @Override + public PlayListInfo call() throws Exception { + final PlayListExtractor extractor = getService(serviceId) + .getPlayListExtractorInstance(playlistUrl, pageNumber); + + return PlayListInfo.getInfo(extractor); + } + }; + + + Observable.fromCallable(task) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + if (disposable == null || disposable.isDisposed()) { + disposable = d; + isLoading.set(true); + } else { + d.dispose(); + } + } + + @Override + public void onNext(@NonNull PlayListInfo playListInfo) { + if (DEBUG) Log.d(TAG, "onReceive() called with: info = [" + playListInfo + "]"); + if (playListInfo == null || isRemoving() || !isVisible()) return; + + handlePlayListInfo(playListInfo, onlyVideos, true); + isLoading.set(false); + pageNumber++; + } + + @Override + public void onError(@NonNull Throwable e) { + onRxError(e, "Observer failure"); + } + + @Override + public void onComplete() { + if (disposable != null) { + disposable.dispose(); + disposable = null; + } + } + }); + } + + + /*////////////////////////////////////////////////////////////////////////// + // Utils + //////////////////////////////////////////////////////////////////////////*/ + + private void loadPage(int page) { + if (DEBUG) Log.d(TAG, "loadPage() called with: page = [" + page + "]"); + isLoading.set(true); + pageNumber = page; + infoListAdapter.showFooter(false); + + animateView(loadingProgressBar, true, 200); + animateView(errorPanel, false, 200); + + imageLoader.cancelDisplayTask(headerBannerView); + imageLoader.cancelDisplayTask(headerAvatarView); + + headerTitleView.setText(playlistTitle != null ? playlistTitle : ""); + headerBannerView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.channel_banner)); + headerAvatarView.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy)); + if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(playlistTitle != null ? playlistTitle : ""); + + loadMore(true); + } + + private void setPlaylist(int serviceId, String playlistUrl, String title) { + this.serviceId = serviceId; + this.playlistUrl = playlistUrl; + this.playlistTitle = title; + } + + private void handlePlayListInfo(PlayListInfo info, boolean onlyVideos, boolean addVideos) { + currentPlaylistInfo = info; + + animateView(errorPanel, false, 300); + animateView(playlistStreams, true, 200); + animateView(loadingProgressBar, false, 200); + + if (!onlyVideos) { + if (activity.getSupportActionBar() != null) activity.getSupportActionBar().invalidateOptionsMenu(); + + headerRootLayout.setVisibility(View.VISIBLE); + //animateView(loadingProgressBar, false, 200, null); + + if (!TextUtils.isEmpty(info.playList_name)) { + if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(info.playList_name); + headerTitleView.setText(info.playList_name); + playlistTitle = info.playList_name; + } else playlistTitle = ""; + + if (!TextUtils.isEmpty(info.banner_url)) { + imageLoader.displayImage(info.banner_url, headerBannerView, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id)); + } + + if (!TextUtils.isEmpty(info.avatar_url)) { + headerAvatarView.setVisibility(View.VISIBLE); + imageLoader.displayImage(info.avatar_url, headerAvatarView, displayImageOptions, new ImageErrorLoadingListener(activity, getView(), info.service_id)); + } + + infoListAdapter.showFooter(true); + } + + hasNextPage = info.hasNextPage; + if (!hasNextPage) infoListAdapter.showFooter(false); + + //if (!listRestored) { + if (addVideos) infoListAdapter.addInfoItemList(info.related_streams); + //} + } + + @Override + protected void setErrorMessage(String message, boolean showRetryButton) { + super.setErrorMessage(message, showRetryButton); + + animateView(playlistStreams, false, 200); + currentPlaylistInfo = null; + } + + /*////////////////////////////////////////////////////////////////////////// + // Error Handlers + //////////////////////////////////////////////////////////////////////////*/ + + private void onRxError(final Throwable exception, final String tag) { + if (exception instanceof IOException) { + onRecoverableError(R.string.network_error); + } else { + onUnrecoverableError(exception, tag); + } + } + + private void onRecoverableError(int messageId) { + if (!this.isAdded()) return; + + if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]"); + setErrorMessage(getString(messageId), true); + } + + private void onUnrecoverableError(Throwable exception, final String tag) { + if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]"); + ErrorActivity.reportError( + getContext(), + exception, + MainActivity.class, + null, + ErrorActivity.ErrorInfo.make(UserAction.REQUESTED_PLAYLIST, "Feed", tag, R.string.general_error) + ); + + activity.finish(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java b/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java new file mode 100644 index 000000000..b43c7e356 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java @@ -0,0 +1,4 @@ +package org.schabi.newpipe.fragments.search; + +public class PlaylistService { +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/FeedFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/FeedFragment.java new file mode 100644 index 000000000..df92449d2 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/FeedFragment.java @@ -0,0 +1,496 @@ +package org.schabi.newpipe.fragments.subscription; + +import android.os.Bundle; +import android.os.Parcelable; +import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.jakewharton.rxbinding2.view.RxView; + +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.schabi.newpipe.MainActivity; +import org.schabi.newpipe.R; +import org.schabi.newpipe.database.subscription.SubscriptionEntity; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.channel.ChannelInfo; +import org.schabi.newpipe.fragments.BaseFragment; +import org.schabi.newpipe.info_list.InfoItemBuilder; +import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.report.ErrorActivity; +import org.schabi.newpipe.util.NavigationHelper; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import io.reactivex.Flowable; +import io.reactivex.MaybeObserver; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.annotations.NonNull; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + +import static org.schabi.newpipe.report.UserAction.REQUESTED_CHANNEL; +import static org.schabi.newpipe.util.AnimationUtils.animateView; + +public class FeedFragment extends BaseFragment { + private static final String VIEW_STATE_KEY = "view_state_key"; + private static final String INFO_ITEMS_KEY = "info_items_key"; + + private static final int FEED_LOAD_SIZE = 4; + private static final int LOAD_ITEM_DEBOUNCE_INTERVAL = 500; + + private final String TAG = "FeedFragment@" + Integer.toHexString(hashCode()); + + private View inflatedView; + private View emptyPanel; + private View loadItemFooter; + + private InfoListAdapter infoListAdapter; + private RecyclerView resultRecyclerView; + + private Parcelable viewState; + private AtomicBoolean retainFeedItems; + + private SubscriptionEngine subscriptionEngine; + + private Disposable loadItemObserver; + private Disposable subscriptionObserver; + private Subscription feedSubscriber; + + /////////////////////////////////////////////////////////////////////////// + // Fragment LifeCycle + /////////////////////////////////////////////////////////////////////////// + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + subscriptionEngine = SubscriptionEngine.getInstance(getContext()); + + retainFeedItems = new AtomicBoolean(false); + + if (infoListAdapter == null) { + infoListAdapter = new InfoListAdapter(getActivity()); + } + + if (savedInstanceState != null) { + // Get recycler view state + viewState = savedInstanceState.getParcelable(VIEW_STATE_KEY); + + // Deserialize and get recycler adapter list + final Object[] serializedInfoItems = (Object[]) savedInstanceState.getSerializable(INFO_ITEMS_KEY); + if (serializedInfoItems != null) { + final InfoItem[] infoItems = Arrays.copyOf( + serializedInfoItems, + serializedInfoItems.length, + InfoItem[].class + ); + final List feedInfos = Arrays.asList(infoItems); + infoListAdapter.addInfoItemList( feedInfos ); + } + + // Already displayed feed items survive configuration changes + retainFeedItems.set(true); + } + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { + if (inflatedView == null) { + inflatedView = inflater.inflate(R.layout.fragment_subscription, container, false); + } + return inflatedView; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + if (resultRecyclerView != null) { + outState.putParcelable( + VIEW_STATE_KEY, + resultRecyclerView.getLayoutManager().onSaveInstanceState() + ); + } + + if (infoListAdapter != null) { + outState.putSerializable(INFO_ITEMS_KEY, infoListAdapter.getItemsList().toArray()); + } + } + + @Override + public void onDestroyView() { + // Do not monitor for updates when user is not viewing the feed fragment. + // This is a waste of bandwidth. + if (loadItemObserver != null) loadItemObserver.dispose(); + if (subscriptionObserver != null) subscriptionObserver.dispose(); + if (feedSubscriber != null) feedSubscriber.cancel(); + + loadItemObserver = null; + subscriptionObserver = null; + feedSubscriber = null; + + loadItemFooter = null; + + // Retain the already displayed items for backstack pops + retainFeedItems.set(true); + + super.onDestroyView(); + } + + @Override + public void onDestroy() { + subscriptionEngine = null; + + super.onDestroy(); + } + + /////////////////////////////////////////////////////////////////////////// + // Fragment Views + /////////////////////////////////////////////////////////////////////////// + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (DEBUG) Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "], inflater = [" + inflater + "]"); + super.onCreateOptionsMenu(menu, inflater); + + ActionBar supportActionBar = activity.getSupportActionBar(); + if (supportActionBar != null) { + supportActionBar.setDisplayShowTitleEnabled(true); + supportActionBar.setDisplayHomeAsUpEnabled(true); + } + } + + private RecyclerView.OnScrollListener getOnScrollListener() { + return new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + viewState = recyclerView.getLayoutManager().onSaveInstanceState(); + } + } + }; + } + + @Override + protected void initViews(View rootView, Bundle savedInstanceState) { + super.initViews(rootView, savedInstanceState); + + if (infoListAdapter == null) return; + + animateView(errorPanel, false, 200); + animateView(loadingProgressBar, true, 200); + + emptyPanel = rootView.findViewById(R.id.empty_panel); + + resultRecyclerView = rootView.findViewById(R.id.result_list_view); + resultRecyclerView.setLayoutManager(new LinearLayoutManager(activity)); + + loadItemFooter = activity.getLayoutInflater().inflate(R.layout.load_item_footer, resultRecyclerView, false); + infoListAdapter.setFooter(loadItemFooter); + infoListAdapter.showFooter(false); + infoListAdapter.setOnStreamInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + @Override + public void selected(int serviceId, String url, String title) { + NavigationHelper.openVideoDetailFragment(getFragmentManager(), serviceId, url, title); + } + }); + + resultRecyclerView.setAdapter(infoListAdapter); + resultRecyclerView.addOnScrollListener(getOnScrollListener()); + + if (viewState != null) { + resultRecyclerView.getLayoutManager().onRestoreInstanceState(viewState); + viewState = null; + } + + if (activity.getSupportActionBar() != null) activity.getSupportActionBar().setTitle(R.string.fragment_whats_new); + + populateFeed(); + } + + private void resetFragment() { + if (subscriptionObserver != null) subscriptionObserver.dispose(); + if (infoListAdapter != null) infoListAdapter.clearStreamItemList(); + } + + @Override + protected void reloadContent() { + resetFragment(); + populateFeed(); + } + + @Override + protected void setErrorMessage(String message, boolean showRetryButton) { + super.setErrorMessage(message, showRetryButton); + + resetFragment(); + } + + /** + * Changes the state of the load item footer. + * + * If the current state of the feed is loaded, this displays the load item button and + * starts its reactor. + * + * Otherwise, show a spinner in place of the loader button. */ + private void setLoader(final boolean isLoaded) { + if (loadItemFooter == null) return; + + if (loadItemObserver != null) loadItemObserver.dispose(); + + if (isLoaded) { + loadItemObserver = getLoadItemObserver(loadItemFooter); + } + + loadItemFooter.findViewById(R.id.paginate_progress_bar).setVisibility(isLoaded ? View.GONE : View.VISIBLE); + loadItemFooter.findViewById(R.id.load_more_text).setVisibility(isLoaded ? View.VISIBLE : View.GONE); + } + + /////////////////////////////////////////////////////////////////////////// + // Feeds Loader + /////////////////////////////////////////////////////////////////////////// + + /** + * Responsible for reacting to subscription database updates and displaying feeds. + * + * Upon each update, the feed info list is cleared unless the fragment is + * recently recovered from a configuration change or backstack. + * + * All existing and pending feed requests are dropped. + * + * The newly received list of subscriptions is then transformed into a + * flowable, reacting to pulling requests. + * + * Pulled requests are transformed first into ChannelInfo, then Stream Info items and + * displayed on the feed fragment. + **/ + private void populateFeed() { + final Consumer> consumer = new Consumer>() { + @Override + public void accept(@NonNull List subscriptionEntities) throws Exception { + animateView(loadingProgressBar, false, 200); + + if (subscriptionEntities.isEmpty()) { + infoListAdapter.clearStreamItemList(); + emptyPanel.setVisibility(View.VISIBLE); + } else { + emptyPanel.setVisibility(View.INVISIBLE); + } + + // show progress bar on receiving a non-empty updated list of subscriptions + if (!retainFeedItems.get() && !subscriptionEntities.isEmpty()) { + infoListAdapter.clearStreamItemList(); + animateView(loadingProgressBar, true, 200); + } + + retainFeedItems.set(false); + Flowable.fromIterable(subscriptionEntities) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(getSubscriptionObserver()); + } + }; + + final Consumer onError = new Consumer() { + @Override + public void accept(@NonNull Throwable exception) throws Exception { + onRxError(exception, "Subscription Database Reactor"); + } + }; + + if (subscriptionObserver != null) subscriptionObserver.dispose(); + subscriptionObserver = subscriptionEngine.getSubscription() + .onErrorReturnItem(Collections.emptyList()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(consumer, onError); + } + + /** + * Responsible for reacting to user pulling request and starting a request for new feed stream. + * + * On initialization, it automatically requests the amount of feed needed to display + * a minimum amount required (FEED_LOAD_SIZE). + * + * Upon receiving a user pull, it creates a Single Observer to fetch the ChannelInfo + * containing the feed streams. + **/ + private Subscriber getSubscriptionObserver() { + return new Subscriber() { + @Override + public void onSubscribe(Subscription s) { + if (feedSubscriber != null) feedSubscriber.cancel(); + feedSubscriber = s; + + final int requestSize = FEED_LOAD_SIZE - infoListAdapter.getItemsList().size(); + if (requestSize > 0) { + requestFeed(requestSize); + } else { + setLoader(true); + } + + animateView(loadingProgressBar, false, 200); + // Footer spinner persists until subscription list is exhausted. + infoListAdapter.showFooter(true); + } + + @Override + public void onNext(SubscriptionEntity subscriptionEntity) { + setLoader(false); + + subscriptionEngine.getChannelInfo(subscriptionEntity) + .observeOn(AndroidSchedulers.mainThread()) + .onErrorComplete() + .subscribe(getChannelInfoObserver()); + } + + @Override + public void onError(Throwable exception) { + onRxError(exception, "Feed Pull Reactor"); + } + + @Override + public void onComplete() { + infoListAdapter.showFooter(false); + } + }; + } + + /** + * On each request, a subscription item from the updated table is transformed + * into a ChannelInfo, containing the latest streams from the channel. + * + * Currently, the feed uses the first into from the list of streams. + * + * If chosen feed already displayed, then we request another feed from another + * subscription, until the subscription table runs out of new items. + * + * This Observer is self-contained and will dispose itself when complete. However, this + * does not obey the fragment lifecycle and may continue running in the background + * until it is complete. This is done due to RxJava2 no longer propagate errors once + * an observer is unsubscribed while the thread process is still running. + * + * To solve the above issue, we can either set a global RxJava Error Handler, or + * manage exceptions case by case. This should be done if the current implementation is + * too costly when dealing with larger subscription sets. + **/ + private MaybeObserver getChannelInfoObserver() { + return new MaybeObserver() { + Disposable observer; + @Override + public void onSubscribe(Disposable d) { + observer = d; + } + + // Called only when response is non-empty + @Override + public void onSuccess(ChannelInfo channelInfo) { + emptyPanel.setVisibility(View.INVISIBLE); + + if (infoListAdapter == null || channelInfo.related_streams.isEmpty()) return; + + final InfoItem item = channelInfo.related_streams.get(0); + // Keep requesting new items if the current one already exists + if (!doesItemExist(infoListAdapter.getItemsList(), item)) { + infoListAdapter.addInfoItem(item); + } else { + requestFeed(1); + } + onDone(); + } + + @Override + public void onError(Throwable exception) { + onRxError(exception, "Feed Display Reactor"); + onDone(); + } + + // Called only when response is empty + @Override + public void onComplete() { + onDone(); + } + + private void onDone() { + setLoader(true); + + observer.dispose(); + observer = null; + } + }; + } + + private boolean doesItemExist(final List items, final InfoItem item) { + for (final InfoItem existingItem: items) { + if (existingItem.infoType() == item.infoType() && + existingItem.getTitle().equals(item.getTitle()) && + existingItem.getLink().equals(item.getLink())) return true; + } + return false; + } + + private void requestFeed(final int count) { + if (feedSubscriber == null) return; + + feedSubscriber.request(count); + } + + private Disposable getLoadItemObserver(@NonNull final View itemLoader) { + final Consumer onNext = new Consumer() { + @Override + public void accept(Object o) throws Exception { + requestFeed(FEED_LOAD_SIZE); + } + }; + + final Consumer onError = new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + onRxError(throwable, "Load Button Reactor"); + } + }; + + return RxView.clicks(itemLoader) + .debounce(LOAD_ITEM_DEBOUNCE_INTERVAL, TimeUnit.MILLISECONDS) + .subscribe(onNext, onError); + } + + /////////////////////////////////////////////////////////////////////////// + // Fragment Error Handling + /////////////////////////////////////////////////////////////////////////// + + private void onRxError(final Throwable exception, final String tag) { + if (exception instanceof IOException) { + onRecoverableError(R.string.network_error); + } else { + onUnrecoverableError(exception, tag); + } + } + + private void onRecoverableError(int messageId) { + if (!this.isAdded()) return; + + if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]"); + setErrorMessage(getString(messageId), true); + } + + private void onUnrecoverableError(Throwable exception, final String tag) { + if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]"); + ErrorActivity.reportError(getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_CHANNEL, "Feed", tag, R.string.general_error)); + + activity.finish(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionEngine.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionEngine.java new file mode 100644 index 000000000..25285db41 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionEngine.java @@ -0,0 +1,170 @@ +package org.schabi.newpipe.fragments.subscription; + +import android.content.Context; + +import org.schabi.newpipe.NewPipeDatabase; +import org.schabi.newpipe.database.AppDatabase; +import org.schabi.newpipe.database.subscription.SubscriptionDAO; +import org.schabi.newpipe.database.subscription.SubscriptionEntity; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.channel.ChannelExtractor; +import org.schabi.newpipe.extractor.channel.ChannelInfo; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; + +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import io.reactivex.Completable; +import io.reactivex.CompletableSource; +import io.reactivex.Flowable; +import io.reactivex.Maybe; +import io.reactivex.Scheduler; +import io.reactivex.annotations.NonNull; +import io.reactivex.functions.Function; +import io.reactivex.schedulers.Schedulers; + +/** Subscription Service singleton: + * Provides a basis for channel Subscriptions. + * Provides access to subscription table in database as well as + * up-to-date observations on the subscribed channels + * */ +public class SubscriptionEngine { + + private static SubscriptionEngine sInstance; + private static final Object LOCK = new Object(); + + public static SubscriptionEngine getInstance(Context context) { + if (sInstance == null) { + synchronized (LOCK) { + if (sInstance == null) { + sInstance = new SubscriptionEngine(context); + } + } + } + return sInstance; + } + + protected final String TAG = "SubscriptionEngine@" + Integer.toHexString(hashCode()); + private static final int SUBSCRIPTION_DEBOUNCE_INTERVAL = 500; + private static final int SUBSCRIPTION_THREAD_POOL_SIZE = 4; + + private AppDatabase db; + private Flowable> subscription; + + private Scheduler subscriptionScheduler; + + private SubscriptionEngine(Context context) { + db = NewPipeDatabase.getInstance( context ); + subscription = getSubscriptionInfos(); + + final Executor subscriptionExecutor = Executors.newFixedThreadPool(SUBSCRIPTION_THREAD_POOL_SIZE); + subscriptionScheduler = Schedulers.from(subscriptionExecutor); + } + + /** Part of subscription observation pipeline + * @see SubscriptionEngine#getSubscription() + */ + private Flowable> getSubscriptionInfos() { + return subscriptionTable().findAll() + // Wait for a period of infrequent updates and return the latest update + .debounce(SUBSCRIPTION_DEBOUNCE_INTERVAL, TimeUnit.MILLISECONDS) + .share() // Share allows multiple subscribers on the same observable + .replay(1) // Replay synchronizes subscribers to the last emitted result + .autoConnect(); + } + + /** + * Provides an observer to the latest update to the subscription table. + * + * This observer may be subscribed multiple times, where each subscriber obtains + * the latest synchronized changes available, effectively share the same data + * across all subscribers. + * + * This observer has a debounce cooldown, meaning if multiple updates are observed + * in the cooldown interval, only the latest changes are emitted to the subscribers. + * This reduces the amount of observations caused by frequent updates to the database. + * */ + @android.support.annotation.NonNull + public Flowable> getSubscription() { + return subscription; + } + + public Maybe getChannelInfo(final SubscriptionEntity subscriptionEntity) { + final StreamingService service = getService(subscriptionEntity.getServiceId()); + if (service == null) return Maybe.empty(); + + final String url = subscriptionEntity.getUrl(); + final Callable callable = new Callable() { + @Override + public ChannelInfo call() throws Exception { + final ChannelExtractor extractor = service.getChannelExtractorInstance(url, 0); + return ChannelInfo.getInfo(extractor); + } + }; + + return Maybe.fromCallable(callable).subscribeOn(subscriptionScheduler); + } + + private StreamingService getService(final int serviceId) { + try { + return NewPipe.getService(serviceId); + } catch (ExtractionException e) { + return null; + } + } + + /** Returns the database access interface for subscription table. */ + public SubscriptionDAO subscriptionTable() { + return db.subscriptionDAO(); + } + + public Completable updateChannelInfo(final int serviceId, + final String channelUrl, + final ChannelInfo info) { + final Function, CompletableSource> update = new Function, CompletableSource>() { + @Override + public CompletableSource apply(@NonNull List subscriptionEntities) throws Exception { + if (subscriptionEntities.size() == 1) { + SubscriptionEntity subscription = subscriptionEntities.get(0); + + // Subscriber count changes very often, making this check almost unnecessary. + // Consider removing it later. + if (isSubscriptionUpToDate(channelUrl, info, subscription)) { + subscription.setData(info.channel_name, info.avatar_url, "", info.subscriberCount); + + return update(subscription); + } + } + + return Completable.complete(); + } + }; + + return subscriptionTable().findAll(serviceId, channelUrl) + .firstOrError() + .flatMapCompletable(update); + } + + private Completable update(final SubscriptionEntity updatedSubscription) { + return Completable.fromRunnable(new Runnable() { + @Override + public void run() { + subscriptionTable().update(updatedSubscription); + } + }); + } + + private boolean isSubscriptionUpToDate(final String channelUrl, + final ChannelInfo info, + final SubscriptionEntity entity) { + return channelUrl.equals( entity.getUrl() ) && + info.service_id == entity.getServiceId() && + info.channel_name.equals( entity.getTitle() ) && + info.avatar_url.equals( entity.getThumbnailUrl() ) && + info.subscriberCount == entity.getSubscriberCount(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java index c4dfbc50d..afcb03d2b 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/subscription/SubscriptionFragment.java @@ -1,9 +1,7 @@ package org.schabi.newpipe.fragments.subscription; -import android.content.Context; import android.os.Bundle; import android.os.Parcelable; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -12,43 +10,50 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; -import org.schabi.newpipe.fragments.BaseStateFragment; +import org.schabi.newpipe.fragments.BaseFragment; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.KioskTranslator; +import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.NavigationHelper; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import icepick.State; import io.reactivex.Observer; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import static org.schabi.newpipe.report.UserAction.REQUESTED_CHANNEL; import static org.schabi.newpipe.util.AnimationUtils.animateView; -public class SubscriptionFragment extends BaseStateFragment> { +public class SubscriptionFragment extends BaseFragment { + private static final String VIEW_STATE_KEY = "view_state_key"; + private final String TAG = "SubscriptionFragment@" + Integer.toHexString(hashCode()); + + private View inflatedView; + private View emptyPanel; private View headerRootLayout; + private View whatsNewView; private InfoListAdapter infoListAdapter; - private RecyclerView itemsList; - - @State - protected Parcelable itemsListState; + private RecyclerView resultRecyclerView; + private Parcelable viewState; /* Used for independent events */ - private CompositeDisposable disposables = new CompositeDisposable(); - private SubscriptionService subscriptionService; + private CompositeDisposable disposables; + private SubscriptionEngine subscriptionEngine; /////////////////////////////////////////////////////////////////////////// // Fragment LifeCycle @@ -66,10 +71,15 @@ public class SubscriptionFragment extends BaseStateFragment() { - @Override - public void selected(ChannelInfoItem selectedItem) { - // Requires the parent fragment to find holder for fragment replacement - NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), selectedItem.service_id, selectedItem.url, selectedItem.name); - - } - }); - - headerRootLayout.setOnClickListener(new View.OnClickListener() { + private View.OnClickListener getWhatsNewOnClickListener() { + return new View.OnClickListener() { @Override public void onClick(View view) { NavigationHelper.openWhatsNewFragment(getParentFragment().getFragmentManager()); } - }); + }; + } + + @Override + protected void initViews(View rootView, Bundle savedInstanceState) { + super.initViews(rootView, savedInstanceState); + + emptyPanel = rootView.findViewById(R.id.empty_panel); + + resultRecyclerView = rootView.findViewById(R.id.result_list_view); + resultRecyclerView.setLayoutManager(new LinearLayoutManager(activity)); + resultRecyclerView.addOnScrollListener(getOnScrollListener()); + + if (infoListAdapter == null) { + infoListAdapter = new InfoListAdapter(getActivity()); + infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, resultRecyclerView, false)); + infoListAdapter.showFooter(false); + infoListAdapter.setOnChannelInfoItemSelectedListener(new InfoItemBuilder.OnInfoItemSelectedListener() { + @Override + public void selected(int serviceId, String url, String title) { + /* Requires the parent fragment to find holder for fragment replacement */ + NavigationHelper.openChannelFragment(getParentFragment().getFragmentManager(), serviceId, url, title); + } + }); + } + + headerRootLayout = activity.getLayoutInflater().inflate(R.layout.subscription_header, resultRecyclerView, false); + infoListAdapter.setHeader(headerRootLayout); + + whatsNewView = headerRootLayout.findViewById(R.id.whatsNew); + whatsNewView.setOnClickListener(getWhatsNewOnClickListener()); + + resultRecyclerView.setAdapter(infoListAdapter); + + populateView(); + } + + @Override + protected void reloadContent() { + populateView(); + } + + @Override + protected void setErrorMessage(String message, boolean showRetryButton) { + super.setErrorMessage(message, showRetryButton); + resetFragment(); } private void resetFragment() { @@ -153,12 +199,13 @@ public class SubscriptionFragment extends BaseStateFragment>() { @Override public void onSubscribe(Disposable d) { - showLoading(); - disposables.add(d); + animateView(loadingProgressBar, true, 200); + + disposables.add( d ); } @Override public void onNext(List subscriptions) { - handleResult(subscriptions); + animateView(loadingProgressBar, true, 200); + + infoListAdapter.clearStreamItemList(); + infoListAdapter.addInfoItemList( getSubscriptionItems(subscriptions) ); + + animateView(loadingProgressBar, false, 200); + + emptyPanel.setVisibility(subscriptions.isEmpty() ? View.VISIBLE : View.INVISIBLE); + + if (viewState != null && resultRecyclerView != null) { + resultRecyclerView.getLayoutManager().onRestoreInstanceState(viewState); + } } @Override public void onError(Throwable exception) { - SubscriptionFragment.this.onError(exception); + if (exception instanceof IOException) { + onRecoverableError(R.string.network_error); + } else { + onUnrecoverableError(exception); + } } @Override public void onComplete() { + } }; } - @Override - public void handleResult(@NonNull List result) { - super.handleResult(result); - - infoListAdapter.clearStreamItemList(); - - if (result.isEmpty()) { - showEmptyState(); - } else { - infoListAdapter.addInfoItemList(getSubscriptionItems(result)); - if (itemsListState != null) { - itemsList.getLayoutManager().onRestoreInstanceState(itemsListState); - itemsListState = null; - } - - hideLoading(); - } - } - - private List getSubscriptionItems(List subscriptions) { List items = new ArrayList<>(); - for (final SubscriptionEntity subscription : subscriptions) items.add(subscription.toChannelInfoItem()); + for (final SubscriptionEntity subscription: subscriptions) { + ChannelInfoItem item = new ChannelInfoItem(); + item.webPageUrl = subscription.getUrl(); + item.serviceId = subscription.getServiceId(); + item.channelName = subscription.getTitle(); + item.thumbnailUrl = subscription.getThumbnailUrl(); + item.subscriberCount = subscription.getSubscriberCount(); + item.description = subscription.getDescription(); + items.add( item ); + } Collections.sort(items, new Comparator() { @Override public int compare(InfoItem o1, InfoItem o2) { - return o1.name.compareToIgnoreCase(o2.name); + return o1.getTitle().compareToIgnoreCase(o2.getTitle()); } }); + return items; } - /*////////////////////////////////////////////////////////////////////////// - // Contract - //////////////////////////////////////////////////////////////////////////*/ - - @Override - public void showLoading() { - super.showLoading(); - animateView(itemsList, false, 100); - } - - @Override - public void hideLoading() { - super.hideLoading(); - animateView(itemsList, true, 200); - } - - @Override - public void showEmptyState() { - super.showEmptyState(); - animateView(itemsList, false, 200); - } - /////////////////////////////////////////////////////////////////////////// // Fragment Error Handling /////////////////////////////////////////////////////////////////////////// - @Override - protected boolean onError(Throwable exception) { - resetFragment(); - if (super.onError(exception)) return true; + private void onRecoverableError(int messageId) { + if (!this.isAdded()) return; - onUnrecoverableError(exception, UserAction.SOMETHING_ELSE, "none", "Subscriptions", R.string.general_error); - return true; + if (DEBUG) Log.d(TAG, "onError() called with: messageId = [" + messageId + "]"); + setErrorMessage(getString(messageId), true); + } + + private void onUnrecoverableError(Throwable exception) { + if (DEBUG) Log.d(TAG, "onUnrecoverableError() called with: exception = [" + exception + "]"); + ErrorActivity.reportError(getContext(), exception, MainActivity.class, null, ErrorActivity.ErrorInfo.make(REQUESTED_CHANNEL, "unknown", "unknown", R.string.general_error)); + activity.finish(); } } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/PlaylistInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/PlaylistInfoItemHolder.java new file mode 100644 index 000000000..ffd3b09e8 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/info_list/PlaylistInfoItemHolder.java @@ -0,0 +1,49 @@ +package org.schabi.newpipe.info_list; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.InfoItem; + +/** + * Created by Christian Schabesberger on 12.02.17. + * + * Copyright (C) Christian Schabesberger 2016 + * ChannelInfoItemHolder .java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class PlaylistInfoItemHolder extends InfoItemHolder { + public final ImageView itemThumbnailView; + public final TextView itemPlaylistTitleView; + public final TextView itemAdditionalDetailView; + + public final View itemRoot; + + PlaylistInfoItemHolder(View v) { + super(v); + itemRoot = v.findViewById(R.id.itemRoot); + itemThumbnailView = v.findViewById(R.id.itemThumbnailView); + itemPlaylistTitleView = v.findViewById(R.id.itemPlaylistTitleView); + itemAdditionalDetailView = v.findViewById(R.id.itemAdditionalDetails); + } + + @Override + public InfoItem.InfoType infoType() { + return InfoItem.InfoType.PLAYLIST; + } +} diff --git a/app/src/main/res/layout/playlist_item.xml b/app/src/main/res/layout/playlist_item.xml new file mode 100644 index 000000000..cb734ae15 --- /dev/null +++ b/app/src/main/res/layout/playlist_item.xml @@ -0,0 +1,51 @@ + + + + + + + + + + \ No newline at end of file From cbcd281784ce07c9def74bf44676f65169ea0d11 Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Mon, 28 Aug 2017 17:38:37 -0700 Subject: [PATCH 054/154] -Added MediaSourceManager and Playlist adapters. --- .../fragments/playlist/PlaylistFragment.java | 93 +++++++++-- .../fragments/search/PlaylistService.java | 4 - .../newpipe/player/BackgroundPlayer.java | 6 +- .../org/schabi/newpipe/player/BasePlayer.java | 9 +- .../newpipe/player/MediaSourceManager.java | 21 +++ .../newpipe/playlist/ExternalPlaylist.java | 99 +++++++++++ .../org/schabi/newpipe/playlist/Playlist.java | 38 +++++ .../newpipe/playlist/PlaylistAdapter.java | 154 ++++++++++++++++++ .../schabi/newpipe/playlist/PlaylistItem.java | 109 +++++++++++++ .../newpipe/playlist/PlaylistItemBuilder.java | 116 +++++++++++++ .../newpipe/playlist/PlaylistItemHolder.java | 43 +++++ .../main/res/layout/activity_main_player.xml | 12 +- .../main/res/layout/playlist_stream_item.xml | 70 ++++++++ 13 files changed, 753 insertions(+), 21 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java create mode 100644 app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/Playlist.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlaylistItem.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java create mode 100644 app/src/main/res/layout/playlist_stream_item.xml diff --git a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java index 0493fd525..032b227e8 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java @@ -79,6 +79,11 @@ public class PlaylistFragment extends BaseFragment { private ImageView headerAvatarView; private TextView headerTitleView; + /*////////////////////////////////////////////////////////////////////////*/ + // Reactors + //////////////////////////////////////////////////////////////////////////*/ + private Disposable loadingReactor; + /*////////////////////////////////////////////////////////////////////////*/ public PlaylistFragment() { @@ -153,8 +158,8 @@ public class PlaylistFragment extends BaseFragment { public void onStop() { if (DEBUG) Log.d(TAG, "onStop() called"); - disposable.dispose(); - disposable = null; + if (loadingReactor != null) loadingReactor.dispose(); + loadingReactor = null; super.onStop(); } @@ -221,7 +226,7 @@ public class PlaylistFragment extends BaseFragment { protected void initViews(View rootView, Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); - playlistStreams = (RecyclerView) rootView.findViewById(R.id.channel_streams_view); + playlistStreams = rootView.findViewById(R.id.channel_streams_view); playlistStreams.setLayoutManager(new LinearLayoutManager(activity)); if (infoListAdapter == null) { @@ -238,9 +243,9 @@ public class PlaylistFragment extends BaseFragment { infoListAdapter.setHeader(headerRootLayout); infoListAdapter.setFooter(activity.getLayoutInflater().inflate(R.layout.pignate_footer, playlistStreams, false)); - headerBannerView = (ImageView) headerRootLayout.findViewById(R.id.playlist_banner_image); - headerAvatarView = (ImageView) headerRootLayout.findViewById(R.id.playlist_avatar_view); - headerTitleView = (TextView) headerRootLayout.findViewById(R.id.playlist_title_view); + headerBannerView = headerRootLayout.findViewById(R.id.playlist_banner_image); + headerAvatarView = headerRootLayout.findViewById(R.id.playlist_avatar_view); + headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view); } protected void initListeners() { @@ -280,7 +285,71 @@ public class PlaylistFragment extends BaseFragment { return NewPipe.getService(serviceId); } - Disposable disposable; + private void loadAll() { + final Callable task = new Callable() { + @Override + public PlayListInfo call() throws Exception { + int pageCount = 0; + + final PlayListExtractor extractor = getService(serviceId) + .getPlayListExtractorInstance(playlistUrl, 0); + + final PlayListInfo info = PlayListInfo.getInfo(extractor); + + boolean hasNext = info.hasNextPage; + while(hasNext) { + pageCount++; + + final PlayListExtractor moreExtractor = getService(serviceId) + .getPlayListExtractorInstance(playlistUrl, pageCount); + + final PlayListInfo moreInfo = PlayListInfo.getInfo(moreExtractor); + + info.related_streams.addAll(moreInfo.related_streams); + hasNext = moreInfo.hasNextPage; + } + return info; + } + }; + + Observable.fromCallable(task) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Observer() { + @Override + public void onSubscribe(@NonNull Disposable d) { + if (loadingReactor == null || loadingReactor.isDisposed()) { + loadingReactor = d; + isLoading.set(true); + } else { + d.dispose(); + } + } + + @Override + public void onNext(@NonNull PlayListInfo playListInfo) { + if (DEBUG) Log.d(TAG, "onReceive() called with: info = [" + playListInfo + "]"); + if (playListInfo == null || isRemoving() || !isVisible()) return; + + handlePlayListInfo(playListInfo, false, true); + isLoading.set(false); + pageNumber++; + } + + @Override + public void onError(@NonNull Throwable e) { + onRxError(e, "Observer failure"); + } + + @Override + public void onComplete() { + if (loadingReactor != null) { + loadingReactor.dispose(); + loadingReactor = null; + } + } + }); + } private void loadMore(final boolean onlyVideos) { final Callable task = new Callable() { @@ -300,8 +369,8 @@ public class PlaylistFragment extends BaseFragment { .subscribe(new Observer() { @Override public void onSubscribe(@NonNull Disposable d) { - if (disposable == null || disposable.isDisposed()) { - disposable = d; + if (loadingReactor == null || loadingReactor.isDisposed()) { + loadingReactor = d; isLoading.set(true); } else { d.dispose(); @@ -325,9 +394,9 @@ public class PlaylistFragment extends BaseFragment { @Override public void onComplete() { - if (disposable != null) { - disposable.dispose(); - disposable = null; + if (loadingReactor != null) { + loadingReactor.dispose(); + loadingReactor = null; } } }); diff --git a/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java b/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java deleted file mode 100644 index b43c7e356..000000000 --- a/app/src/main/java/org/schabi/newpipe/fragments/search/PlaylistService.java +++ /dev/null @@ -1,4 +0,0 @@ -package org.schabi.newpipe.fragments.search; - -public class PlaylistService { -} diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index be9247569..8659ca645 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -344,13 +344,15 @@ public class BackgroundPlayer extends Service { @Override public void onFastRewind() { - super.onFastRewind(); +// super.onFastRewind(); + simpleExoPlayer.seekTo(0, 0); triggerProgressUpdate(); } @Override public void onFastForward() { - super.onFastForward(); +// super.onFastForward(); + simpleExoPlayer.seekTo(2, 0); triggerProgressUpdate(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index f90352fa1..4d5631bc9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -47,7 +47,9 @@ import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; +import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource; +import com.google.android.exoplayer2.source.LoopingMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; @@ -248,7 +250,12 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O changeState(STATE_LOADING); isPrepared = false; - mediaSource = buildMediaSource(url, format); + + final MediaSource ms = buildMediaSource(url, format); + final DynamicConcatenatingMediaSource dcms = new DynamicConcatenatingMediaSource(); + dcms.addMediaSource(ms); + mediaSource = dcms; + dcms.addMediaSource(new LoopingMediaSource(ms, 2)); if (simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) simpleExoPlayer.stop(); if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos); diff --git a/app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java new file mode 100644 index 000000000..ee4ef5df4 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java @@ -0,0 +1,21 @@ +package org.schabi.newpipe.player; + +import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource; +import com.google.android.exoplayer2.source.MediaSource; + +import org.schabi.newpipe.playlist.Playlist; + +import java.util.List; + +public class MediaSourceManager { + + private DynamicConcatenatingMediaSource source; + + private Playlist playlist; + private List sources; + + public MediaSourceManager(Playlist playlist) { + this.source = new DynamicConcatenatingMediaSource(); + this.playlist = playlist; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java new file mode 100644 index 000000000..9ffc71303 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java @@ -0,0 +1,99 @@ +package org.schabi.newpipe.playlist; + +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.playlist.PlayListExtractor; +import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.extractor.playlist.PlayListInfoItem; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; +import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicInteger; + +import io.reactivex.Maybe; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Consumer; +import io.reactivex.schedulers.Schedulers; + +public class ExternalPlaylist extends Playlist { + + private AtomicInteger pageNumber; + + private StreamingService service; + + public ExternalPlaylist(final PlayListInfoItem playlist) { + super(); + service = getService(playlist.serviceId); + pageNumber = new AtomicInteger(0); + + load(playlist); + } + + private void load(final PlayListInfoItem playlist) { + final int page = pageNumber.getAndIncrement(); + + final Callable task = new Callable() { + @Override + public PlayListInfo call() throws Exception { + PlayListExtractor extractor = service.getPlayListExtractorInstance(playlist.getLink(), page); + return PlayListInfo.getInfo(extractor); + } + }; + + final Consumer onSuccess = new Consumer() { + @Override + public void accept(PlayListInfo playListInfo) throws Exception { + streams.addAll(extractPlaylistItems(playListInfo)); + changeBroadcast.onNext(streams); + } + }; + + Maybe.fromCallable(task) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .onErrorComplete() + .subscribe(onSuccess); + } + + private List extractPlaylistItems(final PlayListInfo info) { + List result = new ArrayList<>(); + for (final InfoItem stream : info.related_streams) { + if (stream instanceof StreamInfoItem) { + result.add(new PlaylistItem((StreamInfoItem) stream)); + } + } + return result; + } + + @Override + boolean isComplete() { + return false; + } + + @Override + void load(int index) { + while (streams.size() < index) { + pageNumber.incrementAndGet(); + } + } + + @Override + Observable get(int index) { + return null; + } + + private StreamingService getService(final int serviceId) { + try { + return NewPipe.getService(serviceId); + } catch (ExtractionException e) { + return null; + } + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java b/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java new file mode 100644 index 000000000..02faaa755 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java @@ -0,0 +1,38 @@ +package org.schabi.newpipe.playlist; + +import android.support.annotation.NonNull; + +import org.schabi.newpipe.extractor.stream_info.StreamInfo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import io.reactivex.Observable; +import io.reactivex.subjects.PublishSubject; + +public abstract class Playlist { + private final String TAG = "Playlist@" + Integer.toHexString(hashCode()); + + private final int LOAD_BOUND = 2; + + List streams; + PublishSubject> changeBroadcast; + + Playlist() { + streams = Collections.synchronizedList(new ArrayList()); + changeBroadcast = PublishSubject.create(); + } + + @NonNull + public PublishSubject> getChangeBroadcast() { + return changeBroadcast; + } + + abstract boolean isComplete(); + + abstract void load(int index); + + abstract Observable get(int index); +} + diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java new file mode 100644 index 000000000..6f6c924d7 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java @@ -0,0 +1,154 @@ +package org.schabi.newpipe.playlist; + +import android.app.Activity; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.info_list.StreamInfoItemHolder; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Christian Schabesberger on 01.08.16. + * + * Copyright (C) Christian Schabesberger 2016 + * InfoListAdapter.java is part of NewPipe. + * + * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class PlaylistAdapter extends RecyclerView.Adapter { + private static final String TAG = PlaylistAdapter.class.toString(); + + private final PlaylistItemBuilder playlistItemBuilder; + private final List playlistItems; + private boolean showFooter = false; + private View header = null; + private View footer = null; + + public class HFHolder extends RecyclerView.ViewHolder { + public HFHolder(View v) { + super(v); + view = v; + } + public View view; + } + + public void showFooter(boolean show) { + showFooter = show; + notifyDataSetChanged(); + } + + public PlaylistAdapter(List data) { + playlistItemBuilder = new PlaylistItemBuilder(); + playlistItems = data; + } + + public void setSelectedListener(PlaylistItemBuilder.OnSelectedListener listener) { + playlistItemBuilder.setOnSelectedListener(listener); + } + + public void addInfoItemList(List data) { + if(data != null) { + playlistItems.addAll(data); + notifyDataSetChanged(); + } + } + + public void addInfoItem(PlaylistItem data) { + if (data != null) { + playlistItems.add(data); + notifyDataSetChanged(); + } + } + + public void clearStreamItemList() { + if(playlistItems.isEmpty()) { + return; + } + playlistItems.clear(); + notifyDataSetChanged(); + } + + public void setHeader(View header) { + this.header = header; + notifyDataSetChanged(); + } + + public void setFooter(View footer) { + this.footer = footer; + notifyDataSetChanged(); + } + + public List getItemsList() { + return playlistItems; + } + + @Override + public int getItemCount() { + int count = playlistItems.size(); + if(header != null) count++; + if(footer != null && showFooter) count++; + return count; + } + + // don't ask why we have to do that this way... it's android accept it -.- + @Override + public int getItemViewType(int position) { + if(header != null && position == 0) { + return 0; + } else if(header != null) { + position--; + } + if(footer != null && position == playlistItems.size() && showFooter) { + return 1; + } + return 2; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int type) { + switch(type) { + case 0: + return new HFHolder(header); + case 1: + return new HFHolder(footer); + case 2: + return new StreamInfoItemHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.playlist_stream_item, parent, false)); + default: + Log.e(TAG, "Trollolo"); + return null; + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int i) { + if(holder instanceof PlaylistItemHolder) { + if(header != null) { + i--; + } + playlistItemBuilder.buildStreamInfoItem((PlaylistItemHolder) holder, playlistItems.get(i)); + } else if(holder instanceof HFHolder && i == 0 && header != null) { + ((HFHolder) holder).view = header; + } else if(holder instanceof HFHolder && i == playlistItems.size() && footer != null && showFooter) { + ((HFHolder) holder).view = footer; + } + } +} diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItem.java b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItem.java new file mode 100644 index 000000000..cd65b335b --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItem.java @@ -0,0 +1,109 @@ +package org.schabi.newpipe.playlist; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.stream_info.StreamExtractor; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; +import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; + +import java.io.Serializable; +import java.util.concurrent.Callable; + +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.functions.Action; +import io.reactivex.functions.Consumer; +import io.reactivex.schedulers.Schedulers; + +public class PlaylistItem implements Serializable { + + private String title; + private String url; + private int serviceId; + private int duration; + + private boolean isDone; + private Throwable error; + private Maybe stream; + + public PlaylistItem(final StreamInfoItem streamInfoItem) { + this.title = streamInfoItem.getTitle(); + this.url = streamInfoItem.getLink(); + this.serviceId = streamInfoItem.service_id; + this.duration = streamInfoItem.duration; + + this.isDone = false; + this.stream = getInfo(); + } + + @NonNull + public String getTitle() { + return title; + } + + @NonNull + public String getUrl() { + return url; + } + + public int getServiceId() { + return serviceId; + } + + public int getDuration() { + return duration; + } + + public boolean isDone() { + return isDone; + } + + @Nullable + public Throwable getError() { + return error; + } + + @NonNull + public Maybe getStream() { + return stream; + } + + public void load() { + stream.subscribe(); + } + + @NonNull + private Maybe getInfo() { + final Callable task = new Callable() { + @Override + public StreamInfo call() throws Exception { + final StreamExtractor extractor = NewPipe.getService(serviceId).getExtractorInstance(url); + return StreamInfo.getVideoInfo(extractor); + } + }; + + final Consumer onError = new Consumer() { + @Override + public void accept(Throwable throwable) throws Exception { + error = throwable; + } + }; + + final Action onComplete = new Action() { + @Override + public void run() throws Exception { + isDone = true; + } + }; + + return Maybe.fromCallable(task) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .doOnError(onError) + .onErrorComplete() + .doOnComplete(onComplete) + .cache(); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java new file mode 100644 index 000000000..1a74240ce --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java @@ -0,0 +1,116 @@ +package org.schabi.newpipe.playlist; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; + +import org.schabi.newpipe.ImageErrorLoadingListener; +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.AbstractStreamInfo; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.channel.ChannelInfoItem; +import org.schabi.newpipe.extractor.playlist.PlayListInfoItem; +import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; +import org.schabi.newpipe.info_list.ChannelInfoItemHolder; +import org.schabi.newpipe.info_list.InfoItemHolder; +import org.schabi.newpipe.info_list.PlaylistInfoItemHolder; +import org.schabi.newpipe.info_list.StreamInfoItemHolder; + +import java.util.Locale; + +/** + * Created by Christian Schabesberger on 26.09.16. + *

+ * Copyright (C) Christian Schabesberger 2016 + * InfoItemBuilder.java is part of NewPipe. + *

+ * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class PlaylistItemBuilder { + + private static final String TAG = PlaylistItemBuilder.class.toString(); + + public interface OnSelectedListener { + void selected(int serviceId, String url, String title); + } + + private OnSelectedListener onStreamInfoItemSelectedListener; + + public PlaylistItemBuilder() {} + + public void setOnSelectedListener(OnSelectedListener listener) { + this.onStreamInfoItemSelectedListener = listener; + } + + public View buildView(ViewGroup parent, final PlaylistItem item) { + final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + final View itemView = inflater.inflate(R.layout.stream_item, parent, false); + final PlaylistItemHolder holder = new PlaylistItemHolder(itemView); + + buildStreamInfoItem(holder, item); + + return itemView; + } + + + public void buildStreamInfoItem(PlaylistItemHolder holder, final PlaylistItem item) { + if (!TextUtils.isEmpty(item.getTitle())) holder.itemVideoTitleView.setText(item.getTitle()); + + if (item.getDuration() > 0) { + holder.itemDurationView.setText(getDurationString(item.getDuration())); + } else { + holder.itemDurationView.setVisibility(View.GONE); + } + + holder.itemRoot.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if(onStreamInfoItemSelectedListener != null) { + onStreamInfoItemSelectedListener.selected(item.getServiceId(), item.getUrl(), item.getTitle()); + } + } + }); + } + + + public static String getDurationString(int duration) { + if(duration < 0) { + duration = 0; + } + String output; + int days = duration / (24 * 60 * 60); /* greater than a day */ + duration %= (24 * 60 * 60); + int hours = duration / (60 * 60); /* greater than an hour */ + duration %= (60 * 60); + int minutes = duration / 60; + int seconds = duration % 60; + + //handle days + if (days > 0) { + output = String.format(Locale.US, "%d:%02d:%02d:%02d", days, hours, minutes, seconds); + } else if(hours > 0) { + output = String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds); + } else { + output = String.format(Locale.US, "%d:%02d", minutes, seconds); + } + return output; + } +} diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java new file mode 100644 index 000000000..d1251c535 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java @@ -0,0 +1,43 @@ +package org.schabi.newpipe.playlist; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.info_list.InfoItemHolder; + +/** + * Created by Christian Schabesberger on 01.08.16. + *

+ * Copyright (C) Christian Schabesberger 2016 + * StreamInfoItemHolder.java is part of NewPipe. + *

+ * NewPipe is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * NewPipe is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with NewPipe. If not, see . + */ + +public class PlaylistItemHolder extends RecyclerView.ViewHolder { + + public final TextView itemVideoTitleView, itemDurationView; + public final View itemRoot; + + public PlaylistItemHolder(View v) { + super(v); + itemRoot = v.findViewById(R.id.itemRoot); + itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView); + itemDurationView = (TextView) v.findViewById(R.id.itemDurationView); + } +} diff --git a/app/src/main/res/layout/activity_main_player.xml b/app/src/main/res/layout/activity_main_player.xml index e3ef022f9..5b9246f8c 100644 --- a/app/src/main/res/layout/activity_main_player.xml +++ b/app/src/main/res/layout/activity_main_player.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/playlist_stream_item.xml b/app/src/main/res/layout/playlist_stream_item.xml new file mode 100644 index 000000000..52fac4e31 --- /dev/null +++ b/app/src/main/res/layout/playlist_stream_item.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + \ No newline at end of file From 7c9c3de6449fd00c5fa97e48ac27e2100b1ad64a Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Tue, 29 Aug 2017 08:00:11 -0700 Subject: [PATCH 055/154] -Rename playlist in players to play queue. --- .../org/schabi/newpipe/player/BasePlayer.java | 7 +- ...ourceManager.java => PlaybackManager.java} | 22 ++++-- ...alPlaylist.java => ExternalPlayQueue.java} | 79 ++++++++++++------- .../schabi/newpipe/playlist/PlayQueue.java | 42 ++++++++++ ...listAdapter.java => PlayQueueAdapter.java} | 72 +++++++++++------ .../{PlaylistItem.java => PlayQueueItem.java} | 4 +- ...emHolder.java => PlayQueueItemHolder.java} | 4 +- .../org/schabi/newpipe/playlist/Playlist.java | 38 --------- .../newpipe/playlist/PlaylistItemBuilder.java | 40 +--------- 9 files changed, 164 insertions(+), 144 deletions(-) rename app/src/main/java/org/schabi/newpipe/player/{MediaSourceManager.java => PlaybackManager.java} (50%) rename app/src/main/java/org/schabi/newpipe/playlist/{ExternalPlaylist.java => ExternalPlayQueue.java} (60%) create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java rename app/src/main/java/org/schabi/newpipe/playlist/{PlaylistAdapter.java => PlayQueueAdapter.java} (63%) rename app/src/main/java/org/schabi/newpipe/playlist/{PlaylistItem.java => PlayQueueItem.java} (95%) rename app/src/main/java/org/schabi/newpipe/playlist/{PlaylistItemHolder.java => PlayQueueItemHolder.java} (93%) delete mode 100644 app/src/main/java/org/schabi/newpipe/playlist/Playlist.java diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 4d5631bc9..3746927be 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -250,12 +250,7 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O changeState(STATE_LOADING); isPrepared = false; - - final MediaSource ms = buildMediaSource(url, format); - final DynamicConcatenatingMediaSource dcms = new DynamicConcatenatingMediaSource(); - dcms.addMediaSource(ms); - mediaSource = dcms; - dcms.addMediaSource(new LoopingMediaSource(ms, 2)); + mediaSource = buildMediaSource(url, format); if (simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) simpleExoPlayer.stop(); if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos); diff --git a/app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java similarity index 50% rename from app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java rename to app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java index ee4ef5df4..8b3973a54 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java @@ -3,19 +3,31 @@ package org.schabi.newpipe.player; import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource; import com.google.android.exoplayer2.source.MediaSource; -import org.schabi.newpipe.playlist.Playlist; +import org.schabi.newpipe.playlist.PlayQueue; import java.util.List; -public class MediaSourceManager { +public class PlaybackManager { private DynamicConcatenatingMediaSource source; - private Playlist playlist; + private PlayQueue playQueue; + private int index; + private List sources; - public MediaSourceManager(Playlist playlist) { + public PlaybackManager(PlayQueue playQueue, int index) { this.source = new DynamicConcatenatingMediaSource(); - this.playlist = playlist; + + this.playQueue = playQueue; + this.index = index; + + + + } + + interface OnChangeListener { + void isLoading(); + void isLoaded(); } } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java similarity index 60% rename from app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java rename to app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java index 9ffc71303..fba48d82d 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlaylist.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java @@ -16,26 +16,61 @@ import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.Maybe; -import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; -public class ExternalPlaylist extends Playlist { +public class ExternalPlayQueue extends PlayQueue { + + private final static int LOAD_PROXIMITY = 10; + + private boolean isComplete; private AtomicInteger pageNumber; private StreamingService service; - public ExternalPlaylist(final PlayListInfoItem playlist) { - super(); - service = getService(playlist.serviceId); - pageNumber = new AtomicInteger(0); + private PlayListInfoItem playlist; - load(playlist); + public ExternalPlayQueue(final PlayListInfoItem playlist) { + super(); + this.service = getService(playlist.serviceId); + this.pageNumber = new AtomicInteger(0); + this.playlist = playlist; + + fetch(); } - private void load(final PlayListInfoItem playlist) { + @Override + public boolean isComplete() { + return isComplete; + } + + @Override + public void load(int index, boolean loadNeighbors) { + if (index > streams.size() || streams.get(index) == null) return; + + streams.get(index).load(); + + if (loadNeighbors) { + int leftBound = index - LOAD_BOUND >= 0 ? index - LOAD_BOUND : 0; + int rightBound = index + LOAD_BOUND < streams.size() ? index + LOAD_BOUND : streams.size() - 1; + + for (int i = leftBound; i < rightBound; i++) { + final PlayQueueItem item = streams.get(i); + if (item != null) item.load(); + } + } + } + + @Override + public Maybe get(int index) { + if (index > streams.size() || streams.get(index) == null) return Maybe.empty(); + return streams.get(index).getStream(); + } + + + public synchronized void fetch() { final int page = pageNumber.getAndIncrement(); final Callable task = new Callable() { @@ -49,8 +84,10 @@ public class ExternalPlaylist extends Playlist { final Consumer onSuccess = new Consumer() { @Override public void accept(PlayListInfo playListInfo) throws Exception { + if (!playListInfo.hasNextPage) isComplete = true; + streams.addAll(extractPlaylistItems(playListInfo)); - changeBroadcast.onNext(streams); + notifyChange(); } }; @@ -61,33 +98,16 @@ public class ExternalPlaylist extends Playlist { .subscribe(onSuccess); } - private List extractPlaylistItems(final PlayListInfo info) { - List result = new ArrayList<>(); + private List extractPlaylistItems(final PlayListInfo info) { + List result = new ArrayList<>(); for (final InfoItem stream : info.related_streams) { if (stream instanceof StreamInfoItem) { - result.add(new PlaylistItem((StreamInfoItem) stream)); + result.add(new PlayQueueItem((StreamInfoItem) stream)); } } return result; } - @Override - boolean isComplete() { - return false; - } - - @Override - void load(int index) { - while (streams.size() < index) { - pageNumber.incrementAndGet(); - } - } - - @Override - Observable get(int index) { - return null; - } - private StreamingService getService(final int serviceId) { try { return NewPipe.getService(serviceId); @@ -95,5 +115,4 @@ public class ExternalPlaylist extends Playlist { return null; } } - } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java new file mode 100644 index 000000000..ecf9e578f --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -0,0 +1,42 @@ +package org.schabi.newpipe.playlist; + +import android.support.annotation.NonNull; + +import org.schabi.newpipe.extractor.stream_info.StreamInfo; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import io.reactivex.Maybe; +import io.reactivex.subjects.BehaviorSubject; + +public abstract class PlayQueue { + private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); + + final int LOAD_BOUND = 2; + + protected List streams; + private BehaviorSubject> changeBroadcast; + + PlayQueue() { + streams = Collections.synchronizedList(new ArrayList()); + changeBroadcast = BehaviorSubject.create(); + } + + @NonNull + public List getStreams() { + return streams; + } + + public void notifyChange() { + changeBroadcast.onNext(streams); + } + + public abstract boolean isComplete(); + + public abstract void load(int index, boolean loadNeighbors); + + public abstract Maybe get(int index); +} + diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java similarity index 63% rename from app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java rename to app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java index 6f6c924d7..662f9f2f1 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.playlist; -import android.app.Activity; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; @@ -10,7 +9,6 @@ import android.view.ViewGroup; import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.StreamInfoItemHolder; -import java.util.ArrayList; import java.util.List; /** @@ -33,11 +31,11 @@ import java.util.List; * along with NewPipe. If not, see . */ -public class PlaylistAdapter extends RecyclerView.Adapter { - private static final String TAG = PlaylistAdapter.class.toString(); +public class PlayQueueAdapter extends RecyclerView.Adapter { + private static final String TAG = PlayQueueAdapter.class.toString(); private final PlaylistItemBuilder playlistItemBuilder; - private final List playlistItems; + private final PlayQueue playQueue; private boolean showFooter = false; private View header = null; private View footer = null; @@ -55,34 +53,60 @@ public class PlaylistAdapter extends RecyclerView.Adapter data) { - playlistItemBuilder = new PlaylistItemBuilder(); - playlistItems = data; + public PlayQueueAdapter(PlayQueue playQueue) { + this.playlistItemBuilder = new PlaylistItemBuilder(); + this.playQueue = playQueue; } public void setSelectedListener(PlaylistItemBuilder.OnSelectedListener listener) { playlistItemBuilder.setOnSelectedListener(listener); } - public void addInfoItemList(List data) { + public void addItems(List data) { if(data != null) { - playlistItems.addAll(data); - notifyDataSetChanged(); + playQueue.getStreams().addAll(data); + notifyPlaylistChange(); } } - public void addInfoItem(PlaylistItem data) { + public void addItem(PlayQueueItem data) { if (data != null) { - playlistItems.add(data); - notifyDataSetChanged(); + playQueue.getStreams().add(data); + notifyPlaylistChange(); } } - public void clearStreamItemList() { - if(playlistItems.isEmpty()) { + public void removeItem(int index) { + if (index < playQueue.getStreams().size()) { + playQueue.getStreams().remove(index); + notifyPlaylistChange(); + } + } + + public void swapItems(int source, int target) { + final List items = playQueue.getStreams(); + if (source < items.size() && target < items.size()) { + final PlayQueueItem sourceItem = items.get(source); + final PlayQueueItem targetItem = items.get(target); + + items.set(target, sourceItem); + items.set(source, targetItem); + + notifyPlaylistChange(); + } + } + + public void clear() { + if(playQueue.getStreams().isEmpty()) { return; } - playlistItems.clear(); + playQueue.getStreams().clear(); + + notifyPlaylistChange(); + } + + private void notifyPlaylistChange() { + playQueue.notifyChange(); notifyDataSetChanged(); } @@ -96,13 +120,13 @@ public class PlaylistAdapter extends RecyclerView.Adapter getItemsList() { - return playlistItems; + public List getItems() { + return playQueue.getStreams(); } @Override public int getItemCount() { - int count = playlistItems.size(); + int count = playQueue.getStreams().size(); if(header != null) count++; if(footer != null && showFooter) count++; return count; @@ -116,7 +140,7 @@ public class PlaylistAdapter extends RecyclerView.Adapter stream; - public PlaylistItem(final StreamInfoItem streamInfoItem) { + public PlayQueueItem(final StreamInfoItem streamInfoItem) { this.title = streamInfoItem.getTitle(); this.url = streamInfoItem.getLink(); this.serviceId = streamInfoItem.service_id; diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemHolder.java similarity index 93% rename from app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java rename to app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemHolder.java index d1251c535..c25210ee2 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItemHolder.java @@ -29,12 +29,12 @@ import org.schabi.newpipe.info_list.InfoItemHolder; * along with NewPipe. If not, see . */ -public class PlaylistItemHolder extends RecyclerView.ViewHolder { +public class PlayQueueItemHolder extends RecyclerView.ViewHolder { public final TextView itemVideoTitleView, itemDurationView; public final View itemRoot; - public PlaylistItemHolder(View v) { + public PlayQueueItemHolder(View v) { super(v); itemRoot = v.findViewById(R.id.itemRoot); itemVideoTitleView = (TextView) v.findViewById(R.id.itemVideoTitleView); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java b/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java deleted file mode 100644 index 02faaa755..000000000 --- a/app/src/main/java/org/schabi/newpipe/playlist/Playlist.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.schabi.newpipe.playlist; - -import android.support.annotation.NonNull; - -import org.schabi.newpipe.extractor.stream_info.StreamInfo; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import io.reactivex.Observable; -import io.reactivex.subjects.PublishSubject; - -public abstract class Playlist { - private final String TAG = "Playlist@" + Integer.toHexString(hashCode()); - - private final int LOAD_BOUND = 2; - - List streams; - PublishSubject> changeBroadcast; - - Playlist() { - streams = Collections.synchronizedList(new ArrayList()); - changeBroadcast = PublishSubject.create(); - } - - @NonNull - public PublishSubject> getChangeBroadcast() { - return changeBroadcast; - } - - abstract boolean isComplete(); - - abstract void load(int index); - - abstract Observable get(int index); -} - diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java index 1a74240ce..829f502ab 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlaylistItemBuilder.java @@ -1,48 +1,14 @@ package org.schabi.newpipe.playlist; -import android.content.Context; import android.text.TextUtils; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; - -import org.schabi.newpipe.ImageErrorLoadingListener; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.AbstractStreamInfo; -import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.channel.ChannelInfoItem; -import org.schabi.newpipe.extractor.playlist.PlayListInfoItem; -import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; -import org.schabi.newpipe.info_list.ChannelInfoItemHolder; -import org.schabi.newpipe.info_list.InfoItemHolder; -import org.schabi.newpipe.info_list.PlaylistInfoItemHolder; -import org.schabi.newpipe.info_list.StreamInfoItemHolder; import java.util.Locale; -/** - * Created by Christian Schabesberger on 26.09.16. - *

- * Copyright (C) Christian Schabesberger 2016 - * InfoItemBuilder.java is part of NewPipe. - *

- * NewPipe is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - *

- * NewPipe is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - *

- * You should have received a copy of the GNU General Public License - * along with NewPipe. If not, see . - */ public class PlaylistItemBuilder { @@ -60,10 +26,10 @@ public class PlaylistItemBuilder { this.onStreamInfoItemSelectedListener = listener; } - public View buildView(ViewGroup parent, final PlaylistItem item) { + public View buildView(ViewGroup parent, final PlayQueueItem item) { final LayoutInflater inflater = LayoutInflater.from(parent.getContext()); final View itemView = inflater.inflate(R.layout.stream_item, parent, false); - final PlaylistItemHolder holder = new PlaylistItemHolder(itemView); + final PlayQueueItemHolder holder = new PlayQueueItemHolder(itemView); buildStreamInfoItem(holder, item); @@ -71,7 +37,7 @@ public class PlaylistItemBuilder { } - public void buildStreamInfoItem(PlaylistItemHolder holder, final PlaylistItem item) { + public void buildStreamInfoItem(PlayQueueItemHolder holder, final PlayQueueItem item) { if (!TextUtils.isEmpty(item.getTitle())) holder.itemVideoTitleView.setText(item.getTitle()); if (item.getDuration() > 0) { From dcdcf17f5e557149eaddb731e19a717b1624630f Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Thu, 31 Aug 2017 10:07:18 -0700 Subject: [PATCH 056/154] -Added on change event bus to Play Queue. -Added playback manager for player interaction. --- .../org/schabi/newpipe/player/BasePlayer.java | 30 ++- .../newpipe/player/PlaybackManager.java | 195 ++++++++++++++++-- .../newpipe/playlist/ExternalPlayQueue.java | 79 +++---- .../schabi/newpipe/playlist/PlayQueue.java | 117 ++++++++++- .../newpipe/playlist/PlayQueueAdapter.java | 82 ++++---- .../newpipe/playlist/PlayQueueEvent.java | 27 +++ .../newpipe/playlist/PlayQueueItem.java | 3 +- ...Builder.java => PlayQueueItemBuilder.java} | 8 +- ...st_stream_item.xml => play_queue_item.xml} | 0 9 files changed, 419 insertions(+), 122 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/playlist/PlayQueueEvent.java rename app/src/main/java/org/schabi/newpipe/playlist/{PlaylistItemBuilder.java => PlayQueueItemBuilder.java} (91%) rename app/src/main/res/layout/{playlist_stream_item.xml => play_queue_item.xml} (100%) diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 3746927be..6e68253f1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -47,9 +47,7 @@ import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; -import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource; import com.google.android.exoplayer2.source.ExtractorMediaSource; -import com.google.android.exoplayer2.source.LoopingMediaSource; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.dash.DashMediaSource; @@ -72,6 +70,7 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene import org.schabi.newpipe.Downloader; import org.schabi.newpipe.R; +import org.schabi.newpipe.playlist.PlayQueue; import java.io.File; import java.text.DecimalFormat; @@ -86,9 +85,9 @@ import java.util.concurrent.atomic.AtomicBoolean; * @author mauriciocolli */ @SuppressWarnings({"WeakerAccess", "unused"}) -public abstract class BasePlayer implements Player.EventListener, AudioManager.OnAudioFocusChangeListener { +public abstract class BasePlayer implements Player.EventListener, + AudioManager.OnAudioFocusChangeListener, PlaybackManager.PlaybackListener { // TODO: Check api version for deprecated audio manager methods - public static final boolean DEBUG = false; public static final String TAG = "BasePlayer"; @@ -117,6 +116,13 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O protected long videoStartPos = -1; protected String uploaderName = ""; + /*////////////////////////////////////////////////////////////////////////// + // Playlist + //////////////////////////////////////////////////////////////////////////*/ + + protected PlaybackManager playbackManager; + protected PlayQueue playQueue; + /*////////////////////////////////////////////////////////////////////////// // Player //////////////////////////////////////////////////////////////////////////*/ @@ -540,6 +546,22 @@ public abstract class BasePlayer implements Player.EventListener, AudioManager.O @Override public void onPositionDiscontinuity() { + int newIndex = simpleExoPlayer.getCurrentWindowIndex(); + + } + + /*////////////////////////////////////////////////////////////////////////// + // Playback Listener + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void block() { + if (currentState != STATE_LOADING) changeState(STATE_LOADING); + } + + @Override + public void unblock() { + if (currentState != STATE_PLAYING) changeState(STATE_PLAYING); } /*////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java index 8b3973a54..3873d7c1c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java @@ -3,31 +3,200 @@ package org.schabi.newpipe.player; import com.google.android.exoplayer2.source.DynamicConcatenatingMediaSource; import com.google.android.exoplayer2.source.MediaSource; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.playlist.PlayQueue; +import org.schabi.newpipe.playlist.PlayQueueEvent; +import org.schabi.newpipe.playlist.PlayQueueItem; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import io.reactivex.Maybe; +import io.reactivex.annotations.NonNull; + public class PlaybackManager { - private DynamicConcatenatingMediaSource source; + private DynamicConcatenatingMediaSource mediaSource; + private List queueSource; + private int sourceIndex; + private PlaybackListener listener; private PlayQueue playQueue; - private int index; - - private List sources; - - public PlaybackManager(PlayQueue playQueue, int index) { - this.source = new DynamicConcatenatingMediaSource(); - - this.playQueue = playQueue; - this.index = index; + private Subscription playQueueReactor; + interface PlaybackListener { + void block(); + void unblock(); + void sync(); + MediaSource sourceOf(StreamInfo info); } - interface OnChangeListener { - void isLoading(); - void isLoaded(); + public PlaybackManager(@NonNull final PlaybackListener listener, + @NonNull final PlayQueue playQueue) { + this.mediaSource = new DynamicConcatenatingMediaSource(); + this.queueSource = Collections.synchronizedList(new ArrayList(10)); + this.sourceIndex = 0; + + this.listener = listener; + this.playQueue = playQueue; + + playQueue.getPlayQueueFlowable().subscribe(getReactor()); + } + + @NonNull + public DynamicConcatenatingMediaSource getMediaSource() { + return mediaSource; + } + + private void reload() { + listener.block(); + load(0); + } + + public void refreshMedia(final int newMediaIndex) { + if (newMediaIndex == sourceIndex) return; + + if (newMediaIndex == sourceIndex + 1) { + playQueue.incrementIndex(); + mediaSource.removeMediaSource(0); + queueSource.remove(0); + } else { + //something went wrong + onInit(); + } + } + + private void removeCurrent() { + listener.block(); + mediaSource.removeMediaSource(0); + queueSource.remove(0); + listener.unblock(); + } + + private Subscription loaderReactor; + + private void load() { + if (mediaSource.getSize() < 5 && queueSource.size() < 5) load(mediaSource.getSize()); + } + + private void load(final int from) { + clear(from); + + if (loaderReactor != null) loaderReactor.cancel(); + + List> maybes = new ArrayList<>(); + for (int i = from; i < 5; i++) { + final int index = playQueue.getIndex() + i; + final PlayQueueItem item = playQueue.get(index); + queueSource.set(i, item); + maybes.add(item.getStream()); + } + + Maybe.concat(maybes).subscribe(new Subscriber() { + @Override + public void onSubscribe(Subscription s) { + loaderReactor = s; + } + + @Override + public void onNext(StreamInfo streamInfo) { + mediaSource.addMediaSource(listener.sourceOf(streamInfo)); + onLoaded(); + } + + @Override + public void onError(Throwable t) { + playQueue.remove(queueSource.size()); + } + + @Override + public void onComplete() { + } + }); + } + + private void onLoaded() { + if (mediaSource.getSize() > 0 && queueSource.size() > 0) listener.unblock(); + } + + private void onInit() { + listener.block(); + load(); + } + + private void clear(int from) { + listener.block(); + while (mediaSource.getSize() > from) { + queueSource.remove(from); + mediaSource.removeMediaSource(from); + } + listener.unblock(); + } + + private Subscriber getReactor() { + return new Subscriber() { + @Override + public void onSubscribe(@NonNull Subscription d) { + if (playQueueReactor != null) playQueueReactor.cancel(); + playQueueReactor = d; + playQueueReactor.request(1); + } + + @Override + public void onNext(@NonNull PlayQueueEvent event) { + if (playQueue.getStreams().size() - playQueue.getIndex() < 10 && !playQueue.isComplete()) { + listener.block(); + playQueue.fetch(); + } + + switch (event) { + case INIT: + onInit(); + break; + case APPEND: + load(); + break; + case REMOVE_CURRENT: + removeCurrent(); + load(); + break; + case SELECT: + reload(); + break; + case REMOVE: + case SWAP: + load(1); + break; + case CLEAR: + clear(0); + break; + case NEXT: + default: + break; + } + + onLoaded(); + if (playQueueReactor != null) playQueueReactor.request(1); + } + + @Override + public void onError(@NonNull Throwable e) { + + } + + @Override + public void onComplete() { + // Never completes, only canceled + } + }; + } + + public void dispose() { + if (playQueueReactor != null) playQueueReactor.cancel(); } } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java index fba48d82d..3d1831a0e 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java @@ -1,44 +1,45 @@ package org.schabi.newpipe.playlist; import org.schabi.newpipe.extractor.InfoItem; -import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlayListExtractor; import org.schabi.newpipe.extractor.playlist.PlayListInfo; -import org.schabi.newpipe.extractor.playlist.PlayListInfoItem; import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.extractor.stream_info.StreamInfoItem; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.Maybe; import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; public class ExternalPlayQueue extends PlayQueue { - - private final static int LOAD_PROXIMITY = 10; + private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode()); private boolean isComplete; - private AtomicInteger pageNumber; - private StreamingService service; + private String playlistUrl; - private PlayListInfoItem playlist; + private AtomicInteger pageNumber; + private Disposable fetchReactor; - public ExternalPlayQueue(final PlayListInfoItem playlist) { - super(); - this.service = getService(playlist.serviceId); - this.pageNumber = new AtomicInteger(0); - this.playlist = playlist; + public ExternalPlayQueue(final String playlistUrl, + final PlayListInfo info, + final int nextPage, + final int index) { + super(index); + this.service = getService(info.service_id); + this.pageNumber = new AtomicInteger(nextPage); + this.playlistUrl = playlistUrl; - fetch(); + getStreams().addAll(extractPlaylistItems(info)); } @Override @@ -47,36 +48,25 @@ public class ExternalPlayQueue extends PlayQueue { } @Override - public void load(int index, boolean loadNeighbors) { - if (index > streams.size() || streams.get(index) == null) return; - - streams.get(index).load(); - - if (loadNeighbors) { - int leftBound = index - LOAD_BOUND >= 0 ? index - LOAD_BOUND : 0; - int rightBound = index + LOAD_BOUND < streams.size() ? index + LOAD_BOUND : streams.size() - 1; - - for (int i = leftBound; i < rightBound; i++) { - final PlayQueueItem item = streams.get(i); - if (item != null) item.load(); - } - } + public void load(int index) { + if (index > getStreams().size() || getStreams().get(index) == null) return; + getStreams().get(index).load(); } @Override - public Maybe get(int index) { - if (index > streams.size() || streams.get(index) == null) return Maybe.empty(); - return streams.get(index).getStream(); + public PlayQueueItem get(int index) { + if (index > getStreams().size() || getStreams().get(index) == null) return null; + return getStreams().get(index); } - - public synchronized void fetch() { - final int page = pageNumber.getAndIncrement(); + @Override + public void fetch() { + if (fetchReactor != null && !fetchReactor.isDisposed()) return; final Callable task = new Callable() { @Override public PlayListInfo call() throws Exception { - PlayListExtractor extractor = service.getPlayListExtractorInstance(playlist.getLink(), page); + PlayListExtractor extractor = service.getPlayListExtractorInstance(playlistUrl, pageNumber.get()); return PlayListInfo.getInfo(extractor); } }; @@ -86,18 +76,23 @@ public class ExternalPlayQueue extends PlayQueue { public void accept(PlayListInfo playListInfo) throws Exception { if (!playListInfo.hasNextPage) isComplete = true; - streams.addAll(extractPlaylistItems(playListInfo)); - notifyChange(); + append(extractPlaylistItems(playListInfo)); + pageNumber.incrementAndGet(); } }; - Maybe.fromCallable(task) + fetchReactor = Maybe.fromCallable(task) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .onErrorComplete() .subscribe(onSuccess); } + @Override + public void dispose() { + if (fetchReactor != null) fetchReactor.dispose(); + } + private List extractPlaylistItems(final PlayListInfo info) { List result = new ArrayList<>(); for (final InfoItem stream : info.related_streams) { @@ -107,12 +102,4 @@ public class ExternalPlayQueue extends PlayQueue { } return result; } - - private StreamingService getService(final int serviceId) { - try { - return NewPipe.getService(serviceId); - } catch (ExtractionException e) { - return null; - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index ecf9e578f..87e21cfee 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -2,41 +2,138 @@ package org.schabi.newpipe.playlist; import android.support.annotation.NonNull; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.stream_info.StreamInfo; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; import io.reactivex.Maybe; import io.reactivex.subjects.BehaviorSubject; public abstract class PlayQueue { private final String TAG = "PlayQueue@" + Integer.toHexString(hashCode()); - final int LOAD_BOUND = 2; + private List streams; + private AtomicInteger queueIndex; - protected List streams; - private BehaviorSubject> changeBroadcast; + private BehaviorSubject changeBroadcast; + private Flowable playQueueFlowable; - PlayQueue() { + PlayQueue(final int index) { streams = Collections.synchronizedList(new ArrayList()); + queueIndex = new AtomicInteger(index); + changeBroadcast = BehaviorSubject.create(); + playQueueFlowable = changeBroadcast.startWith(PlayQueueEvent.INIT).toFlowable(BackpressureStrategy.BUFFER); + } + + // a queue is complete if it has loaded all items in an external playlist + // single stream or local queues are always complete + public abstract boolean isComplete(); + + // load in the background the item at index, may do nothing if the queue is incomplete + public abstract void load(int index); + + // load partial queue in the background, does nothing if the queue is complete + public abstract void fetch(); + + // returns a Rx Future to the stream info of the play queue item at index + // may return an empty of the queue is incomplete + public abstract PlayQueueItem get(int index); + + public abstract void dispose(); + + public int size() { + return streams.size(); } @NonNull public List getStreams() { - return streams; + return Collections.unmodifiableList(streams); } - public void notifyChange() { - changeBroadcast.onNext(streams); + @NonNull + public Flowable getPlayQueueFlowable() { + return playQueueFlowable; } - public abstract boolean isComplete(); + private void broadcast(final PlayQueueEvent event) { + changeBroadcast.onNext(event); + } - public abstract void load(int index, boolean loadNeighbors); + public int getIndex() { + return queueIndex.get(); + } - public abstract Maybe get(int index); + public void setIndex(final int index) { + queueIndex.set(index); + broadcast(PlayQueueEvent.SELECT); + } + + public void incrementIndex() { + queueIndex.incrementAndGet(); + broadcast(PlayQueueEvent.NEXT); + } + + protected void append(final PlayQueueItem item) { + streams.add(item); + broadcast(PlayQueueEvent.APPEND); + } + + protected void append(final Collection items) { + streams.addAll(items); + broadcast(PlayQueueEvent.APPEND); + } + + public void remove(final int index) { + if (index < streams.size()) { + streams.remove(index); + broadcast(PlayQueueEvent.REMOVE); + } + } + + protected void clear() { + if (!streams.isEmpty()) { + streams.clear(); + broadcast(PlayQueueEvent.CLEAR); + } + } + + protected void swap(final int source, final int target) { + final List items = streams; + if (source < items.size() && target < items.size()) { + // Swap two items + final PlayQueueItem sourceItem = items.get(source); + final PlayQueueItem targetItem = items.get(target); + + items.set(target, sourceItem); + items.set(source, targetItem); + + // If the current playing index is one of the swapped indices, change that as well + final int index = queueIndex.get(); + if (index == source || index == target) { + final int newIndex = index == source ? target : source; + queueIndex.set(newIndex); + } + + broadcast(PlayQueueEvent.SWAP); + } + } + + protected StreamingService getService(final int serviceId) { + try { + return NewPipe.getService(serviceId); + } catch (ExtractionException e) { + return null; + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java index 662f9f2f1..170311f7d 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueAdapter.java @@ -11,6 +11,9 @@ import org.schabi.newpipe.info_list.StreamInfoItemHolder; import java.util.List; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Consumer; + /** * Created by Christian Schabesberger on 01.08.16. * @@ -34,12 +37,14 @@ import java.util.List; public class PlayQueueAdapter extends RecyclerView.Adapter { private static final String TAG = PlayQueueAdapter.class.toString(); - private final PlaylistItemBuilder playlistItemBuilder; + private final PlayQueueItemBuilder playQueueItemBuilder; private final PlayQueue playQueue; private boolean showFooter = false; private View header = null; private View footer = null; + private Disposable playQueueReactor; + public class HFHolder extends RecyclerView.ViewHolder { public HFHolder(View v) { super(v); @@ -48,66 +53,57 @@ public class PlayQueueAdapter extends RecyclerView.Adapter data) { - if(data != null) { - playQueue.getStreams().addAll(data); - notifyPlaylistChange(); - } + public void add(final List data) { + playQueue.append(data); } - public void addItem(PlayQueueItem data) { - if (data != null) { - playQueue.getStreams().add(data); - notifyPlaylistChange(); - } + public void add(final PlayQueueItem data) { + playQueue.append(data); } - public void removeItem(int index) { - if (index < playQueue.getStreams().size()) { - playQueue.getStreams().remove(index); - notifyPlaylistChange(); - } + public void remove(final int index) { + playQueue.remove(index); } - public void swapItems(int source, int target) { - final List items = playQueue.getStreams(); - if (source < items.size() && target < items.size()) { - final PlayQueueItem sourceItem = items.get(source); - final PlayQueueItem targetItem = items.get(target); - - items.set(target, sourceItem); - items.set(source, targetItem); - - notifyPlaylistChange(); - } + public void swap(final int source, final int target) { + playQueue.swap(source, target); } public void clear() { - if(playQueue.getStreams().isEmpty()) { - return; - } - playQueue.getStreams().clear(); - - notifyPlaylistChange(); + playQueue.clear(); } - private void notifyPlaylistChange() { - playQueue.notifyChange(); - notifyDataSetChanged(); + private Disposable getReactor() { + final Consumer onNext = new Consumer() { + @Override + public void accept(PlayQueueEvent playQueueEvent) throws Exception { + notifyDataSetChanged(); + } + }; + + return playQueue.getPlayQueueFlowable() + .toObservable() + .subscribe(onNext); + } + + public void dispose() { + if (playQueueReactor != null) playQueueReactor.dispose(); } public void setHeader(View header) { @@ -155,7 +151,7 @@ public class PlayQueueAdapter extends RecyclerView.Adapter Date: Thu, 31 Aug 2017 17:47:56 -0700 Subject: [PATCH 057/154] -Added separate events for play queue index removal. --- .../java/org/schabi/newpipe/player/BasePlayer.java | 12 ++++++++++++ .../org/schabi/newpipe/player/PlaybackManager.java | 14 +++++++++----- .../org/schabi/newpipe/playlist/PlayQueue.java | 12 ++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index 6e68253f1..df588df5d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -70,6 +70,7 @@ import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListene import org.schabi.newpipe.Downloader; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.playlist.PlayQueue; import java.io.File; @@ -564,6 +565,17 @@ public abstract class BasePlayer implements Player.EventListener, if (currentState != STATE_PLAYING) changeState(STATE_PLAYING); } + @Override + public void sync(final StreamInfo info) { + + } + + @Override + public MediaSource sourceOf(final StreamInfo info) { + + return null; + } + /*////////////////////////////////////////////////////////////////////////// // General Player //////////////////////////////////////////////////////////////////////////*/ diff --git a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java index 3873d7c1c..72f1daede 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java @@ -31,9 +31,9 @@ public class PlaybackManager { interface PlaybackListener { void block(); void unblock(); - void sync(); - MediaSource sourceOf(StreamInfo info); + void sync(final StreamInfo info); + MediaSource sourceOf(final StreamInfo info); } public PlaybackManager(@NonNull final PlaybackListener listener, @@ -58,6 +58,10 @@ public class PlaybackManager { load(0); } + public void changeSource(final int index) { + + } + public void refreshMedia(final int newMediaIndex) { if (newMediaIndex == sourceIndex) return; @@ -67,7 +71,7 @@ public class PlaybackManager { queueSource.remove(0); } else { //something went wrong - onInit(); + init(); } } @@ -124,7 +128,7 @@ public class PlaybackManager { if (mediaSource.getSize() > 0 && queueSource.size() > 0) listener.unblock(); } - private void onInit() { + private void init() { listener.block(); load(); } @@ -156,7 +160,7 @@ public class PlaybackManager { switch (event) { case INIT: - onInit(); + init(); break; case APPEND: load(); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index 87e21cfee..67adc0cf2 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -5,7 +5,6 @@ import android.support.annotation.NonNull; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.stream_info.StreamInfo; import java.util.ArrayList; import java.util.Collection; @@ -15,7 +14,6 @@ import java.util.concurrent.atomic.AtomicInteger; import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; -import io.reactivex.Maybe; import io.reactivex.subjects.BehaviorSubject; public abstract class PlayQueue { @@ -94,8 +92,14 @@ public abstract class PlayQueue { } public void remove(final int index) { - if (index < streams.size()) { - streams.remove(index); + if (index >= streams.size()) return; + final boolean isCurrent = index == queueIndex.get(); + + streams.remove(index); + + if (isCurrent) { + broadcast(PlayQueueEvent.REMOVE_CURRENT); + } else { broadcast(PlayQueueEvent.REMOVE); } } From b85982301163f9e389ae56c371f60b1d2201e1a2 Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Fri, 1 Sep 2017 12:10:36 -0700 Subject: [PATCH 058/154] -Hooking playback manager and play queue into main video player. --- .../fragments/playlist/PlaylistFragment.java | 34 +++++++++++- .../org/schabi/newpipe/player/BasePlayer.java | 8 +-- .../newpipe/player/MainVideoPlayer.java | 12 +++- .../newpipe/player/PlaybackManager.java | 55 ++++++++++++++----- .../schabi/newpipe/player/VideoPlayer.java | 38 ++++++++++++- .../newpipe/playlist/ExternalPlayQueue.java | 13 +---- .../schabi/newpipe/playlist/PlayQueue.java | 11 ++-- .../newpipe/playlist/PlayQueueItem.java | 5 -- app/src/main/res/layout/playlist_header.xml | 15 +++++ 9 files changed, 147 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java index 032b227e8..dd58b6567 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/playlist/PlaylistFragment.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.fragments.playlist; +import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -17,6 +18,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; @@ -29,14 +31,19 @@ import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.playlist.PlayListExtractor; import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.fragments.BaseFragment; import org.schabi.newpipe.fragments.search.OnScrollBelowItemsListener; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.info_list.InfoListAdapter; +import org.schabi.newpipe.player.BasePlayer; +import org.schabi.newpipe.player.MainVideoPlayer; +import org.schabi.newpipe.player.VideoPlayer; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.Utils; import java.io.IOException; import java.io.Serializable; @@ -78,6 +85,7 @@ public class PlaylistFragment extends BaseFragment { private ImageView headerBannerView; private ImageView headerAvatarView; private TextView headerTitleView; + private Button headerPlayAllButton; /*////////////////////////////////////////////////////////////////////////*/ // Reactors @@ -95,6 +103,15 @@ public class PlaylistFragment extends BaseFragment { return instance; } + public void play(Context context, Class targetClazz) { + Intent mIntent = new Intent(context, targetClazz) + .putExtra("url", playlistUrl) + .putExtra("nextPage", 1) + .putExtra("index", 0) + .putExtra("stream", currentPlaylistInfo); + startActivity(mIntent); + } + /*////////////////////////////////////////////////////////////////////////// // Fragment's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @@ -246,6 +263,9 @@ public class PlaylistFragment extends BaseFragment { headerBannerView = headerRootLayout.findViewById(R.id.playlist_banner_image); headerAvatarView = headerRootLayout.findViewById(R.id.playlist_avatar_view); headerTitleView = headerRootLayout.findViewById(R.id.playlist_title_view); + + headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); + headerPlayAllButton.setVisibility(View.VISIBLE); } protected void initListeners() { @@ -266,6 +286,13 @@ public class PlaylistFragment extends BaseFragment { loadMore(true); } }); + + headerPlayAllButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + play(activity, MainVideoPlayer.class); + } + }); } @@ -434,7 +461,7 @@ public class PlaylistFragment extends BaseFragment { } private void handlePlayListInfo(PlayListInfo info, boolean onlyVideos, boolean addVideos) { - currentPlaylistInfo = info; + if (currentPlaylistInfo == null) currentPlaylistInfo = info; animateView(errorPanel, false, 300); animateView(playlistStreams, true, 200); @@ -468,7 +495,10 @@ public class PlaylistFragment extends BaseFragment { if (!hasNextPage) infoListAdapter.showFooter(false); //if (!listRestored) { - if (addVideos) infoListAdapter.addInfoItemList(info.related_streams); + if (addVideos) { + infoListAdapter.addInfoItemList(info.related_streams); + currentPlaylistInfo.related_streams.addAll(info.related_streams); + } //} } diff --git a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java index df588df5d..17c2f0b5a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -72,6 +72,7 @@ import org.schabi.newpipe.Downloader; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream_info.StreamInfo; import org.schabi.newpipe.playlist.PlayQueue; +import org.schabi.newpipe.util.Utils; import java.io.File; import java.text.DecimalFormat; @@ -257,7 +258,6 @@ public abstract class BasePlayer implements Player.EventListener, changeState(STATE_LOADING); isPrepared = false; - mediaSource = buildMediaSource(url, format); if (simpleExoPlayer.getPlaybackState() != Player.STATE_IDLE) simpleExoPlayer.stop(); if (videoStartPos > 0) simpleExoPlayer.seekTo(videoStartPos); @@ -548,7 +548,7 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void onPositionDiscontinuity() { int newIndex = simpleExoPlayer.getCurrentWindowIndex(); - + playbackManager.refreshMedia(newIndex); } /*////////////////////////////////////////////////////////////////////////// @@ -567,12 +567,12 @@ public abstract class BasePlayer implements Player.EventListener, @Override public void sync(final StreamInfo info) { - + videoTitle = info.title; + channelName = info.uploader; } @Override public MediaSource sourceOf(final StreamInfo info) { - return null; } diff --git a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java index 301200dfc..ebb5b52aa 100644 --- a/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/MainVideoPlayer.java @@ -454,9 +454,15 @@ public class MainVideoPlayer extends Activity { @Override public boolean onDoubleTap(MotionEvent e) { if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); - if (!playerImpl.isPlaying()) return false; - if (e.getX() > playerImpl.getRootView().getWidth() / 2) playerImpl.onFastForward(); - else playerImpl.onFastRewind(); + //if (!playerImpl.isPlaying()) return false; + + if (e.getX() > playerImpl.getRootView().getWidth() / 2) + playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); + //playerImpl.onFastForward(); + else + playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() - 1); + //playerImpl.onFastRewind(); + return true; } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java index 72f1daede..a98d9d3a1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlaybackManager.java @@ -15,10 +15,14 @@ import java.util.Collections; import java.util.List; import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.annotations.NonNull; +import io.reactivex.schedulers.Schedulers; public class PlaybackManager { + private static final int WINDOW_SIZE = 5; + private DynamicConcatenatingMediaSource mediaSource; private List queueSource; private int sourceIndex; @@ -58,8 +62,11 @@ public class PlaybackManager { load(0); } - public void changeSource(final int index) { - + public void changeSource(final MediaSource newSource) { + listener.block(); + this.mediaSource.removeMediaSource(0); + this.mediaSource.addMediaSource(0, newSource); + listener.unblock(); } public void refreshMedia(final int newMediaIndex) { @@ -71,7 +78,7 @@ public class PlaybackManager { queueSource.remove(0); } else { //something went wrong - init(); + reload(); } } @@ -85,7 +92,8 @@ public class PlaybackManager { private Subscription loaderReactor; private void load() { - if (mediaSource.getSize() < 5 && queueSource.size() < 5) load(mediaSource.getSize()); + if (mediaSource.getSize() < WINDOW_SIZE && queueSource.size() < WINDOW_SIZE) + load(mediaSource.getSize()); } private void load(final int from) { @@ -94,23 +102,33 @@ public class PlaybackManager { if (loaderReactor != null) loaderReactor.cancel(); List> maybes = new ArrayList<>(); - for (int i = from; i < 5; i++) { + for (int i = from; i < WINDOW_SIZE; i++) { final int index = playQueue.getIndex() + i; final PlayQueueItem item = playQueue.get(index); - queueSource.set(i, item); + + if (queueSource.size() > i) queueSource.set(i, item); + else queueSource.add(item); + maybes.add(item.getStream()); } - Maybe.concat(maybes).subscribe(new Subscriber() { + Maybe.concat(maybes).subscribe(getSubscriber()); + } + + private Subscriber getSubscriber() { + return new Subscriber() { @Override public void onSubscribe(Subscription s) { + if (loaderReactor != null) loaderReactor.cancel(); loaderReactor = s; + s.request(1); } @Override public void onNext(StreamInfo streamInfo) { mediaSource.addMediaSource(listener.sourceOf(streamInfo)); - onLoaded(); + tryUnblock(); + loaderReactor.request(1); } @Override @@ -120,11 +138,13 @@ public class PlaybackManager { @Override public void onComplete() { + if (loaderReactor != null) loaderReactor.cancel(); + loaderReactor = null; } - }); + }; } - private void onLoaded() { + private void tryUnblock() { if (mediaSource.getSize() > 0 && queueSource.size() > 0) listener.unblock(); } @@ -134,11 +154,15 @@ public class PlaybackManager { } private void clear(int from) { - listener.block(); while (mediaSource.getSize() > from) { queueSource.remove(from); mediaSource.removeMediaSource(from); } + } + + private void clear() { + listener.block(); + clear(0); listener.unblock(); } @@ -153,7 +177,7 @@ public class PlaybackManager { @Override public void onNext(@NonNull PlayQueueEvent event) { - if (playQueue.getStreams().size() - playQueue.getIndex() < 10 && !playQueue.isComplete()) { + if (playQueue.getStreams().size() - playQueue.getIndex() < WINDOW_SIZE && !playQueue.isComplete()) { listener.block(); playQueue.fetch(); } @@ -177,14 +201,14 @@ public class PlaybackManager { load(1); break; case CLEAR: - clear(0); + clear(); break; case NEXT: default: break; } - onLoaded(); + tryUnblock(); if (playQueueReactor != null) playQueueReactor.request(1); } @@ -195,12 +219,13 @@ public class PlaybackManager { @Override public void onComplete() { - // Never completes, only canceled + dispose(); } }; } public void dispose() { if (playQueueReactor != null) playQueueReactor.cancel(); + playQueueReactor = null; } } diff --git a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java index fa25cc957..644deaf32 100644 --- a/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/VideoPlayer.java @@ -56,7 +56,11 @@ import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.StreamInfo; +import org.schabi.newpipe.extractor.playlist.PlayListInfo; +import org.schabi.newpipe.playlist.ExternalPlayQueue; import org.schabi.newpipe.util.AnimationUtils; +import org.schabi.newpipe.util.Utils; import java.io.Serializable; import java.util.ArrayList; @@ -198,7 +202,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. } @SuppressWarnings("unchecked") - public void handleIntent(Intent intent) { + public void handleIntent2(Intent intent) { super.handleIntent(intent); if (DEBUG) Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); if (intent == null) return; @@ -217,6 +221,38 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer. play(true); } + @Override + public MediaSource sourceOf(final StreamInfo info) { + videoStreamsList = Utils.getSortedStreamVideosList(context, info.video_streams, info.video_only_streams, false); + videoOnlyAudioStream = Utils.getHighestQualityAudio(info.audio_streams); + + return buildMediaSource(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format)); + } + + @Override + public void unblock() { + play(true); + super.unblock(); + } + + public void handleIntent(Intent intent) { + if (intent == null) return; + + selectedIndexStream = 0; + + String url = intent.getStringExtra("url"); + int nextPage = intent.getIntExtra("nextPage", 0); + int index = intent.getIntExtra("index", 0); + + PlayListInfo info; + Serializable serializable = intent.getSerializableExtra("stream"); + if (serializable instanceof PlayListInfo) info = (PlayListInfo) serializable; + else return; + + playQueue = new ExternalPlayQueue(url, info, nextPage, index); + playbackManager = new PlaybackManager(this, playQueue); + mediaSource = playbackManager.getMediaSource(); + } public void play(boolean autoPlay) { playUrl(getSelectedVideoStream().url, MediaFormat.getSuffixById(getSelectedVideoStream().format), autoPlay); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java index 3d1831a0e..4fab68a1b 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/ExternalPlayQueue.java @@ -34,12 +34,11 @@ public class ExternalPlayQueue extends PlayQueue { final PlayListInfo info, final int nextPage, final int index) { - super(index); + super(index, extractPlaylistItems(info)); + this.service = getService(info.service_id); this.pageNumber = new AtomicInteger(nextPage); this.playlistUrl = playlistUrl; - - getStreams().addAll(extractPlaylistItems(info)); } @Override @@ -47,12 +46,6 @@ public class ExternalPlayQueue extends PlayQueue { return isComplete; } - @Override - public void load(int index) { - if (index > getStreams().size() || getStreams().get(index) == null) return; - getStreams().get(index).load(); - } - @Override public PlayQueueItem get(int index) { if (index > getStreams().size() || getStreams().get(index) == null) return null; @@ -93,7 +86,7 @@ public class ExternalPlayQueue extends PlayQueue { if (fetchReactor != null) fetchReactor.dispose(); } - private List extractPlaylistItems(final PlayListInfo info) { + private static List extractPlaylistItems(final PlayListInfo info) { List result = new ArrayList<>(); for (final InfoItem stream : info.related_streams) { if (stream instanceof StreamInfoItem) { diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java index 67adc0cf2..99f46261e 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueue.java @@ -25,8 +25,14 @@ public abstract class PlayQueue { private BehaviorSubject changeBroadcast; private Flowable playQueueFlowable; - PlayQueue(final int index) { + PlayQueue() { + this(0, Collections.emptyList()); + } + + PlayQueue(final int index, final List startWith) { streams = Collections.synchronizedList(new ArrayList()); + streams.addAll(startWith); + queueIndex = new AtomicInteger(index); changeBroadcast = BehaviorSubject.create(); @@ -37,9 +43,6 @@ public abstract class PlayQueue { // single stream or local queues are always complete public abstract boolean isComplete(); - // load in the background the item at index, may do nothing if the queue is incomplete - public abstract void load(int index); - // load partial queue in the background, does nothing if the queue is complete public abstract void fetch(); diff --git a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java index 0f2cb3202..a028d33e1 100644 --- a/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java +++ b/app/src/main/java/org/schabi/newpipe/playlist/PlayQueueItem.java @@ -69,10 +69,6 @@ public class PlayQueueItem { return stream; } - public void load() { - stream.subscribe(); - } - @NonNull private Maybe getInfo() { final Callable task = new Callable() { @@ -101,7 +97,6 @@ public class PlayQueueItem { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnError(onError) - .onErrorComplete() .doOnComplete(onComplete) .cache(); } diff --git a/app/src/main/res/layout/playlist_header.xml b/app/src/main/res/layout/playlist_header.xml index 192363359..7de6f79a9 100644 --- a/app/src/main/res/layout/playlist_header.xml +++ b/app/src/main/res/layout/playlist_header.xml @@ -78,4 +78,19 @@ tools:ignore="RtlHardcoded" tools:text="234 videos"/> +