{ "note": "Where every shipped source file under patches/ and extensions/ came from. A rule naming a single file wins over the directory rule around it; otherwise each file matches exactly one rule. A ported rule names the upstream repository, the commit it was copied from, the repositories it came through and its licence, and every file's header names a repository of its rule, by URL. Every upstream named here must also appear in NOTICE. ProvenanceTest checks all of it. A Facebook-family repository named here also has to be allowed by sources/facebook-sources.json: one files are ported from has to be adopted there, with this rule's commit, and one a rule credits has to be adopted or a candidate. scripts/test-facebook-sources.ps1 checks that side.", "rules": [ { "paths": ["patches/src/main/kotlin/app/morphe/util/BundleIdentity.kt"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only" }, { "paths": ["patches/src/main/kotlin/app/morphe/util/ReferenceIndex.kt"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #216. One pass over the app's classes records which types, member names and strings each one names, so a patch's search visits only the classes that could match, in the order classDefForEach visits them. Classes a patch has changed are always visited." }, { "paths": ["patches/src/main/kotlin/app/morphe/util/PatchListGenerator.kt"], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/hushfeed", "commit": "1f1f81a29ffbe8468a41067b22c05a50283ef9c4", "via": [ "https://github.com/icysymmetra/tiktok-patches-for-morphe", "https://github.com/RookieEnough/De-Vanced", "https://github.com/MorpheApp/morphe-patches" ], "license": "GPL-3.0-only with the Morphe section 7 notices", "changes": "Moved to morphe-patcher 2.0: the default flag in place of use, the per-target compatibility list read as it is, and the old compatiblePackages map rebuilt from it (stable targets only, unless a package declares nothing else) for Manager 1.x." }, { "paths": ["patches/src/main/kotlin/app/morphe/patches/facebook/shared/MobileConfigReads.kt"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 582: one check for each way a build reads a MobileConfig flag (straight from the config, through a static helper, or through a helper that casts its argument to the config first), shared by the patches that hook a flag read." }, { "paths": ["patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/StyleableTokens.kt"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 582 for issue #154. It follows styleable arrays from obtainStyledAttributes through their TypedArray index reads, so Material You keeps a plain colour for tokens read with getInteger or getInt." }, { "paths": [ "extensions/shared/**", "patches/src/main/kotlin/app/morphe/util/**", "patches/src/main/kotlin/app/morphe/patches/shared/misc/extension/**", "patches/stub/**" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/hushfeed", "commit": "1f1f81a29ffbe8468a41067b22c05a50283ef9c4", "via": [ "https://github.com/icysymmetra/tiktok-patches-for-morphe", "https://github.com/MorpheApp/morphe-patches", "https://github.com/MorpheApp/morphe-patches-library", "https://gitlab.com/ReVanced/revanced-patches" ], "license": "GPL-3.0-only with the Morphe section 7 notices", "changes": "Facebook host and id names in DiagnosticRedactor, Facebook wording in user-facing messages, HushfeedPause renamed HushfacebookPause with its keys and marker file, the extension name BundleVerifier expects. L10n moved from Hushfeed's TikTok extension into the shared library, with the en-XA and ar-XB pseudo-locales, lists joined and sentences capitalized in the text's own language, and the messages the shared settings screen shows read through it (Logger's debug toast stays English, for whoever reads the report). SettingsJson refuses a name given twice with an exception of its own. Changed files carry a 'Modified for Hushfacebook' line." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/shared/compat/**" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/hushfeed", "commit": "1f1f81a29ffbe8468a41067b22c05a50283ef9c4", "via": [ "https://github.com/icysymmetra/tiktok-patches-for-morphe", "https://github.com/RookieEnough/De-Vanced", "https://github.com/andrewliang25/morphe-patches" ], "license": "GPL-3.0-only", "changes": "Rewritten for Facebook: package, both Meta signer digests, and the 577.0.0.50.72 arm64 target with its version code and floor as andrewliang25/morphe-patches declares it." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/shared/**", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/**", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/**", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/externalbrowser/**", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/resignedtrust/**", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/**", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/**" ], "origin": "ported", "upstream": "https://github.com/andrewliang25/morphe-patches", "commit": "5db2e57e133aede5297c48b419168cf30fd89953", "via": [ "https://github.com/SapitoSucio/FroggoMorphePatches" ], "license": "GPL-3.0", "changes": "Rewritten from the source at that commit, not cherry-picked. Packages, extension descriptors, log tags, injected method names and the cache folder renamed; plain patch names with categories; the two feed guards merged into one shared hook; a settings switch in front of the story, reel, browser and download hooks, and the reel sidebar's Download button and the story menu's Save item asking theirs before they go in, so Pause takes both out; the player-source recorder shared with Download any video through a call and a switch of each patch's own; the save folder and its top folder read per file from the settings, the top folder with its MediaStore collection; the DASH track and the single file picked for the download quality read per save, by the track's own FBQualityLabel; a video named from the file name template, with the id the story's recorded player, the post or the reel's player params (their toString) gives; the list register of the story filter computed for static and wide parameters; the borrowed v0 in the reel collection hooks checked; the DASH join ends a track on an empty read and shifts both tracks by one lead-in; the reel handler answers null instead of Facebook's renamed kotlin.Unit, and sends the reel's link instead of saving it while links are sent, with a long press copying it; the debug println lines removed; the browser's link shim unwrapped only in the host and path forms Facebook 577 and 580 recognise as one themselves, plus /l.php on messenger.com, a shim inside a shim included. Saves fetch only what MediaUrlPolicy allows, every redirect included, into a cache file first, and reach MediaStore only once the declared type, the first bytes, the length and the 512 MB cap (the two DASH tracks together) all check out; save steps go to the diagnostic report without file names; a save reports its progress to a notification with a Cancel button and stops when cancelled, and lists its pending gallery row until it's settled. The story menu's save action is found among the types the menu creates, by the event it reports as a literal or as a string table entry, where Facebook 581 moved it. Andrew Liang credits FroggoMorphePatches for some ideas and implementations." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/HidePostPromptsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/prompts/HideReelPromptsPatch.kt" ], "origin": "ported", "upstream": "https://github.com/andrewliang25/morphe-patches", "commit": "16464b2ba606745de0ebe9cc52f18ef42e6a05d8", "via": [], "license": "GPL-3.0", "changes": "Rewritten from the two patches at that commit. The anchors are the same: the bumper component's constructor string and its one static (props) -> Z predicate, and the reel overlay enum's INTERESTED_OR_NOT_INTERESTED_BUMPER constant and the one (reel) -> Z method that reads it. Instead of returning false from the first instruction, each answer the predicate gives goes through an extension hook with a switch, so pausing Hushfacebook or turning a switch off brings the prompt back, and the diagnostic report counts the prompts kept out. Both are checked on 580 as well as 577, and a missing or doubled anchor stops patching with a message naming it." }, { "paths": ["patches/src/main/kotlin/app/morphe/patches/facebook/feed/followinghome/FollowingFeedHomePatch.kt"], "origin": "ported", "upstream": "https://github.com/ArunTS96/FroggoMorphePatches", "commit": "319cde236e373ff8feb66fa30e99028e3d9dff6e", "via": [], "license": "GPL-3.0", "changes": "Rewritten from the Following feed swap on that commit's fix/following-feed-home branch, which changes Home's feed type for the Following feed's at the start of Facebook 573's news feed parameter builder. The hook point is the same: the first instruction of that static builder, whose second parameter is the feed type. 573's obfuscated names aren't carried over. The builder is found in 577, 580 and 581 by the Companion.setPagedNewsfeedParams trace section it opens, the two feed types by the names they keep (top_stories and most_recent), and the swap is made by the extension's FollowingHome behind a switch that starts off, so pausing Hushfacebook or turning the switch off gives Home its ranked feed again. The extension, written here, sits under the feed extension's own rule." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/updates/playstore/DisablePlayStoreUpdatesPatch.kt", "extensions/facebook/src/main/java/app/morphe/extension/facebook/updates/DisablePlayStoreUpdatesPatch.java" ], "origin": "ported", "upstream": "https://github.com/MorpheApp/morphe-patches", "commit": "1bcd0bcedf1238e71b0bd8815da2c10df7ef5bd2", "via": [ "https://gitlab.com/ReVanced/revanced-patches" ], "license": "GPL-3.0-only with the Morphe section 7 notices", "changes": "Copied from Morphe's universal Disable Play Store updates at that commit, its two files' Morphe notice blocks kept whole, with a line above each saying what changed. The manifest half is the same: the version code read before any patch finalizes and raised to Int.MAX_VALUE after. The extension's code is unchanged and moved into the Facebook extension's updates package, so it ships in Hushfacebook's extension and not the change-version-code one. The patch is Facebook only, tells SettingsStatus it's in, and walks every class for the PackageInfo.versionCode reads and getLongVersionCode calls it sends through the extension instead of matching them by fingerprint, leaving out only the extension class, so Hushfacebook's own extension reads the real version too. The fallback for an iget destination past v15 is gone, since iget only addresses v0 to v15." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/ads/affiliate/HideAffiliateLinksPatch.kt" ], "origin": "ported", "upstream": "https://github.com/andrewliang25/morphe-patches", "commit": "a934b40608e5645cf119dd4101ded7767a9cdc73", "via": [], "license": "GPL-3.0", "changes": "Rewritten from the patch at that commit. The anchors are the same: the static Z predicate the reel overlay asks before adding its AFFILIATE_EYEBROW constant, the one string lookup the feed video plugin's isFooterHidden method makes, and the one static card reader of OrganicAffiliateFloatingCtaPlugin. Instead of returning false or null from the first instruction, each answer goes through an extension hook with a switch, so pausing Hushfacebook or turning the switch off brings the cards back, and the diagnostic report counts the cards kept out in each place. Checked on 580 as well as 577. On 580 the overlay's list builder is inlined into its render, and the same shape still matches once. Added for Hushfacebook (issue #89): a fourth anchor, the reel overlay's filterCtasForCategorySuppression, whose every answer goes to the extension beside the full list of call-to-action kinds, so the other shop cards on reels go too." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/AffiliateLinks.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch behind Hide affiliate product links. The patch hands it each answer of the three places that ask whether a post has a product card (the reel overlay's predicate, the feed's footer id lookup and the comment sheet's card reader), and while the switch is on and Hushfacebook isn't paused it answers no or null, the answer for a post without a shop link. Any other time, and on any throw, Facebook's own answer stands. The reel overlay's list of call-to-action kinds comes through as well, and while the switch is on it loses the shop kinds (tagged products, storefront, Shop similar and the affiliate cards)." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/MediaUrlPolicy.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: which addresses a save may fetch (HTTPS on 443, no user name, Meta's media CDNs only, and on a direct route a public or fake-IP answer)." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/LogoEmoji.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook (#360): draws one emoji after the Facebook logo or the Home header text at the top of Home, in its own colors." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/StoryPhoto.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 582 (#38): finds the photo a music photo Story was made from in the story card's media model and gives the story menu its Save photo item." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/PostXButton.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 (#389): finds the check that asks whether a feed post's header draws its X and sends its answer through a switch." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/StoryOverlayVideo.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 584 (#207): reads the reel a story reshares from the story tree's overlays, so Save on that still card takes the reel's video." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/StoryCardShape.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 583 (#207): with Debug logging on, logs a value-free outline of a story card's trees and its media fields' kinds when a story save finds no video, so a report shows where a shared video's address sits." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/DownloadQuality.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/FileNameTemplate.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/OpenInAnotherApp.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PostDetails.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SaveFolder.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SaveTo.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SendLink.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/VideoMenuItem.java", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/video/DownloadVideoPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/video/Fingerprints.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Download to phone in the menu of a feed or Watch video, added after Facebook fills the menu so its own Save and Download rows stay as they are, found in Facebook 577 and 580 through the video feed menu's log tag, the kept GraphQL models and the hashes of their field names; the save folder, one sanitized folder name under the top folder Save to picks, Movies and Pictures, DCIM or Download, each written through the MediaStore collection Android allows there; the download quality, the ceiling a save holds the DASH track and the single file to, falling back to the nearest above it; and the video file name, a template with {date}, {video_id}, {owner} and {posted} cleaned like the folder name, kept off the photo prefix, a typed extension and one name for every save, and bounded in bytes, with the poster and the post day read off every model's kept TreeJNI accessors by the hash of the field's GraphQL name, from the reel's story, which the reel sidebar hands its assembly call, the story card's own tree, found by the card's kept getTimestamp, and the post and media a video's menu holds; and a Download that sends the reel's or the video's facebook.com link, built from its id alone, to a named app or Android's chooser instead of saving it. The post menu hook is shared with Download any photo's Save photo item, each with a helper of its own." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/LinkCleaner.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/OwnPostLink.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/sharelinks/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the tracking keys Facebook adds to shared and opened links, taken out offline with everything else in the link left as written, and the hooks on the methods of Facebook 577 and 580 that add them. Also a switch that hands out a shared post's own address in place of the facebook.com/share/ link Facebook keeps for it, hooked where Facebook 581 builds a post's share link from that cache." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/LinkHistory.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/externalbrowser/LinkHistoryWriters.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a switch that keeps Facebook's own browser from building its two link history writers, hooked first in the factory of each kept writer class in Facebook 582, where a yes takes the factory's own null." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/PageEventsLink.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/externalbrowser/PageEventsLinkHook.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 (#319): every address Facebook's link mapper is asked about goes to the extension first, which sends a Page's events address to the Page itself and adds the Events tab to the fb://profile/ link the server answers for it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/TranslatedStart.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/translatedstart/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Start on x86 devices, which adds Facebook's MprotectCode start-up task and its later half to the list of start-up tasks Facebook's own cold start experiments skip, on an x86 device running Facebook's arm code translated, found in Facebook 577 and 580 through the log line that names that list." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/IconDensity.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 (#365): compares the density Facebook's fb_launch_logo bitmap resolves to with the screen's, and on a 64-bit phone whose install lacks images for its screen says once per install, and under the settings status card, which download has them." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/OpenInFacebook.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/openinfacebook/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 (#111): an Open in Facebook share target declared in the manifest that takes the first Facebook link out of shared text and hands it to Facebook's own link handler, so a link Messenger keeps in its browser can be opened in Facebook." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/GenAiLabel.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/AiCharacterPosts.java", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/aidetected/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "via": [ "https://github.com/SapitoSucio/FroggoMorphePatches" ], "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the rule that hides feed posts Facebook's own detection marked as made with AI. The idea follows the Facebook 573 AI filter of FroggoMorphePatches, which ArunTS96/FroggoMorphePatches and mben25/morphe-patches carry too. No code of it is here: that filter writes down 573's obfuscated names, while this finds GraphQLStory's accessor in 577 and 580 by the schema keys it loads, holds it to the flag Facebook's own GenAiTransparencyPlugin reads, and reads the flag through kept members. The opt-in creator label switch reads the plugin's second flag, was_self_disclosed_as_ai_generated, through GraphQLStory's self-disclosure accessor, found and held the same way. Hide Meta AI in the feed takes out the Meta AI cards Facebook adds between posts by their type name, and the opt-in Hide AI character posts switch takes out posts featuring one of Meta's AI characters through the finder Facebook's own attachment renderers ask for that style, found in 577, 580 and 581 by the literal they hand it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/CommentTimeColour.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/MaterialYouTheme.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/ReactColours.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/ReactionRing.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/TonePalette.java", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/BloksSheetColours.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/CommentTimeColours.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/LinkBoxFallbacks.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/MaterialYouThemePatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/MaterialYouStyles.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/ReactColourHooks.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/ReactionRings.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/TabBarColours.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Material You theme. Dark-mode colours move to the wallpaper palette (Android 12 and newer) or to a fixed tonal palette of Facebook's blue (Android 11) at the same lightness, through the four colour routes the AMOLED patch uses and after it, and through night copies of Facebook's dark FDS styles for the views it inflates from its own layouts. React Native screens such as Marketplace home, whose colours come from their JavaScript, go through React's own background, border and image tint setters and its text colour span, which both themes hook. The colour tokens and dark surfaces it recolours were read off Facebook's own light and dark themes in the 577 and 580 builds, not taken from another patch source. For issue #65, the colour the tab bar asks for its selected tab, which draws the line over that tab and tints its icon, takes the wallpaper accent at tone 80 in dark mode; it's found through TabBarContainerLayout's colour setter and its Paint, and has exactly two readers on each build. For issue #138, the time on Facebook's older comment row, which TimestampPlugin (a class Redex keeps) colours from one theme attribute read, goes through a check both themes share: in dark mode a colour too close to the page to read becomes Facebook's dark secondary text, themed like the rest. For issue #37, a link box whose MobileConfig colour comes back empty takes its dark colour from a prop, from a second config's grey enum or from FDS's light or dark chooser. The first two, found by the register the config's parse answers into, and the chooser's dark answer go through the same dark-branch rule as the colours Facebook writes after its own dark check. For issue #141, once Facebook says its light mode is on, the grays, blues and pale tints its light FDS style gives those same tokens, read from both 582 inputs, take the palette at the same lightness too, as do the shared and server blues and the selected tab. For issue #147, the ring between a post's overlapping reaction icons goes through a check both themes share: FDSInlineReactions, found by its own component name, resolves the ring from its style's clipColor (a field name Redex keeps) through FDSColors, and in dark mode an opaque answer becomes a clear one, which makes Facebook's own facepile cut the ring out so the page shows through. For issue #37 again, a Bloks bottom sheet such as the account switcher paints its rounded background and its corner view from the dark half of a light and dark colour pair the screen sends; its container is found as the one FrameLayout built from two of Facebook's ColorData and a float array of radii, read from a heap dump of the open sheet on 584, and a dark grey there takes the palette's neutral at the same lightness." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/MetaUpsells.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/upsells/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581: Hide Meta upsells, six switches that start off. With Edits on, every read of the Reels composer landing's two Edits header flags and every fetch_edits_app_deep_dive_pill request flag answers off. With Threads on, the cross-posting onboarding's should-show check answers no. With Meta Verified on, the after-post sheet's eligibility check and the dispatcher's one request for its label answer no. With avatar stickers on, the three avatar sticker upsell components draw nothing. With Imagine on, the post call-to-action selector's check answers no for ImagineMePlugin, every question the post composer asks about its AI_GEN_IMAGINE capability answers no, and Create story's tile builder gets its list of tools without IMAGINE. With the share sheet's Threads switch on, every list of share sheet item types the item picker returns loses SHARE_TO_THREADS, through the share sheet hook it shares with Share sheet items." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/ShareSheetItems.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/ShareSheetGroups.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/ShareSheetPeople.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/sharesheet/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 581: Share sheet items, the idea of the Share sheet modification patch in icysymmetra/tiktok-patches-for-morphe with none of its TikTok code. Every list of share sheet item types the item picker returns goes through the extension, which takes out the types picked under Links and keeps the rest in Facebook's order, and remembers the types it has seen so the list can offer them. The hook is its own nameless patch, which Hide Meta upsells uses for its Threads switch too. Its group switch, the idea of HushGram's Hide group buttons on the share sheet, hands the share sheet footer's Send to group FDSButton to the extension where its guards meet, and the boolean in front of each GROUP_PLUS new-group entry in the sheet's body and search row, so the switch leaves the footer without the button and answers each entry off. Its people switch, written from Facebook 583 for issue #169, hands the extension each add of the sheet body's row of people to send to and of that row's loading placeholder, and skips the add while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/AccentColor.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/AccentResources.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/ColourTable.java", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/AccentColorPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/AccentResourceBlues.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Accent color, which swaps Facebook's blues on a chosen set of FDS tokens for a preset's tonal ramp at the lightness each had, holding text roles to 4.5:1. It rides the colour resolver hooks the themes share and steps aside for the Material You theme. Its route five lists the colour resources Facebook's FDS styles give the tokens, when they hold a blue, and gives each activity's resources a colour table for them through a ResourcesLoader (the way Material Components overrides colours), so views inflated from layout XML take the accent too, while route one turns the table's colours back into Facebook's before it decides. Its route six hands the fill of each Bloks box, which Facebook's server sends as a hex colour, to the same rules as a primary button's fill as the box's drawable is built, and the colour of each span of Bloks text (a profile's bio link) to the link rules before the span is made. Its route seven gives the blues of React Native text and backgrounds (Marketplace) the accent through the React hooks the themes share." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/DarkMode.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Facebook's own answer to whether its dark mode is on, kept from each answer its dark mode controller gives. Both themes ask it, since the Video tab stays dark in light mode with the same colours dark mode uses." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/ForceDarkMode.java", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/ForceDarkModePatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Force dark mode, a switch that has Facebook's dark mode controller answer dark whatever its own setting says, for tablets whose Facebook settings have no Dark mode row (issue #64)." }, { "paths": [ "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/ReadOnlyPreferences.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a read-only view of the settings file for Facebook's side processes, so none of them opens it as SharedPreferences, whose first load can delete a file the main process is part way through saving." }, { "paths": [ "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/JavaCrashReport.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: keeps the Java crash that ends Facebook, with its stack trace and the events before it, so the diagnostic report can show it after the restart." }, { "paths": [ "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/CrashFile.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: writes the redacted crash report to Download/Hushfacebook through MediaStore, keeps the newest five, and takes a file back when the process lived through the exception." }, { "paths": [ "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/QuickSetups.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/ReopenSettingsAfterRestart.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/ReportProblem.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/settings/preference/SettingsSearch.java" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/HushPinterest", "commit": "c5be4d9e0a1b08396837b4a49c0bfd62aa5d9e7e", "via": [ "https://github.com/SysAdminDoc/hushfeed" ], "license": "GPL-3.0-only", "changes": "The settings standard, copied from HushPinterest, which wrote these four against the same shared library from Hushfeed: the quick setups that turn a group of switches on together, the return to the settings page a restart was asked from, the report a problem page with its prefilled issue, and the search over the settings. The package and the app's name are Hushfacebook's, and ReportProblem's example of the line it writes names a Hushfacebook version and a Facebook build. Each file carries a 'Modified for Hushfacebook' line." }, { "paths": [ "extensions/shared/library/src/main/l10n/zh-rTW.tsv" ], "origin": "ported", "upstream": "https://github.com/JhouHank/HushFacebook", "commit": "be766e7e629931d376974c642435de22f8970c26", "license": "GPL-3.0-only", "changes": "Traditional Chinese (Taiwan) translation contributed by JhouHank in pull request 227. The submitted translations are preserved, with contributor credit and one added Save video row for the later Story menu change. The contributor credits their Piko Instagram translations for terminology; no Piko source revision was supplied." }, { "paths": [ "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTranslations.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableDe.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableEs.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableIn.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTablePt_rBR.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableRu.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableTr.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableVi.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableZh_rCN.java", "extensions/shared/library/src/main/java/app/morphe/extension/shared/L10nTableZh_rTW.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Generated for Hushfacebook by scripts/gen-l10n.py from the translation tables in extensions/shared/library/src/main/l10n, including the separately credited Traditional Chinese contribution." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SaveControl.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SavedFileActions.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SaveLeftovers.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/Mp4Join.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/AacReencode.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/TransferRate.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/VideoTranscode.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/OutputAllowance.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a running save's notification with its progress and a Cancel button, the removal of the work files and pending gallery rows a save left when Android ended the process, and the MP4 writer that joins a DASH picture and its sound for the formats MediaMuxer won't write, VP9 always and AV1 before Android 14, by copying the fragments' boxes and samples into one plain MP4, and the conversions that turn a save's xHE-AAC sound into AAC-LC and its VP9, AV1 or H.265 picture into H.264 for the apps that take only those, with the count that holds a conversion's output to the room reserved for it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/settings/SettingsBackup.java" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/hushfeed", "commit": "bcc57ee555f4346c4eb1cbdae9f6ca8a3fa6fef4", "via": [ "https://github.com/icysymmetra/tiktok-patches-for-morphe" ], "license": "GPL-3.0-only", "changes": "Ported from Hushfeed's settings backup and cut down to what Hushfacebook keeps: an exact list of its feature switches and the save folder in place of every setting, with no Feature Gate Lab, reset, undo or journal. An import is read into a snapshot that a preview shows before anything is written, and applied in one preference commit. A file declaring a newer version, a name given twice, a switch that isn't true or false, a folder that isn't one clean folder name, or more than 64 KB changes nothing, each with its own reason, and names this build doesn't know are counted rather than applied." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/settings/SettingsBackupPreference.java" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/hushfeed", "commit": "bcc57ee555f4346c4eb1cbdae9f6ca8a3fa6fef4", "via": [ "https://github.com/icysymmetra/tiktok-patches-for-morphe" ], "license": "GPL-3.0-only", "changes": "Ported from Hushfeed's backup rows: Export settings and Import settings on the framework preference page inside Hushfacebook's settings dialog, answered through the page's onActivityResult, with the import's preview dialog kept in the page's saved state so it comes back after the activity is rebuilt. No Reset or Undo rows, and every message read from L10n." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/settings/SettingsHeader.java" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/HushGram", "commit": "bb6775bf5f81ea821a078632305ece87bb328f30", "via": [], "license": "GPL-3.0-only", "changes": "Ported from HushGram's settings header: the centred launcher icon, the name in the gradient title and the version line, at HushGram's sizes. HushGram's is a row of its list; here the three parts go straight into the settings screen's collapsing column over the list, the gradient is Facebook's blues lifted to 7:1 on the page, the icon is Hushfacebook's logo from the drawable SettingsLogo.kt adds (then the launcher icon, then the patched mark in the heading colour), and the version line names Facebook." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/settings/SettingsLogo.kt" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/HushGram", "commit": "bb6775bf5f81ea821a078632305ece87bb328f30", "via": [], "license": "GPL-3.0-only", "changes": "Ported from HushGram's app icon patch (AppIcons.kt: artFiles and the write loop of addAppIcons): pictures the patch carries in its own resources go into the decoded res folder's drawable-nodpi, after asking the decoder for the patch's names and refusing before any write when one is already there. Here it's one picture, Hushfacebook's logo for the settings header, added by a resource patch the settings patch depends on, with no aliases or adaptive icons." }, { "paths": [ "patches/src/main/resources/app/morphe/patches/facebook/misc/settings/settings_logo.png" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Hushfacebook's own logo (assets/icons/icon-256.png), 256 px with clear corners, bundled so the settings header shows it on every patched build." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/hook/**", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/FeedTypeNames.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/DiscoverUnits.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/StoryModels.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/Recommendations.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/storiestray/**", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/reels/**", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/refresh/**", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/extension/**", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/settings/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/**", "extensions/facebook/src/main/java/app/morphe/extension/facebook/settings/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "via": [ "https://github.com/SysAdminDoc/hushfeed", "https://github.com/andrewliang25/morphe-patches", "https://github.com/SapitoSucio/FroggoMorphePatches" ], "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the shared feed hook and filter (the chokepoint and getters follow andrewliang25/morphe-patches, the PROMOTION rule follows FroggoMorphePatches), the extension context hook, the settings switches and status flags, and the launcher-shortcut settings screen built the way Hushfeed's settings are. The screen's opt-in release check, which asks api.github.com for the latest Hushfacebook release, was written here too." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryAdvance.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/autoadvance/BlockStoryAutoAdvancePatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a Story progress-completion guard on Facebook 577 and 580 that leaves manual navigation alone. Its Loop stories switch starts a held story again through the restart Facebook's own completion navigator makes, found in 577, 580 and 581 by its shape (the callback's playback environment, a static lookup on it and an interface reset taking a boolean) and called from a static method the patch adds to the callback's class. The idea follows the loopStories option of FroggoMorphePatches' Facebook 573 story playback patch; no code of it is here, since that patch writes down 573's obfuscated names and pauses and resumes around its reset." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/SeenBuckets.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryMarks.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StorySeen.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StorySeenButton.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/seen/StorySeenAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/seen/ViewStoriesAnonymouslyPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch on the report of viewed stories that Facebook's story viewer sends as DirectSeenMutation, which puts the viewer on a story's viewer list. The patch finds the mutation's query class in Facebook 577 and 580 by its name and root field, the one method that creates it and fills story_ids_list, and the one other method of that class that calls it, and asks the extension first thing in that sender, which returns without sending while the switch is on. Its Mark as seen button (issue #88) follows the design of HushGram's button of the same name by the same maintainer, written again here for Facebook: the hook hands the sender's arguments to the extension and sends the set of marked card ids it answers, a second hook in the story viewer's seen helper names the card on screen, and the eye is drawn over StoryViewerActivity's window, since Facebook's story header is a Litho component. Loukious/FacebookAppAdsRemover and gnadgnaoh/NexAlloy-XES offer the same feature; neither is licensed for reuse, so nothing was taken from them. For issue #61 a bucket watched to its newest story is marked watched on the phone: the patch reads the five calls Facebook 582 makes to publish is_bucket_seen_by_viewer on a DirectMessageThreadBucket with GraphQLConsistency.publishWithFullConsistency off its own success callback, fills extension stubs with them and with StoryBucket's card list, session field and seen answer, and hooks the seen helper's per-card method and RegularStoryBucket's constructor; the extension keeps up to 500 watched buckets for 48 hours in a preferences file of its own." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/**", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Reels viewer clean-up, which drops the chips under a reel by their GraphQL type, answers Facebook's own Follow check and its config for removing the Following button, and skips the footer's hot comment and friends' reaction queries, each behind its own switch, with the hooks found in Facebook 577 and 580 by kept literals and Redex names. Also a switch that plays each reel once, answering the shouldLoopVideo read in FbGrootPlayer's end-of-video step, found in Facebook 581 by its completion log string and VideoPlayerParams' debug dump, with no for a reel." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/updates/**", "patches/src/main/kotlin/app/morphe/patches/facebook/updates/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch on Facebook's own update prompts. Facebook 577 and 580 carry no installer of their own, so the patch fails the two QuickPromotion filters that ask Meta App Manager about updates, skips the push handler that has the manager look for one, and fails the chat promotion filter aimed at versions below a ceiling, each found by its shape and the literals it keeps." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/font/**", "patches/src/main/kotlin/app/morphe/patches/facebook/font/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the font swap. It stands at the exit of the typeface repository's resolver, found in Facebook 577 and 580 by the refusal it throws for a family with no source, at the exit of the variable-font builders the kept com.meta.foa.typefacebuilder.ApiUtils makes, and at the exit of React Native's typeface resolver, found by the variation settings refusal its utilities log, at the exit of Facebook's own Roboto builder, found by the message it logs when it can't make one, and at each read of Android's default typefaces in Facebook's code. It answers the phone's default font, or a font file picked in the settings and copied into Facebook's files after a check, at the same weight and slant, for Meta's interface families only, which it knows by the enum constant names Redex keeps and by the family names React Native screens ask for. At the Roboto builder and the default typeface reads it answers a picked file only, since both are the phone's font already." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/ReelWatchHistory.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/watchhistory/DontSendReelWatchHistoryPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/watchhistory/SeenStateSend.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch on the batches of watched reels Facebook's Reels viewer sends as FbShortsSeenStateMutation. The patch finds the batcher's flush in Facebook 577 and 580 by the mutation's name, its video_ids input and the Redex name of the runnable it hands its executor, and puts the extension in place of that hand-over. The extension holds a batch back while the switch is on and sends anything it doesn't recognise as Facebook handed it over." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/coexist/**", "patches/src/main/kotlin/app/morphe/patches/facebook/coexist/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the rename that lets Meta's own apps install beside a re-signed Facebook. It renames the two signature permissions Facebook shares with Messenger, Lite, Business Suite and Workplace, com.facebook.permission.prod.FB_APP_COMMUNICATION and com.facebook.receiver.permission.ACCESS, to app.hushfacebook. names in every place the manifest names them, and hands every literal Facebook's code loads for them to an extension that answers the name the install holds. Messenger-Z's notes on the collision were read for the behavior only; no code came from them." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/resignedtrust/MessengerLinkAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the anchors and stub fills behind the Debug logging test of a same-key Messenger's link, which finds Facebook's message expiration flag reader and one of its Context-to-session lookups on Facebook 577 and 580 and has the extension call them on request." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/resignedtrust/FbnsSignersPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the push service's package check (582's FbnsServiceDelegateV2 hashes PackageInfo.signatures and stops the service when the hash isn't Meta's) gets Facebook's original certificate for this app's own package, so a re-signed build's push process stops restarting. The same fix was first written for HushThreads (its issue #6) by the same author." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/resignedtrust/FamilyLinkHandoffPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 (#287): the family intent scope's null refusal asks the extension first, which hands a plain Instagram or Threads web link to that app's own exported link activity with nothing else attached." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/emoji/**", "patches/src/main/kotlin/app/morphe/patches/facebook/emoji/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch to the phone's emoji font. It asks the extension first thing in Facebook's emoji typeface provider, found in Facebook 577 and 580 by the end-to-end system emoji flag and the log tag it keeps, and hands back the phone's default typeface while the switch is on, so the spans and pickers that draw emoji with Meta's downloaded font draw the phone's emoji font instead. It also asks the extension first thing in the maker of Meta's emoji picture addresses, found by the picture base address it holds, and answers no address while the switch is on, so a chat's big emoji stays drawn with the provider's typeface instead of Meta's PNG." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/**", "patches/src/main/kotlin/app/morphe/patches/facebook/navigation/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the tab Facebook opens on. The patch checks that Facebook 577 and 580 still pick the main screen's start tab from an intent's target_tab_id, by the one method that answers a tab id from that extra, and puts the extension first in the onCreate the main screen inherits. For a start from the launcher icon with nothing to restore, the extension hands the screen a copy of its intent asking for the chosen tab's id, the id each kept TabTag subclass hands TabTag, and leaves links, notifications, shortcuts and restored screens alone. Marketplace only is written here too: it asks the extension right after Facebook's hidden-tab set answers for each configured tab, in the one static method that builds the tab bar's shown tabs from NavigationConfig's list, so Home, Video, Friends, Feeds, Groups, Gaming and Events go the way a tab hidden in Facebook's own editor does. Hide the Reels tab is written here too: it shares that one call, a tab bar filter both patches depend on, and drops only WatchTab, the one TabTag class 577 and 580 keep for the tab accounts call Reels or Video, leaving a tab Facebook's own set already hides to Facebook. A start sent to the hidden tab asks for Home instead. Tab bar at the bottom is written here too: in 577 and 580 a static field set from fb4a_bottom_tabs_override_enabled holds the key of Facebook's own override of where the tab bar goes, and each method that reads it from FbSharedPreferences as a TriState and takes its ordinal gets a call right after the ordinal, which answers YES's ordinal while the switch is on. A load of the key that goes into anything else, Facebook's tab menu writing it, is left alone. No read, a read that answers no TriState or takes no ordinal right away, or a jump to the instruction after the ordinal stops patching with a message. See-through bars is written here too, with no hook of its own: Facebook's main screen, found by its manifest name as it resumes, has its top bar and tab bar found by the classes Redex keeps (NavigationBar and TabBarContainerLayout), and as each frame draws the alpha of each background that spans a bar is scaled and later given back. On Android 12 and newer the view under each bar records the part the bar covers into a RenderNode and draws it blurred in its overlay." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/**", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Get Messenger card in Facebook's own Chats. Facebook 577 and 580 build the rows above the chat list from plugins that each answer whether they show, and the card's plugin is found by the one builder that tags its banner mib-native-dismissible-top-banner. Facebook already leaves the card out when Messenger is installed, but only for a Messenger isSameSignature matches with its own key, which a re-signed build never does. The patch asks the extension first in that plugin's show question, and the extension answers no while the switch is on and com.facebook.orca is installed and enabled, whatever key signed it. Open Messenger from the top bar is written here too: the top bar's Messenger icon taps through one static method, the only one in 577 and 580 loading both entry_point_navbar_global_icon_ and entry_point_navbar_global_icon_reels_tab, which can hand the tap to a shared static Messenger button handler, the one static method of its shape loading long_press. The patch asks the extension first in both, and the extension starts Messenger's launcher entry for a plain tap while the switch is on and Messenger is installed." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/**", "patches/src/main/kotlin/app/morphe/patches/facebook/media/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: tap to play. The patch finds FbGrootPlayer's play, its inner pause and its bind in Facebook 577 and 580 by the trace strings they keep, the older Rich Video Player's playback controller by its play and pause log lines, Facebook's Autoplay settings checker by the refusal its constructor names it in, and FbFragmentActivity's touch dispatch by its framework name. The extension holds a start unless the player is already playing something a tap started, the start's EventTriggerType is one only something you did sends, or a tap ended within a second and the trigger isn't one Facebook sends because something came into view or came back. The Autoplay setting reads Off while the switch is on, and nothing is written to it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/menu/**", "patches/src/main/kotlin/app/morphe/patches/facebook/menu/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Upgrades and Also from Meta sections of Facebook's Menu. Facebook 577 and 580 type each Menu group with one enum under kept names, UPSELL and PRODUCTS_FROM_FACEBOOK among them, and build every group from a native section and a section for what the server sends with it. The patch finds the first by the one children builder loading upsell_bookmarks_key and the second by the one loading ABOVE_NATIVE, REPLACE_NATIVE and BELOW_NATIVE, and asks the extension about the group right after each reads it. A yes hands back an empty list of children, built the way the section builds its own. Each group has its own switch, and every other group is left as Facebook built it. The same folder holds Hushfacebook in the Menu: the Settings and privacy group's row list, the one list the native section builds from the session, a list and a flag, goes through the extension, which adds one row made by a factory the patch adds to Facebook's row item class, copying the first row's icon and giving it an id of its own. The Menu's row tap handler, found by the bookmarks_menu trace, opens the settings for that id, and its two loggers skip it. The Muse card is a Menu bookmark drawn with a Native Templates promotion. The patch finds its component by the kept updateState:BookmarkFolderItemComponent.updateNtContentDismissed string and, right after the render reads whether the card was dismissed, hands the answer and the bookmark to the extension, which answers yes for the bookmark named Muse, read through the kept TreeJNI.getString by the hash of name." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/search/**", "patches/src/main/kotlin/app/morphe/patches/facebook/search/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Meta AI in Facebook's search. On Facebook 577 and 580 the patch finds three of Facebook's own decisions from kept names and literals. The results page's question of whether to add a Meta AI answer is the one boolean over FbUserSession and SearchResultsMutableContext that both callers of the meta_ai_answer_entities page builder ask first, and the patch answers no there. A page of results is built by one constructor from a list of result modules, whose role is a constant of the enum Facebook loads where it logs a failed GraphQLGraphSearchResultRole parse, and the extension leaves out Meta AI's answer and prompt roles (SEARCH_META_AI_ANSWER with its body, full body and loading state, HCM_SEARCH_META_AI_ANSWER, HCM_RELATED_META_AI_PROMPTS with its loading state, and HCM_SEARCH_AI_MODE) and keeps every other role. The page's name comes along, so the Meta AI tab's own page, the one its builder names search_meta_ai_answer_module, keeps its answer. A suggestion's flag to open Meta AI is the boolean the results router asks GraphSearchQuerySpec before it sends a search to META_AI_TAB, and the patch hands it to the extension where the suggestion parser stores it from the server's reply. The Meta AI button and the results page's Meta AI tab go around all three. The same folder holds the search screen before anything is typed: on 583 the section naming itself SearchTypeaheadPaginableSection draws each entry of null_state_results_paginating from a module tree it reads off the entry's node, and an empty row when that is null. The patch hands the node and the module to the extension right after the read and keeps its answer, which is null for the entry whose node type is SearchNullStateNativeTemplatesResult and whose rendered_modules string lists TRENDING_QUERY_SUGGESTION, the For you section, while that switch is on. With Debug logging on, the extension also logs each entry's type and module names. Search with a picture lives here too: the top bar hook hands the search button to the extension as the bar makes it, and with the switch on a long press starts the Google app's googleapp://lens link, then https://lens.google.com/ in a browser, then shows a toast when nothing answers." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/comments/**", "patches/src/main/kotlin/app/morphe/patches/facebook/comments/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the order comments open in. Facebook 577 and 580 build every request for a post's comments from FetchFeedbackParams, and the request's builder sets comment_rendering_intent_token from one of its strings only when it isn't empty, so an ordinary comment sheet leaves the order to Facebook's servers. The patch finds that builder by the three variable names it reads from the params (comment_rendering_intent_token, feedback_id and focused_comment_id), finds the one params constructor that stores those three from its own parameters, and asks the extension first there. It also tells the extension first in the comment sheet's pick handler, the one method taking the session and a token that fetches UpdateCommentOrderType_FetchFeedbackQuery. The extension fills in RANKED_FILTERED_INTENT_V1, RECENT_ACTIVITY_INTENT_V1 or RANKED_UNFILTERED_CHRONOLOGICAL_REPLIES_INTENT_V1 only for a request that names no order and no focused comment, and asks again for the order picked in a post's menu until Facebook restarts." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/ProfileAdFilter.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/SearchAdFilter.java", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredprofile/HideSponsoredProfilePostsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredprofile/ProfilePostAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredsearch/HideSponsoredSearchResultsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredsearch/SearchResultAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the ads in search results and on profiles. In Facebook 577 and 580 every page of search results is built by one constructor from the two builders that take a GraphQLResult and the search's SearchResultsMutableContext, and its module list is the parameter they fill with the module class's own list converter. The module class is the one holding the search result role enum it reads from result_role, and its static initializer keeps the enum's SEARCH_ADS, DEPENDENT_SEARCH_ADS, LATE_DEPENDENT_SEARCH_ADS and TOP_POSITION_SEARCH_ADS as its ads. The extension leaves a module out of the page when its role is one of those or one of the five other roles the enum names after ads. On a profile, the one method holding sponsored_timeline_stories_test_key is the timeline story component's render, which asks GraphQLStory for its sponsored_data; the extension has it draw nothing for a story that carries it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/profile/LocalProfileCounts.java", "patches/src/main/kotlin/app/morphe/patches/facebook/profile/LocalProfileCountsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: your own friends, followers and following numbers on your own profile, on this phone only. The design follows Hushfeed's local profile counts (digits only, blank keeps the real number) with no shared code. Facebook 585 lays the top of a profile out with Bloks, so the patch passes every string the Bloks node classes' getString returns to the extension, which swaps a number that sits before a friends, followers or following word. The profile screen is the class named com.facebook.timeline.fragment.ProfileFragment, whose onFragmentCreate hands the extension the screen so it can compare the profile id argument with the user id of the ViewerContext the screen holds." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoriesTrayCircles.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/tray/StoriesTrayCirclesPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Home's stories row drawn as round pictures at a chosen size. Facebook 585 builds the row as Litho tiles in a list with no round variant, so the patch finds the row's render method by its trace string, reads the list view id it stores, and hands every pull to refresh wrapper a list factory returns to the extension, which clips each tile of that list to a circle and scales it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryTaps.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/taps/StoryTapsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a play and pause button over a story and stories that change only on a double tap. Facebook 585's story tap controller is found by the string it writes into its trace, and its back and forward navigations by the enum constants they pass, so the extension is asked in front of each and can skip it. Facebook's gesture listener gets a double tap callback that hands the second tap's lift to its own single tap code. The pause is Facebook's own, made through the controller's pauser under the reason its play and pause toggle uses, and the button is a view the extension adds to the window." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryProgressBar.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryViewerCleanup.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/cleanup/StoryProgressHook.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/cleanup/StoryViewerCleanupPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: switches that take the song and effect credits off a story's subtitle line and the suggested replies off its bottom. Facebook 585 draws each as a Litho component that names itself in its class initializer, so the patch finds the one-credit line, the turning credit line and the reply suggestions by those names and asks the extension at the top of each render. On a yes the one-credit line and the suggestions draw nothing, as Facebook's own render already does for a story without them, and the turning line answers the plain subtitle its constructor kept, unless the flag render tests before its Trending or Suggested label is set. A third switch draws a progress bar along the bottom of a story: each listener that writes \"StoryViewerProgressBar.onProgressUpdated\" hands the extension its segments and its own progress argument right after it moves them, and the extension draws the bar in the overlay of the story's card." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/profile/ProfileNotes.java", "patches/src/main/kotlin/app/morphe/patches/facebook/profile/HideProfileNotesPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the note bubble on profile pictures. Facebook 584 lays the top of a profile out with Bloks, and its two Bloks node classes are the only concrete classes keeping their attributes in an instance SparseArray that implement getBoolean, getId, getInt, getLong, getString and put under those names. Their child getters are their instance methods taking an int and answering a List, and the main node's layout getter wraps a lone child in Collections.singletonList. The patch hands every list they return to the extension, which leaves out a node whose own string attributes name com.bloks.www.bloks.profile.note. (the bubble's tap action) or com.bloks.www.bloks.profile.async.fetch.note_bubble (the action that loads its content)." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PostDownloadButton.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostSaveButton.java", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/actionrow/ActionRowAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/actionrow/PostActionButtonsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Save and Download buttons in the row under a post. In Facebook 584 the row's button types come from the one static method answering a List that loads XFBUFIButtonWithFallbackOptions, and its class builds one button from a type enum, the row's state and a Map in the static method whose cases load \"Save\". Every list the type method returns goes through the extension, which adds the enum's SAVE constant while its switch is on. The row method seals its lists of buttons with a static call taking a List and answering an ImmutableList, the last argument of a constructor taking only ImmutableLists; before each seal a generated helper reads the post from the state the way Save's case reads it, asks the extension for a Download button, and builds it with the constructors Save's case uses: the icon from the icon enum's DOWNLOAD and the style enum's OUTLINE, the two texts, the label taking a String and a Function1, the tag, and the button." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/ProfileSuggestionAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the People you may know carousel on your own profile. In Facebook 577 and 580 it is one Litho section that hands the name ProfilePeopleYouMayKnowSection to the section base class, and the profile's root section adds it only when the profile is the viewer's. The patch finds the one children builder loading that name, holds it to the section's constructor, to the base class's getLogTag() and setChildren of the builder's Children type, and to that type's public empty constructor and getChildren(), and asks the extension first there. The extension reads the name back through getLogTag() and has the builder hand back an empty Children while the People you may know switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/NotificationFooter.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the list of more posts under a post opened from a notification. A post opened from Notifications is fetched with a query whose builder sets show_notification_permalink_footer to a MobileConfig answer, once per query variant, and with it true the server adds a footer of more posts that never passes the feed's edge filter. The patch hands each answer to the extension right where the builder reads it, and the extension answers false while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/SuggestedGroupsCard.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Suggested for you groups card on the Groups tab, whose feed never passes the edge filter Home's copy goes through. In Facebook 585 every copy of the card is laid out by GroupsYouShouldJoinComponent, the one layout holding the Kotlin message of its cast to GroupsYouShouldJoinFeedUnit, and that layout already returns null when it has nothing to show. The patch asks the extension first thing there and returns that same null while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/FindFriendsCard.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Find friends card where the feed runs out of friends' posts. In Facebook 583 the end of the feed is one section whose children builder adds a part for each of its pieces, and the card is the one that names itself EndOfFeedCIUnitSection. The builder reads a string off the section's settings and builds the card only when that string isn't null. The patch finds the one String field read guarded by a null check right before the card's section is created, and hands the string to the extension, which answers null while the switch is on so the card is skipped the way some feed types already skip it." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/FeedEndHint.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the bottom of Home. In Facebook 585 Home's list ends with an empty component whose visibility handlers call one static method of its class with a visible flag, and that method opens a trace named InterruptionStateManager.onEndOfFeedUnitVisible. The patch finds the one static method holding that trace whose last parameter is a boolean and hands the flag to the extension first thing, so it can say Hush hid the suggestions after the last post it kept." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/GroupJoinState.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: posts from groups you aren't in. In Facebook 583 a post in a group carries the group as its to field, a Profile model that answers the type name Group, and that model reads viewer_join_state through getCachedEnum as an enum naming CAN_JOIN, CAN_REQUEST, MEMBER and the rest. Facebook's Join button shows for CAN_JOIN and CAN_REQUEST. The patch finds the one GraphQLStory accessor of to by its field key and type tag, the one accessor of viewer_join_state on that model by its key, and the enum by the names its static initializer holds, and fills two extension stubs with calls to them. The extension hides a post whose group answers either Join button state while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/suggested/RelatedPosts.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Related posts list under a post's buttons. In Facebook 584 it is InlineRelatedPostsPlugin, a plugin of the socket that names itself FeedStoryBelowUFIFooterSocket, and the socket's static name table names it along with the Meta AI comment summary. The patch finds that table and the socket's per-plugin check the way Hide Meta AI comment summaries does, and goes first in the check with the plugin's name from the table. The extension answers no for the Related posts plugin while the switch is on, so the socket moves on to its next plugin." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/PostCounts.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the numbers on a post's Like, Comment and Share buttons. In Facebook 583 one static builder turns a button's count into its text, answering an empty text for a missing or zero count, and the Share button plugin (a kept class name) calls it. The patch finds the builder through that plugin by its shape and holds it to the empty answer and its plural lookup, then has it ask the extension first thing. While the switch is on the builder answers its own empty text, so each button shows its word." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/PostFollowText.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #396: the Follow a collab post or a partnership ad adds after its second name. In Facebook 585 CollabFollowTitleTextPlugin and FBPADualHeaderFollowTextPlugin (kept class names) each build that \" · Follow\" text themselves and hand it to the title segment's (click, CharSequence) constructor as their last step, outside the follow check Hide the Follow link on posts already answers. The patch hands the text to the extension right before that constructor, and the extension answers an empty text of the same kind while the switch is on, so the header has no dot, no word and nothing to tap." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryRings.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/rings/HideStoryRingsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #397: the story and LIVE rings on profile pictures. In Facebook 585 the feed's post header decorators build components that name themselves FeedStoryRingComponent and FeedLiveRingComponent, and their ring checks ask two statics that take the post's GraphQLActor and answer whether to draw a ring; the patch hands every answer of both to the extension. The comment ring helper is the static of the class holding the comment StoryRingPlugin name that answers a ring state, and the patch has it answer the state Facebook builds for someone with no story when the extension says so. The profile photo (FDSProfilePhoto, a kept class name) takes its ring kind as a constructor argument that may be null, and in the two renders that test for a ring the patch passes that argument through the extension, which answers null while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/ProfilePictures.java", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/profilepictures/HideProfilePicturesPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #400: the profile pictures at the top of posts and next to comments. In Facebook 585 the one static holding the FeedStoryBaseHeader.shell.fds trace name switches on the header's actor type, every case jumps to one merge with the picture model in one register, and its default sets that register to null, Facebook's own header with no picture; the patch hands the model to the extension on the merge's own label, so every case passes through it. The comment render holding the comment_profile_picture test tag builds the picture's wrapper once and adds it to the row, which already skips null and has a null path of its own; the patch hands the built picture to the extension right after the build. The extension answers null while each switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/PostJoinButton.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Join button in the header of a post from a group you haven't joined. One instance method appends it to the header and carries a trace string naming the group header title menu helper. The patch finds that method by the string and its shape (returns void, five parameters, one a SpannableStringBuilder) and has it ask the extension first thing, returning before it adds the button while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/ReactionPreview.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the reaction preview under a post. Two draw methods paint it, the facepile drawable that the FDSInlineReactions component's static factory returns and the onDraw of PillsBlingBarView (a kept class name). The patch finds each by its shape and has it ask the extension first thing, returning before it paints while the switch is on, so the views keep their size and their taps." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postprompts/CommentReactionPreview.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the reaction preview on comments. Comments draw the same facepile drawable as posts, so the patch marks the FDSInlineReactions component a comment builds (found by the kept strings FDSComment and inline_reaction_transition_key) and copies that mark onto the drawable the one paired method makes from it, letting the drawable's draw ask the comment switch instead of the post switch." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredreels/ReelItemStory.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the reel ads Facebook builds as ordinary reels. In Facebook 577 and 580 the reel ad item's base class has one no-argument getter for its GraphQLStory, declared on one public interface up its supertypes that every reel item implements. The patch finds that interface and GraphQLStory's one sponsored_data accessor, and fills three extension stubs with them: whether an object is a reel item, the item's story, and the story's sponsored data. The extension drops a reel whose story carries sponsored data, the check Facebook's own Reels code makes before it shows a promotion over a reel." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredreels/AdPills.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the floating ad button on a reel or video ad's comments. In Facebook 577, 580 and 581 the comment sheet's indicator pill numbers its eight plugins, one static method turns a number into the plugin's class name, and one static check beside it decides whether that plugin's button shows. The patch finds the name table by the Reels ad plugin's class name and the check by its shape, and has the check ask the extension first with the table's name for its number. While the switch is on, the Reels, Watch and in-stream ad buttons answer no. The pill's other plugins are Facebook's call." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredreels/ReelsAdPool.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Reels and Watch ad pool. In Facebook 577, 580 and 581 one class, which names itself VideoHomeSponsoredPool, extends the story pool base every client-side story loader draws from, and its two instance methods that hand back an ad edge log -WVCDF-NO-AD when the pool has none to give. The patch finds that class by the literal and those two methods by their shape, and puts a call of the extension first in each: while the switch is on they answer null, the same answer the pool gives when no slot is free, so the loader takes the next organic reel and the pool never marks an ad as used." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredreels/ProfileReelAds.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the profile Reels tab's ad query. In Facebook 577, 580 and 581 a profile or Page's Reels tab asks for its ads with ProfileReelsAsyncAdsQuery, from one non-static void method holding that name, outside the Reels ad pool. The patch finds that method by the literal and its shape and puts a call of the extension first in it: while Hide sponsored reels is on, the method returns before it sends the query, so the tab shows only that profile's own reels." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/notifications/**", "patches/src/main/kotlin/app/morphe/patches/facebook/notifications/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the kinds of push notification a user picks. Facebook 577 and 580 parse each push into the kept SystemTrayNotification, whose mType holds the payload's type, and read that type as a constant name of the kept NotificationType enum, before the first colon and ignoring case. Every push is posted through the one method of the kept SystemTrayNotificationManager that loads show_notif_start. The patch finds it by that literal and its kept parameter types, reads mType from the push its builder holds, and returns before anything is posted when the extension says the kind is blocked. Twelve kinds sit under six switches that all start off, and every other kind posts as Facebook posts it." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoriesTrayPaging.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/SuggestedStories.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/suggested/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the stories Facebook suggests in the Stories tray. In Facebook 577 and 580 every answer of the tray's fetch goes through the one method holding StoriesTrayLightFetchControllerQueryOps.postProcessResult, which hands the buckets as an ImmutableList to the one constructor of the tray data class it answers, and the classic tray's receiver, the one method holding optimistic_data_adapter_receives, reads that list. The patch goes first in that constructor and hands the list to the extension, which leaves out a bucket whose is_story_bucket_suggested flag is true or whose first label is SUGGESTED, the two things the tray's card reads before it writes Suggested. The flag is read through the bucket interface method the receiver calls and a tree class answers with that field's key, and the label through the one static helper answering the label enum, which also names NEWFRIEND and TRENDING, for that interface. Every other bucket and anything that isn't a bucket stays. For issue #317 Home's tray, which never goes through that constructor, is filtered in the parser that turns its buckets into tiles, and when the filter leaves its row too short to drag the extension asks the tray's scroll listener, made a Pager by the patch, for the next page the way a drag would." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplaceAdFilter.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplaceResponseDiagnostics.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplaceSearchAds.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplaceSearchSyntax.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/MarketplaceFilterButton.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplacePathRemapper.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/MarketplaceListingAge.java", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredmarketplace/HideSponsoredMarketplaceListingsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredmarketplace/MarketplaceRequestAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredmarketplace/MarketplaceResponseAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredmarketplace/MarketplaceVideoAdAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/sponsoredmarketplace/MarketplacePriceLineAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the ads in Marketplace's feed. Facebook 577 and 580 draw Marketplace with React Native, and its Relay queries go out through Facebook's own Networking module, whose sendRequest is the one method holding FBNetworkingModule_React_Native. It reads a POST body from the request data's string entry into a StringEntity, and the query's tracking name (RelayFBNetwork_ and the query's name) from the same data. The patch hands the body and the data to the extension right after that read. For a Marketplace query whose form-encoded variables name shouldSkipAdRequest or shouldSkipBoostedListingAdRequest at their top level as false or null, the extension sets them to true and changes nothing else. For the four ads-only queries Facebook's own Tigon callbacks list (MarketplaceHomeFeedAdsQueryRendererQuery, MarketplaceHomeFeedAdsPaginationQuery, MarketplaceHomeFeedBoostedListingAdsQuery and MarketplaceHomeFeedBoostedListingAdsPaginationQuery) it answers no body, which the module reports to JavaScript as a request it couldn't make. Marketplace search's results query names neither variable and has no ads-only query, so its ads come back among the listings. The module's answers reach JavaScript through the kept FBTigonRequest$FBTigonCallbacks: onBody hands each piece of text, a toString() result, to React Native's didReceiveNetworkIncrementalData emitter and onEOM hands a whole one to didReceiveNetworkData, each with the request's state at hand, which sendRequest builds with the tracking name in one String field. The patch reads that field and hands the text, the name and the state to the extension right where the text lands. For a Marketplace query with Search in its name, other than the typeahead and mutations, the extension reads each whole JSON payload and takes out a result whose node's type ends in AdStory or that carries a sponsored_data object with an ad id, moving later streamed and deferred indices of its list down past the ads taken out. A streamed ad gives way to a payload with null data that keeps its extensions. A payload that fails to be read goes on as it came, and after it nothing more is taken out while later indices still move. Right before onEOM reports an answer complete through didCompleteNetworkResponse, the patch asks the extension for text still waiting for a payload to finish and hands it to the incremental emitter as one last piece. A video ad that reaches Marketplace's feed anyway isn't drawn, an idea De-Vanced also has, written here from Facebook 577, 580 and 581: the two Litho components that draw one, found by the strings only each holds (its layout spec's own name, and MarketplaceVideoAdQuery with MarketplaceVideoAdComponent_), ask the extension first and return null on a yes, before the ad's video is fetched. For issue #303 a listing page's answer (MarketplacePDPSurfaceQuery on 585) goes through the same response hooks: each listing in it that carries creation_time gets how long ago and the date written after the text of its location_text, and a second copy of the listing under the same id gets the same. The same date also goes under the price: on 585 the Fabric text builder that lays out the listing page's text runs is found by its strings, and the price line it builds is handed to the extension, which appends the date unless the listing is a vehicle (one with a vehicle_ field that holds something), since Facebook already writes one under a vehicle's price." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostText.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostWords.java", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/words/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: hiding feed posts by words the person lists. In Facebook 577 and 580 GraphQLStory has one accessor that asks getCachedModel for the field keyed message as the type tagged TextWithEntities, and GraphQLStory's own toString calls it, labels its text message.text and reads that with a static helper that is getCachedString of the key text. The patch finds that accessor and the one of attached_story as Story by their keys, holds the first to toString's reading, and fills two stubs in PostText with them. The rule runs in the shared feed guard: the extension reads both texts through the kept getCachedString after checking the model's type tag, folds them and the two bounded phrase lists with NFKC case folding, lets a keep phrase win, keeps any post whose text it can't read, and reports only counts." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostSources.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: hiding feed posts from people, Pages and sites the person lists. In Facebook 581 GraphQLStory reads its authors with an accessor that asks getCachedModelList for the field keyed actors as the type tagged Actor, and its attachments the same way as StoryAttachment. The patch finds both by their keys and fills two stubs in PostSources. The extension reads each author's id and name and each attachment's url through the kept getCachedString, follows Facebook's l.facebook.com redirect to the site it carries, reads a shared post the same way, matches ids, whole names and sites with their subdomains against the bounded list, keeps any post it can't read, and reports only which line matched by number." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostPattern.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostTypes.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/PostReactions.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/ReactionCeiling.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/TopicPacks.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a regular expression matcher for the word lists that never backtracks and counts its steps against a budget per post, and the four switches for kinds of feed post, which read an attachment's style_list and a story's text_format_metadata through accessors the Hide posts by words patch fills in. Also the reaction ceiling, which reads a story's feedback, its reactors and their count through two more accessors the patch fills in, and the topic packs the Words to hide editor adds." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/metaai/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: hiding the row of Meta AI questions under posts. In Facebook 577 and 580 one static method loads both DeepDivePillSocket and the kept class name of GenAiDeepDivePillPlugin. It loads every pill plugin's class name into one register and asks a static (model, index) -> Z check of its own class whether that plugin applies, twice per plugin, each answer in a move-result. The patch puts a call after each answer that hands the extension the answer and the name, and the extension answers no for Meta AI's plugin while its switch is on, so the socket goes on to the next plugin. A missing or doubled socket, plugin names in more than one register, or a register past v15 stops patching with a message." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/postdates/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: keeping the date on post headers. In Facebook 577 and 580 one method, the render of the post header subtitle that names itself FDSPostHeaderSubtitle, loads both in_cycling_experiment and subtitle_cycling_text_count. It works out whether the header rotates its subtitle, logs that boolean as in_cycling_experiment through String.valueOf right after the call that answers it, and then branches on it between a component that rotates the subtitle plugins' texts and the one line with the date. The patch finds that method by the two strings, checks the log's shape, and hands the answer to the extension at the log's own label, which answers no while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/whoshared/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a See who shared row in a post's menu. In Facebook 584 the list of people who shared a post opens through one public static (Context, GraphQLStory, String) method holding the message Activity is required to launch reshares list, and only share count rows behind a server test call it. The patch finds that launcher by its shape and message, writes the call to it into the extension's openList stub, and adds the row through the same post menu hook Download any video uses, so the list and what it shows are Facebook's." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/carousel/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: a photo carousel for posts with several photos or videos. In Facebook 585 one static (int) method names each album layout's class, MultiMediaAttachmentHScrollPlugin among them, and a static (props, int) boolean of the same class switches on the layout to say whether it applies. The patch finds the name table by that class name, reads the swipeable row's case from the table and the switch, and hands the answer of the row's feed check to the extension before Facebook tests it. It also puts a call at the start of the check that hands the extension the layout's name, so the three layouts a poster picks answer no while the switch is on." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/posttext/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: showing a post's whole text in the feed. In Facebook 584 one method takes a Spannable and a CharacterStyle, returns a Spannable, and reads the post line limit key (0x32d58eae). It calls a static helper that answers the cut text as a SpannableString, or null when the text fits, and branches on that answer right away. The patch finds that method by its shape and the key, checks there's one such call followed by its move-result and the null check, and hands the answer to the extension, which answers null while the switch is on so the whole text goes out." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/seen/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: hiding posts you've already seen. Facebook's viewport logger saves a post's seen state in ViewportLoggingHandler.persistSeenState(session, feedUnit) once the post has left the screen after a counted view. The method keeps its name and its VPVDLOGGING trace string, and it reads the unit's id through the kept getCacheId interface method, which together find it once in 577, 580 and 581. Its one caller, the dwell runnable traced VPVDLOGGING.ViewportLoggingHandler.logFeedUnitDurationInternal, skips that call on the News Feed itself (native_newsfeed, friendly_feed and the most recent tab), so the patch hands the unit to the extension's SeenPosts in the runnable instead, on a range call right after the 250 ms dwell check and before that surface check. The extension remembers a hash of the id, and the shared feed guard drops a remembered post on a later load." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/feed/feedsheader/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Feeds tab without its header. In Facebook 577 and 580 the Feeds tab is com.facebook.feed.fragment.FeedFiltersFragment, which keeps its member names. Its shouldInitializeNavBar answers whether the tab's chrome builds a title row, and the patch hands each answer to the extension at the return's own label, which answers no while the switch is on. Its onCreateView hands the container kept in tabBarContainer to the controller that fills and shows the filters. Right before that controller's new-instance the patch asks the extension, and on a yes puts a new container of the field's type in that register, built from the old one's Context the way the fragment builds its own, so the controller fills one that's never on screen. The fragment's feedFiltersFragmentContainerController posts a runnable Redex still names FeedFiltersFragmentContainerController$setViewPagerTopMargin$1, whose run() sets the posts' top margin to the filters' height when its boolean says they show. The patch hands that boolean to the extension between the read and the branch on it, and the extension answers no for a tab whose filters went to the container that's never on screen, so the posts start at the top." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/composer/**", "patches/src/main/kotlin/app/morphe/patches/facebook/composer/**" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the people Facebook offers to tag while you write. In Facebook 577 and 580 every text box that can tag people is one AutoCompleteTextView subclass, found by the one performFiltering(CharSequence, int) that logs ImplicitMentionsCommonWordsFilter: JSON Parsing Exception. Right after it compares the first character of the word at the cursor with @, it reads a boolean of its MentionsAutoCompleteBehavior and returns before any lookup when the boolean is set, and both of those branches land on the lookup. The patch hands that boolean and the box (the behaviour's one field typed as the box's class) to the extension right after the read and keeps the answer in the same register. The extension answers yes while its switch is on, and closes a list of people left open by an earlier @. Words starting with @ or # never reach that read." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/ResumePlayback.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/ResumePoints.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/resume/ResumeAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/resume/ResumeLongVideosPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: resume long videos. FacebookAppAdsRemover describes the behavior and has no licence, so nothing was taken from its code. The patch finds FbGrootPlayer in Facebook 577 and 580 by its play trace, then its kept-name maybeTrackVideoStart and maybeTrackVideoStop, its release, bind and static seek by the trace strings they keep, its public seek as the one that hands on to that seek, its position reader as the one int method of the interface that declares isPlaying, and its length reader as the other int method its own remaining-time sum asks. VideoPlayerParams' field names come from the debug dump that reports each under its real name. The extension saves where a video longer than two minutes stopped, up to 200 of them for 30 days in a preferences file of its own, forgets one stopped near its end, and seeks back once on the video's next first start unless a seek came first or Facebook placed the start itself." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/FrameStep.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/framestep/FrameStepAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/framestep/FrameStepPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Frame step, for issue #95, from Facebook 582. It finds FbGrootPlayer the way Resume long videos does and hooks its kept-name maybeTrackVideoStart and maybeTrackVideoStop, its release and its static seek, plus the one Integer-and-int boolean check that seek asks before dropping a small seek, and fills stubs with the position reader, isPlaying, the public seek and the one ViewGroup field the attach holding FbGrootPlayer.attachPlayerViewInternal writes. GestureReactionComponent, found by its constructor's name, makes a view whose constructor sets one OnTouchListener; the hook goes first in its onTouch. The extension steps a paused player under a quick side-third tap by 33 ms with BY_INITIAL_FRAME_PERFECT_SEEK (BY_USER gets 582's fast seek, which landed up to two seconds early) and turns the tap's up into a cancel. A moment later it reads where the player landed, and when that's off it asks once more with the offset taken off and keeps the offset for that player's later steps." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/PinchZoom.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/pinch/PinchToZoomPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Pinch to zoom, for issue #220, from Facebook 584. The patch puts a call first in FbFragmentActivity.dispatchTouchEvent, the override every Facebook screen shares and the one Tap to play already finds, and first in the kept-name ProfileFragment.onViewCreated(View, Bundle), which hands the extension each profile's root view. The extension zooms only in the story viewer, known by its kept class name, and under a registered profile root, since the news feed has its own pinch in the same activity. When a second finger lands on the topmost ImageView with a picture, TextureView, SurfaceView or Litho host that mounts no text and no child views, it copies that view with PixelCopy (a SurfaceView from its own surface) or draws it, lays the copy over a dim scrim in the window's decor view, follows the fingers' span and midpoint up to four times, and turns the rest of the gesture into a cancel for Facebook. Once fewer than two fingers are down the copy eases back over 200 ms to where the photo sits and goes. Inside a profile or the story viewer the search looks past views that hold no media, like the story viewer's clear tap layer. Above a profile's root it looks past only views that draw nothing at the fingers (Facebook keeps an empty layer over every screen) and stops at anything that does. A Litho host found first gives way to a picture right under it that spans all of it, like the band a profile's cover draws over its photo. Pages opened from a link run in an activity whose dispatchTouchEvent goes through a delegate, and that delegate calls back into FbFragmentActivity's, so the one call covers them too." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/SkipSilence.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/silence/SkipSilentPartsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Skip silent parts, for issue #144, from Facebook 582. Each of the two ExoPlayer copies Facebook ships has a DefaultAudioSink that hands its processor chain, a field of its own, the skip-silence flag and then calls the kept-name AudioSink.Listener method onSkipSilenceEnabledChanged. The patch finds each method calling that name after handing one boolean to such a chain, asks the extension for the flag into a register nothing reads there, and hands the chain that answer, leaving the sink's stored flag alone. The listener's own dispatch, which hands nothing on, is left out. The extension answers yes while its switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/FeedSound.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/feedsound/KeepFeedVideosMutedPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Keep feed videos muted, a Reddit request, from Facebook 583. Facebook's feed sound state is the one class with a kept-name startNewSession method, which hands a flag to a setter of its own taking a reason enum and a flag. The same class has one setter taking that reason, a String and a flag, which first reads the sound flag both setters store and compares it with the one it's asked for. The patch asks the extension for the session setter's flag before anything reads it, and for the reasoned setter's flag right after its read, each answer going back in the flag's own register. While the switch is on a new session starts muted, and a sound-on whose reason name doesn't start with BY_USER, and that doesn't come through onKeyUp or the kept-name maybeUnmuteOnDeviceVolumeIncrease, keeps the flag the state holds." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryUpload.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/upload/UploadStoriesInHdPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Upload Stories in HD, for issue #123, from Facebook 582. The publisher's kept Kotlin internal name getUploadOperationForCreate builds a new post's UploadOperation, and for a post bound only for a Story it puts each video's upload quality into a Bundle under video_upload_quality. Facebook's video uploader passes only high on to the upload server as its encoding hint. The patch finds that one putString, asks the extension for the value into the value's own register right before it, and leaves the rest of the method alone. The extension answers high while its switch is on and keeps Facebook's raw." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/stories/StoryMusic.java", "patches/src/main/kotlin/app/morphe/patches/facebook/stories/music/LongerStoryMusicPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Longer music on photo stories, for issue #156, from Facebook 584. The Story composer's music clip length is the one static method taking FbUserSession and the composer state, answering an int, that asks the composer configuration for its FeedAttachmentConfig and holds 30000 and 15000. For a photo it returns a MobileConfig double turned into an int when that is above zero, else 15000. The patch asks the extension right before both of those returns, into the returned register, and leaves the rest of the method alone. The extension answers at least 30000 while its switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/DoubleTapLike.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/doubletap/DoubleTapAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/doubletap/TurnOffDoubleTapLikePatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the switch on liking a reel or a video with a double tap. The patch finds, in Facebook 577 and 580, the reel like helper by its kept trace FbShortsMutationUtil.mutateViewerLikeReaction, its double-tap like as its one instance method taking two objects and a boolean, GestureReactionComponent by the name its constructor gives it, that component's gesture view through onCreateMountContent, the view's heart by the shortFormVideoUnit key it logs, and the feed attachment's own double tap as the one onDoubleTap animating translationY. It hands the extension the gesture view's double-tap handler after every read of it, the double-tap like's key for the reel, the helper like's source and the attachment's double tap. While the switch is on the extension answers that there's no handler and no key, holds back a like whose source is DOUBLE_TAP and leaves the attachment's double tap unhandled, and otherwise hands back what Facebook handed over." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/DoubleTapSkip.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/doubletapskip/DoubleTapSkipPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Double tap to skip, for issue #225, from Facebook 584. Facebook's players carry a double-tap skip of their own, a Kotlin lambda found by its kept captured names $edgeWidthPercent, $skipDuration and $videoDurationInMs and its invoke taking a MotionEvent and the view, which seeks on a double tap at the left or right edge only when one MobileConfig boolean is on. The patch takes that flag's id from the lambda's one read, finds every read of the id in the classes naming MobileConfigUnsafeContext (the lambda and the player builders that pick the skip and the edge from it), refuses when a load of the id isn't a read it knows, and hands each answer to the extension right after its move-result. The extension answers yes while its switch is on and Facebook's own answer otherwise." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/TapShowsControls.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/tapcontrols/TapShowsControlsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Tap shows the controls, for issue #225, from Facebook 584. Facebook's reel tap handler, found as the one method holding both the tap_to_show_player_control and reels_controls_tap_unmute log names, holds a dormant answer to a tap that shows the controls with a pause button and lets the reel play on, behind one config boolean Facebook ships off. The patch finds that boolean as the interface read guarded by an if-eqz and an if-ne landing on the same label, hands it to the extension after its move-result, and passes both of Facebook's waits before the controls hide (the unified player's int read in the handler, and the wide constant the controls' controller posts with its hide runnable next to announceControlsShowingForScreenReaders) through the extension too. The unified reel player skips posting any hide, through a static boolean check right before the handler's int read, so that check's answer goes through the extension as well. It refuses when a site isn't found exactly once or a branch lands inside it. The extension answers yes, a 3 second wait and no skip while its switch is on, and Facebook's own answers otherwise." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/ReelSpeed.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/ReplaySpeed.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/reelspeed/KeepReelSpeedPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/reelspeed/ReelSpeedAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/reelspeed/ReplaySpeedSite.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: keep the reel speed. The patch finds, in Facebook 577 and 580, FbGrootPlayer by its play trace, then its speed setter as the one instance (F)V method reading HeroPlayerSetting.enableLastPlaybackSpeedCacheUpdate, its PlayerOrigin getter as the one method answering a PlayerOrigin, its bind by its bindVideoSources trace and its kept-name maybeTrackVideoStart, and the Reels menu's speed toast as the one static (Context, float) method holding InlinePlaybackSpeedAttributeSelector. The setter, the bind, the start and the toast hand the extension their arguments first thing, and the extension's stubs are filled with the setter and the origin getter. The extension keeps a speed picked in the Reels menu, in memory only, with the origin of the player it was set on, and sets it on the first start after each bind of a player with that origin. For issue #238, from Facebook 585, the gear menu builder (the one method holding UNIFIED_PLAYER_VDD_ENGAGEMENT_STATE that casts to the class holding video_player_control_gear_playback_speed_tap) asks the extension right before it leaves Playback speed out of a full-screen video that is not live, found as an if-nez, an if-eqz and a goto onto the speed row, and the extension says yes unless the video is an ad or its switch is off." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/ReelHold.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/FeedVideoHold.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/hold/HoldReelFor2xPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/hold/ReelHoldAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: hold a reel for 2x, using the hold-to-speed-up Facebook's Reels controls already have behind server flags. The patch finds, in Facebook 577 and 580, the long-press handlers as the lambdas keeping $shouldDispatchLongPressEventDirectly and $immersiveFeedPlayerConfig and holding \"speed_up\", the release listeners as the lambdas keeping $isInLongPress2xPlaybackSpeed and that config, the speed-up flag as the one flag the handlers ask the config, the release flag as the one the listeners ask straight before it, the renders of FbShortsVideoControlComponent and FbShortsViewerOverlayComponent that make those lambdas, the edge check as the one method of its shape the handlers call, and FbFragmentActivity.dispatchTouchEvent. Each flag answer the handlers, the listeners and the renders take, and each answer of the edge check, goes through the extension, which says yes while the switch is on, the listeners' yes only until a lift they hear after a hold, which it tells from the touches it reads. The hold speed both speed-ups read goes through the extension too, which makes one no faster than normal 2x. FbGrootPlayer's speed setter hands the extension the player and the speed first thing and goes on with the speed it answers, and the extension's stub is filled with the player's speed getter, the one the listeners read, so a hold's lift puts back the speed the reel played at before the hold. Its second switch, Hold a feed video for 2x (issue #229), finds FbGrootPlayer's attach of its view in Facebook 584 by its attachPlayerViewInternal trace and the one ViewGroup field of the player it writes. The attach hands the extension the container and the player first thing, the touch dispatch hands it the activity and the event, and the extension's stubs are filled with that field and the speed setter, so a hold it times on a video playing in a feed list sets 2x and the lift puts back the speed before." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/PlaybackQuality.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/QualityChoice.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/QualityList.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/SurfaceQuality.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/quality/DefaultPlaybackQualityPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/quality/QualityAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/quality/QualityRow.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the quality videos play at, an idea De-Vanced also has, written from Facebook's own code. The patch finds, in Facebook 577 and 580, the DASH format evaluator as the class of the one (String)V call in the method holding HeroServicePlayer.setCustomQualityInternal, built with an AbrContextAwareConfiguration; that custom-quality setter's array of tracks and the static method and field it reads a track's label with; and the String field the evaluator hands its own setter at its first choice, its preselected label. The setter asks the extension first with its label, the constructor tells it about the evaluator before each return, and the extension's stubs are filled with the preselected label, the tracks and a track's label. While the switch is on and a quality is chosen, the extension leaves a marker as the preselected label of an evaluator Facebook gave none, and turns it into the label of the track the chosen quality plays when the setter gets it. Reels and stories can each have a quality of their own (an idea De-Vanced also has): two more stubs read the player origin and sub-origin from the evaluator's AbrContextAwareConfiguration's playbackPreferences, the two String fields the configuration's constructor compares with fb_stories and fb_shorts_viewer, and the extension sorts a video by those names." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/AnalyticsUploads.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/CameraRollProcessing.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/analytics/AnalyticsUploadAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/analytics/CameraRollAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/analytics/HoldAnalyticsUploadsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hold back analytics uploads, found in Facebook 577, 580 and 581 by names Facebook keeps. A check goes just before the one kickOffUpload() call, in the run() of the runnable the kept NativeXAnalyticsAppJobHandler schedules every three minutes, and the one resumeUploading(String) call, in the kept NativeXAnalyticsLowPriorityInit, and skips the call on a yes. The Papaya job's start, the most derived onStartJob along the kept FBPapayaJobService's superclasses, passes the answer of its config gate through the extension, so a held job takes Facebook's own off path. The camera roll switch, found in Facebook 582: a check goes first in the static (FbUserSession)Z config check the kept PipelineManager$runPipeline$2 calls next to its adv_pro_app_job_disabled reason, and answers it false on a yes; the kept MediaCountUploadAppJob's first config read, the kill switch whose branch cancels the MediaCountUploadWorker work, passes its answer through the extension." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/GameAds.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/GameBridge.java", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/games/BlockInstantGamesAdsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/games/GameAdAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Block Instant Games ads, an idea De-Vanced also has, written here from Facebook 577, 580 and 581. The game bridge's postMessage(String, String), kept by name for the WebView, holds the five ad message types; the hook goes first in it and, for an ad message with the switch on, rejects the game's promise through the bridge's own (String, String, String) reject method, the one calling the call that holds \"Unexpected exception while constructing JSONObject to be dispatched to Javascript: rejectPromise\", with the SDK's code for an ad it can't serve. De-Vanced drops the message instead. Games opened from Facebook 583's main screen run from a downloaded module that registers a QuicksilverAndroid bridge of its own, so the game WebView's base class wraps that bridge as it's added, and the wrapper answers its ad messages with no ad, or as watched for a rewarded one." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/AudienceNetwork.java", "patches/src/main/kotlin/app/morphe/patches/facebook/ads/audiencenetwork/AudienceNetworkAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook, for the ported Disable Audience Network: keeping its components off in Facebook 583 after an install and at every start. The manifest gives each disabled component the default-state=false meta-data Facebook's component manager checks before it turns manifest-disabled components back on, the startup task that enables the ipc components in a state the server picks passes its calls through the extension, and the extension sets any component an earlier install left on back to disabled." }, { "paths": ["extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/CloseWhenLeft.java"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Close Facebook when you leave it. Three seconds after the last Facebook screen stops, it stops Facebook's own started services, takes its tasks out of the recent apps and ends its processes, and it waits while a Facebook process is in use, a Hushfacebook save is running or another app's screen sits on a Facebook task. The screen turning off with Facebook in front isn't a leave." }, { "paths": ["extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/FeedGaps.java"], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the gray line between feed posts on the AMOLED theme. A hook first in Litho's ComponentHost.dispatchDraw paints the strip Facebook leaves under each row of android:id/list a step above the black page, behind a switch that starts off. The hook itself sits in AmoledThemePatch.kt under that file's rule." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/AmoledBackground.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/AmoledResources.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook, for issue #214: the AMOLED theme's Background color row, which changes the dark background after patching. AmoledBackground is the patch's Background colour option rule in Java, so the row takes the colours the option takes and gives each the same surfaces. AmoledResources gives each activity's resources a colour table for the colour resources the patch wrote its background over, listed by the patch, through a ResourcesLoader on the Resources Accent color's route five keeps for the same assets, so views inflated from layout XML take the row's colour too." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/AmoledLightBackground.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/FdsTokens.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook, for issue #214: the AMOLED theme's Light background color row, which swaps light mode's white for a light colour after patching. AmoledLightBackground takes a colour only when Facebook's light secondary text can be darkened to 4.5:1 on it and still stay apart from the primary text, and moves Facebook's light greys down from it by as much as they sit below white. FdsTokens parses the FDS colour tables MaterialYouTheme declares, so the AMOLED theme can read them without loading Material You's class." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/Screenshots.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/screenshots/AllowScreenshotsAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/screenshots/AllowScreenshotsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Allow screenshots, an idea ReVanced's universal Remove screenshot restriction patch, Loukious and NexAlloy share, written here from Facebook 577, 580 and 581. Every Window.addFlags(int) and setFlags(int, int) call outside the extension becomes a static call of the extension on the same registers, and every write of WindowManager.LayoutParams.flags first runs its value through the extension, which takes FLAG_SECURE out while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/AppIcons.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/appicons/UnlockAppIconsAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/appicons/UnlockAppIconsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Unlock app icons, based on the idea of HushMessenger's patch with the same name and traced here on Facebook 582 for issues #109 and #160. The subscription benefit provider is found by the strings SUBSBenefitDataProvider and subs_active_benefits. Its one (String)Z instance method checks a benefit name against its Set. The hook goes first and answers true for CUSTOM_APP_ICON while the switch is on. The App icon page's (Set)V callback also loads CUSTOM_APP_ICON and calls Set.contains. Its answer passes through the extension. The Home header hook follows Facebook's stock wordmark load and replaces that drawable with the explicitly enabled non-default launcher alias." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/appicons/LauncherIconStyle.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the Icon style option of Hushfacebook settings for issue #278, after the idea of Hushfeed's two plain app icons. No artwork ships. The resource patch reads the application icon from the manifest, finds its one adaptive-icon configuration and the themed-icon vector its monochrome layer names, and writes a solid background color and a one-color copy of that vector inline into the adaptive icon. The monochrome layer stays." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/SaveStorage.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #274: lists the SD cards Android's StorageManager and MediaStore both report as mounted, picks the one the Save on setting names, and falls back to the phone's storage while it's out of the phone." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/PauseUnderComments.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #266: pauses the reel playing on screen when a comment sheet opens, through Tap to play's player bridge, holds the reel's own restarts while the sheet is up, and plays it on once the sheet's views leave the window." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/HeaderPicture.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/HeaderText.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/headertext/HomeHeaderTextPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook, for issue #230: Home header text. The hook sits at the same site Unlock app icons uses, right after the Home header's builder loads Facebook's stock wordmark, and hands the logo to HeaderText. That swaps the wordmark for a drawable that draws the saved words at the wordmark's height, in bold sans serif, through the color filter the header gives its wordmark, so the words follow Facebook's light and dark colors. A picture picked in the settings is shrunk to at most 1024 by 256 pixels with ImageDecoder, kept as a PNG in Facebook's files by HeaderPicture, and drawn at the wordmark's height in its own colors in place of the words. A pre-draw listener puts the words or the picture back when Facebook loads its wordmark again, and the picked app icon steps aside while there are either." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/AppLock.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Lock Facebook, which covers Facebook's screens on a cold start and on a return after the chosen time until Android's BiometricPrompt passes with a biometric or the phone's screen lock. Facebook's own AuthAppLockState has no lock screen behind it in 577, 580 and 581, so the lock runs from the settings entry's activity callbacks rather than a patch." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/OpeningPause.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Opening pause, a cover over Facebook's first screen when it's opened from its launcher icon that counts down 5 or 10 seconds before Open now works, while Close or Back sends Facebook away. Like Lock Facebook it runs from the settings entry's activity callbacks rather than a patch, so links, notifications and shares open straight away." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/BreakTimer.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Take breaks, which counts foreground time on Home, Feeds, Video and screens a reel played on, and after the chosen use time covers those screens for the chosen break length while chats, profiles and the Menu keep working. It runs from the settings entry's activity callbacks plus Resume playback's player-start hook, and keeps a break under way across restarts and reboots." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/BlockedListPictures.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hide pictures on the Blocked list. When Facebook's React Native Blocking page comes to the front, found by its route in the intent, each layout pass hides the square images of 32 dp or more, which are the blocked people's pictures, and leaves the 24 dp plus beside Add to blocked list. It runs from the settings entry's activity callbacks rather than a patch." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/TextSize.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Text size, which sets the font scale Facebook's resources report to the phone's times a chosen 85% to 130%, on each activity as it's created and resumes and on the application's resources, from the settings entry's callbacks rather than a patch. Facebook's native views and Litho text both read Configuration.fontScale and DisplayMetrics.scaledDensity from their context's resources in 577, 580 and 581." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/WindowsAbove.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the windows a Facebook screen has open above its own, such as the comment sheet Facebook 581 shows as a dialog fragment, read from Android's WindowInspector for the report's Last screen left lines and Material You's sheet colours." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/Haptics.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/haptics/HapticsAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/haptics/TurnOffHapticsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Turn off haptics, an idea from De-Vanced's Facebook clean-up patch, written here from Facebook 577, 580 and 581. Every View.performHapticFeedback(int) and (int, int) call outside the extension, and every Vibrator.vibrate call that takes a VibrationEffect, becomes a static call of the extension on the same registers, which skips it while the switch is on. The timed and patterned Vibrator calls are left as they are." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/CarrierBanners.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/carrierbanners/HideCarrierBannersPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 582: Facebook Flex's banner method, found by its manual switcher log steps, hands the banner's key to the extension where every banner it shows meets, and a key whose switch is on is hidden with Facebook's own hide call. The Data mode keys and the Free mode keys each have their own switch." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/OriginalChatMedia.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/OriginalPhoto.java", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/OriginalChatMediaAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/OriginalChatMediaPatch.kt" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/HushMessenger", "commit": "e1e90d839e142ca225dcf345b6b35116475e6e64", "via": [], "license": "GPL-3.0-only", "changes": "Rewritten for Facebook from HushMessenger's original photo and original video controls. Facebook's in-app chats use the same DefaultMediaTranscoder, so the photo hooks (transcodeImage and transcodeImageAsync, handed their arguments in one range call) and OriginalPhoto's JPEG copy are the same idea and nearly the same code. The video hook moved: Facebook asks extractMimeTypeAndCheckCanSkipVideoTranscoding, in a build with two trailing ints, whether a file may skip the re-encode, and the size check sits there (bitrate times duration over 8000, plus slack times 1000) instead of in the transcoder's private worker. The anchors are found by names Facebook keeps and by the shape of that check, before anything changes, on 577, 580 and 581. A settings switch in front of every hook, Pause and cold start answered with Facebook's own behavior, the diagnostic report counting what goes out, a send with Facebook's standard size treated like an HD one because the switch asks for originals, and failures recorded in the report instead of Messenger's private log." }, { "paths": [ "patches/src/main/kotlin/app/morphe/patches/facebook/misc/gmscore/GmsCoreSupportPatch.kt" ], "origin": "ported", "upstream": "https://github.com/SysAdminDoc/HushMessenger", "commit": "6761b36660b67092f5345479781c7bd9602f772b", "via": [ "https://github.com/SysAdminDoc/HushPinterest", "https://github.com/MorpheApp/morphe-patches", "https://gitlab.com/ReVanced/revanced-patches" ], "license": "GPL-3.0-only with the Morphe section 7 notices", "changes": "HushMessenger's GmsCore support moved to Facebook. Facebook 585 has the same Play services availability check and the same androidx Credential Manager framework provider, and Facebook's original certificate is the one Messenger's is. Hushfacebook's own classes under app/morphe keep Google's names, the preflight asks the extension for its gmsCore status method in place of Messenger's version code check, and the patch marks itself in SettingsStatus so the settings page and the diagnostic report know it's patched in." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/VideoLocation.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: before Send chat photos and videos at original quality lets a video skip Facebook's re-encode, a read-only walk over the file's top-level MP4 box headers and its movie box, capped at 4 MB, looks through every udta and meta of the movie, its tracks and their media for a place (a ©xyz or loci box, a location or ISO 6709 key, GPS tags in XMP). A file that isn't local, is cut short, fragmented, laid out in a way it doesn't read, or has a track that isn't video or sound keeps the re-encode." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/TypingIndicator.java", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/HideTypingIndicatorPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/TypingAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hide typing indicator, an idea shared with HushMessenger's Hide typing indicator and the ConversationTypingContext runnable De-Vanced and ReVanced find, written here from Facebook 577, 580 and 581. Mailbox's typing setter hands its flag to the extension first, and the chat and comment typing runnables, found by the names Redex keeps, ask the extension before they send." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/CommentGif.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PhotoFormat.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/MenuTop.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PhotoMenuItem.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PhotoSave.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PhotoSearch.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/PhotoSearchFiles.java", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/photo/CommentGifAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/photo/DownloadPhotoPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/photo/PhotoSaveAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/photo/PhotoSizeAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581: the photo viewer's two reads of can_viewer_download ask the switch, and Save photo's kept newSavePhotoAction hands its listener to the extension, which saves the photo's biggest image through Hushfacebook's downloader and falls back to Facebook's own save; and, behind a second switch, Save photo in the menu of a post holding photos, added through the same post menu hook as Download to phone, reading the post's attachments, an album's subattachments and the post a share wraps through the kept GraphQL models' getters found by their field hashes, and saving each photo in turn; and, behind a third switch, Search photo in the menu of a post holding one photo (#245, #248), which fetches the photo into a cache folder of its own and hands it to the app picked in Android's chooser through an unexported, read-only provider the patch declares; and Save GIF in a GIF comment's long-press sheet, built with the sheet's own Save photo row builder and icon from the comment's animated image. A save asks Meta's CDN for the biggest copy through the same resize call Facebook's own Save photo makes, and a picture the CDN sends as AVIF or WebP is written out as a JPEG." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/CommentVideo.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 584 and 585 (#318): Save video in a video comment's long-press sheet, added at Save GIF's point with the sheet's own Save photo row builder. It reads the video's id and files from the comment's attachments through the kept GraphQL models' getters found by their field hashes, and saves through Download to phone's quality picker, naming the file from the video id only." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/ReelMenuDownload.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/ReelSidebarDiagnostics.java", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/reel/ReelMoreSheet.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581: a Download row in a reel's More sheet, for accounts whose reels lack the sidebar Download any reel puts its button in. The sheet builder is found by the Clear mode row id it holds, the row constructor, click interface and icon wrap by their shapes, and the download icon by the enum constant Facebook's own download row reads; the builder's first call adds the row through a helper that looks up the reel's player and hands it to the same save the sidebar button uses. A private, bounded missing-sidebar diagnostic for Facebook 582 reads component paths around count nodes after a verified reel start with Debug logging and downloads on. Known sidebar hooks suppress the read, and no screen text, count values, resource ids or URLs reach its report." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/ReelCaption.java", "patches/src/main/kotlin/app/morphe/patches/facebook/downloads/reel/ReelCaptionRow.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 584: a Copy caption row in a reel's More sheet. The reel caption's render is found by the component name it gives its text and the tap lambda whose kept Kotlin names give the caption and the reel's type; right after the render builds that lambda, a helper added to its class reads the reel and the whole caption off it and hands them to the extension, which keeps a bounded set of captions by video id. The sheet builder's first call adds the row through a helper, drawn with the copy icon Facebook's own icon enum names, only for a reel whose caption was seen. The tap copies the text to the clipboard." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/download/ClipboardLink.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook, an idea from Loukious's module (behavior only, no code): behind a second switch of Download any video, a facebook.com or fb.watch reel or video link on the clipboard is offered for download once per link when a Facebook screen comes to the front and gains focus, read only with the switch on; a share or fb.watch link is followed over Facebook's own https redirects to its video id, and the save reuses the player source Facebook records for that id, opening the video through Facebook's kept IntentUriHandler first when no player was built yet." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/chats/ReadReceipts.java", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/HideReadReceiptsPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/chats/ReadReceiptAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hide read receipts, an idea shared with HushMessenger's Hide read receipts, written here from Facebook 577, 580 and 581. Mailbox's mark-read, found by its API name, asks the extension once its future is made and hands that future back unsent while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/PictureInPicture.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/PictureInPictureRow.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/pip/PictureInPicturePatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/pip/PictureInPictureRow.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: picture-in-picture for reels through the picture-in-picture Facebook ships for its Reels viewer behind server flags, an idea from De-Vanced's Facebook picture-in-picture patch, written here from Facebook 577, 580 and 581. The patch finds ReelsPipUtil by the surface name only it loads and its one activity check by the system feature it asks for, and the Reels viewer's arming step by two player origins it names, with the surface gate it asks first. The extension answers yes first in both on Android 12 or later where the phone has the feature. It also hears ReelsPipUtil arm and disarm and FbGrootPlayer play and pause, arms the window again for the reel a swipe starts through a helper the patch adds to ReelsPipUtil, holds auto-enter off while the reel on screen is paused, and gives Facebook's window viewer a view id nothing on screen holds. A reel's More sheet gets a Picture-in-picture row, built with Facebook's own row class like the Download row there, whose tap opens the window at once, and a second switch keeps auto-enter off so that row is the only way in. No code came from De-Vanced." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/HdrBrightness.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/SdrRenditions.java", "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/TextureVideo.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/hdr/TextureVideo.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/media/hdr/TurnOffHdrBrightnessPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581, for issue #66: every call of Window.setColorMode and Window.setDesiredHdrHeadroom outside the extension goes through the extension, which asks for the default colour mode and no headroom in place of Facebook's HDR window while the switch is on, and each SurfaceView Facebook builds asks Android 15 for no headroom. From 582, Play SDR versions of HDR videos: the DASH manifest string Facebook's parse logging DashManifestHelper2 reads goes through SdrRenditions first, which drops an HDR rendition (PQ or HLG transfer, or Dolby Vision) when an SDR one at least as tall is in the same manifest. From 585, for issue #308, Fix washed-out video colors: the player's must-use-a-SurfaceView decision, found by the force_texture_view_for_blur key it loads, answers no through TextureVideo, and each read of the player's TextureView preference behind that no answers yes, so the player draws into a TextureView the GPU composes with the screen. No Facebook patch source in the ledger has an HDR switch or one for the player's view." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/media/ProgressBar.java", "patches/src/main/kotlin/app/morphe/patches/facebook/media/progressbar/KeepProgressBarPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581, for issue #73: the Reels viewer's bottom progress bar plugin, found by the name it gives itself, is made full size where Facebook would shrink it, and the full-screen video controls' fade timer, in the class FeedFullscreenVideoControlsPlugin extends, isn't set while the switch is on. No Facebook patch source in the ledger describes keeping a progress bar." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/comments/MetaAiSummaries.java", "patches/src/main/kotlin/app/morphe/patches/facebook/comments/summaries/HideMetaAiCommentSummariesPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581: the comment sheet's top content socket and the socket under a post's buttons are found by the names they give themselves, each socket's name table by the Meta AI summary plugins it names, and its per-plugin check by the plugin numbers it shares with the table. The extension answers no for the summary plugins in each check while the switch is on. No Facebook patch source in the ledger describes Meta AI's comment summaries." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/comments/CommentSheetOptions.java", "patches/src/main/kotlin/app/morphe/patches/facebook/comments/options/CommentSheetOptionsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581: the comment box's attachment button socket is found by the name it gives itself, its name table by the GIF and sticker button plugins it names, and its per-button check, in another class, by the plugin numbers it shares with the table. The extension answers no for those two buttons while the switch is on. The reaction picker a long press on Like opens is found as the method that builds the kept RopeStyleUFIDockView and logs reactions_dock, and it returns early while Like only is on. The comment section is found by the name it gives itself, its reply flag through the expandReplySection update's arm of its state's update switch, and the extension's answer is written into that flag as the initial state finishes. The settings row opens Facebook's own fb://facebook_settings route. No Facebook patch source in the ledger describes these." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/MarketplaceSellerProfile.java", "patches/src/main/kotlin/app/morphe/patches/facebook/navigation/sellerprofile/ShowSellerViewProfilePatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 577, 580 and 581 and their React Native bundle, for issue #68: a seller's Marketplace page offers View profile only while the experiment flag qe_marketplace_commerce_profile_page:contextual_view_header_enabled reads true, so the config module's getBool and getBoolWithoutLogging ask the extension first, which answers true for that one name while the switch is on. The module's four by-id boolean reads ask it too, and it answers true for the same flag's stable id (config key 49154, param key 15 in each build's rn_params.txt). No Facebook code was copied." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/ListingGap.java", "patches/src/main/kotlin/app/morphe/patches/facebook/navigation/listinggap/RemoveListingGapPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook from Facebook 585 on an Android 17 emulator, for issue #333: a Marketplace listing is a React Native page whose header pads its top by the safe area Facebook's SafeArea module reports, the whole window's insets, while Android 17 already places the page below the status bar. Before the module's static Insets-to-map method runs, in safeAreaInsets and in the listener it sets on the decor view, the insets and the decor go to the extension, which keeps only the part of each side that overlaps the React Native root view. Until the root is placed, the room inside the native frame the page fills (its padding, and the margins of a container still waiting for its first layout) stands in for it. No Facebook code was copied." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/ScreenTransitions.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/Fingerprints.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/FragmentSlides.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/PagerTabs.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/PhotoPage.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/PhotoZoom.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/TransitionAnchors.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/transitions/TurnOffScreenTransitionsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Turn off screen transitions, an idea from De-Vanced's Facebook clean-up patch, written here from Facebook 577, 580 and 581. The activity callbacks the settings patch registers ask Android for no transition as a screen comes to the front and as it closes, and the tab bar's pager controller and the sliding panel ask the extension before they decide to slide. Tab strips inside a screen ask too: the listener Facebook's TabbedViewPagerIndicator puts on each tab, found from the Redex name of the strip's onPageSelected runnable, hands its slide flag to the extension first, and the composer's kept MinutiaeTabbedPickerActivity.setTab calls the pager's two-argument setCurrentItem with no slide while the switch is on. From Facebook 583, screens opened inside the app ask too: the fragment transaction's setter of its four custom animations, found from the field labels in BackStackRecord's dump, hands each one to the extension, which answers none. The photo viewer's open and close zoom, the Rebound spring the kept SutroPhotoAnimationDialogFragment sets going, is put at its end value at once while the switch is on. The page a multi-photo post's photo opens on, the kept PhotoAnimationDialogFragment, grows its photo with a ValueAnimator method that takes its length as a Long, and its open, close and slide-away methods hand their animators and background fade a long length; each goes through the extension, which answers 0 while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/misc/ScreenshotDetection.java", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/screenshots/BlockScreenshotDetectionPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/misc/screenshots/ScreenshotDetectionAnchors.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Block screenshot detection, an idea ReVanced's universal Prevent screenshot detection patch, Loukious and NexAlloy share, written here from Facebook 577, 580 and 581. A check goes first in the kept com.facebook.screenshot.ScreenshotContentObserver's onChange(boolean, Uri), the photo library observer every screenshot detector shares, and every registerScreenCaptureCallback call on an Activity and every WindowManager add/removeScreenRecordingCallback call outside the extension becomes a static call of the extension on the same registers, which doesn't make it while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/HiddenTabs.java", "patches/src/main/kotlin/app/morphe/patches/facebook/navigation/hidetabs/HideTabsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hide tabs, an idea Loukious has for the Marketplace and Games tabs, written here as another rule of the tab bar filter. Each hideable tab is known by the class Facebook keeps for it (FeedsTab and MostRecentFeedTab, FriendRequestsTab, MarketplaceTab, GroupsTargetedTab, GamesTab and GamesTabWithSNESControllerIcon, EventsTab), and a start meant for a hidden tab opens Home." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/feed/AutoTranslation.java", "patches/src/main/kotlin/app/morphe/patches/facebook/feed/autotranslation/TurnOffAutoTranslationPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Turn off auto-translation. Posts: the one getter of the post translatability model's translation_type (a no-argument BaseModelWithTree method returning the enum whose static setup names AUTO_TRANSLATION and SEE_TRANSLATION, reading the field's key with getCachedEnum) hands its answer to the extension, which turns AUTO_TRANSLATION into SEE_TRANSLATION while the switch is on, so the feed picks its See translation plugin over its auto-translate one. Reels: the two reads of the caption's translatable flag in the class that records FbShortsViewerFooterComponent.updateAutoTranslateTriggered go through the extension, which answers false while the switch is on, so the footer never asks for a caption translation. Comments: the two reads of the comment's translation type (the enum whose static setup names AUTO_TRANSLATE, MACHINE_TRANSLATION and ORIGINAL) in the class that records CommentBodyComponent.updateIsTranslated, one for its starting state and one for its render, go through the extension, which answers ORIGINAL while the switch is on, so a comment starts in its own words and See translation fetches the translation on a tap." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/MarkColors.java", "patches/src/main/kotlin/app/morphe/patches/facebook/layout/theme/MarkColorsPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Badge and logo colors. It hooks the themes' FDS colour resolvers (each resolver on FDSColors and the view code's theme resolver) with its own target, after theirs, and the extension swaps the colour of the NOTIFICATION_BADGE token for the Badge color row's code and of FB_WORDMARK and FB_LOGO for the Logo color row's, keeping Facebook's alpha. Every other token, a blank row and Pause leave Facebook's colour. The patch refuses to apply when the FDS token enum lacks any of the three names. For #297 it also asks the selected tab's colour hook in TabBarColours.kt, and the Selected tab color row's code replaces that colour, keeping Facebook's alpha." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/theme/SelectedTab.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #297: the one target of the selected tab's colour hook. It hands the colour to Material You's accent when that patch is in, then to the Selected tab color row's code when Badge and logo colors is in, so a picked colour is the last word." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/TabBadges.java", "patches/src/main/kotlin/app/morphe/patches/facebook/navigation/tabbadges/HideTabBadgesPatch.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Hide tab badges, the per-tab version of the shortcut-bar dot controls Meta gives the web. It shares the tab bar's one count hook with Hide the Reels tab dot, the jewel controller's static count for each TabTag, which now sits in a hook patch both depend on, and answers 0 for each tab whose switch is on, known by the class Facebook keeps for it. For the app icon, Facebook 577, 580 and 581 write their own launcher count through one writer per launcher family, each naming itself with a kept *LauncherBadgesInterface literal and all sharing one (FbUserSession, int) -> TriState interface found from the generic writer. The extension goes first in every writer and swaps the count for 0 while the switch is on." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/navigation/RoundedTabBar.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook for issue #344: Rounded tab bar. No patch of its own: the tab bar patch's lifecycle watcher gives each main screen a pre-draw listener that finds the bar the way See-through bars does, clips it to a round-ended outline around its row of tabs and scales it to 90% about a point that keeps the pill a few dp above the row's bottom. Off, paused or with the bar at the top it puts back the bar's own outline, clip, scale and pivot. See-through bars keeps its frost inside the pill with the same corners." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/ads/FeedAdPills.java" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: the floating ad button on a feed ad's comments. Hide sponsored posts puts its own question in front of the comment pill's check that AdPills.kt hooks for Hide sponsored reels, and this answers no for the permalink and flyout ad plugins while the sponsored posts switch is on. Each patch finds the check whether or not the other asked first." }, { "paths": [ "extensions/facebook/src/main/java/app/morphe/extension/facebook/reels/NewestReels.java", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/order/NewestReelsFirstPatch.kt", "patches/src/main/kotlin/app/morphe/patches/facebook/reels/order/ReelOrder.kt" ], "origin": "original", "upstream": "https://github.com/SysAdminDoc/Hushfacebook", "license": "GPL-3.0-only", "changes": "Written for Hushfacebook: Newest reels first, for issue #134, from Facebook 584. The Reels ranker that logs as FbShortsUDDCSRRanker reranks the loaded pool in place each time it places the next reel, and its callers take the head of the list. The patch finds that rerank by the tag, its (object, List, int) shape and its one static (List, Comparator) sort, checks that the sort's list register holds the List parameter at every return, and hands the list to the extension before each return, branches into a return included. Every kept-name createSignalForModel, which makes a pool item's ranking signal, gets a hook before each return: it casts the parameter to the model class the method casts to, reads that class's one FeedType field and, when the extension says the feed is one of the fb_shorts feeds, the parameter interface's one no-argument Object getter, the reel's GraphQL edge. The extension reads the edge's node creation_time, keeps it by the signal (whose equals follows its key) in a weak map, and at the rerank's return puts the timed signals in their own slots newest first with a stable sort, leaving untimed items where they are." } ] }