1
mirror of https://github.com/revanced/revanced-integrations synced 2025-11-19 03:23:27 +01:00

Compare commits

...

25 Commits

Author SHA1 Message Date
semantic-release-bot
3c69108caa chore(release): 0.115.1-dev.1 [skip ci]
## [0.115.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0...v0.115.1-dev.1) (2023-08-05)

### Bug Fixes

* **YouTube - Hide Shorts components:** Hide new type of shorts in feed ([#453](https://github.com/ReVanced/revanced-integrations/issues/453)) ([6959808](6959808f0c))
2023-08-05 20:12:10 +00:00
johnconner122
6959808f0c fix(YouTube - Hide Shorts components): Hide new type of shorts in feed (#453) 2023-08-05 22:08:10 +02:00
semantic-release-bot
e0d77a0060 chore(release): 0.115.0 [skip ci]
# [0.115.0](https://github.com/ReVanced/revanced-integrations/compare/v0.114.0...v0.115.0) (2023-08-02)

### Bug Fixes

* **YouTube - SponsorBlock:** Fix skip highlight button showing when set to 'show in seekbar' ([ea7bc57](ea7bc57375))
* **YouTube - Spoof App Version:** Remove 17.30.35 target ([e906c6d](e906c6d3a2))
* **YouTube:** fix potential litho filter thread race ([d3f523f](d3f523f9dd))

### Features

* **Tiktok - Feed filter:** Add more filters ([#445](https://github.com/ReVanced/revanced-integrations/issues/445)) ([c16c038](c16c038396))
* **YouTube - Hide layout components:** Hide `chips shelf` ([#448](https://github.com/ReVanced/revanced-integrations/issues/448)) ([c7d2a9b](c7d2a9b310))
* **YouTube:** add `Player Flyout Menu` patch ([#416](https://github.com/ReVanced/revanced-integrations/issues/416)) ([b2085ae](b2085aeebf))

### Performance Improvements

* **YouTube:** Filter litho components using prefix tree ([#447](https://github.com/ReVanced/revanced-integrations/issues/447)) ([18f2900](18f29004b8))
2023-08-02 23:39:14 +00:00
oSumAtrIX
2ab34fce31 chore: merge branch dev to main (#446) 2023-08-03 01:35:56 +02:00
semantic-release-bot
cf76cbe72f chore(release): 0.115.0-dev.7 [skip ci]
# [0.115.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.6...v0.115.0-dev.7) (2023-08-02)

### Features

* **Tiktok - Feed filter:** Add more filters ([#445](https://github.com/ReVanced/revanced-integrations/issues/445)) ([c16c038](c16c038396))
2023-08-02 12:12:32 +00:00
Vu Hoan Huy
c16c038396 feat(Tiktok - Feed filter): Add more filters (#445)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-02 14:09:07 +02:00
semantic-release-bot
d2a8c33109 chore(release): 0.115.0-dev.6 [skip ci]
# [0.115.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.5...v0.115.0-dev.6) (2023-08-02)

### Bug Fixes

* **YouTube:** fix potential litho filter thread race ([d3f523f](d3f523f9dd))
2023-08-02 11:09:39 +00:00
LisoUseInAIKyrios
d3f523f9dd fix(YouTube): fix potential litho filter thread race 2023-08-02 15:04:29 +04:00
semantic-release-bot
1c2976526e chore(release): 0.115.0-dev.5 [skip ci]
# [0.115.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.4...v0.115.0-dev.5) (2023-08-01)

### Performance Improvements

* **YouTube:** Filter litho components using prefix tree ([#447](https://github.com/ReVanced/revanced-integrations/issues/447)) ([18f2900](18f29004b8))
2023-08-01 17:09:27 +00:00
LisoUseInAIKyrios
18f29004b8 perf(YouTube): Filter litho components using prefix tree (#447)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2023-08-01 21:05:31 +04:00
semantic-release-bot
0207496926 chore(release): 0.115.0-dev.4 [skip ci]
# [0.115.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.3...v0.115.0-dev.4) (2023-07-30)

### Features

* **YouTube:** add `Player Flyout Menu` patch ([#416](https://github.com/ReVanced/revanced-integrations/issues/416)) ([b2085ae](b2085aeebf))
2023-07-30 09:55:03 +00:00
johnconner122
b2085aeebf feat(YouTube): add Player Flyout Menu patch (#416)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2023-07-30 13:51:16 +04:00
LisoUseInAIKyrios
ac8de0a3b7 chore(YouTube - Spoof App Version): Adjust target version 2023-07-27 09:19:05 +04:00
semantic-release-bot
d4bc6cff68 chore(release): 0.115.0-dev.3 [skip ci]
# [0.115.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.2...v0.115.0-dev.3) (2023-07-27)

### Bug Fixes

* **YouTube - Spoof App Version:** Remove 17.30.35 target ([e906c6d](e906c6d3a2))
2023-07-27 04:01:17 +00:00
LisoUseInAIKyrios
e906c6d3a2 fix(YouTube - Spoof App Version): Remove 17.30.35 target 2023-07-27 07:56:58 +04:00
semantic-release-bot
fdc56bf68e chore(release): 0.115.0-dev.2 [skip ci]
# [0.115.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.1...v0.115.0-dev.2) (2023-07-26)

### Bug Fixes

* **YouTube - SponsorBlock:** Fix skip highlight button showing when set to 'show in seekbar' ([ea7bc57](ea7bc57375))
2023-07-26 21:29:00 +00:00
LisoUseInAIKyrios
ea7bc57375 fix(YouTube - SponsorBlock): Fix skip highlight button showing when set to 'show in seekbar' 2023-07-27 01:24:13 +04:00
semantic-release-bot
8ef81235d7 chore(release): 0.115.0-dev.1 [skip ci]
# [0.115.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.114.0...v0.115.0-dev.1) (2023-07-26)

### Features

* **YouTube - Hide layout components:** Hide `chips shelf` ([#448](https://github.com/ReVanced/revanced-integrations/issues/448)) ([c7d2a9b](c7d2a9b310))
2023-07-26 13:51:32 +00:00
johnconner122
c7d2a9b310 feat(YouTube - Hide layout components): Hide chips shelf (#448) 2023-07-26 15:47:56 +02:00
oSumAtrIX
d7fc06a647 ci: clean after building 2023-07-21 19:17:06 +02:00
semantic-release-bot
67cf3e2eb4 chore(release): 0.114.0 [skip ci]
# [0.114.0](https://github.com/ReVanced/revanced-integrations/compare/v0.113.0...v0.114.0) (2023-07-21)

### Bug Fixes

* **Tiktok - Settings:** check for null when opening ads settings ([#443](https://github.com/ReVanced/revanced-integrations/issues/443)) ([8c61561](8c61561d63))

### Features

* **youtube/theme:** add a switch to enable/disable custom seekbar color ([#442](https://github.com/ReVanced/revanced-integrations/issues/442)) ([2a784a4](2a784a47d1))
2023-07-21 15:08:36 +00:00
oSumAtrIX
dcd97f3143 chore: merge branch dev to main (#444) 2023-07-21 17:04:35 +02:00
oSumAtrIX
e1011e42df ci: do not cache build directory 2023-07-21 16:58:00 +02:00
semantic-release-bot
e2f25cd5ae chore(release): 0.114.0-dev.1 [skip ci]
# [0.114.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.113.1-dev.1...v0.114.0-dev.1) (2023-07-20)

### Features

* **youtube/theme:** add a switch to enable/disable custom seekbar color ([#442](https://github.com/ReVanced/revanced-integrations/issues/442)) ([2a784a4](2a784a47d1))
2023-07-20 17:28:49 +00:00
LisoUseInAIKyrios
2a784a47d1 feat(youtube/theme): add a switch to enable/disable custom seekbar color (#442) 2023-07-20 21:26:50 +04:00
49 changed files with 1799 additions and 580 deletions

View File

@@ -30,7 +30,6 @@ jobs:
${{ runner.home }}/.gradle/caches
${{ runner.home }}/.gradle/wrapper
.gradle
build
node_modules
key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
- name: Setup Java
@@ -38,7 +37,7 @@ jobs:
- name: Build with Gradle
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew clean --no-daemon
run: ./gradlew build clean
- name: Setup semantic-release
run: npm install
- name: Release

View File

@@ -1,3 +1,99 @@
## [0.115.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0...v0.115.1-dev.1) (2023-08-05)
### Bug Fixes
* **YouTube - Hide Shorts components:** Hide new type of shorts in feed ([#453](https://github.com/ReVanced/revanced-integrations/issues/453)) ([6959808](https://github.com/ReVanced/revanced-integrations/commit/6959808f0c24e638a33f671728f7a9668f2de2c2))
# [0.115.0](https://github.com/ReVanced/revanced-integrations/compare/v0.114.0...v0.115.0) (2023-08-02)
### Bug Fixes
* **YouTube - SponsorBlock:** Fix skip highlight button showing when set to 'show in seekbar' ([ea7bc57](https://github.com/ReVanced/revanced-integrations/commit/ea7bc5737584ce967becd5b9e5d0d57f5d2fbc49))
* **YouTube - Spoof App Version:** Remove 17.30.35 target ([e906c6d](https://github.com/ReVanced/revanced-integrations/commit/e906c6d3a2195c94c926f9903f1e4e66c3714e3e))
* **YouTube:** fix potential litho filter thread race ([d3f523f](https://github.com/ReVanced/revanced-integrations/commit/d3f523f9dd5c3638cf9e2ba849b91e446daa99fa))
### Features
* **Tiktok - Feed filter:** Add more filters ([#445](https://github.com/ReVanced/revanced-integrations/issues/445)) ([c16c038](https://github.com/ReVanced/revanced-integrations/commit/c16c038396bde365ce1eec7b385eeb1f485efe99))
* **YouTube - Hide layout components:** Hide `chips shelf` ([#448](https://github.com/ReVanced/revanced-integrations/issues/448)) ([c7d2a9b](https://github.com/ReVanced/revanced-integrations/commit/c7d2a9b3101439b241218c45d0c4b64012adab65))
* **YouTube:** add `Player Flyout Menu` patch ([#416](https://github.com/ReVanced/revanced-integrations/issues/416)) ([b2085ae](https://github.com/ReVanced/revanced-integrations/commit/b2085aeebf4cfc21d69c0abfe545b382e2c82abe))
### Performance Improvements
* **YouTube:** Filter litho components using prefix tree ([#447](https://github.com/ReVanced/revanced-integrations/issues/447)) ([18f2900](https://github.com/ReVanced/revanced-integrations/commit/18f29004b8d570915a0228e292f1256785ac2cab))
# [0.115.0-dev.7](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.6...v0.115.0-dev.7) (2023-08-02)
### Features
* **Tiktok - Feed filter:** Add more filters ([#445](https://github.com/ReVanced/revanced-integrations/issues/445)) ([c16c038](https://github.com/ReVanced/revanced-integrations/commit/c16c038396bde365ce1eec7b385eeb1f485efe99))
# [0.115.0-dev.6](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.5...v0.115.0-dev.6) (2023-08-02)
### Bug Fixes
* **YouTube:** fix potential litho filter thread race ([d3f523f](https://github.com/ReVanced/revanced-integrations/commit/d3f523f9dd5c3638cf9e2ba849b91e446daa99fa))
# [0.115.0-dev.5](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.4...v0.115.0-dev.5) (2023-08-01)
### Performance Improvements
* **YouTube:** Filter litho components using prefix tree ([#447](https://github.com/ReVanced/revanced-integrations/issues/447)) ([18f2900](https://github.com/ReVanced/revanced-integrations/commit/18f29004b8d570915a0228e292f1256785ac2cab))
# [0.115.0-dev.4](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.3...v0.115.0-dev.4) (2023-07-30)
### Features
* **YouTube:** add `Player Flyout Menu` patch ([#416](https://github.com/ReVanced/revanced-integrations/issues/416)) ([b2085ae](https://github.com/ReVanced/revanced-integrations/commit/b2085aeebf4cfc21d69c0abfe545b382e2c82abe))
# [0.115.0-dev.3](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.2...v0.115.0-dev.3) (2023-07-27)
### Bug Fixes
* **YouTube - Spoof App Version:** Remove 17.30.35 target ([e906c6d](https://github.com/ReVanced/revanced-integrations/commit/e906c6d3a2195c94c926f9903f1e4e66c3714e3e))
# [0.115.0-dev.2](https://github.com/ReVanced/revanced-integrations/compare/v0.115.0-dev.1...v0.115.0-dev.2) (2023-07-26)
### Bug Fixes
* **YouTube - SponsorBlock:** Fix skip highlight button showing when set to 'show in seekbar' ([ea7bc57](https://github.com/ReVanced/revanced-integrations/commit/ea7bc5737584ce967becd5b9e5d0d57f5d2fbc49))
# [0.115.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.114.0...v0.115.0-dev.1) (2023-07-26)
### Features
* **YouTube - Hide layout components:** Hide `chips shelf` ([#448](https://github.com/ReVanced/revanced-integrations/issues/448)) ([c7d2a9b](https://github.com/ReVanced/revanced-integrations/commit/c7d2a9b3101439b241218c45d0c4b64012adab65))
# [0.114.0](https://github.com/ReVanced/revanced-integrations/compare/v0.113.0...v0.114.0) (2023-07-21)
### Bug Fixes
* **Tiktok - Settings:** check for null when opening ads settings ([#443](https://github.com/ReVanced/revanced-integrations/issues/443)) ([8c61561](https://github.com/ReVanced/revanced-integrations/commit/8c61561d6375cfad3571cea98013b549ed6ecd6b))
### Features
* **youtube/theme:** add a switch to enable/disable custom seekbar color ([#442](https://github.com/ReVanced/revanced-integrations/issues/442)) ([2a784a4](https://github.com/ReVanced/revanced-integrations/commit/2a784a47d18da90ce23fcd527eef1f6e2d8e166d))
# [0.114.0-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.113.1-dev.1...v0.114.0-dev.1) (2023-07-20)
### Features
* **youtube/theme:** add a switch to enable/disable custom seekbar color ([#442](https://github.com/ReVanced/revanced-integrations/issues/442)) ([2a784a4](https://github.com/ReVanced/revanced-integrations/commit/2a784a47d18da90ce23fcd527eef1f6e2d8e166d))
## [0.113.1-dev.1](https://github.com/ReVanced/revanced-integrations/compare/v0.113.0...v0.113.1-dev.1) (2023-07-20)

View File

@@ -1,9 +0,0 @@
package app.revanced.integrations.patches;
import app.revanced.integrations.settings.SettingsEnum;
public class HideWatchInVRPatch {
public static boolean hideWatchInVR() {
return SettingsEnum.HIDE_WATCH_IN_VR.getBoolean();
}
}

View File

@@ -2,21 +2,25 @@ package app.revanced.integrations.patches.components;
import android.view.View;
import androidx.annotation.Nullable;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.ReVancedUtils;
import app.revanced.integrations.utils.StringTrieSearch;
public final class AdsFilter extends Filter {
private final String[] exceptions;
private final StringTrieSearch exceptions = new StringTrieSearch();
public AdsFilter() {
exceptions = new String[]{
exceptions.addPatterns(
"home_video_with_context", // Don't filter anything in the home page video component.
"related_video_with_context", // Don't filter anything in the related video component.
"comment_thread", // Don't filter anything in the comments.
"|comment.", // Don't filter anything in the comments replies.
"library_recent_shelf",
};
"library_recent_shelf"
);
final var buttonedAd = new StringFilterGroup(
SettingsEnum.HIDE_BUTTONED_ADS,
@@ -95,11 +99,12 @@ public final class AdsFilter extends Filter {
}
@Override
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
if (ReVancedUtils.containsAny(path, exceptions))
public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (exceptions.matches(path))
return false;
return super.isFiltered(path, identifier, _protobufBufferArray);
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
/**

View File

@@ -1,5 +1,7 @@
package app.revanced.integrations.patches.components;
import androidx.annotation.Nullable;
import app.revanced.integrations.settings.SettingsEnum;
final class ButtonsFilter extends Filter {
@@ -33,7 +35,8 @@ final class ButtonsFilter extends Filter {
SettingsEnum.HIDE_ACTION_BUTTONS,
"ContainerType|video_action_button",
"|CellType|CollectionType|CellType|ContainerType|button.eml|"
)
),
actionBarRule
);
}
@@ -45,10 +48,12 @@ final class ButtonsFilter extends Filter {
}
@Override
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
if (isEveryFilterGroupEnabled())
if (actionBarRule.check(identifier).isFiltered()) return true;
public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (matchedGroup == actionBarRule) {
return isEveryFilterGroupEnabled();
}
return super.isFiltered(path, identifier, _protobufBufferArray);
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
}

View File

@@ -2,15 +2,16 @@ package app.revanced.integrations.patches.components;
import android.os.Build;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.ReVancedUtils;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.StringTrieSearch;
@RequiresApi(api = Build.VERSION_CODES.N)
public final class LayoutComponentsFilter extends Filter {
private final String[] exceptions;
private final StringTrieSearch exceptions = new StringTrieSearch();
private final CustomFilterGroup custom;
private static final ByteArrayAsStringFilterGroup mixPlaylists = new ByteArrayAsStringFilterGroup(
@@ -20,13 +21,13 @@ public final class LayoutComponentsFilter extends Filter {
@RequiresApi(api = Build.VERSION_CODES.N)
public LayoutComponentsFilter() {
exceptions = new String[]{
exceptions.addPatterns(
"home_video_with_context",
"related_video_with_context",
"comment_thread", // skip filtering anything in the comments
"|comment.", // skip filtering anything in the comments replies
"library_recent_shelf",
};
"library_recent_shelf"
);
custom = new CustomFilterGroup(
SettingsEnum.CUSTOM_FILTER,
@@ -136,6 +137,11 @@ public final class LayoutComponentsFilter extends Filter {
"cell_divider" // layout residue (gray line above the buttoned ad),
);
final var chipsShelf = new StringFilterGroup(
SettingsEnum.HIDE_CHIPS_SHELF,
"chips_shelf"
);
this.pathFilterGroups.addAll(
channelBar,
communityPosts,
@@ -155,26 +161,32 @@ public final class LayoutComponentsFilter extends Filter {
artistCard,
imageShelf,
subscribersCommunityGuidelines,
channelMemberShelf
channelMemberShelf,
custom
);
this.identifierFilterGroups.addAll(graySeparator);
this.identifierFilterGroups.addAll(
graySeparator,
chipsShelf
);
}
@Override
public boolean isFiltered(final String path, final String identifier, final byte[] _protobufBufferArray) {
if (custom.isEnabled() && custom.check(path).isFiltered())
return true;
if (ReVancedUtils.containsAny(path, exceptions))
public boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (matchedGroup != custom && exceptions.matches(path))
return false; // Exceptions are not filtered.
return super.isFiltered(path, identifier, _protobufBufferArray);
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
// Called from a different place then the other filters.
/**
* Injection point.
*
* Called from a different place then the other filters.
*/
public static boolean filterMixPlaylists(final byte[] bytes) {
return mixPlaylists.isEnabled() && mixPlaylists.check(bytes).isFiltered();
return mixPlaylists.check(bytes).isFiltered();
}
}

View File

@@ -1,5 +1,7 @@
package app.revanced.integrations.patches.components;
import androidx.annotation.Nullable;
// Abuse LithoFilter for CustomPlaybackSpeedPatch.
public final class PlaybackSpeedMenuFilterPatch extends Filter {
// Must be volatile or synchronized, as litho filtering runs off main thread and this field is then access from the main thread.
@@ -13,8 +15,9 @@ public final class PlaybackSpeedMenuFilterPatch extends Filter {
}
@Override
boolean isFiltered(final String path, final String identifier, final byte[] protobufBufferArray) {
isPlaybackSpeedMenuVisible = super.isFiltered(path, identifier, protobufBufferArray);
boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
isPlaybackSpeedMenuVisible = true;
return false;
}

View File

@@ -0,0 +1,74 @@
package app.revanced.integrations.patches.components;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import app.revanced.integrations.settings.SettingsEnum;
public class PlayerFlyoutMenuItemsFilter extends Filter {
// Search the buffer only if the flyout menu identifier is found.
// Handle the searching in this class instead of adding to the global filter group (which searches all the time)
private final ByteArrayFilterGroupList flyoutFilterGroupList = new ByteArrayFilterGroupList();
@RequiresApi(api = Build.VERSION_CODES.N)
public PlayerFlyoutMenuItemsFilter() {
identifierFilterGroups.addAll(new StringFilterGroup(null, "overflow_menu_item.eml|"));
flyoutFilterGroupList.addAll(
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_QUALITY_MENU,
"yt_outline_gear"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_CAPTIONS_MENU,
"closed_caption"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_LOOP_VIDEO_MENU,
"yt_outline_arrow_repeat_1_"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_AMBIENT_MODE_MENU,
"yt_outline_screen_light"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_REPORT_MENU,
"yt_outline_flag"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_HELP_MENU,
"yt_outline_question_circle"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_MORE_INFO_MENU,
"yt_outline_info_circle"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_SPEED_MENU,
"yt_outline_play_arrow_half_circle"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_AUDIO_TRACK_MENU,
"yt_outline_person_radar"
),
new ByteArrayAsStringFilterGroup(
SettingsEnum.HIDE_WATCH_IN_VR_MENU,
"yt_outline_vr"
)
);
}
@Override
boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
// Only 1 group is added to the parent class, so the matched group must be the overflow menu.
if (matchedIndex == 0 && flyoutFilterGroupList.check(protobufBufferArray).isFiltered()) {
// Super class handles logging.
return super.isFiltered(path, identifier, protobufBufferArray, matchedList, matchedGroup, matchedIndex);
}
return false;
}
}

View File

@@ -1,22 +1,40 @@
package app.revanced.integrations.patches.components;
import android.view.View;
import app.revanced.integrations.settings.SettingsEnum;
import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
import static app.revanced.integrations.utils.ReVancedUtils.hideViewBy1dpUnderCondition;
import static app.revanced.integrations.utils.ReVancedUtils.hideViewUnderCondition;
import android.view.View;
import androidx.annotation.Nullable;
import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
import app.revanced.integrations.settings.SettingsEnum;
public final class ShortsFilter extends Filter {
// Set by patch.
public static PivotBar pivotBar;
final StringFilterGroupList shortsFilterGroup = new StringFilterGroupList();
private final StringFilterGroup reelChannelBar = new StringFilterGroup(
null,
"reel_channel_bar"
);
private static final String REEL_CHANNEL_BAR_PATH = "reel_channel_bar";
public static PivotBar pivotBar; // Set by patch.
private final StringFilterGroup channelBar;
private final StringFilterGroup soundButton;
private final StringFilterGroup infoPanel;
public ShortsFilter() {
channelBar = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_CHANNEL_BAR,
REEL_CHANNEL_BAR_PATH
);
soundButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_SOUND_BUTTON,
"reel_pivot_button"
);
infoPanel = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_INFO_PANEL,
"shorts_info_panel_overview"
);
final var thanksButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_THANKS_BUTTON,
"suggested_action"
@@ -32,45 +50,30 @@ public final class ShortsFilter extends Filter {
"sponsor_button"
);
final var soundButton = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_SOUND_BUTTON,
"reel_pivot_button"
);
final var infoPanel = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_INFO_PANEL,
"shorts_info_panel_overview"
);
final var channelBar = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS_CHANNEL_BAR,
"reel_channel_bar"
);
final var shorts = new StringFilterGroup(
SettingsEnum.HIDE_SHORTS,
"shorts_shelf",
"inline_shorts",
"shorts_grid",
"shorts_video_cell"
"shorts_video_cell",
"shorts_pivot_item"
);
shortsFilterGroup.addAll(soundButton, infoPanel);
pathFilterGroups.addAll(joinButton, subscribeButton, channelBar);
pathFilterGroups.addAll(joinButton, subscribeButton, channelBar, soundButton, infoPanel);
identifierFilterGroups.addAll(shorts, thanksButton);
}
@Override
boolean isFiltered(final String path, final String identifier,
final byte[] protobufBufferArray) {
boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
if (matchedGroup == soundButton || matchedGroup == infoPanel || matchedGroup == channelBar) return true;
// Filter the path only when reelChannelBar is visible.
if (reelChannelBar.check(path).isFiltered())
if (this.pathFilterGroups.contains(path)) return true;
if (pathFilterGroups == matchedList) {
return path.contains(REEL_CHANNEL_BAR_PATH);
}
if (shortsFilterGroup.contains(path)) return true;
return this.identifierFilterGroups.contains(identifier);
return identifierFilterGroups == matchedList;
}
public static void hideShortsShelf(final View shortsShelfView) {

View File

@@ -1,5 +1,7 @@
package app.revanced.integrations.patches.components;
import androidx.annotation.Nullable;
import app.revanced.integrations.settings.SettingsEnum;
// Abuse LithoFilter for OldVideoQualityMenuPatch.
@@ -15,8 +17,9 @@ public final class VideoQualityMenuFilterPatch extends Filter {
}
@Override
boolean isFiltered(final String path, final String identifier, final byte[] protobufBufferArray) {
isVideoQualityMenuVisible = super.isFiltered(path, identifier, protobufBufferArray);
boolean isFiltered(String path, @Nullable String identifier, byte[] protobufBufferArray,
FilterGroupList matchedList, FilterGroup matchedGroup, int matchedIndex) {
isVideoQualityMenuVisible = true;
return false;
}

View File

@@ -26,7 +26,8 @@ public final class OldVideoQualityMenuPatch {
// The quality menu is a RecyclerView with 3 children. The third child is the "Advanced" quality menu.
addRecyclerListener(linearLayout, 3, 2, recyclerView -> {
// Check if the current view is the quality menu.
if (VideoQualityMenuFilterPatch.isVideoQualityMenuVisible) {// Hide the video quality menu.
if (VideoQualityMenuFilterPatch.isVideoQualityMenuVisible) {
VideoQualityMenuFilterPatch.isVideoQualityMenuVisible = false;
linearLayout.setVisibility(View.GONE);
// Click the "Advanced" quality menu to show the "old" quality menu.

View File

@@ -1,22 +1,19 @@
package app.revanced.integrations.patches.playback.speed;
import static app.revanced.integrations.patches.playback.quality.OldVideoQualityMenuPatch.addRecyclerListener;
import android.preference.ListPreference;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import com.facebook.litho.ComponentHost;
import java.util.Arrays;
import app.revanced.integrations.patches.components.PlaybackSpeedMenuFilterPatch;
import app.revanced.integrations.settings.SettingsEnum;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.integrations.utils.ReVancedUtils;
import com.facebook.litho.ComponentHost;
import java.util.Arrays;
import static app.revanced.integrations.patches.playback.quality.OldVideoQualityMenuPatch.addRecyclerListener;
public class CustomPlaybackSpeedPatch {
/**
@@ -110,23 +107,24 @@ public class CustomPlaybackSpeedPatch {
}
/*
* To reduce copy paste between two similar code paths.
* To reduce copy and paste between two similar code paths.
*/
public static void onFlyoutMenuCreate(final LinearLayout linearLayout) {
// The playback rate menu is a RecyclerView with 2 children. The third child is the "Advanced" quality menu.
addRecyclerListener(linearLayout, 2, 1, recyclerView -> {
if (PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible &&
recyclerView.getChildCount() == 1 &&
recyclerView.getChildAt(0) instanceof ComponentHost
) {
linearLayout.setVisibility(View.GONE);
if (PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible) {
PlaybackSpeedMenuFilterPatch.isPlaybackSpeedMenuVisible = false;
// Close the new Playback speed menu and instead show the old one.
showOldPlaybackSpeedMenu();
if (recyclerView.getChildCount() == 1 && recyclerView.getChildAt(0) instanceof ComponentHost) {
linearLayout.setVisibility(View.GONE);
// DismissView [R.id.touch_outside] is the 1st ChildView of the 3rd ParentView.
((ViewGroup) linearLayout.getParent().getParent().getParent())
.getChildAt(0).performClick();
// Close the new Playback speed menu and instead show the old one.
showOldPlaybackSpeedMenu();
// DismissView [R.id.touch_outside] is the 1st ChildView of the 3rd ParentView.
((ViewGroup) linearLayout.getParent().getParent().getParent())
.getChildAt(0).performClick();
}
}
});
}

View File

@@ -18,6 +18,7 @@ import app.revanced.integrations.settings.SettingsEnum;
*/
public class ProgressBarDrawable extends Drawable {
private final Paint paint = new Paint();
@Override
@@ -25,7 +26,7 @@ public class ProgressBarDrawable extends Drawable {
if (SettingsEnum.HIDE_SEEKBAR_THUMBNAIL.getBoolean()) {
return;
}
paint.setColor(SeekbarColorPatch.getCustomSeekbarColor());
paint.setColor(SeekbarColorPatch.getSeekbarColor());
canvas.drawRect(getBounds(), paint);
}

View File

@@ -8,8 +8,10 @@ import app.revanced.integrations.utils.ReVancedUtils;
public final class SeekbarColorPatch {
private static final boolean USE_SEEKBAR_CUSTOM_COLOR = SettingsEnum.SEEKBAR_CUSTOM_COLOR.getBoolean();
/**
* Default color of seekbar.
* Default color of the seekbar.
*/
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
@@ -19,9 +21,11 @@ public final class SeekbarColorPatch {
private static final float ORIGINAL_SEEKBAR_COLOR_BRIGHTNESS;
/**
* Color value of {@link SettingsEnum#SEEKBAR_COLOR}
* If {@link SettingsEnum#SEEKBAR_CUSTOM_COLOR} is enabled,
* this is the color value of {@link SettingsEnum#SEEKBAR_CUSTOM_COLOR_VALUE}.
* Otherwise this is {@link #ORIGINAL_SEEKBAR_COLOR}.
*/
private static int customSeekbarColor;
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;
/**
* Custom seekbar hue, saturation, and brightness values.
@@ -33,22 +37,24 @@ public final class SeekbarColorPatch {
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
ORIGINAL_SEEKBAR_COLOR_BRIGHTNESS = hsv[2];
loadCustomSeekbarColorHSV();
}
private static void loadCustomSeekbarColorHSV() {
try {
customSeekbarColor = Color.parseColor(SettingsEnum.SEEKBAR_COLOR.getString());
Color.colorToHSV(customSeekbarColor, customSeekbarColorHSV);
} catch (Exception ex) {
ReVancedUtils.showToastShort("Invalid seekbar color value. Using default value.");
SettingsEnum.SEEKBAR_COLOR.saveValue(SettingsEnum.SEEKBAR_COLOR.defaultValue);
loadCustomSeekbarColorHSV();
if (USE_SEEKBAR_CUSTOM_COLOR) {
loadCustomSeekbarColor();
}
}
public static int getCustomSeekbarColor() {
return customSeekbarColor;
private static void loadCustomSeekbarColor() {
try {
seekbarColor = Color.parseColor(SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.getString());
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
} catch (Exception ex) {
ReVancedUtils.showToastShort("Invalid seekbar color value. Using default value.");
SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(SettingsEnum.SEEKBAR_CUSTOM_COLOR_VALUE.defaultValue);
loadCustomSeekbarColor();
}
}
public static int getSeekbarColor() {
return seekbarColor;
}
@@ -96,7 +102,7 @@ public final class SeekbarColorPatch {
*/
private static int getSeekbarColorValue(int originalColor) {
try {
if (customSeekbarColor == ORIGINAL_SEEKBAR_COLOR) {
if (!USE_SEEKBAR_CUSTOM_COLOR || originalColor == seekbarColor) {
return originalColor; // nothing to do
}
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
@@ -111,7 +117,7 @@ public final class SeekbarColorPatch {
hsv[1] = customSeekbarColorHSV[1];
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 1);
final int replacementAlpha = clamp(Color.alpha(customSeekbarColor) + alphaDifference, 0, 255);
final int replacementAlpha = clamp(Color.alpha(seekbarColor) + alphaDifference, 0, 255);
final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
LogHelper.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
originalColor, replacementColor));

View File

@@ -105,6 +105,7 @@ public enum SettingsEnum {
HIDE_CAST_BUTTON("revanced_hide_cast_button", BOOLEAN, TRUE, true),
HIDE_COMMENTS_SECTION("revanced_hide_comments_section", BOOLEAN, FALSE, true),
HIDE_CREATE_BUTTON("revanced_hide_create_button", BOOLEAN, TRUE, true),
HIDE_CHIPS_SHELF("revanced_hide_chips_shelf", BOOLEAN, TRUE),
HIDE_CROWDFUNDING_BOX("revanced_hide_crowdfunding_box", BOOLEAN, FALSE, true),
HIDE_EMAIL_ADDRESS("revanced_hide_email_address", BOOLEAN, FALSE),
HIDE_ENDSCREEN_CARDS("revanced_hide_endscreen_cards", BOOLEAN, TRUE),
@@ -116,21 +117,23 @@ public enum SettingsEnum {
HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", BOOLEAN, FALSE),
HIDE_PLAYER_OVERLAY("revanced_hide_player_overlay", BOOLEAN, FALSE, true),
HIDE_PREVIEW_COMMENT("revanced_hide_preview_comment", BOOLEAN, FALSE, true),
HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE, true),
HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE, true),
HIDE_SEEKBAR("revanced_hide_seekbar", BOOLEAN, FALSE),
HIDE_SEEKBAR_THUMBNAIL("revanced_hide_seekbar_thumbnail", BOOLEAN, FALSE),
HIDE_HOME_BUTTON("revanced_hide_home_button", BOOLEAN, FALSE, true),
HIDE_SHORTS_BUTTON("revanced_hide_shorts_button", BOOLEAN, TRUE, true),
HIDE_SUBSCRIPTIONS_BUTTON("revanced_hide_subscriptions_button", BOOLEAN, FALSE, true),
HIDE_TIMESTAMP("revanced_hide_timestamp", BOOLEAN, FALSE),
HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", BOOLEAN, TRUE),
HIDE_WATCH_IN_VR("revanced_hide_watch_in_vr", BOOLEAN, FALSE, true),
PLAYER_POPUP_PANELS("revanced_hide_player_popup_panels", BOOLEAN, FALSE),
SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON("revanced_switch_create_with_notifications_button", BOOLEAN, TRUE, true),
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)),
SPOOF_APP_VERSION_TARGET("revanced_spoof_app_version_target", STRING, "17.08.35", true, parents(SPOOF_APP_VERSION)),
USE_TABLET_MINIPLAYER("revanced_tablet_miniplayer", BOOLEAN, FALSE, true),
WIDE_SEARCHBAR("revanced_wide_searchbar", BOOLEAN, FALSE, true),
SEEKBAR_COLOR("revanced_seekbar_color", STRING, "#FF0000", true),
@Deprecated
DEPRECATED_SEEKBAR_COLOR("revanced_seekbar_color", STRING, "#FF0000"), // TODO: delete this
SEEKBAR_CUSTOM_COLOR("revanced_seekbar_custom_color", BOOLEAN, TRUE, true),
SEEKBAR_CUSTOM_COLOR_VALUE("revanced_seekbar_custom_color_value", STRING, "#FF0000", true, parents(SEEKBAR_CUSTOM_COLOR)),
HIDE_FILTER_BAR_FEED_IN_FEED("revanced_hide_filter_bar_feed_in_feed", BOOLEAN, FALSE, true),
HIDE_FILTER_BAR_FEED_IN_SEARCH("revanced_hide_filter_bar_feed_in_search", BOOLEAN, FALSE, true),
HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS("revanced_hide_filter_bar_feed_in_related_videos", BOOLEAN, FALSE, true),
@@ -146,6 +149,18 @@ public enum SettingsEnum {
HIDE_SHORTS_NAVIGATION_BAR("revanced_hide_shorts_navigation_bar", BOOLEAN, TRUE, true),
HIDE_SHORTS("revanced_hide_shorts", BOOLEAN, FALSE, true),
//Player flyout menu items
HIDE_QUALITY_MENU("revanced_hide_player_flyout_quality", BOOLEAN, FALSE),
HIDE_CAPTIONS_MENU("revanced_hide_player_flyout_captions", BOOLEAN, FALSE),
HIDE_LOOP_VIDEO_MENU("revanced_hide_player_flyout_loop_video", BOOLEAN, FALSE),
HIDE_AMBIENT_MODE_MENU("revanced_hide_player_flyout_ambient_mode", BOOLEAN, FALSE),
HIDE_REPORT_MENU("revanced_hide_player_flyout_report", BOOLEAN, TRUE),
HIDE_HELP_MENU("revanced_hide_player_flyout_help", BOOLEAN, TRUE),
HIDE_SPEED_MENU("revanced_hide_player_flyout_speed", BOOLEAN, FALSE),
HIDE_MORE_INFO_MENU("revanced_hide_player_flyout_more_info", BOOLEAN, TRUE),
HIDE_AUDIO_TRACK_MENU("revanced_hide_player_flyout_audio_track", BOOLEAN, TRUE),
HIDE_WATCH_IN_VR_MENU("revanced_hide_player_flyout_watch_in_vr", BOOLEAN, TRUE),
// Misc
AUTO_CAPTIONS("revanced_auto_captions", BOOLEAN, FALSE),
DISABLE_ZOOM_HAPTICS("revanced_disable_zoom_haptics", BOOLEAN, TRUE),
@@ -359,6 +374,14 @@ public enum SettingsEnum {
// TODO: delete DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU (When? anytime).
migrateOldSettingToNew(DEPRECATED_SHOW_OLD_VIDEO_QUALITY_MENU, SHOW_OLD_VIDEO_QUALITY_MENU);
// TODO: delete this seekbar color migration code
String oldSeekbarColorValue = DEPRECATED_SEEKBAR_COLOR.getString();
if (!oldSeekbarColorValue.equalsIgnoreCase((String) DEPRECATED_SEEKBAR_COLOR.defaultValue)) {
SEEKBAR_CUSTOM_COLOR_VALUE.saveValue(oldSeekbarColorValue);
SEEKBAR_CUSTOM_COLOR.saveValue(true);
DEPRECATED_SEEKBAR_COLOR.saveValue(DEPRECATED_SEEKBAR_COLOR.defaultValue);
}
// endregion
}

View File

@@ -50,7 +50,7 @@ public class SegmentPlaybackController {
private static SponsorSegment[] segments;
/**
* Highlight segment, if one exists.
* Highlight segment, if one exists and the skip behavior is not set to {@link CategoryBehaviour#SHOW_IN_SEEKBAR}.
*/
@Nullable
private static SponsorSegment highlightSegment;
@@ -112,10 +112,13 @@ public class SegmentPlaybackController {
segments = videoSegments;
calculateTimeWithoutSegments();
for (SponsorSegment segment : videoSegments) {
if (segment.category == SegmentCategory.HIGHLIGHT) {
highlightSegment = segment;
return;
if (SegmentCategory.HIGHLIGHT.behaviour == CategoryBehaviour.SKIP_AUTOMATICALLY
|| SegmentCategory.HIGHLIGHT.behaviour == CategoryBehaviour.MANUAL_SKIP) {
for (SponsorSegment segment : videoSegments) {
if (segment.category == SegmentCategory.HIGHLIGHT) {
highlightSegment = segment;
return;
}
}
}
highlightSegment = null;

View File

@@ -0,0 +1,38 @@
package app.revanced.integrations.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
public final class ByteTrieSearch extends TrieSearch<byte[]> {
private static final class ByteTrieNode extends TrieNode<byte[]> {
TrieNode<byte[]> createNode() {
return new ByteTrieNode();
}
char getCharValue(byte[] text, int index) {
return (char) text[index];
}
}
public ByteTrieSearch() {
super(new ByteTrieNode());
}
@Override
public void addPattern(@NonNull byte[] pattern) {
super.addPattern(pattern, pattern.length, null);
}
@Override
public void addPattern(@NonNull byte[] pattern, @NonNull TriePatternMatchedCallback<byte[]> callback) {
super.addPattern(pattern, pattern.length, Objects.requireNonNull(callback));
}
@Override
public boolean matches(@NonNull byte[] textToSearch, @Nullable Object callbackParameter) {
return super.matches(textToSearch, textToSearch.length, callbackParameter);
}
}

View File

@@ -0,0 +1,40 @@
package app.revanced.integrations.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Objects;
/**
* Text pattern searching using a prefix tree (trie).
*/
public final class StringTrieSearch extends TrieSearch<String> {
private static final class StringTrieNode extends TrieNode<String> {
TrieNode<String> createNode() {
return new StringTrieNode();
}
char getCharValue(String text, int index) {
return text.charAt(index);
}
}
public StringTrieSearch() {
super(new StringTrieNode());
}
@Override
public void addPattern(@NonNull String pattern) {
super.addPattern(pattern, pattern.length(), null);
}
@Override
public void addPattern(@NonNull String pattern, @NonNull TriePatternMatchedCallback<String> callback) {
super.addPattern(pattern, pattern.length(), Objects.requireNonNull(callback));
}
@Override
public boolean matches(@NonNull String textToSearch, @Nullable Object callbackParameter) {
return super.matches(textToSearch, textToSearch.length(), callbackParameter);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,10 +4,10 @@ import app.revanced.tiktok.settings.SettingsEnum;
public class DownloadsPatch {
public static String getDownloadPath() {
return SettingsEnum.TIK_DOWN_PATH.getString();
return SettingsEnum.DOWNLOAD_PATH.getString();
}
public static boolean shouldRemoveWatermark() {
return SettingsEnum.TIK_DOWN_WATERMARK.getBoolean();
return SettingsEnum.DOWNLOAD_WATERMARK.getBoolean();
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public class AdsFilter implements IFilter {
@Override
public boolean getEnabled() {
return SettingsEnum.REMOVE_ADS.getBoolean();
}
@Override
public boolean getFiltered(Aweme item) {
return item.isAd() || item.isWithPromotionalMusic();
}
}

View File

@@ -6,33 +6,28 @@ import com.ss.android.ugc.aweme.feed.model.FeedItemList;
import java.util.Iterator;
import java.util.List;
import app.revanced.tiktok.settings.SettingsEnum;
public class FeedItemsFilter {
public final class FeedItemsFilter {
private static final List<IFilter> FILTERS = List.of(
new AdsFilter(),
new LiveFilter(),
new StoryFilter(),
new ImageVideoFilter(),
new ViewCountFilter(),
new LikeCountFilter()
);
public static void filter(FeedItemList feedItemList) {
if (SettingsEnum.TIK_REMOVE_ADS.getBoolean()) removeAds(feedItemList);
if (SettingsEnum.TIK_HIDE_LIVE.getBoolean()) removeLive(feedItemList);
}
Iterator<Aweme> feedItemListIterator = feedItemList.items.iterator();
while (feedItemListIterator.hasNext()) {
Aweme item = feedItemListIterator.next();
if (item == null) continue;
private static void removeAds(FeedItemList feedItemList) {
List<Aweme> items = feedItemList.items;
Iterator<Aweme> it = items.iterator();
while (it.hasNext()) {
Aweme item = it.next();
if (item != null && (item.isAd() || item.isWithPromotionalMusic())) {
it.remove();
}
}
}
private static void removeLive(FeedItemList feedItemList) {
List<Aweme> items = feedItemList.items;
Iterator<Aweme> it = items.iterator();
while (it.hasNext()) {
Aweme item = it.next();
if (item != null && (item.isLive() || item.isLiveReplay())) {
it.remove();
for (IFilter filter : FILTERS) {
boolean enabled = filter.getEnabled();
if (enabled && filter.getFiltered(item)) {
feedItemListIterator.remove();
break;
}
}
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.tiktok.feedfilter;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public interface IFilter {
boolean getEnabled();
boolean getFiltered(Aweme item);
}

View File

@@ -0,0 +1,16 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public class ImageVideoFilter implements IFilter {
@Override
public boolean getEnabled() {
return SettingsEnum.HIDE_IMAGE.getBoolean();
}
@Override
public boolean getFiltered(Aweme item) {
return item.isImage() || item.isPhotoMode();
}
}

View File

@@ -0,0 +1,32 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
import static app.revanced.tiktok.utils.ReVancedUtils.parseMinMax;
public final class LikeCountFilter implements IFilter {
final long minLike;
final long maxLike;
LikeCountFilter() {
long[] minMax = parseMinMax(SettingsEnum.MIN_MAX_LIKES);
minLike = minMax[0];
maxLike = minMax[1];
}
@Override
public boolean getEnabled() {
return true;
}
@Override
public boolean getFiltered(Aweme item) {
AwemeStatistics statistics = item.getStatistics();
if (statistics == null) return false;
long likeCount = statistics.getDiggCount();
return likeCount < minLike || likeCount > maxLike;
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public class LiveFilter implements IFilter {
@Override
public boolean getEnabled() {
return SettingsEnum.HIDE_LIVE.getBoolean();
}
@Override
public boolean getFiltered(Aweme item) {
return item.isLive() || item.isLiveReplay();
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
public class StoryFilter implements IFilter {
@Override
public boolean getEnabled() {
return SettingsEnum.HIDE_STORY.getBoolean();
}
@Override
public boolean getFiltered(Aweme item) {
return item.getIsTikTokStory();
}
}

View File

@@ -0,0 +1,32 @@
package app.revanced.tiktok.feedfilter;
import app.revanced.tiktok.settings.SettingsEnum;
import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
import static app.revanced.tiktok.utils.ReVancedUtils.parseMinMax;
public class ViewCountFilter implements IFilter {
final long minView;
final long maxView;
ViewCountFilter() {
long[] minMax = parseMinMax(SettingsEnum.MIN_MAX_VIEWS);
minView = minMax[0];
maxView = minMax[1];
}
@Override
public boolean getEnabled() {
return true;
}
@Override
public boolean getFiltered(Aweme item) {
AwemeStatistics statistics = item.getStatistics();
if (statistics == null) return false;
long playCount = statistics.getPlayCount();
return playCount < minView || playCount > maxView;
}
}

View File

@@ -1,32 +1,42 @@
package app.revanced.tiktok.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.tiktok.settings.SettingsEnum.ReturnType.BOOLEAN;
import static app.revanced.tiktok.settings.SettingsEnum.ReturnType.STRING;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import app.revanced.tiktok.utils.LogHelper;
import app.revanced.tiktok.utils.ReVancedUtils;
import java.util.HashMap;
import java.util.Map;
import static app.revanced.tiktok.settings.SettingsEnum.ReturnType.BOOLEAN;
import static app.revanced.tiktok.settings.SettingsEnum.ReturnType.STRING;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
public enum SettingsEnum {
//TikTok Settings
TIK_DEBUG("tik_debug", BOOLEAN, FALSE), // must be first value, otherwise logging during loading will not work
TIK_REMOVE_ADS("tik_remove_ads", BOOLEAN, TRUE, true),
TIK_HIDE_LIVE("tik_hide_live", BOOLEAN, FALSE, true),
TIK_DOWN_PATH("tik_down_path", STRING, "DCIM/TikTok"),
TIK_DOWN_WATERMARK("tik_down_watermark", BOOLEAN, TRUE),
TIK_SIMSPOOF("tik_simspoof", BOOLEAN, TRUE, true),
TIK_SIMSPOOF_ISO("tik_simspoof_iso", STRING, "us"),
TIK_SIMSPOOF_MCCMNC("tik_simspoof_mccmnc", STRING, "310160"),
TIK_SIMSPOOF_OP_NAME("tik_simspoof_op_name", STRING, "T-Mobile");
DEBUG("debug", BOOLEAN, FALSE), // Must be first value, otherwise logging during loading will not work.
REMOVE_ADS("remove_ads", BOOLEAN, TRUE, true),
HIDE_LIVE("hide_live", BOOLEAN, FALSE, true),
HIDE_STORY("hide_story", BOOLEAN, FALSE, true),
HIDE_IMAGE("hide_image", BOOLEAN, FALSE, true),
MIN_MAX_VIEWS("min_max_views", STRING, "0-" + Long.MAX_VALUE, true),
MIN_MAX_LIKES("min_max_likes", STRING, "0-" + Long.MAX_VALUE, true),
DOWNLOAD_PATH("down_path", STRING, "DCIM/TikTok"),
DOWNLOAD_WATERMARK("down_watermark", BOOLEAN, TRUE),
SIM_SPOOF("simspoof", BOOLEAN, TRUE, true),
SIM_SPOOF_ISO("simspoof_iso", STRING, "us"),
SIMSPOOF_MCCMNC("simspoof_mccmnc", STRING, "310160"),
SIMSPOOF_OP_NAME("simspoof_op_name", STRING, "T-Mobile");
private static final Map<String, SettingsEnum> pathToSetting = new HashMap<>(2 * values().length);
static {
loadAllSettings();
for (SettingsEnum setting : values()) {
pathToSetting.put(setting.path, setting);
}
}
@NonNull
@@ -47,11 +57,12 @@ public enum SettingsEnum {
SettingsEnum(String path, ReturnType returnType, Object defaultValue) {
this(path, returnType, defaultValue, SharedPrefCategory.TIKTOK_PREFS, false);
}
SettingsEnum(String path, ReturnType returnType, Object defaultValue, boolean rebootApp) {
this(path, returnType, defaultValue, SharedPrefCategory.TIKTOK_PREFS, rebootApp);
}
SettingsEnum(@NonNull String path, @NonNull ReturnType returnType, @NonNull Object defaultValue,
@NonNull SharedPrefCategory prefName, boolean rebootApp) {
SettingsEnum(@NonNull String path, @NonNull ReturnType returnType, @NonNull Object defaultValue, @NonNull SharedPrefCategory prefName, boolean rebootApp) {
this.path = path;
this.returnType = returnType;
this.defaultValue = defaultValue;
@@ -59,6 +70,11 @@ public enum SettingsEnum {
this.rebootApp = rebootApp;
}
@Nullable
public static SettingsEnum getSettingsFromPath(@NonNull String str) {
return pathToSetting.get(str);
}
private static void loadAllSettings() {
try {
Context context = ReVancedUtils.getAppContext();
@@ -144,11 +160,15 @@ public enum SettingsEnum {
return (String) value;
}
/**
* @return the value of this setting as as generic object type.
*/
@NonNull
public Object getObjectValue() {
return value;
}
public enum ReturnType {
BOOLEAN,
INTEGER,
LONG,
FLOAT,
STRING,
BOOLEAN, INTEGER, LONG, FLOAT, STRING,
}
}

View File

@@ -0,0 +1,119 @@
package app.revanced.tiktok.settingsmenu;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Process;
import android.preference.*;
import androidx.annotation.Nullable;
import app.revanced.integrations.utils.LogHelper;
import app.revanced.tiktok.settings.SettingsEnum;
import app.revanced.tiktok.settings.SharedPrefCategory;
import app.revanced.tiktok.settingsmenu.preference.DownloadPathPreference;
import app.revanced.tiktok.settingsmenu.preference.RangeValuePreference;
import app.revanced.tiktok.settingsmenu.preference.categories.DownloadsPreferenceCategory;
import app.revanced.tiktok.settingsmenu.preference.categories.FeedFilterPreferenceCategory;
import app.revanced.tiktok.settingsmenu.preference.categories.IntegrationsPreferenceCategory;
import app.revanced.tiktok.settingsmenu.preference.categories.SimSpoofPreferenceCategory;
import app.revanced.tiktok.utils.ReVancedUtils;
import com.ss.android.ugc.aweme.splash.SplashActivity;
@SuppressWarnings("deprecation")
public class ReVancedPreferenceFragment extends PreferenceFragment {
private boolean registered = false;
private boolean settingsInitialized = false;
SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try {
SettingsEnum setting = SettingsEnum.getSettingsFromPath(str);
if (setting == null) {
return;
}
Preference pref = findPreference(str);
if (pref == null) {
return;
}
if (pref instanceof SwitchPreference) {
SwitchPreference switchPref = (SwitchPreference) pref;
SettingsEnum.setValue(setting, switchPref.isChecked());
} else if (pref instanceof EditTextPreference) {
EditTextPreference editPreference = (EditTextPreference) pref;
SettingsEnum.setValue(setting, editPreference.getText());
} else if (pref instanceof ListPreference) {
ListPreference listPref = (ListPreference) pref;
SettingsEnum.setValue(setting, listPref.getValue());
updateListPreferenceSummary((ListPreference) pref, setting);
} else if (pref instanceof RangeValuePreference) {
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
SettingsEnum.setValue(setting, rangeValuePref.getValue());
} else if (pref instanceof DownloadPathPreference) {
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
SettingsEnum.setValue(setting, downloadPathPref.getValue());
} else {
LogHelper.printException(() -> "Setting cannot be handled: " + pref.getClass() + " " + pref);
return;
}
if (ReVancedUtils.getAppContext() != null && this.settingsInitialized && setting.rebootApp) {
rebootDialog(getActivity());
}
} catch (Exception ex) {
LogHelper.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
}
};
private void updateListPreferenceSummary(ListPreference listPreference, SettingsEnum setting) {
String objectStringValue = setting.getObjectValue().toString();
final int entryIndex = listPreference.findIndexOfValue(objectStringValue);
if (entryIndex >= 0) {
listPreference.setSummary(listPreference.getEntries()[entryIndex]);
listPreference.setValue(objectStringValue);
} else {
listPreference.setSummary(objectStringValue);
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.registered = true;
getPreferenceManager().setSharedPreferencesName(SharedPrefCategory.TIKTOK_PREFS.prefName);
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this.listener);
final Activity context = this.getActivity();
PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(context);
setPreferenceScreen(preferenceScreen);
new FeedFilterPreferenceCategory(context, preferenceScreen);
new DownloadsPreferenceCategory(context, preferenceScreen);
new SimSpoofPreferenceCategory(context, preferenceScreen);
new IntegrationsPreferenceCategory(context, preferenceScreen);
this.settingsInitialized = true;
}
@Override
public void onDestroy() {
if (this.registered) {
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this.listener);
this.registered = false;
}
super.onDestroy();
}
private void reboot(Activity activity) {
int intent = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
((AlarmManager) activity.getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.ELAPSED_REALTIME, 1500L, PendingIntent.getActivity(activity, 0, new Intent(activity, SplashActivity.class), intent));
Process.killProcess(Process.myPid());
}
private void rebootDialog(final Activity activity) {
new AlertDialog.Builder(activity).setMessage("Refresh and restart").setPositiveButton("Restart", (dialog, i) -> reboot(activity)).setNegativeButton("Cancel", null).show();
}
}

View File

@@ -1,242 +0,0 @@
package app.revanced.tiktok.settingsmenu;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.os.Process;
import android.preference.EditTextPreference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import androidx.annotation.Nullable;
import com.ss.android.ugc.aweme.splash.SplashActivity;
import app.revanced.tiktok.settings.SettingsEnum;
import app.revanced.tiktok.settings.SharedPrefCategory;
import app.revanced.tiktok.settingsmenu.preference.DownloadPathPreference;
import app.revanced.tiktok.utils.ReVancedUtils;
public class ReVancedSettingsFragment extends PreferenceFragment {
private boolean Registered = false;
private boolean settingsInitialized = false;
SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
for (SettingsEnum setting : SettingsEnum.values()) {
if (!setting.path.equals(str)) continue;
if (ReVancedUtils.getAppContext() != null && this.settingsInitialized && setting.rebootApp) {
rebootDialog(getActivity());
}
}
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getPreferenceManager().setSharedPreferencesName(SharedPrefCategory.TIKTOK_PREFS.prefName);
getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this.listener);
this.Registered = true;
final Activity context = this.getActivity();
PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(context);
setPreferenceScreen(preferenceScreen);
//Feed filter
if (SettingsStatus.feedFilter) {
PreferenceCategory feedFilter = new PreferenceCategory(context);
feedFilter.setTitle("Feed filter");
preferenceScreen.addPreference(feedFilter);
//Remove ads toggle
{
SwitchPreference preference = new SwitchPreference(context);
feedFilter.addPreference(preference);
preference.setKey(SettingsEnum.TIK_REMOVE_ADS.path);
preference.setDefaultValue(SettingsEnum.TIK_REMOVE_ADS.defaultValue);
preference.setChecked(SettingsEnum.TIK_REMOVE_ADS.getBoolean());
preference.setTitle("Remove feed ads");
preference.setSummary("Remove ads from feed.");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
// FIXME: the value is already saved in the preferences.
// instead of saving again, simple call SettingsEnum#setValue()
final boolean value = (Boolean) newValue;
SettingsEnum.TIK_REMOVE_ADS.saveValue(value);
return true;
});
}
//Hide LiveStreams toggle
{
SwitchPreference preference = new SwitchPreference(context);
feedFilter.addPreference(preference);
preference.setKey(SettingsEnum.TIK_HIDE_LIVE.path);
preference.setDefaultValue(SettingsEnum.TIK_HIDE_LIVE.defaultValue);
preference.setChecked(SettingsEnum.TIK_HIDE_LIVE.getBoolean());
preference.setTitle("Hide livestreams");
preference.setSummary("Hide livestreams from feed.");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean value = (Boolean) newValue;
SettingsEnum.TIK_HIDE_LIVE.saveValue(value);
return true;
});
}
}
//Download
if (SettingsStatus.download) {
PreferenceCategory download = new PreferenceCategory(context);
download.setTitle("Download");
preferenceScreen.addPreference(download);
//Download path
{
DownloadPathPreference preference = new DownloadPathPreference(context);
download.addPreference(preference);
preference.setKey(SettingsEnum.TIK_DOWN_PATH.path);
preference.setDefaultValue(SettingsEnum.TIK_DOWN_PATH.defaultValue);
preference.setValue(SettingsEnum.TIK_DOWN_PATH.getString());
preference.setTitle("Download path");
preference.setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + preference.getValue());
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final String value = (String) newValue;
SettingsEnum.TIK_DOWN_PATH.saveValue(value);
return true;
});
}
//Download watermark
{
SwitchPreference preference = new SwitchPreference(context);
download.addPreference(preference);
preference.setKey(SettingsEnum.TIK_DOWN_WATERMARK.path);
preference.setDefaultValue(SettingsEnum.TIK_DOWN_WATERMARK.defaultValue);
preference.setChecked(SettingsEnum.TIK_DOWN_WATERMARK.getBoolean());
preference.setTitle("Remove watermark");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean value = (Boolean) newValue;
SettingsEnum.TIK_DOWN_WATERMARK.saveValue(value);
return true;
});
}
}
// SpoofSimPatch
if(SettingsStatus.simSpoof) {
PreferenceCategory simSpoof = new PreferenceCategory(context);
simSpoof.setTitle("Bypass regional restriction");
preferenceScreen.addPreference(simSpoof);
//Global Switch
{
SwitchPreference preference = new SwitchPreference(context);
simSpoof.addPreference(preference);
preference.setKey(SettingsEnum.TIK_SIMSPOOF.path);
preference.setDefaultValue(SettingsEnum.TIK_SIMSPOOF.defaultValue);
preference.setChecked(SettingsEnum.TIK_SIMSPOOF.getBoolean());
preference.setTitle("Fake sim card info");
preference.setSummary("Bypass regional restriction by fake sim card information.");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean value = (Boolean) newValue;
SettingsEnum.TIK_SIMSPOOF.saveValue(value);
return true;
});
}
//Country ISO
{
EditTextPreference preference = new EditTextPreference(context);
simSpoof.addPreference(preference);
preference.setKey(SettingsEnum.TIK_SIMSPOOF_ISO.path);
preference.setDefaultValue(SettingsEnum.TIK_SIMSPOOF_ISO.defaultValue);
preference.setText(SettingsEnum.TIK_SIMSPOOF_ISO.getString());
preference.setTitle("Country ISO");
preference.setSummary("us, uk, jp, ...");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final String value = (String) newValue;
SettingsEnum.TIK_SIMSPOOF_ISO.saveValue(value);
return true;
});
}
//Operator mcc+mnc
{
EditTextPreference preference = new EditTextPreference(context);
simSpoof.addPreference(preference);
preference.setKey(SettingsEnum.TIK_SIMSPOOF_MCCMNC.path);
preference.setDefaultValue(SettingsEnum.TIK_SIMSPOOF_MCCMNC.defaultValue);
preference.setText(SettingsEnum.TIK_SIMSPOOF_MCCMNC.getString());
preference.setTitle("Operator mcc+mnc");
preference.setSummary("mcc+mnc");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final String value = (String) newValue;
SettingsEnum.TIK_SIMSPOOF_MCCMNC.saveValue(value);
return true;
});
}
//Operator name
{
EditTextPreference preference = new EditTextPreference(context);
simSpoof.addPreference(preference);
preference.setKey(SettingsEnum.TIK_SIMSPOOF_OP_NAME.path);
preference.setDefaultValue(SettingsEnum.TIK_SIMSPOOF_OP_NAME.defaultValue);
preference.setText(SettingsEnum.TIK_SIMSPOOF_OP_NAME.getString());
preference.setTitle("Operator name");
preference.setSummary("Name of the operator");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final String value = (String) newValue;
SettingsEnum.TIK_SIMSPOOF_OP_NAME.saveValue(value);
return true;
});
}
}
//Integration
PreferenceCategory integration = new PreferenceCategory(context);
integration.setTitle("Integration");
preferenceScreen.addPreference(integration);
//Enable DebugLog toggle
{
SwitchPreference preference = new SwitchPreference(context);
integration.addPreference(preference);
preference.setKey(SettingsEnum.TIK_DEBUG.path);
preference.setDefaultValue(SettingsEnum.TIK_DEBUG.defaultValue);
preference.setChecked(SettingsEnum.TIK_DEBUG.getBoolean());
preference.setTitle("Enable debug log");
preference.setSummary("Show integration debug log.");
preference.setOnPreferenceChangeListener((pref, newValue) -> {
final boolean value = (Boolean) newValue;
SettingsEnum.TIK_DEBUG.saveValue(value);
return true;
});
}
this.settingsInitialized = true;
}
@Override // android.preference.PreferenceFragment, android.app.Fragment
public void onDestroy() {
if (this.Registered) {
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this.listener);
this.Registered = false;
}
super.onDestroy();
}
private void reboot(Activity activity) {
int intent;
intent = PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE;
((AlarmManager) activity.getSystemService(Context.ALARM_SERVICE)).setExact(AlarmManager.ELAPSED_REALTIME, 1500L, PendingIntent.getActivity(activity, 0, new Intent(activity, SplashActivity.class), intent));
Process.killProcess(Process.myPid());
}
private void rebootDialog(final Activity activity) {
new AlertDialog.Builder(activity).
setMessage("Refresh and restart").
setPositiveButton("RESTART", (dialog, i) -> reboot(activity))
.setNegativeButton("CANCEL", null)
.show();
}
}

Some files were not shown because too many files have changed in this diff Show More