You've already forked revanced-patcher
							
							
				mirror of
				https://github.com/revanced/revanced-patcher
				synced 2025-11-02 07:30:52 +01:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
			dependabot
			...
			arsclib-re
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c6fdf97794 | ||
| 
						 | 
					c52f0b80f2 | ||
| 
						 | 
					4b5e25b29c | ||
| 
						 | 
					c752a3c596 | ||
| 
						 | 
					740911a2a3 | ||
| 
						 | 
					242d805c6c | ||
| 
						 | 
					c543fdc18b | ||
| 
						 | 
					d48a8e697f | ||
| 
						 | 
					8749a61d39 | 
@@ -1,3 +0,0 @@
 | 
			
		||||
[*.{kt,kts}]
 | 
			
		||||
ktlint_code_style = intellij_idea
 | 
			
		||||
ktlint_standard_no-wildcard-imports = disabled
 | 
			
		||||
							
								
								
									
										72
									
								
								.github/ISSUE_TEMPLATE/bug-issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								.github/ISSUE_TEMPLATE/bug-issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
name: 🐞 Bug report
 | 
			
		||||
description: Report a very clearly broken issue.
 | 
			
		||||
title: 'bug: <title>'
 | 
			
		||||
labels: [bug]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        # ReVanced bug report
 | 
			
		||||
 | 
			
		||||
        Important to note that your issue may have already been reported before. Please check for existing issues [here](https://github.com/revanced/revanced-patcher/labels/bug).
 | 
			
		||||
 | 
			
		||||
  - type: dropdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Type
 | 
			
		||||
      options:
 | 
			
		||||
        - Crash
 | 
			
		||||
        - Cosmetic
 | 
			
		||||
        - Other
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Bug description
 | 
			
		||||
      description: How did you find the bug? Any additional details that might help?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Steps to reproduce
 | 
			
		||||
      description: Add the steps to reproduce this bug including your environment.
 | 
			
		||||
      placeholder: Step 1. Download some files. Step 2. ...
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Relevant log output
 | 
			
		||||
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
 | 
			
		||||
      render: shell
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Screenshots or videos
 | 
			
		||||
      description: Add screenshots or videos that show the bug here.
 | 
			
		||||
      placeholder: Drag and drop the screenshots/videos into this box.
 | 
			
		||||
    validations:
 | 
			
		||||
      required: false
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Solution
 | 
			
		||||
      description: If applicable, add a possible solution.
 | 
			
		||||
    validations:
 | 
			
		||||
      required: false
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Additional context
 | 
			
		||||
      description: Add additional context here.
 | 
			
		||||
    validations:
 | 
			
		||||
      required: false
 | 
			
		||||
  - type: checkboxes
 | 
			
		||||
    id: acknowledgements
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Acknowledgements
 | 
			
		||||
      description: Your issue will be closed if you haven't done these steps.
 | 
			
		||||
      options:
 | 
			
		||||
        - label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I have written a short but informative title.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I filled out all of the requested information in this issue properly.
 | 
			
		||||
          required: true
 | 
			
		||||
							
								
								
									
										109
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										109
									
								
								.github/ISSUE_TEMPLATE/bug_report.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,109 +0,0 @@
 | 
			
		||||
name: 🐞 Bug report
 | 
			
		||||
description: Report a bug or an issue.
 | 
			
		||||
title: "bug: "
 | 
			
		||||
labels: ["Bug report"]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        <p align="center">
 | 
			
		||||
          <picture>
 | 
			
		||||
            <source
 | 
			
		||||
              width="256px"
 | 
			
		||||
              media="(prefers-color-scheme: dark)"
 | 
			
		||||
              srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
            >
 | 
			
		||||
            <img 
 | 
			
		||||
              width="256px"
 | 
			
		||||
              src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
            >
 | 
			
		||||
          </picture>
 | 
			
		||||
          <br>
 | 
			
		||||
          <a href="https://revanced.app/">
 | 
			
		||||
             <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
                 <img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
             </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://github.com/ReVanced">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
                   <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="http://revanced.app/discord">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
                   <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
                   <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://t.me/app_revanced">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
              </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://x.com/revancedapp">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
              </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
             </picture>
 | 
			
		||||
           </a>
 | 
			
		||||
           <br>
 | 
			
		||||
           <br>
 | 
			
		||||
           Continuing the legacy of Vanced
 | 
			
		||||
        </p>
 | 
			
		||||
 | 
			
		||||
        # ReVanced Patcher bug report
 | 
			
		||||
 | 
			
		||||
        Before creating a new bug report, please keep the following in mind:
 | 
			
		||||
 | 
			
		||||
        - **Do not submit a duplicate bug report**: Search for existing bug reports [here](https://github.com/ReVanced/revanced-patcher/issues?q=label%3A%22Bug+report%22).
 | 
			
		||||
        - **Review the contribution guidelines**: Make sure your bug report adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patcher/blob/main/CONTRIBUTING.md).
 | 
			
		||||
        - **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Bug description
 | 
			
		||||
      description: |
 | 
			
		||||
        - Describe your bug in detail
 | 
			
		||||
        - Add steps to reproduce the bug if possible (Step 1. ... Step 2. ...)
 | 
			
		||||
        - Add images and videos if possible
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Error logs
 | 
			
		||||
      description: Exceptions can be captured by running `logcat | grep AndroidRuntime` in a shell.
 | 
			
		||||
      render: shell
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Solution
 | 
			
		||||
      description: If applicable, add a possible solution to the bug.
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Additional context
 | 
			
		||||
      description: Add additional context here.
 | 
			
		||||
  - type: checkboxes
 | 
			
		||||
    id: acknowledgements
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Acknowledgements
 | 
			
		||||
      description: Your bug report will be closed if you don't follow the checklist below.
 | 
			
		||||
      options:
 | 
			
		||||
        - label: I have checked all open and closed bug reports and this is not a duplicate.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I have chosen an appropriate title.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: All requested information has been provided properly.
 | 
			
		||||
          required: true
 | 
			
		||||
							
								
								
									
										58
									
								
								.github/ISSUE_TEMPLATE/feature-issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								.github/ISSUE_TEMPLATE/feature-issue.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
name: ⭐ Feature request
 | 
			
		||||
description: Create a detailed feature request.
 | 
			
		||||
title: 'feat: <title>'
 | 
			
		||||
labels: [feature-request]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        # ReVanced feature request
 | 
			
		||||
        
 | 
			
		||||
        Do not submit requests for patches here. Please submit them [here](https://github.com/orgs/revanced/discussions/categories/patches) instead.
 | 
			
		||||
        Important to note that your feature request may have already been made before. Please check for existing feature requests [here](https://github.com/revanced/revanced-patcher/labels/feature-request).
 | 
			
		||||
 | 
			
		||||
  - type: dropdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Type
 | 
			
		||||
      options:
 | 
			
		||||
        - Functionality
 | 
			
		||||
        - Cosmetic
 | 
			
		||||
        - Other
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Issue
 | 
			
		||||
      description: What is the current problem. Why does it require a feature request?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Feature
 | 
			
		||||
      description: Describe your feature in detail. How does it solve the issue?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Motivation
 | 
			
		||||
      description: Why should your feature should be considered?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Additional context
 | 
			
		||||
      description: Add additional context here.
 | 
			
		||||
    validations:
 | 
			
		||||
      required: false
 | 
			
		||||
  - type: checkboxes
 | 
			
		||||
    id: acknowledgements
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Acknowledgements
 | 
			
		||||
      description: Your issue will be closed if you haven't done these steps.
 | 
			
		||||
      options:
 | 
			
		||||
        - label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I have written a short but informative title.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I filled out all of the requested information in this issue properly.
 | 
			
		||||
          required: true
 | 
			
		||||
							
								
								
									
										107
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										107
									
								
								.github/ISSUE_TEMPLATE/feature_request.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,107 +0,0 @@
 | 
			
		||||
name: ⭐ Feature request
 | 
			
		||||
description: Create a detailed request for a new feature.
 | 
			
		||||
title: "feat: "
 | 
			
		||||
labels: ["Feature request"]
 | 
			
		||||
body:
 | 
			
		||||
  - type: markdown
 | 
			
		||||
    attributes:
 | 
			
		||||
      value: |
 | 
			
		||||
        <p align="center">
 | 
			
		||||
          <picture>
 | 
			
		||||
            <source
 | 
			
		||||
              width="256px"
 | 
			
		||||
              media="(prefers-color-scheme: dark)"
 | 
			
		||||
              srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
            >
 | 
			
		||||
            <img 
 | 
			
		||||
              width="256px"
 | 
			
		||||
              src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
            >
 | 
			
		||||
          </picture>
 | 
			
		||||
          <br>
 | 
			
		||||
          <a href="https://revanced.app/">
 | 
			
		||||
             <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
                 <img height="24px" src="https://raw.githubusercontent.com/revanced/revanced-patcher/main/assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
             </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://github.com/ReVanced">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
                   <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="http://revanced.app/discord">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
                   <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
               <picture>
 | 
			
		||||
                   <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
                   <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
               </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://t.me/app_revanced">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
              </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://x.com/revancedapp">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
              </picture>
 | 
			
		||||
           </a>   
 | 
			
		||||
           <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
              <picture>
 | 
			
		||||
                 <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
                 <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
             </picture>
 | 
			
		||||
           </a>
 | 
			
		||||
           <br>
 | 
			
		||||
           <br>
 | 
			
		||||
           Continuing the legacy of Vanced
 | 
			
		||||
        </p>
 | 
			
		||||
 | 
			
		||||
        # ReVanced Patcher feature request
 | 
			
		||||
 | 
			
		||||
        Before creating a new feature request, please keep the following in mind:
 | 
			
		||||
 | 
			
		||||
        - **Do not submit a duplicate feature request**: Search for existing feature requests [here](https://github.com/ReVanced/revanced-patcher/issues?q=label%3A%22Feature+request%22).
 | 
			
		||||
        - **Review the contribution guidelines**: Make sure your feature request adheres to it. You can find the guidelines [here](https://github.com/ReVanced/revanced-patcher/blob/main/CONTRIBUTING.md).
 | 
			
		||||
        - **Do not use the issue page for support**: If you need help or have questions, check out other platforms on [revanced.app](https://revanced.app).
 | 
			
		||||
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Feature description
 | 
			
		||||
      description: |
 | 
			
		||||
        - Describe your feature in detail
 | 
			
		||||
        - Add images, videos, links, examples, references, etc. if possible
 | 
			
		||||
        - Add the target application name in case you request a new patch
 | 
			
		||||
  - type: textarea
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Motivation
 | 
			
		||||
      description: |
 | 
			
		||||
        A strong motivation is necessary for a feature request to be considered.
 | 
			
		||||
 | 
			
		||||
        - Why should this feature be implemented? 
 | 
			
		||||
        - What is the explicit use case?
 | 
			
		||||
        - What are the benefits?
 | 
			
		||||
        - What makes this feature important?
 | 
			
		||||
    validations:
 | 
			
		||||
      required: true
 | 
			
		||||
  - type: checkboxes
 | 
			
		||||
    id: acknowledgements
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Acknowledgements
 | 
			
		||||
      description: Your feature request will be closed if you don't follow the checklist below.
 | 
			
		||||
      options:
 | 
			
		||||
        - label: I have checked all open and closed feature requests and this is not a duplicate.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: I have chosen an appropriate title.
 | 
			
		||||
          required: true
 | 
			
		||||
        - label: All requested information has been provided properly.
 | 
			
		||||
          required: true
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,2 +1,2 @@
 | 
			
		||||
firstPRMergeComment: >
 | 
			
		||||
  Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) to receive a role for your contribution.
 | 
			
		||||
  Thank you for contributing to ReVanced. Join us on [Discord](https://revanced.app/discord) if you want to receive a contributor role.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,22 +0,0 @@
 | 
			
		||||
version: 2
 | 
			
		||||
updates:
 | 
			
		||||
  - package-ecosystem: github-actions
 | 
			
		||||
    labels: []
 | 
			
		||||
    directory: /
 | 
			
		||||
    target-branch: dev
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: monthly
 | 
			
		||||
 | 
			
		||||
  - package-ecosystem: npm
 | 
			
		||||
    labels: []
 | 
			
		||||
    directory: /
 | 
			
		||||
    target-branch: dev
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: monthly
 | 
			
		||||
 | 
			
		||||
  - package-ecosystem: gradle
 | 
			
		||||
    labels: []
 | 
			
		||||
    directory: /
 | 
			
		||||
    target-branch: dev
 | 
			
		||||
    schedule:
 | 
			
		||||
      interval: monthly
 | 
			
		||||
							
								
								
									
										25
									
								
								.github/workflows/build_pull_request.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/build_pull_request.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,25 +0,0 @@
 | 
			
		||||
name: Build pull request
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - dev
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  release:
 | 
			
		||||
    name: Build
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
        with:
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
      - name: Cache Gradle
 | 
			
		||||
        uses: burrunan/gradle-cache-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        run: ./gradlew build --no-daemon
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
name: Open a PR to main
 | 
			
		||||
name: PR to main
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
@@ -7,7 +7,7 @@ on:
 | 
			
		||||
  workflow_dispatch:
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  MESSAGE: Merge branch `${{ github.head_ref || github.ref_name }}` to `main`
 | 
			
		||||
  MESSAGE: merge branch `${{ github.head_ref || github.ref_name }}` to `main`
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  pull-request:
 | 
			
		||||
@@ -15,8 +15,7 @@ jobs:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
      - name: Open pull request
 | 
			
		||||
        uses: repo-sync/pull-request@v2
 | 
			
		||||
        with:
 | 
			
		||||
							
								
								
									
										46
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										46
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -6,48 +6,40 @@ on:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
      - dev
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - main
 | 
			
		||||
      - dev
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  release:
 | 
			
		||||
    name: Release
 | 
			
		||||
    permissions:
 | 
			
		||||
      contents: write
 | 
			
		||||
      packages: write
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v5
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
        with:
 | 
			
		||||
          # Make sure the release step uses its own credentials:
 | 
			
		||||
          # https://github.com/cycjimmy/semantic-release-action#private-packages
 | 
			
		||||
          persist-credentials: false
 | 
			
		||||
          fetch-depth: 0
 | 
			
		||||
 | 
			
		||||
      - name: Cache Gradle
 | 
			
		||||
        uses: burrunan/gradle-cache-action@v3
 | 
			
		||||
 | 
			
		||||
      - name: Build
 | 
			
		||||
      - name: Cache
 | 
			
		||||
        uses: actions/cache@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
            ${{ runner.home }}/.gradle/caches
 | 
			
		||||
            ${{ runner.home }}/.gradle/wrapper
 | 
			
		||||
            .gradle
 | 
			
		||||
            build
 | 
			
		||||
            node_modules
 | 
			
		||||
          key: ${{ runner.os }}-gradle-npm-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'package-lock.json') }}
 | 
			
		||||
      - name: Build with Gradle
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        run: ./gradlew build clean
 | 
			
		||||
 | 
			
		||||
      - name: Setup Node.js
 | 
			
		||||
        uses: actions/setup-node@v6
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: "lts/*"
 | 
			
		||||
          cache: 'npm'
 | 
			
		||||
 | 
			
		||||
      - name: Install dependencies
 | 
			
		||||
        run: ./gradlew clean --no-daemon
 | 
			
		||||
      - name: Setup semantic-release
 | 
			
		||||
        run: npm install
 | 
			
		||||
 | 
			
		||||
      - name: Import GPG key
 | 
			
		||||
        uses: crazy-max/ghaction-import-gpg@v6
 | 
			
		||||
        with:
 | 
			
		||||
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
 | 
			
		||||
          passphrase: ${{ secrets.GPG_PASSPHRASE }}
 | 
			
		||||
          fingerprint: ${{ vars.GPG_FINGERPRINT }}
 | 
			
		||||
 | 
			
		||||
      - name: Release
 | 
			
		||||
        env:
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
          GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
 | 
			
		||||
        run: npm exec semantic-release
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								.github/workflows/update_documentation.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								.github/workflows/update_documentation.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,19 +0,0 @@
 | 
			
		||||
name: Update documentation
 | 
			
		||||
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    paths:
 | 
			
		||||
      - docs/**
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  trigger:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    name: Dispatch event to documentation repository
 | 
			
		||||
    if: github.ref == 'refs/heads/main'
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: peter-evans/repository-dispatch@v3
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.DOCUMENTATION_REPO_ACCESS_TOKEN  }}
 | 
			
		||||
          repository: revanced/revanced-documentation
 | 
			
		||||
          event-type: update-documentation
 | 
			
		||||
          client-payload: '{"repo": "${{ github.event.repository.name }}", "ref": "${{ github.ref }}"}'
 | 
			
		||||
							
								
								
									
										11
									
								
								.releaserc
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								.releaserc
									
									
									
									
									
								
							@@ -7,13 +7,7 @@
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "plugins": [
 | 
			
		||||
    [
 | 
			
		||||
      "@semantic-release/commit-analyzer", {
 | 
			
		||||
        "releaseRules": [
 | 
			
		||||
          { "type": "build", "scope": "Needs bump", "release": "patch" }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    "@semantic-release/commit-analyzer",
 | 
			
		||||
    "@semantic-release/release-notes-generator",
 | 
			
		||||
    "@semantic-release/changelog",
 | 
			
		||||
    "gradle-semantic-release-plugin",
 | 
			
		||||
@@ -23,8 +17,7 @@
 | 
			
		||||
        "assets": [
 | 
			
		||||
          "CHANGELOG.md",
 | 
			
		||||
          "gradle.properties"
 | 
			
		||||
        ],
 | 
			
		||||
        "message": "chore: Release v${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    ],
 | 
			
		||||
    [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										973
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										973
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,99 +0,0 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <picture>
 | 
			
		||||
    <source
 | 
			
		||||
      width="256px"
 | 
			
		||||
      media="(prefers-color-scheme: dark)"
 | 
			
		||||
      srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
    >
 | 
			
		||||
    <img 
 | 
			
		||||
      width="256px"
 | 
			
		||||
      src="assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
    >
 | 
			
		||||
  </picture>
 | 
			
		||||
  <br>
 | 
			
		||||
  <a href="https://revanced.app/">
 | 
			
		||||
     <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
         <img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://github.com/ReVanced">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
           <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="http://revanced.app/discord">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://t.me/app_revanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://x.com/revancedapp">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>
 | 
			
		||||
   <br>
 | 
			
		||||
   <br>
 | 
			
		||||
   Continuing the legacy of Vanced
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
# 👋 Contribution guidelines
 | 
			
		||||
 | 
			
		||||
This document describes how to contribute to ReVanced Patcher.
 | 
			
		||||
 | 
			
		||||
## 📖 Resources to help you get started
 | 
			
		||||
 | 
			
		||||
- The [documentation](https://github.com/ReVanced/revanced-patcher/tree/docs/docs) contains the fundamentals
 | 
			
		||||
  of ReVanced Patcher and how to use ReVanced Patcher to create patches
 | 
			
		||||
- [Our backlog](https://github.com/orgs/ReVanced/projects/12) is where we keep track of what we're working on
 | 
			
		||||
- [Issues](https://github.com/ReVanced/revanced-patcher/issues) are where we keep track of bugs and feature requests
 | 
			
		||||
 | 
			
		||||
## 🙏 Submitting a feature request
 | 
			
		||||
 | 
			
		||||
Features can be requested by opening an issue using the
 | 
			
		||||
[Feature request issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Feature+request&projects=&template=feature_request.yml&title=feat%3A+).
 | 
			
		||||
 | 
			
		||||
> **Note**
 | 
			
		||||
> Requests can be accepted or rejected at the discretion of maintainers of ReVanced Patcher.
 | 
			
		||||
> Good motivation has to be provided for a request to be accepted.
 | 
			
		||||
 | 
			
		||||
## 🐞 Submitting a bug report
 | 
			
		||||
 | 
			
		||||
If you encounter a bug while using ReVanced Patcher, open an issue using the
 | 
			
		||||
[Bug report issue template](https://github.com/ReVanced/revanced-patcher/issues/new?assignees=&labels=Bug+report&projects=&template=bug_report.yml&title=bug%3A+).
 | 
			
		||||
 | 
			
		||||
## 📝 How to contribute
 | 
			
		||||
 | 
			
		||||
1. Before contributing, it is recommended to open an issue to discuss your change
 | 
			
		||||
   with the maintainers of ReVanced Patcher. This will help you determine whether your change is acceptable
 | 
			
		||||
   and whether it is worth your time to implement it
 | 
			
		||||
2. Development happens on the `dev` branch. Fork the repository and create your branch from `dev`
 | 
			
		||||
3. Commit your changes
 | 
			
		||||
4. Submit a pull request to the `dev` branch of the repository and reference issues
 | 
			
		||||
   that your pull request closes in the description of your pull request
 | 
			
		||||
5. Our team will review your pull request and provide feedback. Once your pull request is approved,
 | 
			
		||||
   it will be merged into the `dev` branch and will be included in the next release of ReVanced Patcher
 | 
			
		||||
 | 
			
		||||
❤️ Thank you for considering contributing to ReVanced Patcher,  
 | 
			
		||||
ReVanced
 | 
			
		||||
							
								
								
									
										122
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								README.md
									
									
									
									
									
								
							@@ -1,125 +1,3 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <picture>
 | 
			
		||||
    <source
 | 
			
		||||
      width="256px"
 | 
			
		||||
      media="(prefers-color-scheme: dark)"
 | 
			
		||||
      srcset="assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
    >
 | 
			
		||||
    <img 
 | 
			
		||||
      width="256px"
 | 
			
		||||
      src="assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
    >
 | 
			
		||||
  </picture>
 | 
			
		||||
  <br>
 | 
			
		||||
  <a href="https://revanced.app/">
 | 
			
		||||
     <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
         <img height="24px" src="assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://github.com/ReVanced">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
           <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="http://revanced.app/discord">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://t.me/app_revanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://x.com/revancedapp">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>
 | 
			
		||||
   <br>
 | 
			
		||||
   <br>
 | 
			
		||||
   Continuing the legacy of Vanced
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
# 💉 ReVanced Patcher
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
ReVanced Patcher used to patch Android applications.
 | 
			
		||||
 | 
			
		||||
## ❓ About
 | 
			
		||||
 | 
			
		||||
ReVanced Patcher is a library that is used to patch Android applications.  
 | 
			
		||||
It powers [ReVanced Manager](https://github.com/ReVanced/revanced-manager),
 | 
			
		||||
[ReVanced CLI](https://github.com/ReVanced/revanced-cli)
 | 
			
		||||
and [ReVanced Library](https://github.com/ReVanced/revanced-library) and a rich set of patches have been developed
 | 
			
		||||
using ReVanced Patcher in the [ReVanced Patches](https://github.com/ReVanced/revanced-patches) repository.
 | 
			
		||||
 | 
			
		||||
## 💪 Features
 | 
			
		||||
 | 
			
		||||
Some of the features the ReVanced Patcher provides are:
 | 
			
		||||
 | 
			
		||||
- 🔧 **Patch Dalvik VM bytecode**: Disassemble and assemble Dalvik bytecode
 | 
			
		||||
- 📦 **Patch APK resources**: Decode and build Android APK resources
 | 
			
		||||
- 📂 **Patch arbitrary APK files**: Read and write arbitrary files directly from and to APK files
 | 
			
		||||
- 🧩 **Write modular patches**: Extensive API to write modular patches that can patch Dalvik VM bytecode,
 | 
			
		||||
APK resources and arbitrary APK files
 | 
			
		||||
 | 
			
		||||
## 🚀 How to get started
 | 
			
		||||
 | 
			
		||||
To use ReVanced Patcher in your project, follow these steps:
 | 
			
		||||
 | 
			
		||||
1. [Add the repository](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#using-a-published-package)
 | 
			
		||||
to your project
 | 
			
		||||
2. Add the dependency to your project:
 | 
			
		||||
 | 
			
		||||
   ```kt
 | 
			
		||||
    dependencies {
 | 
			
		||||
        implementation("app.revanced:revanced-patcher:{$version}")
 | 
			
		||||
    }
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
For a minimal project configuration, 
 | 
			
		||||
see [ReVanced Patches template](https://github.com/ReVanced/revanced-patches-template).
 | 
			
		||||
 | 
			
		||||
## 📚 Everything else
 | 
			
		||||
 | 
			
		||||
### 📙 Contributing
 | 
			
		||||
 | 
			
		||||
Thank you for considering contributing to ReVanced Patcher.
 | 
			
		||||
You can find the contribution guidelines [here](CONTRIBUTING.md).
 | 
			
		||||
 | 
			
		||||
### 🛠️ Building
 | 
			
		||||
 | 
			
		||||
To build ReVanced Patcher,
 | 
			
		||||
you can follow the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
 | 
			
		||||
 | 
			
		||||
### 📃 Documentation
 | 
			
		||||
 | 
			
		||||
The documentation contains the fundamentals of ReVanced Patcher and how to use ReVanced Patcher to create patches.
 | 
			
		||||
You can find it [here](https://github.com/ReVanced/revanced-patcher/tree/main/docs).
 | 
			
		||||
 | 
			
		||||
## 📜 Licence
 | 
			
		||||
 | 
			
		||||
ReVanced Patcher is licensed under the GPLv3 license. Please see the [licence file](LICENSE) for more information.
 | 
			
		||||
[tl;dr](https://www.tldrlegal.com/license/gnu-general-public-license-v3-gpl-3) you may copy, distribute and modify ReVanced Patcher as long as you track changes/dates in source files.
 | 
			
		||||
Any modifications to ReVanced Patcher must also be made available under the GPL,
 | 
			
		||||
along with build & install instructions.
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										42
									
								
								arsclib-utils/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								arsclib-utils/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
.gradle
 | 
			
		||||
build/
 | 
			
		||||
!gradle/wrapper/gradle-wrapper.jar
 | 
			
		||||
!**/src/main/**/build/
 | 
			
		||||
!**/src/test/**/build/
 | 
			
		||||
 | 
			
		||||
### IntelliJ IDEA ###
 | 
			
		||||
.idea/modules.xml
 | 
			
		||||
.idea/jarRepositories.xml
 | 
			
		||||
.idea/compiler.xml
 | 
			
		||||
.idea/libraries/
 | 
			
		||||
*.iws
 | 
			
		||||
*.iml
 | 
			
		||||
*.ipr
 | 
			
		||||
out/
 | 
			
		||||
!**/src/main/**/out/
 | 
			
		||||
!**/src/test/**/out/
 | 
			
		||||
 | 
			
		||||
### Eclipse ###
 | 
			
		||||
.apt_generated
 | 
			
		||||
.classpath
 | 
			
		||||
.factorypath
 | 
			
		||||
.project
 | 
			
		||||
.settings
 | 
			
		||||
.springBeans
 | 
			
		||||
.sts4-cache
 | 
			
		||||
bin/
 | 
			
		||||
!**/src/main/**/bin/
 | 
			
		||||
!**/src/test/**/bin/
 | 
			
		||||
 | 
			
		||||
### NetBeans ###
 | 
			
		||||
/nbproject/private/
 | 
			
		||||
/nbbuild/
 | 
			
		||||
/dist/
 | 
			
		||||
/nbdist/
 | 
			
		||||
/.nb-gradle/
 | 
			
		||||
 | 
			
		||||
### VS Code ###
 | 
			
		||||
.vscode/
 | 
			
		||||
 | 
			
		||||
### Mac OS ###
 | 
			
		||||
.DS_Store
 | 
			
		||||
							
								
								
									
										18
									
								
								arsclib-utils/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								arsclib-utils/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
plugins {
 | 
			
		||||
    kotlin("jvm")
 | 
			
		||||
    `maven-publish`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
group = "app.revanced"
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    implementation("io.github.reandroid:ARSCLib:1.1.7")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    withSourcesJar()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    jvmToolchain(11)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
package app.revanced.arsc
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An exception thrown when there is an error with APK resources.
 | 
			
		||||
 *
 | 
			
		||||
 * @param message The exception message.
 | 
			
		||||
 * @param throwable The corresponding [Throwable].
 | 
			
		||||
 */
 | 
			
		||||
sealed class ApkResourceException(message: String, throwable: Throwable? = null) : Exception(message, throwable) {
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception when locking resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message The exception message.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
    class Locked(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception when writing resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message The exception message.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
     class Write(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception when reading resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message The exception message.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
    class Read(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception when decoding resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message The exception message.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
    class Decode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception when encoding resources.
 | 
			
		||||
     *
 | 
			
		||||
     * @param message The exception message.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
    class Encode(message: String, throwable: Throwable? = null) : ApkResourceException(message, throwable)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception thrown when a reference could not be resolved.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reference The invalid reference.
 | 
			
		||||
     * @param throwable The corresponding [Throwable].
 | 
			
		||||
     */
 | 
			
		||||
    class InvalidReference(reference: String, throwable: Throwable? = null) :
 | 
			
		||||
        ApkResourceException("Failed to resolve: $reference", throwable) {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * An exception thrown when a reference could not be resolved.
 | 
			
		||||
         *
 | 
			
		||||
         * @param type The type of the reference.
 | 
			
		||||
         * @param name The name of the reference.
 | 
			
		||||
         * @param throwable The corresponding [Throwable].
 | 
			
		||||
         */
 | 
			
		||||
        constructor(type: String, name: String, throwable: Throwable? = null) : this("@$type/$name", throwable)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * An exception thrown when the Apk file not have a resource table, but was expected to have one.
 | 
			
		||||
     */
 | 
			
		||||
    class MissingResourceTable : ApkResourceException("Apk does not have a resource table.")
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,28 @@
 | 
			
		||||
@file:Suppress("MemberVisibilityCanBePrivate")
 | 
			
		||||
 | 
			
		||||
package app.revanced.arsc.archive
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.resource.ResourceContainer
 | 
			
		||||
import com.reandroid.apk.ApkModule
 | 
			
		||||
import com.reandroid.apk.DexFileInputSource
 | 
			
		||||
import com.reandroid.archive.InputSource
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.Flushable
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A class for reading/writing files in an [ApkModule].
 | 
			
		||||
 *
 | 
			
		||||
 * @param module The [ApkModule] to operate on.
 | 
			
		||||
 */
 | 
			
		||||
class Archive(internal val module: ApkModule) : Flushable {
 | 
			
		||||
    val mainPackageResources = ResourceContainer(this, module.tableBlock)
 | 
			
		||||
 | 
			
		||||
    fun save(output: File) {
 | 
			
		||||
        flush()
 | 
			
		||||
        module.writeApk(output)
 | 
			
		||||
    }
 | 
			
		||||
    fun readDexFiles(): MutableList<DexFileInputSource> = module.listDexFiles()
 | 
			
		||||
    fun write(inputSource: InputSource) = module.apkArchive.add(inputSource) // Overwrites existing files.
 | 
			
		||||
    fun read(name: String): InputSource? = module.apkArchive.getInputSource(name)
 | 
			
		||||
    override fun flush() = mainPackageResources.flush()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,7 @@
 | 
			
		||||
package app.revanced.arsc.logging
 | 
			
		||||
interface Logger {
 | 
			
		||||
    fun error(msg: String)
 | 
			
		||||
    fun warn(msg: String)
 | 
			
		||||
    fun info(msg: String)
 | 
			
		||||
    fun trace(msg: String)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,166 @@
 | 
			
		||||
package app.revanced.arsc.resource
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.ApkResourceException
 | 
			
		||||
import com.reandroid.arsc.coder.EncodeResult
 | 
			
		||||
import com.reandroid.arsc.coder.ValueDecoder
 | 
			
		||||
import com.reandroid.arsc.value.Entry
 | 
			
		||||
import com.reandroid.arsc.value.ValueType
 | 
			
		||||
import com.reandroid.arsc.value.array.ArrayBag
 | 
			
		||||
import com.reandroid.arsc.value.array.ArrayBagItem
 | 
			
		||||
import com.reandroid.arsc.value.plurals.PluralsBag
 | 
			
		||||
import com.reandroid.arsc.value.plurals.PluralsBagItem
 | 
			
		||||
import com.reandroid.arsc.value.plurals.PluralsQuantity
 | 
			
		||||
import com.reandroid.arsc.value.style.StyleBag
 | 
			
		||||
import com.reandroid.arsc.value.style.StyleBagItem
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A resource value.
 | 
			
		||||
 */
 | 
			
		||||
sealed class Resource {
 | 
			
		||||
    internal abstract fun write(entry: Entry, resources: ResourceContainer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
internal val Resource.isComplex get() = when (this) {
 | 
			
		||||
    is Scalar -> false
 | 
			
		||||
    is Complex -> true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A simple resource.
 | 
			
		||||
 */
 | 
			
		||||
open class Scalar internal constructor(private val valueType: ValueType, private val value: Int) : Resource() {
 | 
			
		||||
    protected open fun data(resources: ResourceContainer) = value
 | 
			
		||||
 | 
			
		||||
    override fun write(entry: Entry, resources: ResourceContainer) {
 | 
			
		||||
        entry.setValueAsRaw(valueType, data(resources))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal open fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.create(valueType, data(resources))
 | 
			
		||||
    internal open fun toStyleItem(resources: ResourceContainer) = StyleBagItem.create(valueType, data(resources))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A marker class for complex resources.
 | 
			
		||||
 */
 | 
			
		||||
sealed class Complex : Resource()
 | 
			
		||||
 | 
			
		||||
private fun encoded(encodeResult: EncodeResult?) = encodeResult?.let { Scalar(it.valueType, it.value) }
 | 
			
		||||
    ?: throw ApkResourceException.Encode("Failed to encode value")
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encode a color.
 | 
			
		||||
 *
 | 
			
		||||
 * @param hex The hex value of the color.
 | 
			
		||||
 * @return The encoded [Resource].
 | 
			
		||||
 */
 | 
			
		||||
fun color(hex: String) = encoded(ValueDecoder.encodeColor(hex))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encode a dimension or fraction.
 | 
			
		||||
 *
 | 
			
		||||
 * @param value The dimension value such as 24dp.
 | 
			
		||||
 * @return The encoded [Resource].
 | 
			
		||||
 */
 | 
			
		||||
fun dimension(value: String) = encoded(ValueDecoder.encodeDimensionOrFraction(value))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encode a boolean resource.
 | 
			
		||||
 *
 | 
			
		||||
 * @param value The boolean.
 | 
			
		||||
 * @return The encoded [Resource].
 | 
			
		||||
 */
 | 
			
		||||
fun boolean(value: Boolean) = Scalar(ValueType.INT_BOOLEAN, if (value) -Int.MAX_VALUE else 0)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Encode a float.
 | 
			
		||||
 *
 | 
			
		||||
 * @param n The number to encode.
 | 
			
		||||
 * @return The encoded [Resource].
 | 
			
		||||
 */
 | 
			
		||||
fun float(n: Float) = Scalar(ValueType.FLOAT, n.toBits())
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create an integer [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @param n The number to encode.
 | 
			
		||||
 * @return The integer [Resource].
 | 
			
		||||
 */
 | 
			
		||||
fun integer(n: Int) = Scalar(ValueType.INT_DEC, n)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Create a reference [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @param resourceId The target resource.
 | 
			
		||||
 * @return The reference resource.
 | 
			
		||||
 */
 | 
			
		||||
fun reference(resourceId: Int) = Scalar(ValueType.REFERENCE, resourceId)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Resolve and create a reference [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @see reference
 | 
			
		||||
 * @param ref The reference string to resolve.
 | 
			
		||||
 * @param resourceTable The resource table to resolve the reference with.
 | 
			
		||||
 * @return The reference resource.
 | 
			
		||||
 */
 | 
			
		||||
fun reference(resourceTable: ResourceTable, ref: String) = reference(resourceTable.resolve(ref))
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An array [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @param elements The elements of the array.
 | 
			
		||||
 */
 | 
			
		||||
class Array(private val elements: Collection<Scalar>) : Complex() {
 | 
			
		||||
    override fun write(entry: Entry, resources: ResourceContainer) {
 | 
			
		||||
        ArrayBag.create(entry).addAll(elements.map { it.toArrayItem(resources) })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A style resource.
 | 
			
		||||
 *
 | 
			
		||||
 * @param elements The attributes to override.
 | 
			
		||||
 * @param parent A reference to the parent style.
 | 
			
		||||
 */
 | 
			
		||||
class Style(private val elements: Map<String, Scalar>, private val parent: String? = null) : Complex() {
 | 
			
		||||
    override fun write(entry: Entry, resources: ResourceContainer) {
 | 
			
		||||
        val resTable = resources.resourceTable
 | 
			
		||||
        val style = StyleBag.create(entry)
 | 
			
		||||
        parent?.let {
 | 
			
		||||
            style.parentId = resTable.resolve(parent)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        style.putAll(
 | 
			
		||||
            elements.asIterable().associate {
 | 
			
		||||
                StyleBag.resolve(resTable.encodeMaterials, it.key) to it.value.toStyleItem(resources)
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A quantity string [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @param elements A map of the quantity to the corresponding string.
 | 
			
		||||
 */
 | 
			
		||||
class Plurals(private val elements: Map<String, String>) : Complex() {
 | 
			
		||||
    override fun write(entry: Entry, resources: ResourceContainer) {
 | 
			
		||||
        val plurals = PluralsBag.create(entry)
 | 
			
		||||
 | 
			
		||||
        plurals.putAll(elements.asIterable().associate { (k, v) ->
 | 
			
		||||
            PluralsQuantity.value(k) to PluralsBagItem.string(resources.getOrCreateString(v))
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A string [Resource].
 | 
			
		||||
 *
 | 
			
		||||
 * @param value The string value.
 | 
			
		||||
 */
 | 
			
		||||
class StringResource(val value: String) : Scalar(ValueType.STRING, 0) {
 | 
			
		||||
    private fun tableString(resources: ResourceContainer) = resources.getOrCreateString(value)
 | 
			
		||||
 | 
			
		||||
    override fun data(resources: ResourceContainer) = tableString(resources).index
 | 
			
		||||
    override fun toArrayItem(resources: ResourceContainer) = ArrayBagItem.string(tableString(resources))
 | 
			
		||||
    override fun toStyleItem(resources: ResourceContainer) = StyleBagItem.string(tableString(resources))
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,167 @@
 | 
			
		||||
package app.revanced.arsc.resource
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.ApkResourceException
 | 
			
		||||
import app.revanced.arsc.archive.Archive
 | 
			
		||||
import com.reandroid.apk.xmlencoder.EncodeUtil
 | 
			
		||||
import com.reandroid.arsc.chunk.TableBlock
 | 
			
		||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
 | 
			
		||||
import com.reandroid.arsc.value.Entry
 | 
			
		||||
import com.reandroid.arsc.value.ResConfig
 | 
			
		||||
import java.io.Closeable
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.io.Flushable
 | 
			
		||||
 | 
			
		||||
class ResourceContainer(private val archive: Archive, internal val tableBlock: TableBlock) : Flushable {
 | 
			
		||||
    private val packageBlock = tableBlock.pickOne() // Pick the main package block.
 | 
			
		||||
    internal lateinit var resourceTable: ResourceTable // TODO: Set this.
 | 
			
		||||
 | 
			
		||||
    private val lockedResourceFileNames = mutableSetOf<String>()
 | 
			
		||||
 | 
			
		||||
    private fun lock(resourceFile: ResourceFile) {
 | 
			
		||||
        if (resourceFile.name in lockedResourceFileNames) {
 | 
			
		||||
            throw ApkResourceException.Locked("Resource file ${resourceFile.name} is already locked.")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        lockedResourceFileNames.add(resourceFile.name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun unlock(resourceFile: ResourceFile) {
 | 
			
		||||
        lockedResourceFileNames.remove(resourceFile.name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fun <T : ResourceFile> openResource(name: String): ResourceFileEditor<T> {
 | 
			
		||||
        val inputSource = archive.read(name)
 | 
			
		||||
            ?: throw ApkResourceException.Read("Resource file $name not found.")
 | 
			
		||||
 | 
			
		||||
        val resourceFile = when {
 | 
			
		||||
            ResXmlDocument.isResXmlBlock(inputSource.openStream()) -> {
 | 
			
		||||
                val xmlDocument = archive.module
 | 
			
		||||
                    .loadResXmlDocument(inputSource)
 | 
			
		||||
                    .decodeToXml(resourceTable.entryStore, packageBlock.id)
 | 
			
		||||
 | 
			
		||||
                ResourceFile.XmlResourceFile(name, xmlDocument)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else -> {
 | 
			
		||||
                val bytes = inputSource.openStream().use { it.readAllBytes() }
 | 
			
		||||
 | 
			
		||||
                ResourceFile.BinaryResourceFile(name, bytes)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            @Suppress("UNCHECKED_CAST")
 | 
			
		||||
            return ResourceFileEditor(resourceFile as T).also {
 | 
			
		||||
                lockedResourceFileNames.add(name)
 | 
			
		||||
            }
 | 
			
		||||
        } catch (e: ClassCastException) {
 | 
			
		||||
            throw ApkResourceException.Decode("Resource file $name is not ${resourceFile::class}.", e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class ResourceFileEditor<T : ResourceFile> internal constructor(
 | 
			
		||||
        private val resourceFile: T,
 | 
			
		||||
    ) : Closeable {
 | 
			
		||||
        fun use(block: (T) -> Unit) = block(resourceFile)
 | 
			
		||||
        override fun close() {
 | 
			
		||||
            lockedResourceFileNames.remove(resourceFile.name)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun flush() {
 | 
			
		||||
        TODO("Not yet implemented")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Open a resource file, creating it if the file does not exist.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path The resource file path.
 | 
			
		||||
     * @return The corresponding [ResourceFiles],
 | 
			
		||||
     */
 | 
			
		||||
    fun openFile(path: String) = ResourceFiles(createHandle(path), archive)
 | 
			
		||||
 | 
			
		||||
    private fun getPackageBlock() = packageBlock ?: throw ApkResourceException.MissingResourceTable
 | 
			
		||||
 | 
			
		||||
    internal fun getOrCreateString(value: String) =
 | 
			
		||||
        tableBlock?.stringPool?.getOrCreate(value) ?: throw ApkResourceException.MissingResourceTable
 | 
			
		||||
 | 
			
		||||
    private fun Entry.set(resource: Resource) {
 | 
			
		||||
        val existingEntryNameReference = specReference
 | 
			
		||||
 | 
			
		||||
        // Sets this.specReference if the entry is not yet initialized.
 | 
			
		||||
        // Sets this.specReference to 0 if the resource type of the existing entry changes.
 | 
			
		||||
        ensureComplex(resource.isComplex)
 | 
			
		||||
 | 
			
		||||
        if (existingEntryNameReference != 0) {
 | 
			
		||||
            // Preserve the entry name by restoring the previous spec block reference (if present).
 | 
			
		||||
            specReference = existingEntryNameReference
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        resource.write(this, this@ResourceContainer)
 | 
			
		||||
        resourceTable.registerChanged(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve an [Entry] from the resource table.
 | 
			
		||||
     *
 | 
			
		||||
     * @param type The resource type.
 | 
			
		||||
     * @param name The resource name.
 | 
			
		||||
     * @param qualifiers The variant to use.
 | 
			
		||||
     */
 | 
			
		||||
    private fun getEntry(type: String, name: String, qualifiers: String?): Entry? {
 | 
			
		||||
        val resourceId = try {
 | 
			
		||||
            resourceTable.resolve("@$type/$name")
 | 
			
		||||
        } catch (_: ApkResourceException.InvalidReference) {
 | 
			
		||||
            return null
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val config = ResConfig.parse(qualifiers)
 | 
			
		||||
        return tableBlock?.resolveReference(resourceId)?.singleOrNull { it.resConfig == config }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Create a [ResourceFiles.Handle] that can be used to open a [ResourceFiles].
 | 
			
		||||
     * This may involve looking it up in the resource table to find the actual location in the archive.
 | 
			
		||||
     *
 | 
			
		||||
     * @param path The path of the resource.
 | 
			
		||||
     */
 | 
			
		||||
    private fun createHandle(path: String): ResourceFiles.Handle {
 | 
			
		||||
        if (path.startsWith("res/values")) throw ApkResourceException.Decode("Decoding the resource table as a file is not supported")
 | 
			
		||||
 | 
			
		||||
        var onClose = {}
 | 
			
		||||
        var archivePath = path
 | 
			
		||||
 | 
			
		||||
        if (tableBlock != null && path.startsWith("res/") && path.count { it == '/' } == 2) {
 | 
			
		||||
            val file = File(path)
 | 
			
		||||
 | 
			
		||||
            val qualifiers = EncodeUtil.getQualifiersFromResFile(file)
 | 
			
		||||
            val type = EncodeUtil.getTypeNameFromResFile(file)
 | 
			
		||||
            val name = file.nameWithoutExtension
 | 
			
		||||
 | 
			
		||||
            // The resource file names that the app developers used may have been minified, so we have to resolve it with the resource table.
 | 
			
		||||
            // Example: res/drawable-hdpi/icon.png -> res/4a.png
 | 
			
		||||
            getEntry(type, name, qualifiers)?.resValue?.valueAsString?.let {
 | 
			
		||||
                archivePath = it
 | 
			
		||||
            } ?: run {
 | 
			
		||||
                // An entry for this specific resource file was not found in the resource table, so we have to register it after we save.
 | 
			
		||||
                onClose = { setResource(type, name, StringResource(archivePath), qualifiers) }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return ResourceFiles.Handle(path, archivePath, onClose)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun setResource(type: String, entryName: String, resource: Resource, qualifiers: String? = null) =
 | 
			
		||||
        getPackageBlock().getOrCreate(qualifiers, type, entryName).also { it.set(resource) }.resourceId
 | 
			
		||||
 | 
			
		||||
    fun setResources(type: String, resources: Map<String, Resource>, configuration: String? = null) {
 | 
			
		||||
        getPackageBlock().getOrCreateSpecTypePair(type).getOrCreateTypeBlock(configuration).apply {
 | 
			
		||||
            resources.forEach { (entryName, resource) -> getOrCreateEntry(entryName).set(resource) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun flush() {
 | 
			
		||||
        packageBlock?.name = archive
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,91 @@
 | 
			
		||||
package app.revanced.arsc.resource
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.ApkResourceException
 | 
			
		||||
import app.revanced.arsc.archive.Archive
 | 
			
		||||
import com.reandroid.archive.InputSource
 | 
			
		||||
import com.reandroid.xml.XMLDocument
 | 
			
		||||
import com.reandroid.xml.XMLException
 | 
			
		||||
import java.io.*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
abstract class ResourceFile(val name: String) {
 | 
			
		||||
    internal var realName: String? = null
 | 
			
		||||
 | 
			
		||||
    class XmlResourceFile(name: String, val document: XMLDocument) : ResourceFile(name)
 | 
			
		||||
    class BinaryResourceFile(name: String, var bytes: ByteArray) : ResourceFile(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResourceFiles private constructor(
 | 
			
		||||
) : Closeable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Instantiate a [ResourceFiles].
 | 
			
		||||
     *
 | 
			
		||||
     * @param handle The [Handle] associated with this file.
 | 
			
		||||
     * @param archive The [Archive] that the file resides in.
 | 
			
		||||
     */
 | 
			
		||||
    internal constructor(handle: Handle, archive: Archive) : this(
 | 
			
		||||
        handle,
 | 
			
		||||
        archive,
 | 
			
		||||
        try {
 | 
			
		||||
            archive.read(handle.archivePath)
 | 
			
		||||
        } catch (e: XMLException) {
 | 
			
		||||
            throw ApkResourceException.Decode("Failed to decode XML while reading ${handle.virtualPath}", e)
 | 
			
		||||
        } catch (e: IOException) {
 | 
			
		||||
            throw ApkResourceException.Decode("Could not read ${handle.virtualPath}", e)
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val DEFAULT_BUFFER_SIZE = 1024
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var contents = readResult?.data ?: ByteArray(0)
 | 
			
		||||
        set(value) {
 | 
			
		||||
            pendingWrite = true
 | 
			
		||||
            field = value
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    val exists = readResult != null
 | 
			
		||||
 | 
			
		||||
    override fun toString() = handle.virtualPath
 | 
			
		||||
 | 
			
		||||
    override fun close() {
 | 
			
		||||
        if (pendingWrite) {
 | 
			
		||||
            val path = handle.archivePath
 | 
			
		||||
 | 
			
		||||
            if (isXmlResource) archive.writeXml(
 | 
			
		||||
                path,
 | 
			
		||||
                try {
 | 
			
		||||
                    XMLDocument.load(inputStream())
 | 
			
		||||
                } catch (e: XMLException) {
 | 
			
		||||
                    throw ApkResourceException.Encode("Failed to parse XML while writing ${handle.virtualPath}", e)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            ) else archive.writeRaw(path, contents)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        handle.onClose()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        archive.unlock(this)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun inputStream(): InputStream = ByteArrayInputStream(contents)
 | 
			
		||||
 | 
			
		||||
    fun outputStream(bufferSize: Int = DEFAULT_BUFFER_SIZE): OutputStream =
 | 
			
		||||
        object : ByteArrayOutputStream(bufferSize) {
 | 
			
		||||
            override fun close() {
 | 
			
		||||
                this@ResourceFiles.contents = if (buf.size > count) buf.copyOf(count) else buf
 | 
			
		||||
                super.close()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param virtualPath The resource file path. Example: /res/drawable-hdpi/icon.png.
 | 
			
		||||
     * @param archivePath The actual file path in the archive. Example: res/4a.png.
 | 
			
		||||
     * @param onClose An action to perform when the file associated with this handle is closed
 | 
			
		||||
     */
 | 
			
		||||
    internal data class Handle(val virtualPath: String, val archivePath: String, val onClose: () -> Unit)
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,100 @@
 | 
			
		||||
package app.revanced.arsc.resource
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.ApkResourceException
 | 
			
		||||
import com.reandroid.apk.xmlencoder.EncodeException
 | 
			
		||||
import com.reandroid.apk.xmlencoder.EncodeMaterials
 | 
			
		||||
import com.reandroid.arsc.util.FrameworkTable
 | 
			
		||||
import com.reandroid.arsc.value.Entry
 | 
			
		||||
import com.reandroid.common.TableEntryStore
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A high-level API for resolving resources in the resource table, which spans the entire ApkBundle.
 | 
			
		||||
 */
 | 
			
		||||
class ResourceTable(base: ResourceContainer, all: Sequence<ResourceContainer>) {
 | 
			
		||||
    private val packageName = base.tableBlock!!.name
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A [TableEntryStore] used to decode XML.
 | 
			
		||||
     */
 | 
			
		||||
    internal val entryStore = TableEntryStore()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The [EncodeMaterials] to use for resolving resources and encoding XML.
 | 
			
		||||
     */
 | 
			
		||||
    internal val encodeMaterials: EncodeMaterials = object : EncodeMaterials() {
 | 
			
		||||
        /*
 | 
			
		||||
        Our implementation is more efficient because it does not have to loop through every single entry group
 | 
			
		||||
        when the resource id cannot be found in the TableIdentifier, which does not update when you create a new resource.
 | 
			
		||||
        It also looks at the entire table instead of just the current package.
 | 
			
		||||
        */
 | 
			
		||||
        override fun resolveLocalResourceId(type: String, name: String) = resolveLocal(type, name)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The resource mappings which are generated when the ApkBundle is created.
 | 
			
		||||
     */
 | 
			
		||||
    private val tableIdentifier = encodeMaterials.tableIdentifier
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A table of all the resources that have been changed or added.
 | 
			
		||||
     */
 | 
			
		||||
    private val modifiedResources = HashMap<String, HashMap<String, Int>>()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolve a resource id for the specified resource.
 | 
			
		||||
     * Cannot resolve resources from the android framework.
 | 
			
		||||
     *
 | 
			
		||||
     * @param type The type of the resource.
 | 
			
		||||
     * @param name The name of the resource.
 | 
			
		||||
     * @return The id of the resource.
 | 
			
		||||
     */
 | 
			
		||||
    fun resolveLocal(type: String, name: String) =
 | 
			
		||||
        modifiedResources[type]?.get(name)
 | 
			
		||||
            ?: tableIdentifier.get(packageName, type, name)?.resourceId
 | 
			
		||||
            ?: throw ApkResourceException.InvalidReference(
 | 
			
		||||
                type,
 | 
			
		||||
                name
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resolve a resource id for the specified resource.
 | 
			
		||||
     *
 | 
			
		||||
     * @param reference The resource reference string.
 | 
			
		||||
     * @return The id of the resource.
 | 
			
		||||
     */
 | 
			
		||||
    fun resolve(reference: String) = try {
 | 
			
		||||
        encodeMaterials.resolveReference(reference)
 | 
			
		||||
    } catch (e: EncodeException) {
 | 
			
		||||
        throw ApkResourceException.InvalidReference(reference, e)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Notify the [ResourceTable] that an [Entry] has been created or modified.
 | 
			
		||||
     */
 | 
			
		||||
    internal fun registerChanged(entry: Entry) {
 | 
			
		||||
        modifiedResources.getOrPut(entry.typeName, ::HashMap)[entry.name] = entry.resourceId
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        all.forEach {
 | 
			
		||||
            it.tableBlock?.let { table ->
 | 
			
		||||
                entryStore.add(table)
 | 
			
		||||
                tableIdentifier.load(table)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            it.resourceTable = this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        base.also {
 | 
			
		||||
            encodeMaterials.currentPackage = it.tableBlock
 | 
			
		||||
 | 
			
		||||
            it.tableBlock!!.frameWorks.forEach { fw ->
 | 
			
		||||
                if (fw is FrameworkTable) {
 | 
			
		||||
                    entryStore.add(fw)
 | 
			
		||||
                    encodeMaterials.addFramework(fw)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
package app.revanced.arsc.xml
 | 
			
		||||
 | 
			
		||||
import app.revanced.arsc.resource.ResourceContainer
 | 
			
		||||
import app.revanced.arsc.resource.boolean
 | 
			
		||||
import com.reandroid.apk.xmlencoder.EncodeException
 | 
			
		||||
import com.reandroid.apk.xmlencoder.XMLEncodeSource
 | 
			
		||||
import com.reandroid.arsc.chunk.xml.ResXmlDocument
 | 
			
		||||
import com.reandroid.xml.XMLDocument
 | 
			
		||||
import com.reandroid.xml.XMLElement
 | 
			
		||||
import com.reandroid.xml.source.XMLDocumentSource
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Archive input source to lazily encode an [XMLDocument] after it has been modified.
 | 
			
		||||
 *
 | 
			
		||||
 * @param name The file name of this input source.
 | 
			
		||||
 * @param document The [XMLDocument] to encode.
 | 
			
		||||
 * @param resources The [ResourceContainer] to use for encoding.
 | 
			
		||||
 */
 | 
			
		||||
internal class LazyXMLEncodeSource(
 | 
			
		||||
    name: String,
 | 
			
		||||
    val document: XMLDocument,
 | 
			
		||||
    private val resources: ResourceContainer
 | 
			
		||||
) : XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document)) {
 | 
			
		||||
    private var encoded = false
 | 
			
		||||
 | 
			
		||||
    override fun getResXmlBlock(): ResXmlDocument {
 | 
			
		||||
        if (encoded) return super.getResXmlBlock()
 | 
			
		||||
 | 
			
		||||
        XMLEncodeSource(resources.resourceTable.encodeMaterials, XMLDocumentSource(name, document))
 | 
			
		||||
        
 | 
			
		||||
        fun XMLElement.registerIds() {
 | 
			
		||||
            listAttributes().forEach { attr ->
 | 
			
		||||
                if (!attr.value.startsWith("@+id/")) return@forEach
 | 
			
		||||
 | 
			
		||||
                val name = attr.value.split('/').last()
 | 
			
		||||
                resources.setResource("id", name, boolean(false))
 | 
			
		||||
                attr.value = "@id/$name"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            listChildElements().forEach { it.registerIds() }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Handle all @+id/id_name references in the document.
 | 
			
		||||
        document.documentElement.registerIds()
 | 
			
		||||
 | 
			
		||||
        encoded = true
 | 
			
		||||
 | 
			
		||||
        // This will call XMLEncodeSource.getResXmlBlock(),
 | 
			
		||||
        // which will encode the document if it has not already been encoded.
 | 
			
		||||
        try {
 | 
			
		||||
            return super.getResXmlBlock()
 | 
			
		||||
        } catch (e: EncodeException) {
 | 
			
		||||
            throw EncodeException("Failed to encode $name", e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 11 KiB  | 
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 11 KiB  | 
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| 
		 Before Width: | Height: | Size: 2.8 KiB  | 
							
								
								
									
										118
									
								
								build.gradle.kts
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								build.gradle.kts
									
									
									
									
									
								
							@@ -1,119 +1,3 @@
 | 
			
		||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 | 
			
		||||
 | 
			
		||||
plugins {
 | 
			
		||||
    alias(libs.plugins.kotlin)
 | 
			
		||||
    alias(libs.plugins.binary.compatibility.validator)
 | 
			
		||||
    `maven-publish`
 | 
			
		||||
    signing
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
group = "app.revanced"
 | 
			
		||||
 | 
			
		||||
tasks {
 | 
			
		||||
    processResources {
 | 
			
		||||
        expand("projectVersion" to project.version)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    test {
 | 
			
		||||
        useJUnitPlatform()
 | 
			
		||||
        testLogging {
 | 
			
		||||
            events("PASSED", "SKIPPED", "FAILED")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
repositories {
 | 
			
		||||
    mavenCentral()
 | 
			
		||||
    google()
 | 
			
		||||
    maven {
 | 
			
		||||
        // A repository must be specified for some reason. "registry" is a dummy.
 | 
			
		||||
        url = uri("https://maven.pkg.github.com/revanced/registry")
 | 
			
		||||
        credentials {
 | 
			
		||||
            username = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_ACTOR")
 | 
			
		||||
            password = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    // TODO: Convert project to KMP.
 | 
			
		||||
    compileOnly(libs.android) {
 | 
			
		||||
        // Exclude, otherwise the org.w3c.dom API breaks.
 | 
			
		||||
        exclude(group = "xerces", module = "xmlParserAPIs")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    implementation(libs.apktool.lib)
 | 
			
		||||
    implementation(libs.kotlin.reflect)
 | 
			
		||||
    implementation(libs.kotlinx.coroutines.core)
 | 
			
		||||
    implementation(libs.multidexlib2)
 | 
			
		||||
    implementation(libs.smali)
 | 
			
		||||
    implementation(libs.xpp3)
 | 
			
		||||
 | 
			
		||||
    testImplementation(libs.mockk)
 | 
			
		||||
    testImplementation(libs.kotlin.test)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
kotlin {
 | 
			
		||||
    compilerOptions {
 | 
			
		||||
        jvmTarget.set(JvmTarget.JVM_11)
 | 
			
		||||
 | 
			
		||||
        freeCompilerArgs = listOf("-Xcontext-receivers")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
java {
 | 
			
		||||
    targetCompatibility = JavaVersion.VERSION_11
 | 
			
		||||
 | 
			
		||||
    withSourcesJar()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
publishing {
 | 
			
		||||
    repositories {
 | 
			
		||||
        maven {
 | 
			
		||||
            name = "GitHubPackages"
 | 
			
		||||
            url = uri("https://maven.pkg.github.com/revanced/revanced-patcher")
 | 
			
		||||
            credentials {
 | 
			
		||||
                username = System.getenv("GITHUB_ACTOR")
 | 
			
		||||
                password = System.getenv("GITHUB_TOKEN")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    publications {
 | 
			
		||||
        create<MavenPublication>("revanced-patcher-publication") {
 | 
			
		||||
            from(components["java"])
 | 
			
		||||
 | 
			
		||||
            version = project.version.toString()
 | 
			
		||||
 | 
			
		||||
            pom {
 | 
			
		||||
                name = "ReVanced Patcher"
 | 
			
		||||
                description = "Patcher used by ReVanced."
 | 
			
		||||
                url = "https://revanced.app"
 | 
			
		||||
 | 
			
		||||
                licenses {
 | 
			
		||||
                    license {
 | 
			
		||||
                        name = "GNU General Public License v3.0"
 | 
			
		||||
                        url = "https://www.gnu.org/licenses/gpl-3.0.en.html"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                developers {
 | 
			
		||||
                    developer {
 | 
			
		||||
                        id = "ReVanced"
 | 
			
		||||
                        name = "ReVanced"
 | 
			
		||||
                        email = "contact@revanced.app"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                scm {
 | 
			
		||||
                    connection = "scm:git:git://github.com/revanced/revanced-patcher.git"
 | 
			
		||||
                    developerConnection = "scm:git:git@github.com:revanced/revanced-patcher.git"
 | 
			
		||||
                    url = "https://github.com/revanced/revanced-patcher"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
signing {
 | 
			
		||||
    useGpgCmd()
 | 
			
		||||
    sign(publishing.publications["revanced-patcher-publication"])
 | 
			
		||||
    kotlin("jvm") version "1.8.20" apply false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,111 +0,0 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <picture>
 | 
			
		||||
    <source
 | 
			
		||||
      width="256px"
 | 
			
		||||
      media="(prefers-color-scheme: dark)"
 | 
			
		||||
      srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
    >
 | 
			
		||||
    <img 
 | 
			
		||||
      width="256px"
 | 
			
		||||
      src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
    >
 | 
			
		||||
  </picture>
 | 
			
		||||
  <br>
 | 
			
		||||
  <a href="https://revanced.app/">
 | 
			
		||||
     <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
         <img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://github.com/ReVanced">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
           <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="http://revanced.app/discord">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://t.me/app_revanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://x.com/revancedapp">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>
 | 
			
		||||
   <br>
 | 
			
		||||
   <br>
 | 
			
		||||
   Continuing the legacy of Vanced
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
# 💉 Introduction to ReVanced Patcher
 | 
			
		||||
 | 
			
		||||
To create patches for Android apps, it is recommended to know the basic concept of ReVanced Patcher.
 | 
			
		||||
 | 
			
		||||
## 📙 How it works
 | 
			
		||||
 | 
			
		||||
ReVanced Patcher is a library that allows modifying Android apps by applying patches.
 | 
			
		||||
It is built on top of [Smali](https://github.com/google/smali) for bytecode manipulation and [Androlib (Apktool)](https://github.com/iBotPeaches/Apktool)
 | 
			
		||||
for resource decoding and encoding.
 | 
			
		||||
 | 
			
		||||
ReVanced Patcher receives a list of patches and applies them to a given APK file.
 | 
			
		||||
It then returns the modified components of the APK file, such as modified dex files and resources,
 | 
			
		||||
that can be repackaged into a new APK file.
 | 
			
		||||
 | 
			
		||||
ReVanced Patcher has a simple API that allows you to load patches from RVP (JAR or DEX container) files
 | 
			
		||||
and apply them to an APK file. Later on, you will learn how to create patches.
 | 
			
		||||
 | 
			
		||||
```kt
 | 
			
		||||
val patches = loadPatchesFromJar(setOf(File("revanced-patches.rvp")))
 | 
			
		||||
 | 
			
		||||
val patcherResult = Patcher(PatcherConfig(apkFile = File("some.apk"))).use { patcher ->
 | 
			
		||||
    // Here you can access metadata about the APK file through patcher.context.packageMetadata
 | 
			
		||||
    // such as package name, version code, version name, etc.
 | 
			
		||||
 | 
			
		||||
    // Add patches.
 | 
			
		||||
    patcher += patches
 | 
			
		||||
 | 
			
		||||
    // Execute the patches.
 | 
			
		||||
    runBlocking {
 | 
			
		||||
        patcher().collect { patchResult ->
 | 
			
		||||
            if (patchResult.exception != null)
 | 
			
		||||
                logger.info { "\"${patchResult.patch}\" failed:\n${patchResult.exception}" }
 | 
			
		||||
            else
 | 
			
		||||
                logger.info { "\"${patchResult.patch}\" succeeded" }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Compile and save the patched APK file components.
 | 
			
		||||
    patcher.get()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The result of the patcher contains the modified components of the APK file that can be repackaged into a new APK file.
 | 
			
		||||
val dexFiles = patcherResult.dexFiles
 | 
			
		||||
val resources = patcherResult.resources
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## ⏭️ What's next
 | 
			
		||||
 | 
			
		||||
The next page teaches the fundamentals of ReVanced Patches.
 | 
			
		||||
 | 
			
		||||
Continue: [🧩 Introduction to ReVanced Patches](2_patches_intro.md)
 | 
			
		||||
@@ -1,112 +0,0 @@
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <picture>
 | 
			
		||||
    <source
 | 
			
		||||
      width="256px"
 | 
			
		||||
      media="(prefers-color-scheme: dark)"
 | 
			
		||||
      srcset="../assets/revanced-headline/revanced-headline-vertical-dark.svg"
 | 
			
		||||
    >
 | 
			
		||||
    <img 
 | 
			
		||||
      width="256px"
 | 
			
		||||
      src="../assets/revanced-headline/revanced-headline-vertical-light.svg"
 | 
			
		||||
    >
 | 
			
		||||
  </picture>
 | 
			
		||||
  <br>
 | 
			
		||||
  <a href="https://revanced.app/">
 | 
			
		||||
     <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="../assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
         <img height="24px" src="../assets/revanced-logo/revanced-logo.svg" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://github.com/ReVanced">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://i.ibb.co/dMMmCrW/Git-Hub-Mark.png" />
 | 
			
		||||
           <img height="24px" src="https://i.ibb.co/9wV3HGF/Git-Hub-Mark-Light.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="http://revanced.app/discord">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032563-d4e084b7-244e-4358-af50-26bde6dd4996.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://reddit.com/r/revancedapp">
 | 
			
		||||
       <picture>
 | 
			
		||||
           <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
           <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032351-9d9d5619-8ef7-470a-9eec-2744ece54553.png" />
 | 
			
		||||
       </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://t.me/app_revanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032213-faf25ab8-0bc3-4a94-a730-b524c96df124.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://x.com/revancedapp">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/93124920/270180600-7c1b38bf-889b-4d68-bd5e-b9d86f91421a.png">
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/93124920/270108715-d80743fa-b330-4809-b1e6-79fbdc60d09c.png" />
 | 
			
		||||
      </picture>
 | 
			
		||||
   </a>   
 | 
			
		||||
   <a href="https://www.youtube.com/@ReVanced">
 | 
			
		||||
      <picture>
 | 
			
		||||
         <source height="24px" media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
         <img height="24px" src="https://user-images.githubusercontent.com/13122796/178032714-c51c7492-0666-44ac-99c2-f003a695ab50.png" />
 | 
			
		||||
     </picture>
 | 
			
		||||
   </a>
 | 
			
		||||
   <br>
 | 
			
		||||
   <br>
 | 
			
		||||
   Continuing the legacy of Vanced
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
# 👶 Setting up a development environment
 | 
			
		||||
 | 
			
		||||
To start developing patches with ReVanced Patcher, you must prepare a development environment.
 | 
			
		||||
 | 
			
		||||
## 📝 Prerequisites
 | 
			
		||||
 | 
			
		||||
- A Java IDE with Kotlin support, such as [IntelliJ IDEA](https://www.jetbrains.com/idea/)
 | 
			
		||||
- Knowledge of Java, [Kotlin](https://kotlinlang.org), and [Dalvik bytecode](https://source.android.com/docs/core/runtime/dalvik-bytecode)
 | 
			
		||||
- Android reverse engineering skills and tools such as [jadx](https://github.com/skylot/jadx)
 | 
			
		||||
 | 
			
		||||
## 🏃 Prepare the environment
 | 
			
		||||
 | 
			
		||||
Throughout the documentation, [ReVanced Patches](https://github.com/revanced/revanced-patches) will be used as an example project.
 | 
			
		||||
 | 
			
		||||
> [!NOTE]
 | 
			
		||||
> To start a fresh project, 
 | 
			
		||||
> you can use the [ReVanced Patches template](https://github.com/revanced/revanced-patches-template).
 | 
			
		||||
 | 
			
		||||
1. Clone the repository
 | 
			
		||||
 | 
			
		||||
   ```bash
 | 
			
		||||
   git clone https://github.com/revanced/revanced-patches && cd revanced-patches
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
2. Build the project
 | 
			
		||||
 | 
			
		||||
   ```bash
 | 
			
		||||
   ./gradlew build
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
> [!NOTE]
 | 
			
		||||
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
 | 
			
		||||
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
 | 
			
		||||
>
 | 
			
		||||
> Example `gradle.properties` file:
 | 
			
		||||
>
 | 
			
		||||
> ```properties
 | 
			
		||||
> gpr.user = user
 | 
			
		||||
> gpr.key = key
 | 
			
		||||
> ```
 | 
			
		||||
 | 
			
		||||
3. Open the project in your IDE
 | 
			
		||||
 | 
			
		||||
> [!TIP]
 | 
			
		||||
> It is a good idea to set up a complete development environment for ReVanced, so that you can also test your patches
 | 
			
		||||
> by following the [ReVanced documentation](https://github.com/ReVanced/revanced-documentation).
 | 
			
		||||
 | 
			
		||||
## ⏭️ What's next
 | 
			
		||||
 | 
			
		||||
The next page will go into details about a ReVanced patch.
 | 
			
		||||
 | 
			
		||||
Continue: [🧩 Anatomy of a patch](2_2_patch_anatomy.md)
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user