Skip to content

Commit ae03b4f

Browse files
authored
Merge pull request #13420 from TeamNewPipe/mergeDev
Merge dev into refactor
2 parents 44bf345 + 53d8d4d commit ae03b4f

193 files changed

Lines changed: 2257 additions & 1121 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ configure<ApplicationExtension> {
4949
minSdk = 23
5050
targetSdk = 35
5151

52-
versionCode = System.getProperty("versionCodeOverride")?.toInt() ?: 1008
52+
versionCode = System.getProperty("versionCodeOverride")?.toInt() ?: 1010
5353

54-
versionName = "0.28.3"
54+
versionName = "0.28.5"
5555
System.getProperty("versionNameSuffix")?.let { versionNameSuffix = it }
5656

5757
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

app/proguard-rules.pro

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,18 @@
5959
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite {
6060
<fields>;
6161
}
62+
63+
## Keep Kotlinx Serialization classes
64+
-keepclassmembers class kotlinx.serialization.json.** {
65+
*** Companion;
66+
}
67+
-keepclasseswithmembers class kotlinx.serialization.json.** {
68+
kotlinx.serialization.KSerializer serializer(...);
69+
}
70+
-keep,includedescriptorclasses class org.schabi.newpipe.**$$serializer { *; }
71+
-keepclassmembers class org.schabi.newpipe.** {
72+
*** Companion;
73+
}
74+
-keepclasseswithmembers class org.schabi.newpipe.** {
75+
kotlinx.serialization.KSerializer serializer(...);
76+
}

app/src/main/java/org/schabi/newpipe/MainActivity.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package org.schabi.newpipe;
2222

23+
import android.app.AlertDialog;
2324
import android.content.BroadcastReceiver;
2425
import android.content.Context;
2526
import android.content.Intent;
@@ -92,6 +93,8 @@
9293
import org.schabi.newpipe.util.external_communication.ShareUtils;
9394
import org.schabi.newpipe.views.FocusOverlayView;
9495

96+
import java.time.Instant;
97+
import java.time.temporal.ChronoUnit;
9598
import java.util.ArrayList;
9699
import java.util.List;
97100
import java.util.Objects;
@@ -192,6 +195,12 @@ protected void onCreate(final Bundle savedInstanceState) {
192195
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
193196
}
194197

198+
// ReleaseVersionUtil.INSTANCE.isReleaseApk() will be true only for main official build
199+
// We want every release build (nightly, nightly-refactor) to show the popup
200+
if (!DEBUG) {
201+
showKeepAndroidDialog();
202+
}
203+
195204
MigrationManager.showUserInfoIfPresent(this);
196205
}
197206

@@ -889,4 +898,57 @@ private boolean bottomSheetHiddenOrCollapsed() {
889898
|| sheetState == BottomSheetBehavior.STATE_COLLAPSED;
890899
}
891900

901+
private void showKeepAndroidDialog() {
902+
final var prefs = PreferenceManager.getDefaultSharedPreferences(this);
903+
904+
final var now = Instant.now();
905+
final var kaoLastCheck = Instant.ofEpochMilli(prefs.getLong(
906+
getString(R.string.kao_last_checked_key),
907+
0
908+
));
909+
910+
final var supportedLannguages = List.of("fr", "de", "ca", "es", "id", "it", "pl",
911+
"pt", "cs", "sk", "fa", "ar", "tr", "el", "th", "ru", "uk", "ko", "zh", "ja");
912+
final var locale = Localization.getAppLocale();
913+
final String kaoBaseUrl = "https://keepandroidopen.org/";
914+
final String kaoURI;
915+
if (supportedLannguages.contains(locale.getLanguage())) {
916+
if ("zh".equals(locale.getLanguage())) {
917+
kaoURI = kaoBaseUrl + ("TW".equals(locale.getCountry()) ? "zh-TW" : "zh-CN");
918+
} else {
919+
kaoURI = kaoBaseUrl + locale.getLanguage();
920+
}
921+
} else {
922+
kaoURI = kaoBaseUrl;
923+
}
924+
final var solutionURI =
925+
"https://github.com/woheller69/FreeDroidWarn?tab=readme-ov-file#solutions";
926+
927+
if (kaoLastCheck.plus(30, ChronoUnit.DAYS).isBefore(now)) {
928+
final var dialog = new AlertDialog.Builder(this)
929+
.setTitle("Keep Android Open")
930+
.setCancelable(false)
931+
.setMessage(this.getString(R.string.kao_dialog_warning))
932+
.setPositiveButton(this.getString(android.R.string.ok), (d, w) -> {
933+
prefs.edit()
934+
.putLong(
935+
getString(R.string.kao_last_checked_key),
936+
now.toEpochMilli()
937+
)
938+
.apply();
939+
})
940+
.setNeutralButton(this.getString(R.string.kao_solution), null)
941+
.setNegativeButton(this.getString(R.string.kao_dialog_more_info), null)
942+
.show();
943+
944+
// If we use setNeutralButton and etc. dialog will close after pressing the buttons,
945+
// but we want it to close only when positive button is pressed
946+
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v ->
947+
ShareUtils.openUrlInBrowser(this, kaoURI)
948+
);
949+
dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v ->
950+
ShareUtils.openUrlInBrowser(this, solutionURI)
951+
);
952+
}
953+
}
892954
}

app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ private void initToolbar(final Toolbar toolbar) {
344344
toolbar.setNavigationOnClickListener(v -> dismiss());
345345
toolbar.setNavigationContentDescription(R.string.cancel);
346346

347-
okButton = toolbar.findViewById(R.id.okay);
347+
okButton = toolbar.getMenu().findItem(R.id.okay);
348348
okButton.setEnabled(false); // disable until the download service connection is done
349349

350350
toolbar.setOnMenuItemClickListener(item -> {

app/src/main/java/org/schabi/newpipe/error/ErrorActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import org.schabi.newpipe.databinding.ActivityErrorBinding
2525
import org.schabi.newpipe.util.Localization
2626
import org.schabi.newpipe.util.ThemeHelper
2727
import org.schabi.newpipe.util.external_communication.ShareUtils
28+
import org.schabi.newpipe.util.text.setTextWithLinks
2829

2930
/**
3031
* This activity is used to show error details and allow reporting them in various ways.
@@ -100,7 +101,7 @@ class ErrorActivity : AppCompatActivity() {
100101

101102
// normal bugreport
102103
buildInfo(errorInfo)
103-
binding.errorMessageView.text = errorInfo.getMessage(this)
104+
binding.errorMessageView.setTextWithLinks(errorInfo.getMessage(this))
104105
binding.errorView.text = formErrorText(errorInfo.stackTraces)
105106

106107
// print stack trace once again for debugging:

app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.schabi.newpipe.error
33
import android.content.Context
44
import android.os.Parcelable
55
import androidx.annotation.StringRes
6+
import androidx.core.content.ContextCompat
67
import com.google.android.exoplayer2.ExoPlaybackException
78
import com.google.android.exoplayer2.upstream.HttpDataSource
89
import com.google.android.exoplayer2.upstream.Loader
@@ -28,7 +29,7 @@ import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentExcepti
2829
import org.schabi.newpipe.ktx.isNetworkRelated
2930
import org.schabi.newpipe.player.mediasource.FailedMediaSource
3031
import org.schabi.newpipe.player.resolver.PlaybackResolver
31-
import org.schabi.newpipe.util.Localization
32+
import org.schabi.newpipe.util.text.getText
3233

3334
/**
3435
* An error has occurred in the app. This class contains plain old parcelable data that can be used
@@ -135,8 +136,8 @@ class ErrorInfo private constructor(
135136
return getServiceName(serviceId)
136137
}
137138

138-
fun getMessage(context: Context): String {
139-
return message.getString(context)
139+
fun getMessage(context: Context): CharSequence {
140+
return message.getText(context)
140141
}
141142

142143
companion object {
@@ -146,18 +147,23 @@ class ErrorInfo private constructor(
146147
private val stringRes: Int,
147148
private vararg val formatArgs: String
148149
) : Parcelable {
149-
fun getString(context: Context): String {
150-
// use Localization.compatGetString() just in case context is not AppCompatActivity
150+
fun getText(context: Context): CharSequence {
151+
// Ensure locale aware context via ContextCompat.getContextForLanguage() (just in case context is not AppCompatActivity)
152+
val ctx = ContextCompat.getContextForLanguage(context)
151153
return if (formatArgs.isEmpty()) {
152-
Localization.compatGetString(context, stringRes)
154+
ctx.getText(stringRes)
153155
} else {
154-
Localization.compatGetString(context, stringRes, *formatArgs)
156+
// ContextCompat.getString() with formatArgs does not exist, so we just
157+
// replicate its source code but with formatArgs
158+
ctx.resources.getText(stringRes, *formatArgs)
155159
}
156160
}
157161
}
158162

159163
const val SERVICE_NONE = "<unknown_service>"
160164

165+
const val YOUTUBE_IP_BAN_FAQ_URL = "https://newpipe.net/FAQ/#ip-banned-youtube"
166+
161167
private fun getServiceName(serviceId: Int?) = // not using getNameOfServiceById since we want to accept a nullable serviceId and we
162168
// want to default to SERVICE_NONE
163169
ServiceList.all()?.firstOrNull { it.serviceId == serviceId }?.serviceInfo?.name
@@ -245,7 +251,11 @@ class ErrorInfo private constructor(
245251
ErrorMessage(R.string.youtube_music_premium_content)
246252

247253
throwable is SignInConfirmNotBotException ->
248-
ErrorMessage(R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId))
254+
ErrorMessage(
255+
R.string.sign_in_confirm_not_bot_error,
256+
getServiceName(serviceId),
257+
YOUTUBE_IP_BAN_FAQ_URL
258+
)
249259

250260
throwable is ContentNotAvailableException ->
251261
ErrorMessage(R.string.content_not_available)

app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.schabi.newpipe.MainActivity
1616
import org.schabi.newpipe.R
1717
import org.schabi.newpipe.ktx.animate
1818
import org.schabi.newpipe.util.external_communication.ShareUtils
19+
import org.schabi.newpipe.util.text.setTextWithLinks
1920

2021
class ErrorPanelHelper(
2122
private val fragment: Fragment,
@@ -64,7 +65,7 @@ class ErrorPanelHelper(
6465

6566
fun showError(errorInfo: ErrorInfo) {
6667
ensureDefaultVisibility()
67-
errorTextView.text = errorInfo.getMessage(context)
68+
errorTextView.setTextWithLinks(errorInfo.getMessage(context))
6869

6970
if (errorInfo.recaptchaUrl != null) {
7071
showAndSetErrorButtonAction(R.string.recaptcha_solve) {
@@ -109,7 +110,7 @@ class ErrorPanelHelper(
109110
fun showTextError(errorString: String) {
110111
ensureDefaultVisibility()
111112

112-
errorTextView.text = errorString
113+
errorTextView.setTextWithLinks(errorString)
113114

114115
setRootVisible()
115116
}

app/src/main/java/org/schabi/newpipe/fragments/detail/BaseDescriptionFragment.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@ protected void addImagesMetadataItem(final LayoutInflater inflater,
216216
|| image.getWidth() != Image.WIDTH_UNKNOWN
217217
// if even the resolution level is unknown, ?x? will be shown
218218
|| image.getEstimatedResolutionLevel() == Image.ResolutionLevel.UNKNOWN) {
219-
urls.append(imageSizeToText(image.getHeight()));
220-
urls.append('x');
221219
urls.append(imageSizeToText(image.getWidth()));
220+
urls.append('x');
221+
urls.append(imageSizeToText(image.getHeight()));
222222
} else {
223223
switch (image.getEstimatedResolutionLevel()) {
224224
case LOW -> urls.append(getString(R.string.image_quality_low));

app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase;
66
import static org.schabi.newpipe.util.SparseItemUtil.fetchUploaderUrlIfSparse;
77

8+
import android.content.Context;
89
import android.net.Uri;
910

1011
import androidx.annotation.NonNull;
@@ -53,28 +54,33 @@ public enum StreamDialogDefaultEntry {
5354
/**
5455
* Enqueues the stream automatically to the current PlayerType.
5556
*/
56-
ENQUEUE(R.string.enqueue_stream, (fragment, item) ->
57-
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
58-
NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue))
59-
),
57+
ENQUEUE(R.string.enqueue_stream, (fragment, item) -> {
58+
final Context ctx = fragment.requireContext().getApplicationContext();
59+
fetchItemInfoIfSparse(ctx, item, singlePlayQueue ->
60+
NavigationHelper.enqueueOnPlayer(ctx, singlePlayQueue));
61+
}),
6062

6163
/**
6264
* Enqueues the stream automatically to the current PlayerType
6365
* after the currently playing stream.
6466
*/
65-
ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) ->
66-
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
67-
NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue))
68-
),
67+
ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) -> {
68+
final Context ctx = fragment.requireContext().getApplicationContext();
69+
fetchItemInfoIfSparse(ctx, item, singlePlayQueue ->
70+
NavigationHelper.enqueueNextOnPlayer(ctx, singlePlayQueue));
71+
}),
6972

70-
START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) ->
71-
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
72-
NavigationHelper.playOnBackgroundPlayer(
73-
fragment.getContext(), singlePlayQueue, true))),
73+
START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) -> {
74+
final Context ctx = fragment.requireContext().getApplicationContext();
75+
fetchItemInfoIfSparse(ctx, item, singlePlayQueue ->
76+
NavigationHelper.playOnBackgroundPlayer(ctx, singlePlayQueue, true));
77+
}),
7478

75-
START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) ->
76-
fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue ->
77-
NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))),
79+
START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) -> {
80+
final Context ctx = fragment.requireContext().getApplicationContext();
81+
fetchItemInfoIfSparse(ctx, item, singlePlayQueue ->
82+
NavigationHelper.playOnPopupPlayer(ctx, singlePlayQueue, true));
83+
}),
7884

7985
SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> {
8086
throw new UnsupportedOperationException("This needs to be implemented manually "

app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import org.schabi.newpipe.util.NavigationHelper
4949
*/
5050
class MediaBrowserPlaybackPreparer(
5151
private val context: Context,
52-
private val setMediaSessionError: BiConsumer<String, Int>, // error string, error code
52+
private val setMediaSessionError: BiConsumer<CharSequence, Int>, // error string, error code
5353
private val clearMediaSessionError: Runnable,
5454
private val onPrepare: Consumer<Boolean>
5555
) : PlaybackPreparer {
@@ -118,7 +118,7 @@ class MediaBrowserPlaybackPreparer(
118118

119119
private fun onPrepareError(throwable: Throwable) {
120120
setMediaSessionError.accept(
121-
ErrorInfo.getMessage(throwable, null, null).getString(context),
121+
ErrorInfo.getMessage(throwable, null, null).getText(context),
122122
PlaybackStateCompat.ERROR_CODE_APP_ERROR
123123
)
124124
}

0 commit comments

Comments
 (0)