From 71f1bbdcc13a0c05ea48d7acd1c1e63046f0a02c Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Sun, 27 Oct 2019 23:35:51 -0300 Subject: [PATCH 1/3] Use new Localization and Downloader implementations from extractor --- app/build.gradle | 2 +- app/proguard-rules.pro | 2 + .../java/org/schabi/newpipe/DebugApp.java | 4 +- app/src/main/java/org/schabi/newpipe/App.java | 9 +- .../java/org/schabi/newpipe/Downloader.java | 296 ------------------ .../org/schabi/newpipe/DownloaderImpl.java | 156 +++++++++ .../org/schabi/newpipe/ImageDownloader.java | 2 +- .../org/schabi/newpipe/ReCaptchaActivity.java | 2 +- .../newpipe/download/DownloadDialog.java | 39 +-- .../fragments/detail/VideoDetailFragment.java | 10 +- .../holder/CommentsMiniInfoItemHolder.java | 13 +- .../holder/StreamInfoItemHolder.java | 6 +- .../org/schabi/newpipe/player/BasePlayer.java | 4 +- .../settings/ContentSettingsFragment.java | 35 ++- .../schabi/newpipe/util/ExtractorHelper.java | 2 +- .../org/schabi/newpipe/util/Localization.java | 44 +-- .../newpipe/util/StreamItemAdapter.java | 4 +- .../us/shandian/giga/get/DownloadMission.java | 4 +- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/content_settings.xml | 18 +- 20 files changed, 260 insertions(+), 394 deletions(-) delete mode 100644 app/src/main/java/org/schabi/newpipe/Downloader.java create mode 100644 app/src/main/java/org/schabi/newpipe/DownloaderImpl.java diff --git a/app/build.gradle b/app/build.gradle index 3eff64138..bedfe9f4f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,7 +62,7 @@ dependencies { exclude module: 'support-annotations' }) - implementation 'com.github.teamnewpipe:NewPipeExtractor:v0.17.4' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:5c420340ceb39' testImplementation 'junit:junit:4.12' testImplementation 'org.mockito:mockito-core:2.23.0' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 20596c6eb..6b56e1c75 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -17,6 +17,8 @@ #} -dontobfuscate +-keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; } + -keep class org.mozilla.javascript.** { *; } -keep class org.mozilla.classfile.ClassFileWriter diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.java b/app/src/debug/java/org/schabi/newpipe/DebugApp.java index 154fb5a8c..66f73d1e9 100644 --- a/app/src/debug/java/org/schabi/newpipe/DebugApp.java +++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.java @@ -15,7 +15,7 @@ import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.LeakDirectoryProvider; import com.squareup.leakcanary.RefWatcher; -import org.schabi.newpipe.extractor.Downloader; +import org.schabi.newpipe.extractor.downloader.Downloader; import java.io.File; import java.util.concurrent.TimeUnit; @@ -39,7 +39,7 @@ public class DebugApp extends App { @Override protected Downloader getDownloader() { - return org.schabi.newpipe.Downloader.init(new OkHttpClient.Builder() + return DownloaderImpl.init(new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor())); } diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index bdf1e7837..95e56c620 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -21,13 +21,14 @@ import org.acra.config.ACRAConfiguration; import org.acra.config.ACRAConfigurationException; import org.acra.config.ConfigurationBuilder; import org.acra.sender.ReportSenderFactory; -import org.schabi.newpipe.extractor.Downloader; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.report.AcraReportSenderFactory; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.settings.SettingsActivity; import org.schabi.newpipe.util.ExtractorHelper; +import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.StateSaver; import java.io.IOException; @@ -95,7 +96,9 @@ public class App extends Application { SettingsActivity.initSettings(this); NewPipe.init(getDownloader(), - org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(this)); + Localization.getPreferredLocalization(this), + Localization.getPreferredContentCountry(this)); + StateSaver.init(this); initNotificationChannel(); @@ -109,7 +112,7 @@ public class App extends Application { } protected Downloader getDownloader() { - return org.schabi.newpipe.Downloader.init(null); + return DownloaderImpl.init(null); } private void configureRxJavaErrorHandler() { diff --git a/app/src/main/java/org/schabi/newpipe/Downloader.java b/app/src/main/java/org/schabi/newpipe/Downloader.java deleted file mode 100644 index a8a581d1b..000000000 --- a/app/src/main/java/org/schabi/newpipe/Downloader.java +++ /dev/null @@ -1,296 +0,0 @@ -package org.schabi.newpipe; - -import androidx.annotation.Nullable; -import android.text.TextUtils; - -import org.schabi.newpipe.extractor.DownloadRequest; -import org.schabi.newpipe.extractor.DownloadResponse; -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; -import org.schabi.newpipe.extractor.utils.Localization; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; - - -/* - * Created by Christian Schabesberger on 28.01.16. - * - * Copyright (C) Christian Schabesberger 2016 - * Downloader.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 Downloader implements org.schabi.newpipe.extractor.Downloader { - public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"; - - private static Downloader instance; - private String mCookies; - private final OkHttpClient client; - - private Downloader(OkHttpClient.Builder builder) { - this.client = builder - .readTimeout(30, TimeUnit.SECONDS) - //.cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"), 16 * 1024 * 1024)) - .build(); - } - - /** - * It's recommended to call exactly once in the entire lifetime of the application. - * - * @param builder if null, default builder will be used - */ - public static Downloader init(@Nullable OkHttpClient.Builder builder) { - return instance = new Downloader(builder != null ? builder : new OkHttpClient.Builder()); - } - - public static Downloader getInstance() { - return instance; - } - - public String getCookies() { - return mCookies; - } - - public void setCookies(String cookies) { - mCookies = cookies; - } - - /** - * Get the size of the content that the url is pointing by firing a HEAD request. - * - * @param url an url pointing to the content - * @return the size of the content, in bytes - */ - public long getContentLength(String url) throws IOException { - Response response = null; - try { - final Request request = new Request.Builder() - .head().url(url) - .addHeader("User-Agent", USER_AGENT) - .build(); - response = client.newCall(request).execute(); - - String contentLength = response.header("Content-Length"); - return contentLength == null ? -1 : Long.parseLong(contentLength); - } catch (NumberFormatException e) { - throw new IOException("Invalid content length", e); - } finally { - if (response != null) { - response.close(); - } - } - } - - /** - * Download the text file at the supplied URL as in download(String), - * but set the HTTP header field "Accept-Language" to the supplied string. - * - * @param siteUrl the URL of the text file to return the contents of - * @param localization the language and country (usually a 2-character code) to set - * @return the contents of the specified text file - */ - @Override - public String download(String siteUrl, Localization localization) throws IOException, ReCaptchaException { - Map requestProperties = new HashMap<>(); - requestProperties.put("Accept-Language", localization.getLanguage()); - return download(siteUrl, requestProperties); - } - - /** - * Download the text file at the supplied URL as in download(String), - * but set the HTTP headers included in the customProperties map. - * - * @param siteUrl the URL of the text file to return the contents of - * @param customProperties set request header properties - * @return the contents of the specified text file - * @throws IOException - */ - @Override - public String download(String siteUrl, Map customProperties) throws IOException, ReCaptchaException { - return getBody(siteUrl, customProperties).string(); - } - - public InputStream stream(String siteUrl) throws IOException { - try { - return getBody(siteUrl, Collections.emptyMap()).byteStream(); - } catch (ReCaptchaException e) { - throw new IOException(e.getMessage(), e.getCause()); - } - } - - private ResponseBody getBody(String siteUrl, Map customProperties) throws IOException, ReCaptchaException { - final Request.Builder requestBuilder = new Request.Builder() - .method("GET", null).url(siteUrl); - - for (Map.Entry header : customProperties.entrySet()) { - requestBuilder.addHeader(header.getKey(), header.getValue()); - } - - if (!customProperties.containsKey("User-Agent")) { - requestBuilder.header("User-Agent", USER_AGENT); - } - - if (!TextUtils.isEmpty(mCookies)) { - requestBuilder.addHeader("Cookie", mCookies); - } - - final Request request = requestBuilder.build(); - final Response response = client.newCall(request).execute(); - final ResponseBody body = response.body(); - - if (response.code() == 429) { - throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); - } - - if (body == null) { - response.close(); - return null; - } - - return body; - } - - /** - * Download (via HTTP) the text file located at the supplied URL, and return its contents. - * Primarily intended for downloading web pages. - * - * @param siteUrl the URL of the text file to download - * @return the contents of the specified text file - */ - @Override - public String download(String siteUrl) throws IOException, ReCaptchaException { - return download(siteUrl, Collections.emptyMap()); - } - - - @Override - public DownloadResponse get(String siteUrl, DownloadRequest request) throws IOException, ReCaptchaException { - final Request.Builder requestBuilder = new Request.Builder() - .method("GET", null).url(siteUrl); - - Map> requestHeaders = request.getRequestHeaders(); - // set custom headers in request - for (Map.Entry> pair : requestHeaders.entrySet()) { - for(String value : pair.getValue()){ - requestBuilder.addHeader(pair.getKey(), value); - } - } - - if (!requestHeaders.containsKey("User-Agent")) { - requestBuilder.header("User-Agent", USER_AGENT); - } - - if (!TextUtils.isEmpty(mCookies)) { - requestBuilder.addHeader("Cookie", mCookies); - } - - final Request okRequest = requestBuilder.build(); - final Response response = client.newCall(okRequest).execute(); - final ResponseBody body = response.body(); - - if (response.code() == 429) { - throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); - } - - if (body == null) { - response.close(); - return null; - } - - return new DownloadResponse(response.code(), body.string(), response.headers().toMultimap()); - } - - @Override - public DownloadResponse get(String siteUrl) throws IOException, ReCaptchaException { - return get(siteUrl, DownloadRequest.emptyRequest); - } - - @Override - public DownloadResponse post(String siteUrl, DownloadRequest request) throws IOException, ReCaptchaException { - - Map> requestHeaders = request.getRequestHeaders(); - if(null == requestHeaders.get("Content-Type") || requestHeaders.get("Content-Type").isEmpty()){ - // content type header is required. maybe throw an exception here - return null; - } - - String contentType = requestHeaders.get("Content-Type").get(0); - - RequestBody okRequestBody = null; - if (null != request.getRequestBody()) { - okRequestBody = RequestBody.create(MediaType.parse(contentType), request.getRequestBody()); - } - final Request.Builder requestBuilder = new Request.Builder() - .method("POST", okRequestBody).url(siteUrl); - - // set custom headers in request - for (Map.Entry> pair : requestHeaders.entrySet()) { - for(String value : pair.getValue()){ - requestBuilder.addHeader(pair.getKey(), value); - } - } - - if (!requestHeaders.containsKey("User-Agent")) { - requestBuilder.header("User-Agent", USER_AGENT); - } - - if (!TextUtils.isEmpty(mCookies)) { - requestBuilder.addHeader("Cookie", mCookies); - } - - final Request okRequest = requestBuilder.build(); - final Response response = client.newCall(okRequest).execute(); - final ResponseBody body = response.body(); - - if (response.code() == 429) { - throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); - } - - if (body == null) { - response.close(); - return null; - } - - return new DownloadResponse(response.code(), body.string(), response.headers().toMultimap()); - } - - @Override - public DownloadResponse head(String siteUrl) throws IOException, ReCaptchaException { - final Request request = new Request.Builder() - .head().url(siteUrl) - .addHeader("User-Agent", USER_AGENT) - .build(); - final Response response = client.newCall(request).execute(); - - if (response.code() == 429) { - throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); - } - - return new DownloadResponse(response.code(), null, response.headers().toMultimap()); - } - -} \ No newline at end of file diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java new file mode 100644 index 000000000..5738cf61a --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -0,0 +1,156 @@ +package org.schabi.newpipe; + +import android.text.TextUtils; + +import org.schabi.newpipe.extractor.downloader.Downloader; +import org.schabi.newpipe.extractor.downloader.Request; +import org.schabi.newpipe.extractor.downloader.Response; +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; + +import androidx.annotation.Nullable; +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; + +public class DownloaderImpl extends Downloader { + public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0"; + + private static DownloaderImpl instance; + private String mCookies; + private OkHttpClient client; + + private DownloaderImpl(OkHttpClient.Builder builder) { + this.client = builder + .readTimeout(30, TimeUnit.SECONDS) + //.cache(new Cache(new File(context.getExternalCacheDir(), "okhttp"), 16 * 1024 * 1024)) + .build(); + } + + /** + * It's recommended to call exactly once in the entire lifetime of the application. + * + * @param builder if null, default builder will be used + */ + public static DownloaderImpl init(@Nullable OkHttpClient.Builder builder) { + return instance = new DownloaderImpl(builder != null ? builder : new OkHttpClient.Builder()); + } + + public static DownloaderImpl getInstance() { + return instance; + } + + public String getCookies() { + return mCookies; + } + + public void setCookies(String cookies) { + mCookies = cookies; + } + + /** + * Get the size of the content that the url is pointing by firing a HEAD request. + * + * @param url an url pointing to the content + * @return the size of the content, in bytes + */ + public long getContentLength(String url) throws IOException { + try { + final Response response = head(url); + return Long.parseLong(response.getHeader("Content-Length")); + } catch (NumberFormatException e) { + throw new IOException("Invalid content length", e); + } catch (ReCaptchaException e) { + throw new IOException(e); + } + } + + public InputStream stream(String siteUrl) throws IOException { + try { + final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder() + .method("GET", null).url(siteUrl) + .addHeader("User-Agent", USER_AGENT); + + if (!TextUtils.isEmpty(mCookies)) { + requestBuilder.addHeader("Cookie", mCookies); + } + + final okhttp3.Request request = requestBuilder.build(); + final okhttp3.Response response = client.newCall(request).execute(); + final ResponseBody body = response.body(); + + if (response.code() == 429) { + throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); + } + + if (body == null) { + response.close(); + return null; + } + + return body.byteStream(); + } catch (ReCaptchaException e) { + throw new IOException(e.getMessage(), e.getCause()); + } + } + + @Override + public Response execute(@Nonnull Request request) throws IOException, ReCaptchaException { + final String httpMethod = request.httpMethod(); + final String url = request.url(); + final Map> headers = request.headers(); + final byte[] dataToSend = request.dataToSend(); + + RequestBody requestBody = null; + if (dataToSend != null) { + requestBody = RequestBody.create(null, dataToSend); + } + + final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder() + .method(httpMethod, requestBody).url(url) + .addHeader("User-Agent", USER_AGENT); + + if (!TextUtils.isEmpty(mCookies)) { + requestBuilder.addHeader("Cookie", mCookies); + } + + for (Map.Entry> pair : headers.entrySet()) { + final String headerName = pair.getKey(); + final List headerValueList = pair.getValue(); + + if (headerValueList.size() > 1) { + requestBuilder.removeHeader(headerName); + for (String headerValue : headerValueList) { + requestBuilder.addHeader(headerName, headerValue); + } + } else if (headerValueList.size() == 1) { + requestBuilder.header(headerName, headerValueList.get(0)); + } + + } + + final okhttp3.Response response = client.newCall(requestBuilder.build()).execute(); + + if (response.code() == 429) { + response.close(); + + throw new ReCaptchaException("reCaptcha Challenge requested", url); + } + + final ResponseBody body = response.body(); + String responseBodyToReturn = null; + + if (body != null) { + responseBodyToReturn = body.string(); + } + + return new Response(response.code(), response.message(), response.headers().toMultimap(), responseBodyToReturn); + } +} diff --git a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java index eb5e92e88..dfb7d3276 100644 --- a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java +++ b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java @@ -40,7 +40,7 @@ public class ImageDownloader extends BaseImageDownloader { } protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException { - final Downloader downloader = (Downloader) NewPipe.getDownloader(); + final DownloaderImpl downloader = (DownloaderImpl) NewPipe.getDownloader(); return downloader.stream(imageUri); } } diff --git a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java index 7f6af89c1..0a2d51b53 100644 --- a/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ReCaptchaActivity.java @@ -112,7 +112,7 @@ public class ReCaptchaActivity extends AppCompatActivity { // find cookies : s_gl & goojf and Add cookies to Downloader if (find_access_cookies(cookies)) { // Give cookies to Downloader class - Downloader.getInstance().setCookies(mCookies); + DownloaderImpl.getInstance().setCookies(mCookies); // Closing activity and return to parent setResult(RESULT_OK); diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index 1d536ea1a..59bffa933 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -40,12 +40,12 @@ import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.stream.VideoStream; -import org.schabi.newpipe.extractor.utils.Localization; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.settings.NewPipeSettings; @@ -488,35 +488,24 @@ public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheck } private int getSubtitleIndexBy(List streams) { - Localization loc = NewPipe.getPreferredLocalization(); + final Localization preferredLocalization = NewPipe.getPreferredLocalization(); + int candidate = 0; for (int i = 0; i < streams.size(); i++) { - Locale streamLocale = streams.get(i).getLocale(); - String tag = streamLocale.getLanguage().concat("-").concat(streamLocale.getCountry()); - if (tag.equalsIgnoreCase(loc.getLanguage())) { - return i; + final Locale streamLocale = streams.get(i).getLocale(); + + final boolean languageEquals = streamLocale.getLanguage() != null && preferredLocalization.getLanguageCode() != null && + streamLocale.getLanguage().equals(new Locale(preferredLocalization.getLanguageCode()).getLanguage()); + final boolean countryEquals = streamLocale.getCountry() != null && streamLocale.getCountry().equals(preferredLocalization.getCountryCode()); + + if (languageEquals) { + if (countryEquals) return i; + + candidate = i; } } - // fallback - // 1st loop match country & language - // 2nd loop match language only - int index = loc.getLanguage().indexOf("-"); - String lang = index > 0 ? loc.getLanguage().substring(0, index) : loc.getLanguage(); - - for (int j = 0; j < 2; j++) { - for (int i = 0; i < streams.size(); i++) { - Locale streamLocale = streams.get(i).getLocale(); - - if (streamLocale.getLanguage().equalsIgnoreCase(lang)) { - if (j > 0 || streamLocale.getCountry().equalsIgnoreCase(loc.getCountry())) { - return i; - } - } - } - } - - return 0; + return candidate; } StoredDirectoryHelper mainStorageAudio = null; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 37d8851ea..915ed59cc 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1120,9 +1120,15 @@ public class VideoDetailFragment videoTitleToggleArrow.setVisibility(View.VISIBLE); videoTitleToggleArrow.setImageResource(R.drawable.arrow_down); videoDescriptionRootLayout.setVisibility(View.GONE); - if (!TextUtils.isEmpty(info.getUploadDate())) { - videoUploadDateView.setText(Localization.localizeDate(activity, info.getUploadDate())); + + if (info.getUploadDate() != null) { + videoUploadDateView.setText(Localization.localizeUploadDate(activity, info.getUploadDate().date().getTime())); + videoUploadDateView.setVisibility(View.VISIBLE); + } else { + videoUploadDateView.setText(null); + videoUploadDateView.setVisibility(View.GONE); } + prepareDescription(info.getDescription()); updateProgressInfo(info); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index f2bf5df39..6741e4403 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -14,6 +14,7 @@ import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.util.CommentTextOnTouchListener; import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import java.util.regex.Matcher; @@ -101,10 +102,18 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { ellipsize(); } - if (null != item.getLikeCount()) { + if (item.getLikeCount() >= 0) { itemLikesCountView.setText(String.valueOf(item.getLikeCount())); + } else { + itemLikesCountView.setText("-"); + } + + if (item.getPublishedTime() != null) { + itemPublishedTime.setText(Localization + .formatDate(item.getPublishedTime().date().getTime())); + } else { + itemPublishedTime.setText(item.getTextualPublishedTime()); } - itemPublishedTime.setText(item.getPublishedTime()); itemView.setOnClickListener(view -> { toggleEllipsize(); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index ea058bc0e..10c81a200 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -55,11 +55,11 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { if (infoItem.getViewCount() >= 0) { viewsAndDate = Localization.shortViewCount(itemBuilder.getContext(), infoItem.getViewCount()); } - if (!TextUtils.isEmpty(infoItem.getUploadDate())) { + if (!TextUtils.isEmpty(infoItem.getTextualUploadDate())) { if (viewsAndDate.isEmpty()) { - viewsAndDate = infoItem.getUploadDate(); + viewsAndDate = infoItem.getTextualUploadDate(); } else { - viewsAndDate += " • " + infoItem.getUploadDate(); + viewsAndDate += " • " + infoItem.getTextualUploadDate(); } } return viewsAndDate; 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 b3c5716bc..a07afcea9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BasePlayer.java @@ -55,7 +55,7 @@ import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import org.schabi.newpipe.BuildConfig; -import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.local.history.HistoryRecordManager; @@ -209,7 +209,7 @@ public abstract class BasePlayer implements this.progressUpdateReactor = new SerialDisposable(); this.databaseUpdateReactor = new CompositeDisposable(); - final String userAgent = Downloader.USER_AGENT; + final String userAgent = DownloaderImpl.USER_AGENT; final DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(context).build(); this.dataSource = new PlayerDataSource(context, userAgent, bandwidthMeter); 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 d05c23564..39c1d890e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -17,8 +17,8 @@ import com.nononsenseapps.filepicker.Utils; import com.nostra13.universalimageloader.core.ImageLoader; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.utils.Localization; +import org.schabi.newpipe.extractor.localization.ContentCountry; +import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.report.ErrorActivity; import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.util.FilePickerActivityHelper; @@ -53,10 +53,16 @@ public class ContentSettingsFragment extends BasePreferenceFragment { private String thumbnailLoadToggleKey; + private Localization initialSelectedLocalization; + private ContentCountry initialSelectedContentCountry; + @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); thumbnailLoadToggleKey = getString(R.string.download_thumbnail_key); + + initialSelectedLocalization = org.schabi.newpipe.util.Localization.getPreferredLocalization(requireContext()); + initialSelectedContentCountry = org.schabi.newpipe.util.Localization.getPreferredContentCountry(requireContext()); } @Override @@ -108,20 +114,21 @@ public class ContentSettingsFragment extends BasePreferenceFragment { startActivityForResult(i, REQUEST_EXPORT_PATH); return true; }); + } - Preference setPreferredLanguage = findPreference(getString(R.string.content_language_key)); - setPreferredLanguage.setOnPreferenceChangeListener((Preference p, Object newLanguage) -> { - Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity()); - NewPipe.setLocalization(new Localization(oldLocal.getCountry(), (String) newLanguage)); - return true; - }); + @Override + public void onDestroy() { + super.onDestroy(); - Preference setPreferredCountry = findPreference(getString(R.string.content_country_key)); - setPreferredCountry.setOnPreferenceChangeListener((Preference p, Object newCountry) -> { - Localization oldLocal = org.schabi.newpipe.util.Localization.getPreferredExtractorLocal(getActivity()); - NewPipe.setLocalization(new Localization((String) newCountry, oldLocal.getLanguage())); - return true; - }); + final Localization selectedLocalization = org.schabi.newpipe.util.Localization + .getPreferredLocalization(requireContext()); + final ContentCountry selectedContentCountry = org.schabi.newpipe.util.Localization + .getPreferredContentCountry(requireContext()); + + if (!selectedLocalization.equals(initialSelectedLocalization) + || !selectedContentCountry.equals(initialSelectedContentCountry)) { + Toast.makeText(requireContext(), R.string.localization_changes_requires_app_restart, Toast.LENGTH_LONG).show(); + } } @Override 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 c4471942e..0cebe5af3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -32,7 +32,7 @@ import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.SuggestionExtractor; +import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 08c9c6d98..6835a48bd 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -10,6 +10,7 @@ import androidx.annotation.StringRes; import android.text.TextUtils; import org.schabi.newpipe.R; +import org.schabi.newpipe.extractor.localization.ContentCountry; import java.text.DateFormat; import java.text.NumberFormat; @@ -69,16 +70,18 @@ public class Localization { return stringBuilder.toString(); } - public static org.schabi.newpipe.extractor.utils.Localization getPreferredExtractorLocal(Context context) { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + public static org.schabi.newpipe.extractor.localization.Localization getPreferredLocalization(final Context context) { + final String contentLanguage = PreferenceManager + .getDefaultSharedPreferences(context) + .getString(context.getString(R.string.content_language_key), context.getString(R.string.default_language_value)); + return org.schabi.newpipe.extractor.localization.Localization.fromLocalizationCode(contentLanguage); + } - String languageCode = sp.getString(context.getString(R.string.content_language_key), - context.getString(R.string.default_language_value)); - - String countryCode = sp.getString(context.getString(R.string.content_country_key), - context.getString(R.string.default_country_value)); - - return new org.schabi.newpipe.extractor.utils.Localization(countryCode, languageCode); + public static ContentCountry getPreferredContentCountry(final Context context) { + final String contentCountry = PreferenceManager + .getDefaultSharedPreferences(context) + .getString(context.getString(R.string.content_country_key), context.getString(R.string.default_country_value)); + return new ContentCountry(contentCountry); } public static Locale getPreferredLocale(Context context) { @@ -106,27 +109,12 @@ public class Localization { return nf.format(number); } - private static String formatDate(Context context, String date) { - Locale locale = getPreferredLocale(context); - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); - Date datum = null; - try { - datum = formatter.parse(date); - } catch (ParseException e) { - e.printStackTrace(); - } - - DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, locale); - - return df.format(datum); + public static String formatDate(Date date) { + return DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()).format(date); } - public static String localizeDate(Context context, String date) { - Resources res = context.getResources(); - String dateString = res.getString(R.string.upload_date_text); - - String formattedDate = formatDate(context, date); - return String.format(dateString, formattedDate); + public static String localizeUploadDate(Context context, Date date) { + return context.getString(R.string.upload_date_text, formatDate(date)); } public static String localizeViewCount(Context context, long viewCount) { diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java index 49a7125ed..312c47263 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java @@ -10,7 +10,7 @@ import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; -import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; @@ -182,7 +182,7 @@ public class StreamItemAdapter extends BaseA continue; } - final long contentLength = Downloader.getInstance().getContentLength(stream.getUrl()); + final long contentLength = DownloaderImpl.getInstance().getContentLength(stream.getUrl()); streamsWrapper.setSize(stream, contentLength); hasChanged = true; } diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMission.java b/app/src/main/java/us/shandian/giga/get/DownloadMission.java index 815ad9b65..d78f8e32b 100644 --- a/app/src/main/java/us/shandian/giga/get/DownloadMission.java +++ b/app/src/main/java/us/shandian/giga/get/DownloadMission.java @@ -5,7 +5,7 @@ import android.util.Log; import androidx.annotation.Nullable; -import org.schabi.newpipe.Downloader; +import org.schabi.newpipe.DownloaderImpl; import java.io.File; import java.io.FileNotFoundException; @@ -212,7 +212,7 @@ public class DownloadMission extends Mission { HttpURLConnection openConnection(String url, int threadId, long rangeStart, long rangeEnd) throws IOException { HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); conn.setInstanceFollowRedirects(true); - conn.setRequestProperty("User-Agent", Downloader.USER_AGENT); + conn.setRequestProperty("User-Agent", DownloaderImpl.USER_AGENT); conn.setRequestProperty("Accept", "*/*"); // BUG workaround: switching between networks can freeze the download forever diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 07ffdb292..de58d14bd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -380,6 +380,8 @@ This will override your current setup. Do you want to also import settings? Could not load comments + Localization changes will not take effect until the app is restarted + Kiosk Trending diff --git a/app/src/main/res/xml/content_settings.xml b/app/src/main/res/xml/content_settings.xml index 1a1a39e21..596852bb6 100644 --- a/app/src/main/res/xml/content_settings.xml +++ b/app/src/main/res/xml/content_settings.xml @@ -3,15 +3,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:title="@string/content"> - - + + Date: Sun, 27 Oct 2019 23:37:36 -0300 Subject: [PATCH 2/3] Show proper text for live streams watching/listening count --- .../fragments/detail/VideoDetailFragment.java | 8 +++++++- .../info_list/holder/StreamInfoItemHolder.java | 9 ++++++++- .../java/org/schabi/newpipe/util/Localization.java | 8 ++++++++ app/src/main/res/values/strings.xml | 13 +++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 915ed59cc..14e989625 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1067,7 +1067,13 @@ public class VideoDetailFragment uploaderThumb.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.buddy)); if (info.getViewCount() >= 0) { - videoCountView.setText(Localization.localizeViewCount(activity, info.getViewCount())); + if (info.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) { + videoCountView.setText(Localization.listeningCount(activity, info.getViewCount())); + } else if (info.getStreamType().equals(StreamType.LIVE_STREAM)) { + videoCountView.setText(Localization.watchingCount(activity, info.getViewCount())); + } else { + videoCountView.setText(Localization.localizeViewCount(activity, info.getViewCount())); + } videoCountView.setVisibility(View.VISIBLE); } else { videoCountView.setVisibility(View.GONE); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index 10c81a200..6b54d0168 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -7,6 +7,7 @@ import android.widget.TextView; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; @@ -53,7 +54,13 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { private String getStreamInfoDetailLine(final StreamInfoItem infoItem) { String viewsAndDate = ""; if (infoItem.getViewCount() >= 0) { - viewsAndDate = Localization.shortViewCount(itemBuilder.getContext(), infoItem.getViewCount()); + if (infoItem.getStreamType().equals(StreamType.AUDIO_LIVE_STREAM)) { + viewsAndDate = Localization.listeningCount(itemBuilder.getContext(), infoItem.getViewCount()); + } else if (infoItem.getStreamType().equals(StreamType.LIVE_STREAM)) { + viewsAndDate = Localization.watchingCount(itemBuilder.getContext(), infoItem.getViewCount()); + } else { + viewsAndDate = Localization.shortViewCount(itemBuilder.getContext(), infoItem.getViewCount()); + } } if (!TextUtils.isEmpty(infoItem.getTextualUploadDate())) { if (viewsAndDate.isEmpty()) { diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 6835a48bd..9a6a9c96e 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -141,6 +141,14 @@ public class Localization { } } + public static String listeningCount(Context context, long listeningCount) { + return getQuantity(context, R.plurals.listening, R.string.no_one_listening, listeningCount, shortCount(context, listeningCount)); + } + + public static String watchingCount(Context context, long watchingCount) { + return getQuantity(context, R.plurals.watching, R.string.no_one_watching, watchingCount, shortCount(context, watchingCount)); + } + public static String shortViewCount(Context context, long viewCount) { return getQuantity(context, R.plurals.views, R.string.no_views, viewCount, shortCount(context, viewCount)); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index de58d14bd..78dcb5244 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -263,6 +263,19 @@ %s view %s views + + No one is watching + + %s watching + %s watching + + + No one is listening + + %s listener + %s listeners + + No videos %s video From b125ff702abea2d1253b6d21617e741562d1517a Mon Sep 17 00:00:00 2001 From: Mauricio Colli Date: Mon, 28 Oct 2019 01:20:06 -0300 Subject: [PATCH 3/3] Show parsed relative times instead of whatever the service gives us Before, the direct value was given to the user, now it uses the parsed date so we can match the device's language. To get the relative time from the parsed dates, we use the PrettyTime library. Also introduces a debug option to check the service's original value. --- app/build.gradle | 1 + app/proguard-rules.pro | 1 + app/src/main/java/org/schabi/newpipe/App.java | 1 + .../holder/CommentsMiniInfoItemHolder.java | 3 +- .../holder/StreamInfoItemHolder.java | 28 +++++++++++-- .../org/schabi/newpipe/util/Localization.java | 42 +++++++++++++++---- app/src/main/res/values/settings_keys.xml | 2 +- app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/xml/debug_settings.xml | 7 ++++ 9 files changed, 75 insertions(+), 14 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index bedfe9f4f..0afc8d4c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,6 +94,7 @@ dependencies { implementation 'io.reactivex.rxjava2:rxjava:2.2.2' implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1' + implementation 'org.ocpsoft.prettytime:prettytime:4.0.1.Final' implementation "androidx.room:room-runtime:${roomDbLibVersion}" implementation "androidx.room:room-rxjava2:${roomDbLibVersion}" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6b56e1c75..53a9ecd5a 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -18,6 +18,7 @@ -dontobfuscate -keep class org.schabi.newpipe.extractor.timeago.patterns.** { *; } +-keep class org.ocpsoft.prettytime.i18n.** { *; } -keep class org.mozilla.javascript.** { *; } diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index 95e56c620..8698b3c93 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -98,6 +98,7 @@ public class App extends Application { NewPipe.init(getDownloader(), Localization.getPreferredLocalization(this), Localization.getPreferredContentCountry(this)); + Localization.init(); StateSaver.init(this); initNotificationChannel(); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index 6741e4403..4d94ec392 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -109,8 +109,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { } if (item.getPublishedTime() != null) { - itemPublishedTime.setText(Localization - .formatDate(item.getPublishedTime().date().getTime())); + itemPublishedTime.setText(Localization.relativeTime(item.getPublishedTime().date())); } else { itemPublishedTime.setText(item.getTextualPublishedTime()); } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index 6b54d0168..c48934d10 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -1,5 +1,6 @@ package org.schabi.newpipe.info_list.holder; +import android.preference.PreferenceManager; import android.text.TextUtils; import android.view.ViewGroup; import android.widget.TextView; @@ -12,6 +13,8 @@ import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; +import static org.schabi.newpipe.MainActivity.DEBUG; + /* * Created by Christian Schabesberger on 01.08.16. *

@@ -62,13 +65,30 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { viewsAndDate = Localization.shortViewCount(itemBuilder.getContext(), infoItem.getViewCount()); } } - if (!TextUtils.isEmpty(infoItem.getTextualUploadDate())) { + + final String uploadDate = getFormattedRelativeUploadDate(infoItem); + if (!TextUtils.isEmpty(uploadDate)) { if (viewsAndDate.isEmpty()) { - viewsAndDate = infoItem.getTextualUploadDate(); - } else { - viewsAndDate += " • " + infoItem.getTextualUploadDate(); + return uploadDate; } + + return Localization.concatenateStrings(viewsAndDate, uploadDate); } + return viewsAndDate; } + + private String getFormattedRelativeUploadDate(final StreamInfoItem infoItem) { + if (infoItem.getUploadDate() != null) { + String formattedRelativeTime = Localization.relativeTime(infoItem.getUploadDate().date()); + + if (DEBUG && PreferenceManager.getDefaultSharedPreferences(itemBuilder.getContext()) + .getBoolean(itemBuilder.getContext().getString(R.string.show_original_time_ago_key), false)) { + formattedRelativeTime += " (" + infoItem.getTextualUploadDate() + ")"; + } + return formattedRelativeTime; + } else { + return infoItem.getTextualUploadDate(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 9a6a9c96e..9274df848 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -2,25 +2,26 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Resources; import android.preference.PreferenceManager; -import androidx.annotation.NonNull; -import androidx.annotation.PluralsRes; -import androidx.annotation.StringRes; import android.text.TextUtils; +import org.ocpsoft.prettytime.PrettyTime; +import org.ocpsoft.prettytime.units.Decade; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.localization.ContentCountry; import java.text.DateFormat; import java.text.NumberFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; +import androidx.annotation.NonNull; +import androidx.annotation.PluralsRes; +import androidx.annotation.StringRes; + /* * Created by chschtsch on 12/29/15. * @@ -43,11 +44,16 @@ import java.util.Locale; public class Localization { - public final static String DOT_SEPARATOR = " • "; + private static PrettyTime prettyTime; + private static final String DOT_SEPARATOR = " • "; private Localization() { } + public static void init() { + initPrettyTime(); + } + @NonNull public static String concatenateStrings(final String... strings) { return concatenateStrings(Arrays.asList(strings)); @@ -188,4 +194,26 @@ public class Localization { } return output; } + + /*////////////////////////////////////////////////////////////////////////// + // Pretty Time + //////////////////////////////////////////////////////////////////////////*/ + + private static void initPrettyTime() { + prettyTime = new PrettyTime(Locale.getDefault()); + // Do not use decades as YouTube doesn't either. + prettyTime.removeUnit(Decade.class); + } + + private static PrettyTime getPrettyTime() { + // If pretty time's Locale is different, init again with the new one. + if (!prettyTime.getLocale().equals(Locale.getDefault())) { + initPrettyTime(); + } + return prettyTime; + } + + public static String relativeTime(Calendar calendarTime) { + return getPrettyTime().formatUnrounded(calendarTime); + } } diff --git a/app/src/main/res/values/settings_keys.xml b/app/src/main/res/values/settings_keys.xml index fc7abf678..80f2bb1f4 100644 --- a/app/src/main/res/values/settings_keys.xml +++ b/app/src/main/res/values/settings_keys.xml @@ -111,8 +111,8 @@ debug_pref_screen_key allow_heap_dumping_key - allow_disposed_exceptions_key + show_original_time_ago_text_key theme diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 78dcb5244..328128a62 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -458,6 +458,10 @@ Memory leak monitoring may cause the app to become unresponsive when heap dumping Report out-of-lifecycle errors Force reporting of undeliverable Rx exceptions outside of fragment or activity lifecycle after disposal + + Show original time ago on items + Original texts from services will be visible in stream items + Import/export Import diff --git a/app/src/main/res/xml/debug_settings.xml b/app/src/main/res/xml/debug_settings.xml index 7059ee8ce..b51d4e232 100644 --- a/app/src/main/res/xml/debug_settings.xml +++ b/app/src/main/res/xml/debug_settings.xml @@ -18,4 +18,11 @@ android:key="@string/allow_disposed_exceptions_key" android:title="@string/enable_disposed_exceptions_title" android:summary="@string/enable_disposed_exceptions_summary"/> + +