diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 00000000..09ed019c --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,2 @@ +firstPRMergeComment: > + Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role. diff --git a/CHANGELOG.md b/CHANGELOG.md index 08c15dbd..36700c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,66 @@ +# [0.106.0-dev.7](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.6...v0.106.0-dev.7) (2023-05-01) + + +### Bug Fixes + +* **youtube/general-ads:** remove broken ad filter ([#355](https://github.com/revanced/revanced-integrations/issues/355)) ([061ebc6](https://github.com/revanced/revanced-integrations/commit/061ebc65465b2c8ef087c681070b465b82e3ebd5)) + +# [0.106.0-dev.6](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.5...v0.106.0-dev.6) (2023-05-01) + + +### Features + +* **youtube/general-ads:** hide multiple audio track button on video player overlay ([#377](https://github.com/revanced/revanced-integrations/issues/377)) ([7fc7336](https://github.com/revanced/revanced-integrations/commit/7fc73368f161ee1973f36323054f8cbb53b6e7ce)) + +# [0.106.0-dev.5](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.4...v0.106.0-dev.5) (2023-04-30) + + +### Features + +* **youtube/general-ads:** hide new type of ad ([15f9c90](https://github.com/revanced/revanced-integrations/commit/15f9c90941535e93a0779118158c5b4a8accb799)) + +# [0.106.0-dev.4](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.3...v0.106.0-dev.4) (2023-04-30) + + +### Bug Fixes + +* **youtube/return-youtube-dislike:** support old UI layouts ([#378](https://github.com/revanced/revanced-integrations/issues/378)) ([d3f8fb9](https://github.com/revanced/revanced-integrations/commit/d3f8fb97399aafe98a4198234338c6d0196a7e75)) + +# [0.106.0-dev.3](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.2...v0.106.0-dev.3) (2023-04-30) + + +### Features + +* **youtube/hide-get-premium:** hide get premium advertisements under video player ([#376](https://github.com/revanced/revanced-integrations/issues/376)) ([36fd284](https://github.com/revanced/revanced-integrations/commit/36fd2844c468a4a9af3fe6ee5e62775f1e8dbe56)) + +# [0.106.0-dev.2](https://github.com/revanced/revanced-integrations/compare/v0.106.0-dev.1...v0.106.0-dev.2) (2023-04-30) + + +### Features + +* add appreciation message for new contributors ([78d56d4](https://github.com/revanced/revanced-integrations/commit/78d56d4fe182999555ddf5881a10880e3726782e)) + +# [0.106.0-dev.1](https://github.com/revanced/revanced-integrations/compare/v0.105.1-dev.2...v0.106.0-dev.1) (2023-04-28) + + +### Features + +* **youtube/spoof-app-version:** user selectable version to spoof ([#375](https://github.com/revanced/revanced-integrations/issues/375)) ([f6f6c93](https://github.com/revanced/revanced-integrations/commit/f6f6c93c57bdbec13f09acd802f58554cb981f3a)) + +## [0.105.1-dev.2](https://github.com/revanced/revanced-integrations/compare/v0.105.1-dev.1...v0.105.1-dev.2) (2023-04-28) + + +### Bug Fixes + +* **youtube/spoof-signature-verification:** more fixes for subtitle window positions ([#374](https://github.com/revanced/revanced-integrations/issues/374)) ([8cc1b6e](https://github.com/revanced/revanced-integrations/commit/8cc1b6ea4af4e642fb2d97233d50f34b0113f2c0)) + +## [0.105.1-dev.1](https://github.com/revanced/revanced-integrations/compare/v0.105.0...v0.105.1-dev.1) (2023-04-28) + + +### Bug Fixes + +* **youtube:** no longer need to restart the app after changing `copy-video-url` or `downloads` patch ([#372](https://github.com/revanced/revanced-integrations/issues/372)) ([6b15514](https://github.com/revanced/revanced-integrations/commit/6b155148854fbfe155c9384ba8976b5ddf3d5992)) + # [0.105.0](https://github.com/revanced/revanced-integrations/compare/v0.104.0...v0.105.0) (2023-04-27) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 047e7975..6e77d08b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -44,7 +44,7 @@ android { dependencies { compileOnly(project(mapOf("path" to ":dummy"))) compileOnly("androidx.annotation:annotation:1.6.0") - compileOnly("androidx.appcompat:appcompat:1.6.1") + compileOnly("androidx.appcompat:appcompat:1.7.0-alpha02") compileOnly("com.squareup.okhttp3:okhttp:5.0.0-alpha.11") compileOnly("com.squareup.retrofit2:retrofit:2.9.0") } diff --git a/app/src/main/java/app/revanced/integrations/patches/GeneralAdsPatch.java b/app/src/main/java/app/revanced/integrations/patches/GeneralAdsPatch.java index e62d120d..75717de9 100644 --- a/app/src/main/java/app/revanced/integrations/patches/GeneralAdsPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/GeneralAdsPatch.java @@ -33,6 +33,7 @@ public final class GeneralAdsPatch extends Filter { var infoPanel = new BlockRule(SettingsEnum.ADREMOVER_INFO_PANEL_REMOVAL, "publisher_transparency_panel", "single_item_information_panel"); var latestPosts = new BlockRule(SettingsEnum.ADREMOVER_HIDE_LATEST_POSTS, "post_shelf"); var channelGuidelines = new BlockRule(SettingsEnum.ADREMOVER_HIDE_CHANNEL_GUIDELINES, "channel_guidelines_entry_banner"); + var audioTrackButton = new BlockRule(SettingsEnum.HIDE_AUDIO_TRACK_BUTTON, "multi_feed_icon_button"); var artistCard = new BlockRule(SettingsEnum.HIDE_ARTIST_CARDS, "official_card"); var selfSponsor = new BlockRule(SettingsEnum.ADREMOVER_SELF_SPONSOR_REMOVAL, "cta_shelf_card"); var chapterTeaser = new BlockRule(SettingsEnum.ADREMOVER_CHAPTER_TEASER_REMOVAL, "expandable_metadata", "macro_markers_carousel"); @@ -64,8 +65,8 @@ public final class GeneralAdsPatch extends Filter { "carousel_footered_layout", "text_image_button_layout", "primetime_promo", - "feature_grid_interstitial", "product_details", + "full_width_portrait_image_layout", "brand_video_shelf" ); var movieAds = new BlockRule( @@ -97,6 +98,7 @@ public final class GeneralAdsPatch extends Filter { merchandise, infoPanel, channelGuidelines, + audioTrackButton, artistCard, selfSponsor, webLinkPanel, diff --git a/app/src/main/java/app/revanced/integrations/patches/HideGetPremiumPatch.java b/app/src/main/java/app/revanced/integrations/patches/HideGetPremiumPatch.java new file mode 100644 index 00000000..d246c040 --- /dev/null +++ b/app/src/main/java/app/revanced/integrations/patches/HideGetPremiumPatch.java @@ -0,0 +1,12 @@ +package app.revanced.integrations.patches; + +import app.revanced.integrations.settings.SettingsEnum; + +public class HideGetPremiumPatch { + /** + * Injection point. + */ + public static boolean hideGetPremiumView() { + return SettingsEnum.HIDE_GET_PREMIUM.getBoolean(); + } +} diff --git a/app/src/main/java/app/revanced/integrations/patches/MicroGSupport.java b/app/src/main/java/app/revanced/integrations/patches/MicroGSupport.java index b9f9d297..ba242d30 100644 --- a/app/src/main/java/app/revanced/integrations/patches/MicroGSupport.java +++ b/app/src/main/java/app/revanced/integrations/patches/MicroGSupport.java @@ -2,6 +2,7 @@ package app.revanced.integrations.patches; import static app.revanced.integrations.utils.StringRef.str; +import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -28,6 +29,7 @@ public class MicroGSupport { context.startActivity(intent); } + @TargetApi(26) public static void checkAvailability() { var context = Objects.requireNonNull(ReVancedUtils.getContext()); @@ -41,10 +43,11 @@ public class MicroGSupport { System.exit(0); } + try (var client = context.getContentResolver().acquireContentProviderClient(VANCED_MICROG_PROVIDER)) { if (client != null) return; LogHelper.printInfo(() -> "Vanced MicroG is not running in the background"); startIntent(context, DONT_KILL_MY_APP_LINK, str("microg_not_running_warning")); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java b/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java index 690e3e48..93f0020d 100644 --- a/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/ReturnYouTubeDislikePatch.java @@ -2,23 +2,110 @@ package app.revanced.integrations.patches; import static app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike.Vote; -import android.text.SpannableString; +import android.text.Editable; +import android.text.Spannable; import android.text.Spanned; +import android.text.TextWatcher; +import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicReference; import app.revanced.integrations.returnyoutubedislike.ReturnYouTubeDislike; import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.shared.PlayerType; import app.revanced.integrations.utils.LogHelper; +import app.revanced.integrations.utils.ReVancedUtils; public class ReturnYouTubeDislikePatch { + /** + * Resource identifier of old UI dislike button. + */ + private static final int OLD_UI_DISLIKE_BUTTON_RESOURCE_ID + = ReVancedUtils.getResourceIdentifier("dislike_button", "id"); + + /** + * Dislikes text label used by old UI. + */ + @NonNull + private static WeakReference oldUITextViewRef = new WeakReference<>(null); + + /** + * Original old UI 'Dislikes' text before patch modifications. + * Required to reset the dislikes when changing videos and RYD is not available. + * Set only once during the first load. + */ + private static Spanned oldUIOriginalSpan; + + /** + * Replacement span that contains dislike value. Used by {@link #oldUiTextWatcher}. + */ + @Nullable + private static Spanned oldUIReplacementSpan; + + /** + * Old UI dislikes can be set multiple times by YouTube. + * To prevent it from reverting changes made here, this listener overrides any future changes YouTube makes. + */ + private static final TextWatcher oldUiTextWatcher = new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + public void afterTextChanged(Editable s) { + if (oldUIReplacementSpan == null || oldUIReplacementSpan.toString().equals(s.toString())) { + return; + } + s.replace(0, s.length(), oldUIReplacementSpan); + } + }; + + private static void updateOldUIDislikesTextView() { + TextView oldUITextView = oldUITextViewRef.get(); + if (oldUITextView == null) { + return; + } + oldUIReplacementSpan = ReturnYouTubeDislike.getDislikesSpanForRegularVideo(oldUIOriginalSpan, false); + if (!oldUIReplacementSpan.equals(oldUITextView.getText())) { + oldUITextView.setText(oldUIReplacementSpan); + } + } + + /** + * Injection point. Called on main thread. + * + * Used when spoofing the older app versions of {@link SpoofAppVersionPatch}. + */ + public static void setOldUILayoutDislikes(int buttonViewResourceId, @NonNull TextView textView) { + try { + if (!SettingsEnum.RYD_ENABLED.getBoolean() + || buttonViewResourceId != OLD_UI_DISLIKE_BUTTON_RESOURCE_ID) { + return; + } + if (oldUIOriginalSpan == null) { + // Use value of the first instance, as it appears TextViews can be recycled + // and might contain dislikes previously added by the patch. + oldUIOriginalSpan = (Spanned) textView.getText(); + } + oldUITextViewRef = new WeakReference<>(textView); + // No way to check if a listener is already attached, so remove and add again. + textView.removeTextChangedListener(oldUiTextWatcher); + textView.addTextChangedListener(oldUiTextWatcher); + + updateOldUIDislikesTextView(); + } catch (Exception ex) { + LogHelper.printException(() -> "setOldUILayoutDislikes failure", ex); + } + } + /** * Injection point. */ - public static void newVideoLoaded(String videoId) { + public static void newVideoLoaded(@NonNull String videoId) { try { if (!SettingsEnum.RYD_ENABLED.getBoolean()) return; ReturnYouTubeDislike.newVideoLoaded(videoId); @@ -47,14 +134,23 @@ public class ReturnYouTubeDislikePatch { @NonNull AtomicReference textRef, @NonNull CharSequence original) { try { - if (!SettingsEnum.RYD_ENABLED.getBoolean()) { + if (!SettingsEnum.RYD_ENABLED.getBoolean() || PlayerType.getCurrent().isNoneOrHidden()) { return original; } - SpannableString replacement = ReturnYouTubeDislike.getDislikeSpanForContext(conversionContext, original); - if (replacement != null) { - textRef.set(replacement); - return replacement; + + String conversionContextString = conversionContext.toString(); + final boolean isSegmentedButton; + if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) { + isSegmentedButton = true; + } else if (conversionContextString.contains("|dislike_button.eml|")) { + isSegmentedButton = false; + } else { + return original; } + + Spanned replacement = ReturnYouTubeDislike.getDislikesSpanForRegularVideo((Spannable) original, isSegmentedButton); + textRef.set(replacement); + return replacement; } catch (Exception ex) { LogHelper.printException(() -> "onLithoTextLoaded failure", ex); } @@ -71,10 +167,7 @@ public class ReturnYouTubeDislikePatch { if (!SettingsEnum.RYD_ENABLED.getBoolean()) { return original; } - SpannableString replacement = ReturnYouTubeDislike.getDislikeSpanForShort(original); - if (replacement != null) { - return replacement; - } + return ReturnYouTubeDislike.getDislikeSpanForShort(original); } catch (Exception ex) { LogHelper.printException(() -> "onShortsComponentCreated failure", ex); } @@ -97,6 +190,7 @@ public class ReturnYouTubeDislikePatch { for (Vote v : Vote.values()) { if (v.value == vote) { ReturnYouTubeDislike.sendVote(v); + updateOldUIDislikesTextView(); return; } } diff --git a/app/src/main/java/app/revanced/integrations/patches/SpoofAppVersionPatch.java b/app/src/main/java/app/revanced/integrations/patches/SpoofAppVersionPatch.java index 21cb5d07..4967bb95 100644 --- a/app/src/main/java/app/revanced/integrations/patches/SpoofAppVersionPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/SpoofAppVersionPatch.java @@ -5,10 +5,8 @@ import app.revanced.integrations.settings.SettingsEnum; public class SpoofAppVersionPatch { public static String getYouTubeVersionOverride(String version) { - if (SettingsEnum.SPOOF_APP_VERSION.getBoolean()){ - // Override with the most recent version that does not show the new UI player layout. - // If the new UI shows up for some users, then change this to an older version (such as 17.29.34). - return "17.30.34"; + if (SettingsEnum.SPOOF_APP_VERSION.getBoolean()) { + return SettingsEnum.SPOOF_APP_VERSION_TARGET.getString(); } return version; } diff --git a/app/src/main/java/app/revanced/integrations/patches/SpoofSignatureVerificationPatch.java b/app/src/main/java/app/revanced/integrations/patches/SpoofSignatureVerificationPatch.java index 16270f39..95b948d5 100644 --- a/app/src/main/java/app/revanced/integrations/patches/SpoofSignatureVerificationPatch.java +++ b/app/src/main/java/app/revanced/integrations/patches/SpoofSignatureVerificationPatch.java @@ -31,13 +31,27 @@ public class SpoofSignatureVerificationPatch { "SAFg" // Autoplay in scrim }; - @Nullable - private static String currentVideoId; + /** + * On app first start, the first video played usually contains a single non-default window setting value + * and all other subtitle settings for the video are (incorrect) default shorts window settings. + * For this situation, the shorts settings must be replaced. + * + * But some videos use multiple text positions on screen (such as https://youtu.be/3hW1rMNC89o), + * and by chance many of the subtitles uses window positions that match a default shorts position. + * To handle these videos, selectively allowing the shorts specific window settings to 'pass thru' unchanged, + * but only if the video contains multiple non-default subtitle window positions. + * + * Do not enable 'pass thru mode' until this many non default subtitle settings are observed for a single video. + */ + private static final int NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU = 2; /** - * If any of the subtitles settings encountered from the current video have been non default values. + * The number of non default subtitle settings encountered for the current video. */ - private static boolean nonDefaultSubtitlesEncountered; + private static int numberOfNonDefaultSettingsObserved; + + @Nullable + private static String currentVideoId; /** * Injection point. @@ -137,15 +151,19 @@ public class SpoofSignatureVerificationPatch { // then this will incorrectly replace the setting. // But, if the video uses multiple subtitles in different screen locations, then detect the non-default values // and do not replace any window settings for the video (regardless if they match a shorts default). - if (signatureSpoofing && !nonDefaultSubtitlesEncountered && !PlayerType.getCurrent().isNoneOrHidden()) { + if (signatureSpoofing && !PlayerType.getCurrent().isNoneOrHidden() + && numberOfNonDefaultSettingsObserved < NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU) { for (SubtitleWindowReplacementSettings setting : SubtitleWindowReplacementSettings.values()) { if (setting.match(ap, ah, av, vs, sd)) { return setting.replacementSetting(); } } - // Settings appear to be custom subtitles. - nonDefaultSubtitlesEncountered = true; - LogHelper.printDebug(() -> "Non default subtitles found. Using existing settings without replacement."); + + numberOfNonDefaultSettingsObserved++; + LogHelper.printDebug(() -> + numberOfNonDefaultSettingsObserved < NUMBER_OF_NON_DEFAULT_SUBTITLES_BEFORE_ENABLING_PASSTHRU + ? "Non default subtitle found." + : "Multiple non default subtitles found. Allowing all subtitles for this video to pass thru unchanged."); } return new int[]{ap, ah, av}; @@ -160,7 +178,7 @@ public class SpoofSignatureVerificationPatch { return; } currentVideoId = videoId; - nonDefaultSubtitlesEncountered = false; + numberOfNonDefaultSettingsObserved = 0; } catch (Exception ex) { LogHelper.printException(() -> "setCurrentVideoId failure", ex); } diff --git a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java index bc950f70..5bee15ea 100644 --- a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java +++ b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/ReturnYouTubeDislike.java @@ -76,7 +76,7 @@ public class ReturnYouTubeDislike { /** * If {@link #currentVideoId} and the RYD data is for the last shorts loaded. */ - private static volatile boolean lastVideoLoadedWasShort; + private static volatile boolean dislikeDataIsShort; /** * Stores the results of the vote api fetch, and used as a barrier to wait until fetch completes. @@ -141,7 +141,7 @@ public class ReturnYouTubeDislike { LogHelper.printDebug(() -> "Clearing data"); } currentVideoId = videoId; - lastVideoLoadedWasShort = false; + dislikeDataIsShort = false; voteFetchFuture = null; originalDislikeSpan = null; replacementLikeDislikeSpan = null; @@ -198,7 +198,7 @@ public class ReturnYouTubeDislike { // If a Short is opened while a regular video is on screen, this will incorrectly set this as false. // But this check is needed to fix unusual situations of opening/closing the app // while both a regular video and a short are on screen. - lastVideoLoadedWasShort = PlayerType.getCurrent().isNoneOrHidden(); + dislikeDataIsShort = PlayerType.getCurrent().isNoneOrHidden(); // No need to wrap the call in a try/catch, // as any exceptions are propagated out in the later Future#Get call. @@ -207,41 +207,28 @@ public class ReturnYouTubeDislike { } /** - * @return NULL if the span does not need changing or if RYD is not available. + * @return the replacement span containing dislikes, or the original span if RYD is not available. */ - @Nullable - public static SpannableString getDislikeSpanForContext(@NonNull Object conversionContext, @NonNull CharSequence original) { - if (PlayerType.getCurrent().isNoneOrHidden()) { - return null; - } - String conversionContextString = conversionContext.toString(); - final boolean isSegmentedButton; - if (conversionContextString.contains("|segmented_like_dislike_button.eml|")) { - isSegmentedButton = true; - } else if (conversionContextString.contains("|dislike_button.eml|")) { - isSegmentedButton = false; - } else { - return null; - } - - if (lastVideoLoadedWasShort) { + @NonNull + public static Spanned getDislikesSpanForRegularVideo(@NonNull Spanned original, boolean isSegmentedButton) { + if (dislikeDataIsShort) { // user: // 1, opened a video // 2. opened a short (without closing the regular video) // 3. closed the short // 4. regular video is now present, but the videoId and RYD data is still for the short LogHelper.printDebug(() -> "Ignoring getDislikeSpanForContext(), as data loaded is for prior short"); - return null; + return original; } - - return waitForFetchAndUpdateReplacementSpan((Spannable) original, isSegmentedButton); + return waitForFetchAndUpdateReplacementSpan(original, isSegmentedButton); } /** * Called when a Shorts dislike Spannable is created. */ - public static SpannableString getDislikeSpanForShort(@NonNull Spanned original) { - lastVideoLoadedWasShort = true; // it's now certain the video and data are a short + @NonNull + public static Spanned getDislikeSpanForShort(@NonNull Spanned original) { + dislikeDataIsShort = true; // it's now certain the video and data are a short return waitForFetchAndUpdateReplacementSpan(original, false); } @@ -250,17 +237,14 @@ public class ReturnYouTubeDislike { return span.toString().indexOf(MIDDLE_SEPARATOR_CHARACTER) != -1; } - /** - * @return NULL if the span does not need changing or if RYD is not available. - */ - @Nullable - private static SpannableString waitForFetchAndUpdateReplacementSpan(@NonNull Spanned oldSpannable, boolean isSegmentedButton) { + @NonNull + private static Spanned waitForFetchAndUpdateReplacementSpan(@NonNull Spanned oldSpannable, boolean isSegmentedButton) { try { synchronized (videoIdLockObject) { if (replacementLikeDislikeSpan != null) { if (spansHaveEqualTextAndColor(replacementLikeDislikeSpan, oldSpannable)) { LogHelper.printDebug(() -> "Ignoring previously created dislikes span"); - return null; + return oldSpannable; } if (spansHaveEqualTextAndColor(Objects.requireNonNull(originalDislikeSpan), oldSpannable)) { LogHelper.printDebug(() -> "Replacing span with previously created dislike span"); @@ -269,11 +253,11 @@ public class ReturnYouTubeDislike { } if (isSegmentedButton && isPreviouslyCreatedSegmentedSpan(oldSpannable)) { // need to recreate using original, as oldSpannable has prior outdated dislike values - oldSpannable = originalDislikeSpan; - if (oldSpannable == null) { + if (originalDislikeSpan == null) { LogHelper.printDebug(() -> "Cannot add dislikes - original span is null"); // should never happen - return null; + return oldSpannable; } + oldSpannable = originalDislikeSpan; } else { originalDislikeSpan = oldSpannable; // most up to date original } @@ -284,12 +268,12 @@ public class ReturnYouTubeDislike { Future fetchFuture = getVoteFetchFuture(); if (fetchFuture == null) { LogHelper.printDebug(() -> "fetch future not available (user enabled RYD while video was playing?)"); - return null; + return oldSpannable; } RYDVoteData votingData = fetchFuture.get(MAX_MILLISECONDS_TO_BLOCK_UI_WHILE_WAITING_FOR_FETCH_VOTES_TO_COMPLETE, TimeUnit.MILLISECONDS); if (votingData == null) { LogHelper.printDebug(() -> "Cannot add dislike to UI (RYD data not available)"); - return null; + return oldSpannable; } SpannableString replacement = createDislikeSpan(oldSpannable, isSegmentedButton, votingData); @@ -304,7 +288,7 @@ public class ReturnYouTubeDislike { } catch (Exception e) { LogHelper.printException(() -> "waitForFetchAndUpdateReplacementSpan failure", e); // should never happen } - return null; + return oldSpannable; } public static void sendVote(@NonNull Vote vote) { @@ -313,7 +297,7 @@ public class ReturnYouTubeDislike { try { // Must make a local copy of videoId, since it may change between now and when the vote thread runs. String videoIdToVoteFor = getCurrentVideoId(); - if (videoIdToVoteFor == null || lastVideoLoadedWasShort != PlayerType.getCurrent().isNoneOrHidden()) { + if (videoIdToVoteFor == null || dislikeDataIsShort != PlayerType.getCurrent().isNoneOrHidden()) { // User enabled RYD after starting playback of a video. // Or shorts was loaded with regular video present, then shorts was closed, // and then user voted on the now visible original video. diff --git a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java index 970f3e48..dcc5bf9d 100644 --- a/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java +++ b/app/src/main/java/app/revanced/integrations/returnyoutubedislike/requests/ReturnYouTubeDislikeApi.java @@ -136,21 +136,20 @@ public class ReturnYouTubeDislikeApi { /** * Simulates a slow response by doing meaningless calculations. * Used to debug the app UI and verify UI timeout logic works - * - * @param maximumTimeToWait maximum time to wait */ @SuppressWarnings("UnusedReturnValue") - private static long randomlyWaitIfLocallyDebugging(long maximumTimeToWait) { + private static long randomlyWaitIfLocallyDebugging() { final boolean DEBUG_RANDOMLY_DELAY_NETWORK_CALLS = false; // set true to debug UI if (DEBUG_RANDOMLY_DELAY_NETWORK_CALLS) { - final long amountOfTimeToWaste = (long) (Math.random() * maximumTimeToWait); + final long amountOfTimeToWaste = (long) (Math.random() + * (API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS + API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS)); final long timeCalculationStarted = System.currentTimeMillis(); - LogHelper.printDebug(() -> "Artificially creating network delay of: " + amountOfTimeToWaste + " ms"); + LogHelper.printDebug(() -> "Artificially creating network delay of: " + amountOfTimeToWaste + "ms"); long meaninglessValue = 0; while (System.currentTimeMillis() - timeCalculationStarted < amountOfTimeToWaste) { // could do a thread sleep, but that will trigger an exception if the thread is interrupted - meaninglessValue += Long.numberOfLeadingZeros((long) (Math.random() * Long.MAX_VALUE)); + meaninglessValue += Long.numberOfLeadingZeros((long)Math.exp(Math.random())); } // return the value, otherwise the compiler or VM might optimize and remove the meaningless time wasting work, // leaving an empty loop that hammers on the System.currentTimeMillis native call @@ -246,7 +245,7 @@ public class ReturnYouTubeDislikeApi { connection.setConnectTimeout(API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS); // timeout for TCP connection to server connection.setReadTimeout(API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS); // timeout for server response - randomlyWaitIfLocallyDebugging(2*(API_GET_VOTES_TCP_TIMEOUT_MILLISECONDS + API_GET_VOTES_HTTP_TIMEOUT_MILLISECONDS)); + randomlyWaitIfLocallyDebugging(); final int responseCode = connection.getResponseCode(); if (checkIfRateLimitWasHit(responseCode)) { diff --git a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java index 7a72ea7b..c5a21902 100644 --- a/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/app/src/main/java/app/revanced/integrations/settings/SettingsEnum.java @@ -19,13 +19,12 @@ import app.revanced.integrations.utils.StringRef; public enum SettingsEnum { //Download Settings - // TODO: DOWNLOAD_PATH("revanced_download_path", STRING, Environment.getExternalStorageDirectory().getPath() + "/Download"), - DOWNLOADS_BUTTON_SHOWN("revanced_downloads_enabled", BOOLEAN, TRUE, true), + DOWNLOADS_BUTTON_SHOWN("revanced_downloads_enabled", BOOLEAN, TRUE), DOWNLOADS_PACKAGE_NAME("revanced_downloads_package_name", STRING, "org.schabi.newpipe" /* NewPipe */, parents(DOWNLOADS_BUTTON_SHOWN)), // Copy video URL settings - COPY_VIDEO_URL_BUTTON_SHOWN("revanced_copy_video_url_enabled", BOOLEAN, TRUE, true), - COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN("revanced_copy_video_url_timestamp_enabled", BOOLEAN, TRUE, true), + COPY_VIDEO_URL_BUTTON_SHOWN("revanced_copy_video_url_enabled", BOOLEAN, TRUE), + COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN("revanced_copy_video_url_timestamp_enabled", BOOLEAN, TRUE), // Video settings OLD_STYLE_VIDEO_QUALITY_PLAYER_SETTINGS("revanced_use_old_style_quality_settings", BOOLEAN, TRUE), @@ -81,6 +80,7 @@ public enum SettingsEnum { DISABLE_STARTUP_SHORTS_PLAYER("revanced_startup_shorts_player_enabled", BOOLEAN, FALSE), HIDE_ALBUM_CARDS("revanced_hide_album_cards", BOOLEAN, FALSE, true), HIDE_ARTIST_CARDS("revanced_hide_artist_cards", BOOLEAN, FALSE), + HIDE_AUDIO_TRACK_BUTTON("revanced_hide_audio_track_button", BOOLEAN, FALSE), HIDE_AUTOPLAY_BUTTON("revanced_hide_autoplay_button", BOOLEAN, TRUE, true), HIDE_BREAKING_NEWS("revanced_hide_breaking_news", BOOLEAN, TRUE, true), HIDE_CAPTIONS_BUTTON("revanced_hide_captions_button", BOOLEAN, FALSE), @@ -92,6 +92,7 @@ public enum SettingsEnum { HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE), HIDE_FLOATING_MICROPHONE_BUTTON("revanced_hide_floating_microphone_button", BOOLEAN, TRUE, true), HIDE_FULLSCREEN_PANELS("revanced_hide_fullscreen_panels", BOOLEAN, TRUE), + HIDE_GET_PREMIUM("revanced_hide_get_premium", BOOLEAN, TRUE), HIDE_INFO_CARDS("revanced_hide_infocards", BOOLEAN, TRUE), HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE), HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true), @@ -103,6 +104,7 @@ public enum SettingsEnum { HIDE_WATCH_IN_VR("revanced_hide_watch_in_vr", BOOLEAN, FALSE, true), PLAYER_POPUP_PANELS("revanced_player_popup_panels_enabled", BOOLEAN, FALSE), SPOOF_APP_VERSION("revanced_spoof_app_version", BOOLEAN, FALSE, true, "revanced_spoof_app_version_user_dialog_message"), + SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.30.35", true, parents(SPOOF_APP_VERSION)), USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true), WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true), diff --git a/app/src/main/java/app/revanced/integrations/settingsmenu/ReturnYouTubeDislikeSettingsFragment.java b/app/src/main/java/app/revanced/integrations/settingsmenu/ReturnYouTubeDislikeSettingsFragment.java index 3175675e..6a8a0bb2 100644 --- a/app/src/main/java/app/revanced/integrations/settingsmenu/ReturnYouTubeDislikeSettingsFragment.java +++ b/app/src/main/java/app/revanced/integrations/settingsmenu/ReturnYouTubeDislikeSettingsFragment.java @@ -20,33 +20,17 @@ import app.revanced.integrations.settings.SharedPrefCategory; public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment { /** - * If ReturnYouTubeDislike is enabled - */ - private SwitchPreference enabledPreference; - - /** - * If dislikes are shown as percentage + * If dislikes are shown as percentage. */ private SwitchPreference percentagePreference; /** - * If segmented like/dislike button uses smaller compact layout + * If segmented like/dislike button uses smaller compact layout. */ private SwitchPreference compactLayoutPreference; private void updateUIState() { - enabledPreference.setSummary(SettingsEnum.RYD_ENABLED.getBoolean() - ? str("revanced_ryd_enable_summary_on") - : str("revanced_ryd_enable_summary_off")); - - percentagePreference.setSummary(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.getBoolean() - ? str("revanced_ryd_dislike_percentage_summary_on") - : str("revanced_ryd_dislike_percentage_summary_off")); percentagePreference.setEnabled(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.isAvailable()); - - compactLayoutPreference.setSummary(SettingsEnum.RYD_USE_COMPACT_LAYOUT.getBoolean() - ? str("revanced_ryd_compact_layout_summary_on") - : str("revanced_ryd_compact_layout_summary_off")); compactLayoutPreference.setEnabled(SettingsEnum.RYD_USE_COMPACT_LAYOUT.isAvailable()); } @@ -59,9 +43,11 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment { PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(context); setPreferenceScreen(preferenceScreen); - enabledPreference = new SwitchPreference(context); + SwitchPreference enabledPreference = new SwitchPreference(context); enabledPreference.setChecked(SettingsEnum.RYD_ENABLED.getBoolean()); enabledPreference.setTitle(str("revanced_ryd_enable_title")); + enabledPreference.setSummaryOn(str("revanced_ryd_enable_summary_on")); + enabledPreference.setSummaryOff(str("revanced_ryd_enable_summary_off")); enabledPreference.setOnPreferenceChangeListener((pref, newValue) -> { final boolean rydIsEnabled = (Boolean) newValue; SettingsEnum.RYD_ENABLED.saveValue(rydIsEnabled); @@ -75,6 +61,8 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment { percentagePreference = new SwitchPreference(context); percentagePreference.setChecked(SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.getBoolean()); percentagePreference.setTitle(str("revanced_ryd_dislike_percentage_title")); + percentagePreference.setSummaryOn(str("revanced_ryd_dislike_percentage_summary_on")); + percentagePreference.setSummaryOff(str("revanced_ryd_dislike_percentage_summary_off")); percentagePreference.setOnPreferenceChangeListener((pref, newValue) -> { SettingsEnum.RYD_SHOW_DISLIKE_PERCENTAGE.saveValue(newValue); ReturnYouTubeDislike.clearCache(); @@ -86,6 +74,8 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment { compactLayoutPreference = new SwitchPreference(context); compactLayoutPreference.setChecked(SettingsEnum.RYD_USE_COMPACT_LAYOUT.getBoolean()); compactLayoutPreference.setTitle(str("revanced_ryd_compact_layout_title")); + compactLayoutPreference.setSummaryOn(str("revanced_ryd_compact_layout_summary_on")); + compactLayoutPreference.setSummaryOff(str("revanced_ryd_compact_layout_summary_off")); compactLayoutPreference.setOnPreferenceChangeListener((pref, newValue) -> { SettingsEnum.RYD_USE_COMPACT_LAYOUT.saveValue(newValue); ReturnYouTubeDislike.clearCache(); @@ -185,7 +175,7 @@ public class ReturnYouTubeDislikeSettingsFragment extends PreferenceFragment { } } - private String createSummaryText(int value, String summaryStringZeroKey, String summaryStringOneOrMoreKey) { + private static String createSummaryText(int value, String summaryStringZeroKey, String summaryStringOneOrMoreKey) { if (value == 0) { return str(summaryStringZeroKey); } diff --git a/app/src/main/java/app/revanced/integrations/videoplayer/BottomControlButton.java b/app/src/main/java/app/revanced/integrations/videoplayer/BottomControlButton.java index 60923d9f..fe27fc8f 100644 --- a/app/src/main/java/app/revanced/integrations/videoplayer/BottomControlButton.java +++ b/app/src/main/java/app/revanced/integrations/videoplayer/BottomControlButton.java @@ -1,66 +1,64 @@ package app.revanced.integrations.videoplayer; -import android.support.constraint.ConstraintLayout; import android.view.View; +import android.view.ViewGroup; import android.view.animation.Animation; import android.widget.ImageView; - -import java.lang.ref.WeakReference; - +import androidx.annotation.NonNull; +import app.revanced.integrations.settings.SettingsEnum; import app.revanced.integrations.utils.LogHelper; import app.revanced.integrations.utils.ReVancedUtils; +import java.lang.ref.WeakReference; +import java.util.Objects; + public abstract class BottomControlButton { - WeakReference button = new WeakReference<>(null); - ConstraintLayout constraintLayout; - Boolean isButtonEnabled; - Boolean isShowing; + private static final Animation fadeIn = ReVancedUtils.getResourceAnimation("fade_in"); + private static final Animation fadeOut = ReVancedUtils.getResourceAnimation("fade_out"); + private final WeakReference buttonRef; + private final SettingsEnum setting; + protected boolean isVisible; - private Animation fadeIn; - private Animation fadeOut; - - public BottomControlButton(Object obj, String viewId, Boolean isEnabled, View.OnClickListener onClickListener) { - try { - LogHelper.printDebug(() -> "Initializing button with id: " + viewId); - constraintLayout = (ConstraintLayout) obj; - isButtonEnabled = isEnabled; - - ImageView imageView = constraintLayout.findViewById(ReVancedUtils.getResourceIdentifier(viewId, "id")); - if (imageView == null) { - LogHelper.printException(() -> "Couldn't find ImageView with id: " + viewId); - return; - } - imageView.setOnClickListener(onClickListener); - button = new WeakReference<>(imageView); - - fadeIn = ReVancedUtils.getResourceAnimation("fade_in"); - fadeOut = ReVancedUtils.getResourceAnimation("fade_out"); - fadeIn.setDuration(ReVancedUtils.getResourceInteger("fade_duration_fast")); - fadeOut.setDuration(ReVancedUtils.getResourceInteger("fade_duration_scheduled")); - - isShowing = true; - setVisibility(false); - } catch (Exception e) { - LogHelper.printException(() -> "Failed to initialize button with id: " + viewId, e); - } + static { + // TODO: check if these durations are correct. + fadeIn.setDuration(ReVancedUtils.getResourceInteger("fade_duration_fast")); + fadeOut.setDuration(ReVancedUtils.getResourceInteger("fade_duration_scheduled")); } - public void setVisibility(boolean showing) { - if (isShowing == showing) return; + public BottomControlButton(@NonNull ViewGroup bottomControlsViewGroup, @NonNull String imageViewButtonId, + @NonNull SettingsEnum booleanSetting, @NonNull View.OnClickListener onClickListener) { + LogHelper.printDebug(() -> "Initializing button: " + imageViewButtonId); - isShowing = showing; - ImageView imageView = button.get(); - - if (constraintLayout == null || imageView == null) - return; - - if (showing && isButtonEnabled) { - LogHelper.printDebug(() -> "Fading in"); - imageView.setVisibility(View.VISIBLE); - imageView.startAnimation(fadeIn); + if (booleanSetting.returnType != SettingsEnum.ReturnType.BOOLEAN) { + throw new IllegalArgumentException(); } - else if (imageView.getVisibility() == View.VISIBLE) { - LogHelper.printDebug(() -> "Fading out"); + + setting = booleanSetting; + + // Create the button. + ImageView imageView = Objects.requireNonNull(bottomControlsViewGroup.findViewById( + ReVancedUtils.getResourceIdentifier(imageViewButtonId, "id") + )); + imageView.setOnClickListener(onClickListener); + imageView.setVisibility(View.GONE); + + buttonRef = new WeakReference<>(imageView); + } + + public void setVisibility(boolean visible) { + if (isVisible == visible) return; + isVisible = visible; + + ImageView imageView = buttonRef.get(); + if (imageView == null) { + return; + } + + imageView.clearAnimation(); + if (visible && setting.getBoolean()) { + imageView.startAnimation(fadeIn); + imageView.setVisibility(View.VISIBLE); + } else if (imageView.getVisibility() == View.VISIBLE) { imageView.startAnimation(fadeOut); imageView.setVisibility(View.GONE); } diff --git a/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java b/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java index 38a62114..23bcd151 100644 --- a/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java +++ b/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlButton.java @@ -1,25 +1,40 @@ package app.revanced.integrations.videoplayer; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; import app.revanced.integrations.patches.CopyVideoUrlPatch; import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; public class CopyVideoUrlButton extends BottomControlButton { - public static CopyVideoUrlButton instance; + @Nullable + private static CopyVideoUrlButton instance; - public CopyVideoUrlButton(Object obj) { + public CopyVideoUrlButton(ViewGroup viewGroup) { super( - obj, + viewGroup, "copy_video_url_button", - SettingsEnum.COPY_VIDEO_URL_BUTTON_SHOWN.getBoolean(), + SettingsEnum.COPY_VIDEO_URL_BUTTON_SHOWN, view -> CopyVideoUrlPatch.copyUrl(false) ); } + /** + * Injection point. + */ public static void initializeButton(Object obj) { - instance = new CopyVideoUrlButton(obj); + try { + instance = new CopyVideoUrlButton((ViewGroup) obj); + } catch (Exception ex) { + LogHelper.printException(() -> "initializeButton failure", ex); + } } + /** + * Injection point. + */ public static void changeVisibility(boolean showing) { if (instance != null) instance.setVisibility(showing); } diff --git a/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java b/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java index 482ace14..3a94344d 100644 --- a/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java +++ b/app/src/main/java/app/revanced/integrations/videoplayer/CopyVideoUrlTimestampButton.java @@ -1,24 +1,40 @@ package app.revanced.integrations.videoplayer; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; + import app.revanced.integrations.patches.CopyVideoUrlPatch; import app.revanced.integrations.settings.SettingsEnum; +import app.revanced.integrations.utils.LogHelper; public class CopyVideoUrlTimestampButton extends BottomControlButton { - public static CopyVideoUrlTimestampButton instance; + @Nullable + private static CopyVideoUrlTimestampButton instance; - public CopyVideoUrlTimestampButton(Object obj) { + public CopyVideoUrlTimestampButton(ViewGroup bottomControlsViewGroup) { super( - obj, + bottomControlsViewGroup, "copy_video_url_timestamp_button", - SettingsEnum.COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN.getBoolean(), + SettingsEnum.COPY_VIDEO_URL_TIMESTAMP_BUTTON_SHOWN, view -> CopyVideoUrlPatch.copyUrl(true) ); } - public static void initializeButton(Object obj) { - instance = new CopyVideoUrlTimestampButton(obj); + /** + * Injection point. + */ + public static void initializeButton(Object bottomControlsViewGroup) { + try { + instance = new CopyVideoUrlTimestampButton((ViewGroup) bottomControlsViewGroup); + } catch (Exception ex) { + LogHelper.printException(() -> "initializeButton failure", ex); + } } + /** + * Injection point. + */ public static void changeVisibility(boolean showing) { if (instance != null) instance.setVisibility(showing); } diff --git a/app/src/main/java/app/revanced/integrations/videoplayer/DownloadButton.java b/app/src/main/java/app/revanced/integrations/videoplayer/DownloadButton.java index 4b915dc0..dcb581a2 100644 --- a/app/src/main/java/app/revanced/integrations/videoplayer/DownloadButton.java +++ b/app/src/main/java/app/revanced/integrations/videoplayer/DownloadButton.java @@ -3,6 +3,9 @@ package app.revanced.integrations.videoplayer; import android.content.Intent; import android.content.pm.PackageManager; import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.Nullable; import app.revanced.integrations.patches.VideoInformation; import app.revanced.integrations.settings.SettingsEnum; @@ -11,21 +14,32 @@ import app.revanced.integrations.utils.ReVancedUtils; import app.revanced.integrations.utils.StringRef; public class DownloadButton extends BottomControlButton { - public static DownloadButton instance; + @Nullable + private static DownloadButton instance; - public DownloadButton(Object obj) { + public DownloadButton(ViewGroup viewGroup) { super( - obj, + viewGroup, "download_button", - SettingsEnum.DOWNLOADS_BUTTON_SHOWN.getBoolean(), + SettingsEnum.DOWNLOADS_BUTTON_SHOWN, DownloadButton::onDownloadClick ); } + /** + * Injection point. + */ public static void initializeButton(Object obj) { - instance = new DownloadButton(obj); + try { + instance = new DownloadButton((ViewGroup) obj); + } catch (Exception ex) { + LogHelper.printException(() -> "initializeButton failure", ex); + } } + /** + * Injection point. + */ public static void changeVisibility(boolean showing) { if (instance != null) instance.setVisibility(showing); } @@ -38,7 +52,6 @@ public class DownloadButton extends BottomControlButton { boolean packageEnabled = false; try { - assert context != null; packageEnabled = context.getPackageManager().getApplicationInfo(downloaderPackageName, 0).enabled; } catch (PackageManager.NameNotFoundException error) { LogHelper.printDebug(() -> "Downloader could not be found: " + error); diff --git a/build.gradle.kts b/build.gradle.kts index 08d8a748..6c175194 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:7.3.1") + classpath("com.android.tools.build:gradle:8.0.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") // NOTE: Do not place your application dependencies here; they belong diff --git a/gradle.properties b/gradle.properties index e137af41..43f18596 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.jvmargs = -Xmx2048m android.useAndroidX = true -version = 0.105.0 +version = 0.106.0-dev.7 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ae04661e..fae08049 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists