mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			368 Commits
		
	
	
		
			canary-270
			...
			v30.1
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 78f7fa348e | ||
|   | d8c448b99d | ||
|   | d4b83b6a44 | ||
|   | e5d36d1d24 | ||
|   | ff18cb8e70 | ||
|   | 37a9724a54 | ||
|   | d660401063 | ||
|   | 88541d6f49 | ||
|   | ecd6129fe5 | ||
|   | 6dfe9df9e2 | ||
|   | e81de7ec36 | ||
|   | c78da1ce24 | ||
|   | 7b2d40987c | ||
|   | 3a37e8c9c5 | ||
|   | 58b405bce1 | ||
|   | 810174ef73 | ||
|   | 690a5ac033 | ||
|   | 89aad31f7e | ||
|   | 7124db98e3 | ||
|   | 0860e859f7 | ||
|   | 04008949b8 | ||
|   | 39f2940bd1 | ||
|   | 1460317ebd | ||
|   | 12340c9bd5 | ||
|   | c4590fe2ba | ||
|   | b36066bbcd | ||
|   | 65d1c5827c | ||
|   | 1d675c8b2e | ||
|   | 0b494ed7df | ||
|   | 48d9fc24eb | ||
|   | 83426f7f36 | ||
|   | 0e86d4dbcb | ||
|   | 5e050d7456 | ||
|   | 898580bf90 | ||
|   | 86621a4c46 | ||
|   | a834e72b71 | ||
|   | d8cf42af16 | ||
|   | 8c79d66b7b | ||
|   | fada8b148a | ||
|   | dc0acea47c | ||
|   | 78d1200608 | ||
|   | 527bbc0368 | ||
|   | 4c89c7e2b3 | ||
|   | adbea7e313 | ||
|   | 76962f965e | ||
|   | a4b8c5e46b | ||
|   | 83c707439c | ||
|   | 25dd6121f4 | ||
|   | 67f35ad027 | ||
|   | 0c4b8afbc5 | ||
|   | 34b30d7ce1 | ||
|   | 2215088973 | ||
|   | 8b7fb6cdde | ||
|   | 94c7dbedf2 | ||
|   | b1dc47a047 | ||
|   | 62b1310d97 | ||
|   | 0a86916d3a | ||
|   | 9907ce57aa | ||
|   | b92626cacc | ||
|   | 5a762f0a8e | ||
|   | 5dd7a7d804 | ||
|   | 7831f40691 | ||
|   | 4f4b1ff885 | ||
|   | 97901979dd | ||
|   | 287316842c | ||
|   | 608786e8f3 | ||
|   | 9684a35cab | ||
|   | e3e4202954 | ||
|   | 23c2054d46 | ||
|   | a20a2a8fa0 | ||
|   | a2896be4a6 | ||
|   | e9220a28d9 | ||
|   | cf12087e21 | ||
|   | 00c1b36837 | ||
|   | 03e034795d | ||
|   | 79c0fafe43 | ||
|   | d499819ba0 | ||
|   | 86da917174 | ||
|   | 30bd7d6555 | ||
|   | e5a12f0f5f | ||
|   | c85a8434c6 | ||
|   | 427a1ca4e5 | ||
|   | 22884e173a | ||
|   | d1829308e9 | ||
|   | 73840f8721 | ||
|   | c7d1af9805 | ||
|   | 4ad26d3dfb | ||
|   | 0c70b7670c | ||
|   | f44d044095 | ||
|   | 5c1cb13472 | ||
|   | 3327fc668e | ||
|   | 610945ac54 | ||
|   | ddf5474917 | ||
|   | 6ba1685ade | ||
|   | e02b5f7868 | ||
|   | ab2e5d1e7e | ||
|   | f3fef7bfe4 | ||
|   | c34c7838bb | ||
|   | c8a16b0e0c | ||
|   | 14f9ed91a1 | ||
|   | 7a207d4ccf | ||
|   | 92a42d901f | ||
|   | 084d89fcce | ||
|   | 55b036c071 | ||
|   | 30e79310ab | ||
|   | f063fa5054 | ||
|   | 7bd901273c | ||
|   | c1e061603b | ||
|   | cb08504fe5 | ||
|   | c0a1fb77be | ||
|   | 4864c1112a | ||
|   | 9ddeab034b | ||
|   | c4847ed288 | ||
|   | b8f1523fb2 | ||
|   | fb7fa8a6b3 | ||
|   | 9c7d359093 | ||
|   | eb54bc1fd7 | ||
|   | d4a0286e13 | ||
|   | 83e66767ff | ||
|   | 7dc010749b | ||
|   | 8e8d013b1b | ||
|   | bba0373808 | ||
|   | 1fa318dc8c | ||
|   | 6edc5e2037 | ||
|   | 1523ed9f78 | ||
|   | 8e604d2ab8 | ||
|   | 2aba7247a9 | ||
|   | e66fe8533e | ||
|   | b03fbb3917 | ||
|   | c2ece62e4c | ||
|   | 8c972dcf34 | ||
|   | 50af14f2a3 | ||
|   | e0a356b319 | ||
|   | c09a792958 | ||
|   | 0bbfe7f44d | ||
|   | a396abf565 | ||
|   | 1e3edb8883 | ||
|   | 3b8b61bf35 | ||
|   | 6f90456036 | ||
|   | f56fd4e215 | ||
|   | aa35aac5d5 | ||
|   | 1f162b819d | ||
|   | 52ef1d1cb2 | ||
|   | f14e3a89cc | ||
|   | 95d3eac2e0 | ||
|   | 8e73536e02 | ||
|   | 12a0870bc9 | ||
|   | 6ff82c4e86 | ||
|   | c64de35375 | ||
|   | ee5283f4e8 | ||
|   | bd0e954fea | ||
|   | 675471a49e | ||
|   | c90e73ccec | ||
|   | a43c1267d8 | ||
|   | e8958c6b5c | ||
|   | e8a3bf82c6 | ||
|   | 27fd79176a | ||
|   | 28d86a3454 | ||
|   | c6c1a17ae6 | ||
|   | 2b47d47215 | ||
|   | 0e82df9e10 | ||
|   | 893821ad88 | ||
|   | 6b80fbfa99 | ||
|   | 8c3c7d0194 | ||
|   | b94a3d9f2f | ||
|   | 442d0b5ddc | ||
|   | 494615d9a0 | ||
|   | afbfb81837 | ||
|   | 3ed4e258a3 | ||
|   | dddd41c95b | ||
|   | 5f2ca81e86 | ||
|   | c9eac0c438 | ||
|   | b6b34f7612 | ||
|   | e55c413261 | ||
|   | 0399cde50a | ||
|   | 019eb03823 | ||
|   | 363410e1c0 | ||
|   | fc2ef21660 | ||
|   | 18cb659ff3 | ||
|   | 63231d97ce | ||
|   | 9ac81a8a25 | ||
|   | 79af2787ae | ||
|   | f5f9b285c0 | ||
|   | 6c05f2ae85 | ||
|   | 29043e1684 | ||
|   | b73d4a7022 | ||
|   | ad95e8951b | ||
|   | bf591fca12 | ||
|   | dcf027884d | ||
|   | 584f3820fe | ||
|   | 3c7c46307a | ||
|   | 4d80361805 | ||
|   | 9a74e19117 | ||
|   | b1e17706a4 | ||
|   | caad129d69 | ||
|   | da58571ce5 | ||
|   | 2aa7f1c094 | ||
|   | 823e31a91b | ||
|   | fb926ae302 | ||
|   | e0489eeffd | ||
|   | dc9d5a4cac | ||
|   | 143743d0b0 | ||
|   | 563f0d5ad5 | ||
|   | c99f4a591b | ||
|   | 449204e380 | ||
|   | a85c4c6528 | ||
|   | d203a6fff6 | ||
|   | 6c612d66d7 | ||
|   | 540253a55b | ||
|   | 15b7c4ccd1 | ||
|   | 442d5335ea | ||
|   | 8a80eea597 | ||
|   | 5e35703091 | ||
|   | b7ca73f431 | ||
|   | a14fc90f07 | ||
|   | c913f7ec74 | ||
|   | 7f6c9e8411 | ||
|   | bb02ea3a20 | ||
|   | 3981c9665e | ||
|   | 88628fdf3c | ||
|   | 0469817781 | ||
|   | a786801141 | ||
|   | ab86732c89 | ||
|   | 59622d1688 | ||
|   | 58a25a3e2b | ||
|   | 15dca29a87 | ||
|   | 46980819c0 | ||
|   | 4fb6a7268c | ||
|   | c05e963f37 | ||
|   | 7f7f625864 | ||
|   | b25aa8295a | ||
|   | 15a605765c | ||
|   | b575c95710 | ||
|   | a48a9c858a | ||
|   | 0d8d6290a3 | ||
|   | 4dcd733ddd | ||
|   | b62835cbeb | ||
|   | 6ea740b5ab | ||
|   | 7ab98dd5ac | ||
|   | fc8b3400fc | ||
|   | 54428ba415 | ||
|   | 95d1e69d8e | ||
|   | a0f13ab49f | ||
|   | c3e8405020 | ||
|   | a93593ea66 | ||
|   | 23eff70883 | ||
|   | 110dd4a8b9 | ||
|   | d9c2bffc9f | ||
|   | 049db49dc8 | ||
|   | 7c1d2ec61e | ||
|   | a1b2830c06 | ||
|   | 82d1d19267 | ||
|   | 4d4195c02d | ||
|   | 5637a258fc | ||
|   | ee6810f417 | ||
|   | 7098248c64 | ||
|   | 0d31d356ef | ||
|   | b782e7dcb7 | ||
|   | a4671b4698 | ||
|   | 7edd8be169 | ||
|   | 24650eefe4 | ||
|   | 8e1a44e7eb | ||
|   | 2722875190 | ||
|   | 3ca6d06f69 | ||
|   | 10e47248de | ||
|   | e73ff679ac | ||
|   | 53e401fa2d | ||
|   | d2768357da | ||
|   | a6c2ba7c1e | ||
|   | aae5b466fb | ||
|   | 2b7be8b949 | ||
|   | b6511a510d | ||
|   | 704541aef2 | ||
|   | 005560a4c5 | ||
|   | 231a5d1853 | ||
|   | 9e2b59060d | ||
|   | 08ea937f7c | ||
|   | 2baedf74d1 | ||
|   | 32faa4ced6 | ||
|   | ccdb0b5d13 | ||
|   | 8506b672ad | ||
|   | ce2e33bb20 | ||
|   | 6707b72260 | ||
|   | 5885b8c20d | ||
|   | 820710c086 | ||
|   | 51cf196bf7 | ||
|   | dadba44cf9 | ||
|   | 2ce4a5543b | ||
|   | 9112a3a4f5 | ||
|   | 24615afda1 | ||
|   | c5778f398b | ||
|   | 4eb4097b9b | ||
|   | c512496847 | ||
|   | 506961a10d | ||
|   | 3414415907 | ||
|   | dc2ae7cfd1 | ||
|   | 2e86d21c29 | ||
|   | 2654382c43 | ||
|   | 9e26b73813 | ||
|   | 10cd13bf80 | ||
|   | f10ee5f887 | ||
|   | 47cc532d96 | ||
|   | 218327f92b | ||
|   | 4eae66a1a7 | ||
|   | b09ceeb43c | ||
|   | 4fb539c110 | ||
|   | 849b284da5 | ||
|   | 895b5f6cbf | ||
|   | cb3d4ea514 | ||
|   | 0d89a2a97d | ||
|   | 3ca5913055 | ||
|   | df6b808f49 | ||
|   | 09c7ac754b | ||
|   | 805da67c23 | ||
|   | 3c6889505b | ||
|   | c8e9ce7627 | ||
|   | 837c679a31 | ||
|   | 06616659b8 | ||
|   | a34c04f999 | ||
|   | da43ac89a0 | ||
|   | 830fc758b9 | ||
|   | 0f3cfef278 | ||
|   | b32d7bfafd | ||
|   | c0899f2939 | ||
|   | 082330808f | ||
|   | 024da05888 | ||
|   | 377b6d0cc2 | ||
|   | c661009b31 | ||
|   | 613f2d31c5 | ||
|   | 7dbb973db5 | ||
|   | f4502f8be8 | ||
|   | 455b13b83c | ||
|   | 8b98709743 | ||
|   | 1b12f45f39 | ||
|   | a5cad532ff | ||
|   | 070719db50 | ||
|   | 28cccdf7aa | ||
|   | b7e0986a5c | ||
|   | da709745dd | ||
|   | 8b6771d487 | ||
|   | e1b847fbc5 | ||
|   | 7188de1205 | ||
|   | 44fb7dbcbe | ||
|   | 8086b5933c | ||
|   | 7f675f4bf7 | ||
|   | 5e6b53e0da | ||
|   | 5b29fefc65 | ||
|   | 16a168535d | ||
|   | 33f70f8f6d | ||
|   | 4f18a66d73 | ||
|   | 250dc16007 | ||
|   | 7af273e047 | ||
|   | e381aebaa0 | ||
|   | 45d91c9658 | ||
|   | 4a9158f667 | ||
|   | 0d9ee89e7f | ||
|   | abaff72304 | ||
|   | b828e2d0b2 | ||
|   | 53d7cbc11b | ||
|   | 310be7ab47 | ||
|   | 60894e458f | ||
|   | fbebb6ac10 | ||
|   | a9f8c20703 | ||
|   | ae0b15d197 | ||
|   | 869aa62328 | ||
|   | dcd3bc58a3 | ||
|   | a82f17c594 | ||
|   | b38fd1ca5f | 
							
								
								
									
										6
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -12,13 +12,11 @@ | ||||
|  | ||||
| # Denote all files that are truly binary and should not be modified. | ||||
| tools/** binary | ||||
| tools/rustup-wrapper/** -binary | ||||
| tools/elf-cleaner/** -binary | ||||
| *.jar binary | ||||
| *.exe binary | ||||
| *.apk binary | ||||
| *.png binary | ||||
| *.jpg binary | ||||
| *.ttf binary | ||||
|  | ||||
| # Help GitHub detect languages | ||||
| native/jni/external/** linguist-vendored | ||||
| native/jni/systemproperties/** linguist-language=C++ | ||||
|   | ||||
							
								
								
									
										29
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								.github/actions/setup/action.yml
									
									
									
									
										vendored
									
									
								
							| @@ -6,11 +6,11 @@ inputs: | ||||
| runs: | ||||
|   using: "composite" | ||||
|   steps: | ||||
|     - name: Set up JDK 17 | ||||
|     - name: Set up JDK 21 | ||||
|       uses: actions/setup-java@v4 | ||||
|       with: | ||||
|         distribution: "temurin" | ||||
|         java-version: "17" | ||||
|         java-version: "21" | ||||
|  | ||||
|     - name: Set up Python 3 | ||||
|       uses: actions/setup-python@v5 | ||||
| @@ -26,6 +26,15 @@ runs: | ||||
|  | ||||
|     - name: Cache sccache | ||||
|       uses: actions/cache@v4 | ||||
|       if: ${{ github.event_name != 'pull_request' }} | ||||
|       with: | ||||
|         path: .sccache | ||||
|         key: sccache-${{ runner.os }}-${{ github.sha }} | ||||
|         restore-keys: sccache-${{ runner.os }}- | ||||
|  | ||||
|     - name: Restore sccache | ||||
|       uses: actions/cache/restore@v4 | ||||
|       if: ${{ github.event_name == 'pull_request' }} | ||||
|       with: | ||||
|         path: .sccache | ||||
|         key: sccache-${{ runner.os }}-${{ github.sha }} | ||||
| @@ -55,18 +64,18 @@ runs: | ||||
|  | ||||
|     - name: Cache Gradle dependencies | ||||
|       uses: actions/cache@v4 | ||||
|       if: inputs.is-asset-build == 'true' | ||||
|       if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }} | ||||
|       with: | ||||
|         path: | | ||||
|           .gradle/caches | ||||
|           .gradle/wrapper | ||||
|           !.gradle/caches/build-cache-* | ||||
|         key: gradle-cache-${{ hashFiles('gradle/**') }} | ||||
|         key: gradle-cache-${{ hashFiles('app/gradle/**') }} | ||||
|         restore-keys: gradle-cache- | ||||
|  | ||||
|     - name: Restore Gradle dependencies | ||||
|       uses: actions/cache/restore@v4 | ||||
|       if: inputs.is-asset-build == 'false' | ||||
|       if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }} | ||||
|       with: | ||||
|         path: | | ||||
|           .gradle/caches | ||||
| @@ -78,19 +87,17 @@ runs: | ||||
|  | ||||
|     - name: Cache Gradle build cache | ||||
|       uses: actions/cache@v4 | ||||
|       if: inputs.is-asset-build == 'true' | ||||
|       if: ${{ inputs.is-asset-build == 'true' && github.event_name != 'pull_request' }} | ||||
|       with: | ||||
|         path: | | ||||
|           .gradle/caches/build-cache-* | ||||
|         path: .gradle/caches/build-cache-* | ||||
|         key: gradle-build-cache-${{ github.sha }} | ||||
|         restore-keys: gradle-build-cache- | ||||
|  | ||||
|     - name: Restore Gradle build cache | ||||
|       uses: actions/cache/restore@v4 | ||||
|       if: inputs.is-asset-build == 'false' | ||||
|       if: ${{ inputs.is-asset-build == 'false' || github.event_name == 'pull_request' }} | ||||
|       with: | ||||
|         path: | | ||||
|           .gradle/caches/build-cache-* | ||||
|         path: .gradle/caches/build-cache-* | ||||
|         key: gradle-build-cache-${{ github.sha }} | ||||
|         restore-keys: gradle-build-cache- | ||||
|         enableCrossOsArchive: true | ||||
|   | ||||
							
								
								
									
										37
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -6,9 +6,7 @@ on: | ||||
|     paths: | ||||
|       - "app/**" | ||||
|       - "native/**" | ||||
|       - "buildSrc/**" | ||||
|       - "build.py" | ||||
|       - "gradle.properties" | ||||
|       - ".github/workflows/build.yml" | ||||
|   pull_request: | ||||
|     branches: [master] | ||||
| @@ -17,7 +15,7 @@ on: | ||||
| jobs: | ||||
|   build: | ||||
|     name: Build Magisk artifacts | ||||
|     runs-on: macos-14 | ||||
|     runs-on: macos-15 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|     steps: | ||||
| @@ -38,7 +36,7 @@ jobs: | ||||
|         run: ./build.py -v all | ||||
|  | ||||
|       - name: Stop gradle daemon | ||||
|         run: ./gradlew --stop | ||||
|         run: ./app/gradlew --stop | ||||
|  | ||||
|       - name: Upload build artifact | ||||
|         uses: actions/upload-artifact@v4 | ||||
| @@ -60,7 +58,7 @@ jobs: | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         os: [windows-latest, ubuntu-latest] | ||||
|         os: [windows-2025, ubuntu-24.04] | ||||
|     steps: | ||||
|       - name: Check out | ||||
|         uses: actions/checkout@v4 | ||||
| @@ -74,20 +72,23 @@ jobs: | ||||
|         run: python build.py -v -c .github/ci.prop all | ||||
|  | ||||
|       - name: Stop gradle daemon | ||||
|         run: ./gradlew --stop | ||||
|         run: ./app/gradlew --stop | ||||
|  | ||||
|   avd-test: | ||||
|     name: Test API ${{ matrix.version }} (x86_64) | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-24.04 | ||||
|     needs: build | ||||
|     if: ${{ github.event_name != 'push' }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] | ||||
|         version: [23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35] | ||||
|         type: [""] | ||||
|         include: | ||||
|           - version: 35 | ||||
|           - version: 36 | ||||
|             type: "google_apis" | ||||
|           - version: 36 | ||||
|             type: "google_apis_ps16k" | ||||
|  | ||||
|     steps: | ||||
|       - name: Check out | ||||
| @@ -109,7 +110,7 @@ jobs: | ||||
|         timeout-minutes: 10 | ||||
|         env: | ||||
|           AVD_TEST_LOG: 1 | ||||
|         run: scripts/avd_test.sh ${{ matrix.version }} ${{ matrix.type }} | ||||
|         run: scripts/avd.sh test ${{ matrix.version }} ${{ matrix.type }} | ||||
|  | ||||
|       - name: Upload logs on error | ||||
|         if: ${{ failure() }} | ||||
| @@ -122,8 +123,9 @@ jobs: | ||||
|  | ||||
|   avd-test-32: | ||||
|     name: Test API ${{ matrix.version }} (x86) | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ubuntu-24.04 | ||||
|     needs: build | ||||
|     if: ${{ github.event_name != 'push' }} | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
| @@ -150,7 +152,7 @@ jobs: | ||||
|         env: | ||||
|           FORCE_32_BIT: 1 | ||||
|           AVD_TEST_LOG: 1 | ||||
|         run: scripts/avd_test.sh ${{ matrix.version }} | ||||
|         run: scripts/avd.sh test ${{ matrix.version }} | ||||
|  | ||||
|       - name: Upload logs on error | ||||
|         if: ${{ failure() }} | ||||
| @@ -161,20 +163,19 @@ jobs: | ||||
|             kernel.log | ||||
|             logcat.log | ||||
|  | ||||
|   cf_test: | ||||
|   cf-test: | ||||
|     name: Test ${{ matrix.device }} | ||||
|     runs-on: ubuntu-24.04 | ||||
|     needs: build | ||||
|     if: ${{ github.event_name != 'push' }} | ||||
|     env: | ||||
|       CF_HOME: /home/runner/aosp_cf_phone | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         include: | ||||
|           - branch: "aosp-main" | ||||
|             device: "aosp_cf_x86_64_phone" | ||||
|           - branch: "aosp-main-throttled" | ||||
|             device: "aosp_cf_x86_64_phone_pgagnostic" | ||||
|           - branch: "aosp-android-latest-release" | ||||
|             device: "aosp_cf_x86_64_only_phone" | ||||
|  | ||||
|     steps: | ||||
|       - name: Check out | ||||
| @@ -193,7 +194,7 @@ jobs: | ||||
|  | ||||
|       - name: Run Cuttlefish test | ||||
|         timeout-minutes: 10 | ||||
|         run: su $USER -c 'scripts/cuttlefish.sh test' | ||||
|         run: sudo -E -u $USER scripts/cuttlefish.sh test | ||||
|  | ||||
|       - name: Upload logs on error | ||||
|         if: ${{ failure() }} | ||||
|   | ||||
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,18 +2,13 @@ out | ||||
| *.zip | ||||
| *.jks | ||||
| *.apk | ||||
| *.log | ||||
| /config.prop | ||||
| /notes.md | ||||
| /update.sh | ||||
| /app/dict.txt | ||||
|  | ||||
| # Built binaries | ||||
| native/out | ||||
|  | ||||
| # Android Studio / Gradle | ||||
| # Android Studio | ||||
| *.iml | ||||
| .gradle | ||||
| .idea | ||||
| /local.properties | ||||
| /build | ||||
| /captures | ||||
|   | ||||
							
								
								
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -4,21 +4,12 @@ | ||||
| [submodule "lz4"] | ||||
| 	path = native/src/external/lz4 | ||||
| 	url = https://github.com/lz4/lz4.git | ||||
| [submodule "bzip2"] | ||||
| 	path = native/src/external/bzip2 | ||||
| 	url = https://github.com/nemequ/bzip2.git | ||||
| [submodule "xz"] | ||||
| 	path = native/src/external/xz | ||||
| 	url = https://github.com/xz-mirror/xz.git | ||||
| [submodule "libcxx"] | ||||
| 	path = native/src/external/libcxx | ||||
| 	url = https://github.com/topjohnwu/libcxx.git | ||||
| [submodule "zlib"] | ||||
| 	path = native/src/external/zlib | ||||
| 	url = https://android.googlesource.com/platform/external/zlib | ||||
| [submodule "zopfli"] | ||||
| 	path = native/src/external/zopfli | ||||
| 	url = https://github.com/google/zopfli.git | ||||
| [submodule "cxx-rs"] | ||||
| 	path = native/src/external/cxx-rs | ||||
| 	url = https://github.com/topjohnwu/cxx.git | ||||
| @@ -31,6 +22,3 @@ | ||||
| [submodule "crt0"] | ||||
| 	path = native/src/external/crt0 | ||||
| 	url = https://github.com/topjohnwu/crt0.git | ||||
| [submodule "termux-elf-cleaner"] | ||||
| 	path = tools/termux-elf-cleaner | ||||
| 	url = https://github.com/termux/termux-elf-cleaner.git | ||||
|   | ||||
| @@ -16,13 +16,7 @@ Some highlight features: | ||||
|  | ||||
| ## Downloads | ||||
|  | ||||
| [Github](https://github.com/topjohnwu/Magisk/) is the only source where you can get official Magisk information and downloads. | ||||
|  | ||||
| Click the icon below to download Magisk apk. | ||||
|  | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/v27.0) | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/v27.0) | ||||
| [](https://github.com/topjohnwu/Magisk/releases/tag/canary-27007) | ||||
| [Github](https://github.com/topjohnwu/Magisk/releases) is the only source where you can get official Magisk information and downloads. | ||||
|  | ||||
| ## Useful Links | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| /dict.txt | ||||
|  | ||||
| # Gradle | ||||
| .gradle | ||||
| .kotlin | ||||
| /local.properties | ||||
| /build | ||||
| @@ -6,46 +6,37 @@ plugins { | ||||
|     id("androidx.navigation.safeargs.kotlin") | ||||
| } | ||||
|  | ||||
| setupAppCommon() | ||||
| setupMainApk() | ||||
|  | ||||
| kapt { | ||||
|     correctErrorTypes = true | ||||
|     useBuildCache = true | ||||
|     mapDiagnosticLocations = true | ||||
|     javacOptions { | ||||
|         option("-Xmaxerrs", 1000) | ||||
|         option("-Xmaxerrs", "1000") | ||||
|     } | ||||
| } | ||||
|  | ||||
| android { | ||||
|     namespace = "com.topjohnwu.magisk" | ||||
|     buildFeatures { | ||||
|         dataBinding = true | ||||
|     } | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId = "com.topjohnwu.magisk" | ||||
|         vectorDrawables.useSupportLibrary = true | ||||
|         versionName = Config.version | ||||
|         versionCode = Config.versionCode | ||||
|         ndk { | ||||
|             abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64", "riscv64") | ||||
|             debugSymbolLevel = "FULL" | ||||
|         } | ||||
|     compileOptions { | ||||
|         isCoreLibraryDesugaringEnabled = true | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             isMinifyEnabled = true | ||||
|             isShrinkResources = true | ||||
|             proguardFiles("proguard-rules.pro") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildFeatures { | ||||
|         dataBinding = true | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     implementation(project(":app:core")) | ||||
|     implementation(project(":core")) | ||||
|     coreLibraryDesugaring(libs.jdk.libs) | ||||
|  | ||||
|     implementation(libs.indeterminate.checkbox) | ||||
|     implementation(libs.rikka.layoutinflater) | ||||
|   | ||||
| @@ -1,10 +1,13 @@ | ||||
| package com.topjohnwu.magisk.arch | ||||
|  | ||||
| import android.content.ContentResolver | ||||
| import android.view.KeyEvent | ||||
| import androidx.databinding.ViewDataBinding | ||||
| import androidx.navigation.NavController | ||||
| import androidx.navigation.NavDirections | ||||
| import androidx.navigation.fragment.NavHostFragment | ||||
| import androidx.navigation.navOptions | ||||
| import com.topjohnwu.magisk.utils.AccessibilityUtils | ||||
|  | ||||
| abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Binding>() { | ||||
|  | ||||
| @@ -31,7 +34,17 @@ abstract class NavigationActivity<Binding : ViewDataBinding> : UIActivity<Bindin | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         fun navigate(directions: NavDirections, navigation: NavController, cr: ContentResolver) { | ||||
|             if (AccessibilityUtils.isAnimationEnabled(cr)) { | ||||
|                 navigation.navigate(directions) | ||||
|             } else { | ||||
|                 navigation.navigate(directions, navOptions {}) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun NavDirections.navigate() { | ||||
|         navigation.navigate(this) | ||||
|         navigate(this, navigation, contentResolver) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -24,6 +24,7 @@ import androidx.core.widget.ImageViewCompat | ||||
| import androidx.databinding.BindingAdapter | ||||
| import androidx.databinding.InverseBindingAdapter | ||||
| import androidx.databinding.InverseBindingListener | ||||
| import androidx.databinding.InverseMethod | ||||
| import androidx.interpolator.view.animation.FastOutSlowInInterpolator | ||||
| import androidx.recyclerview.widget.DividerItemDecoration | ||||
| import androidx.recyclerview.widget.GridLayoutManager | ||||
| @@ -33,9 +34,11 @@ import androidx.recyclerview.widget.StaggeredGridLayoutManager | ||||
| import com.google.android.material.button.MaterialButton | ||||
| import com.google.android.material.card.MaterialCardView | ||||
| import com.google.android.material.chip.Chip | ||||
| import com.google.android.material.slider.Slider | ||||
| import com.google.android.material.textfield.TextInputLayout | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.core.di.ServiceLocator | ||||
| import com.topjohnwu.magisk.core.model.su.SuPolicy | ||||
| import com.topjohnwu.magisk.utils.TextHolder | ||||
| import com.topjohnwu.superuser.internal.UiThreadHandler | ||||
| import com.topjohnwu.widget.IndeterminateCheckBox | ||||
| @@ -306,3 +309,38 @@ fun TextView.setText(text: TextHolder) { | ||||
| fun Spinner.setAdapter(items: Array<Any>, layoutRes: Int) { | ||||
|     adapter = ArrayAdapter(context, layoutRes, items) | ||||
| } | ||||
|  | ||||
| @BindingAdapter("labelFormatter") | ||||
| fun Slider.setLabelFormatter(formatter: (Float) -> Int) { | ||||
|     setLabelFormatter { value -> resources.getString(formatter(value)) } | ||||
| } | ||||
|  | ||||
| @InverseBindingAdapter(attribute = "android:value") | ||||
| fun Slider.getValueBinding() = value | ||||
|  | ||||
| @BindingAdapter("android:valueAttrChanged") | ||||
| fun Slider.setListener(attrChange: InverseBindingListener) { | ||||
|     addOnSliderTouchListener(object : Slider.OnSliderTouchListener { | ||||
|         override fun onStartTrackingTouch(slider: Slider) = Unit | ||||
|         override fun onStopTrackingTouch(slider: Slider) = attrChange.onChange() | ||||
|     }) | ||||
| } | ||||
|  | ||||
| @InverseMethod("sliderValueToPolicy") | ||||
| fun policyToSliderValue(policy: Int): Float { | ||||
|     return when (policy) { | ||||
|         SuPolicy.DENY -> 1f | ||||
|         SuPolicy.RESTRICT -> 2f | ||||
|         SuPolicy.ALLOW -> 3f | ||||
|         else -> 1f | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun sliderValueToPolicy(value: Float): Int { | ||||
|     return when (value) { | ||||
|         1f -> SuPolicy.DENY | ||||
|         2f -> SuPolicy.RESTRICT | ||||
|         3f -> SuPolicy.ALLOW | ||||
|         else -> SuPolicy.DENY | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,13 +1,18 @@ | ||||
| package com.topjohnwu.magisk.dialog | ||||
|  | ||||
| import android.widget.Toast | ||||
| import androidx.core.os.postDelayed | ||||
| import androidx.lifecycle.lifecycleScope | ||||
| import com.topjohnwu.magisk.core.BuildConfig | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.R | ||||
| import com.topjohnwu.magisk.core.ktx.reboot | ||||
| import com.topjohnwu.magisk.core.ktx.toast | ||||
| import com.topjohnwu.magisk.core.tasks.MagiskInstaller | ||||
| import com.topjohnwu.magisk.events.DialogBuilder | ||||
| import com.topjohnwu.magisk.ui.home.HomeViewModel | ||||
| import com.topjohnwu.magisk.view.MagiskDialog | ||||
| import com.topjohnwu.superuser.internal.UiThreadHandler | ||||
| import kotlinx.coroutines.launch | ||||
|  | ||||
| class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : DialogBuilder { | ||||
| @@ -27,9 +32,15 @@ class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : Dialo | ||||
|                         setCancelable(false) | ||||
|                     } | ||||
|                     dialog.activity.lifecycleScope.launch { | ||||
|                         MagiskInstaller.FixEnv { | ||||
|                         MagiskInstaller.FixEnv().exec { success -> | ||||
|                             dialog.dismiss() | ||||
|                         }.exec() | ||||
|                             context.toast( | ||||
|                                 if (success) R.string.reboot_delay_toast else R.string.setup_fail, | ||||
|                                 Toast.LENGTH_LONG | ||||
|                             ) | ||||
|                             if (success) | ||||
|                                 UiThreadHandler.handler.postDelayed(5000) { reboot() } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ package com.topjohnwu.magisk.dialog | ||||
| import com.topjohnwu.magisk.core.AppContext | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.R | ||||
| import com.topjohnwu.magisk.core.di.ServiceLocator | ||||
| import com.topjohnwu.magisk.core.download.DownloadEngine | ||||
| import com.topjohnwu.magisk.core.download.Subject | ||||
| import com.topjohnwu.magisk.view.MagiskDialog | ||||
| @@ -11,15 +10,10 @@ import java.io.File | ||||
|  | ||||
| class ManagerInstallDialog : MarkDownDialog() { | ||||
|  | ||||
|     private val svc get() = ServiceLocator.networkService | ||||
|  | ||||
|     override suspend fun getMarkdownText(): String { | ||||
|         val text = svc.fetchString(Info.remote.magisk.note) | ||||
|         val text = Info.update.note | ||||
|         // Cache the changelog | ||||
|         AppContext.cacheDir.listFiles { _, name -> name.endsWith(".md") }.orEmpty().forEach { | ||||
|             it.delete() | ||||
|         } | ||||
|         File(AppContext.cacheDir, "${Info.remote.magisk.versionCode}.md").writeText(text) | ||||
|         File(AppContext.cacheDir, "${Info.update.versionCode}.md").writeText(text) | ||||
|         return text | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,15 +1,17 @@ | ||||
| package com.topjohnwu.magisk.dialog | ||||
|  | ||||
| import android.app.ProgressDialog | ||||
| import android.content.Context | ||||
| import android.widget.Toast | ||||
| import androidx.lifecycle.lifecycleScope | ||||
| import com.topjohnwu.magisk.arch.NavigationActivity | ||||
| import com.topjohnwu.magisk.arch.UIActivity | ||||
| import com.topjohnwu.magisk.core.R | ||||
| import com.topjohnwu.magisk.core.ktx.toast | ||||
| import com.topjohnwu.magisk.core.tasks.MagiskInstaller | ||||
| import com.topjohnwu.magisk.events.DialogBuilder | ||||
| import com.topjohnwu.magisk.ui.flash.FlashFragment | ||||
| import com.topjohnwu.magisk.view.MagiskDialog | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import kotlinx.coroutines.launch | ||||
|  | ||||
| class UninstallDialog : DialogBuilder { | ||||
|  | ||||
| @@ -19,7 +21,7 @@ class UninstallDialog : DialogBuilder { | ||||
|             setMessage(R.string.uninstall_magisk_msg) | ||||
|             setButton(MagiskDialog.ButtonType.POSITIVE) { | ||||
|                 text = R.string.restore_img | ||||
|                 onClick { restore(dialog.context) } | ||||
|                 onClick { restore(dialog.activity) } | ||||
|             } | ||||
|             setButton(MagiskDialog.ButtonType.NEGATIVE) { | ||||
|                 text = R.string.complete_uninstall | ||||
| @@ -29,18 +31,20 @@ class UninstallDialog : DialogBuilder { | ||||
|     } | ||||
|  | ||||
|     @Suppress("DEPRECATION") | ||||
|     private fun restore(context: Context) { | ||||
|         val dialog = ProgressDialog(context).apply { | ||||
|             setMessage(context.getString(R.string.restore_img_msg)) | ||||
|     private fun restore(activity: UIActivity<*>) { | ||||
|         val dialog = ProgressDialog(activity).apply { | ||||
|             setMessage(activity.getString(R.string.restore_img_msg)) | ||||
|             show() | ||||
|         } | ||||
|  | ||||
|         Shell.cmd("restore_imgs").submit { result -> | ||||
|             dialog.dismiss() | ||||
|             if (result.isSuccess) { | ||||
|                 context.toast(R.string.restore_done, Toast.LENGTH_SHORT) | ||||
|             } else { | ||||
|                 context.toast(R.string.restore_fail, Toast.LENGTH_LONG) | ||||
|         activity.lifecycleScope.launch { | ||||
|             MagiskInstaller.Restore().exec { success -> | ||||
|                 dialog.dismiss() | ||||
|                 if (success) { | ||||
|                     activity.toast(R.string.restore_done, Toast.LENGTH_SHORT) | ||||
|                 } else { | ||||
|                     activity.toast(R.string.restore_fail, Toast.LENGTH_LONG) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -71,7 +71,7 @@ class FlashFragment : BaseFragment<FragmentFlashMd2Binding>(), MenuProvider { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         defaultOrientation = activity?.requestedOrientation ?: -1 | ||||
|         activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ||||
|         activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED | ||||
|         if (savedInstanceState == null) { | ||||
|             viewModel.startFlashing() | ||||
|         } | ||||
|   | ||||
| @@ -17,6 +17,8 @@ import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.download.DownloadEngine | ||||
| import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding | ||||
| import com.topjohnwu.magisk.core.R as CoreR | ||||
| import androidx.navigation.findNavController | ||||
| import com.topjohnwu.magisk.arch.NavigationActivity | ||||
|  | ||||
| class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider { | ||||
|  | ||||
| @@ -68,7 +70,13 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider { | ||||
|     override fun onMenuItemSelected(item: MenuItem): Boolean { | ||||
|         when (item.itemId) { | ||||
|             R.id.action_settings -> | ||||
|                 HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate() | ||||
|                 activity?.let { | ||||
|                     NavigationActivity.navigate( | ||||
|                         HomeFragmentDirections.actionHomeFragmentToSettingsFragment(), | ||||
|                         it.findNavController(R.id.main_nav_host), | ||||
|                         it.contentResolver, | ||||
|                     ) | ||||
|                 } | ||||
|             R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() } | ||||
|             else -> return super.onOptionsItemSelected(item) | ||||
|         } | ||||
|   | ||||
| @@ -91,16 +91,15 @@ class HomeViewModel( | ||||
|  | ||||
|     override suspend fun doLoadWork() { | ||||
|         appState = State.LOADING | ||||
|         Info.getRemote(svc)?.apply { | ||||
|         Info.fetchUpdate(svc)?.apply { | ||||
|             appState = when { | ||||
|                 BuildConfig.APP_VERSION_CODE < magisk.versionCode -> State.OUTDATED | ||||
|                 BuildConfig.APP_VERSION_CODE < versionCode -> State.OUTDATED | ||||
|                 else -> State.UP_TO_DATE | ||||
|             } | ||||
|  | ||||
|             val isDebug = Config.updateChannel == Config.Value.DEBUG_CHANNEL | ||||
|             managerRemoteVersion = | ||||
|                 ("${magisk.version} (${magisk.versionCode})" + | ||||
|                     if (isDebug) " (D)" else "").asText() | ||||
|                 ("$version (${versionCode})" + if (isDebug) " (D)" else "").asText() | ||||
|         } ?: run { | ||||
|             appState = State.INVALID | ||||
|             managerRemoteVersion = CoreR.string.not_available.asText() | ||||
|   | ||||
| @@ -41,7 +41,7 @@ object RebootMenu { | ||||
|             activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true) { | ||||
|             menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true | ||||
|         } | ||||
|         if (Const.Version.isCanary()) { | ||||
|         if (Const.Version.atLeast_28_0()) { | ||||
|             menu.menu.findItem(R.id.action_reboot_safe_mode).isChecked = Config.bootloop >= 2 | ||||
|         } else { | ||||
|             menu.menu.findItem(R.id.action_reboot_safe_mode).isVisible = false | ||||
|   | ||||
| @@ -14,9 +14,8 @@ import com.topjohnwu.magisk.BR | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.arch.BaseViewModel | ||||
| import com.topjohnwu.magisk.core.AppContext | ||||
| import com.topjohnwu.magisk.core.BuildConfig | ||||
| import com.topjohnwu.magisk.core.BuildConfig.APP_VERSION_CODE | ||||
| import com.topjohnwu.magisk.core.Config | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.base.ContentResultCallback | ||||
| import com.topjohnwu.magisk.core.ktx.toast | ||||
| @@ -70,17 +69,16 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() | ||||
|     init { | ||||
|         viewModelScope.launch(Dispatchers.IO) { | ||||
|             try { | ||||
|                 val file = File(AppContext.cacheDir, "${BuildConfig.APP_VERSION_CODE}.md") | ||||
|                 val text = when { | ||||
|                     file.exists() -> file.readText() | ||||
|                     Const.Url.CHANGELOG_URL.isEmpty() -> "" | ||||
|                 val noteFile = File(AppContext.cacheDir, "${APP_VERSION_CODE}.md") | ||||
|                 val noteText = when { | ||||
|                     noteFile.exists() -> noteFile.readText() | ||||
|                     else -> { | ||||
|                         val str = svc.fetchString(Const.Url.CHANGELOG_URL) | ||||
|                         file.writeText(str) | ||||
|                         str | ||||
|                         val note = svc.fetchUpdate(APP_VERSION_CODE).note | ||||
|                         noteFile.writeText(note) | ||||
|                         note | ||||
|                     } | ||||
|                 } | ||||
|                 val spanned = markwon.toMarkdown(text) | ||||
|                 val spanned = markwon.toMarkdown(noteText) | ||||
|                 withContext(Dispatchers.Main) { | ||||
|                     notes = spanned | ||||
|                 } | ||||
| @@ -100,13 +98,15 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() | ||||
|     } | ||||
|  | ||||
|     override fun onSaveState(state: Bundle) { | ||||
|         state.putParcelable(INSTALL_STATE_KEY, InstallState( | ||||
|             methodId, | ||||
|             step, | ||||
|             Config.keepVerity, | ||||
|             Config.keepEnc, | ||||
|             Config.recovery | ||||
|         )) | ||||
|         state.putParcelable( | ||||
|             INSTALL_STATE_KEY, InstallState( | ||||
|                 methodId, | ||||
|                 step, | ||||
|                 Config.keepVerity, | ||||
|                 Config.keepEnc, | ||||
|                 Config.recovery | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     override fun onRestoreState(state: Bundle) { | ||||
| @@ -124,6 +124,7 @@ class InstallViewModel(svc: NetworkService, markwon: Markwon) : BaseViewModel() | ||||
|         override fun onActivityLaunch() { | ||||
|             AppContext.toast(CoreR.string.patch_file_msg, Toast.LENGTH_LONG) | ||||
|         } | ||||
|  | ||||
|         override fun onActivityResult(result: Uri) { | ||||
|             uri.value = result | ||||
|         } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import android.view.Menu | ||||
| import android.view.MenuInflater | ||||
| import android.view.MenuItem | ||||
| import android.view.View | ||||
| import android.widget.HorizontalScrollView | ||||
| import androidx.core.view.MenuProvider | ||||
| import androidx.core.view.isVisible | ||||
| import com.topjohnwu.magisk.R | ||||
| @@ -12,6 +13,7 @@ import com.topjohnwu.magisk.arch.BaseFragment | ||||
| import com.topjohnwu.magisk.arch.viewModel | ||||
| import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding | ||||
| import com.topjohnwu.magisk.ui.MainActivity | ||||
| import com.topjohnwu.magisk.utils.AccessibilityUtils | ||||
| import com.topjohnwu.magisk.utils.MotionRevealHelper | ||||
| import rikka.recyclerview.addEdgeSpacing | ||||
| import rikka.recyclerview.addItemSpacing | ||||
| @@ -56,6 +58,11 @@ class LogFragment : BaseFragment<FragmentLogMd2Binding>(), MenuProvider { | ||||
|             addItemSpacing(R.dimen.l1, R.dimen.l_50, R.dimen.l1) | ||||
|             fixEdgeEffect() | ||||
|         } | ||||
|  | ||||
|         if (!AccessibilityUtils.isAnimationEnabled(requireContext().contentResolver)) { | ||||
|             val scrollView = view.findViewById<HorizontalScrollView>(R.id.log_scroll_magisk) | ||||
|             scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,108 @@ | ||||
| package com.topjohnwu.magisk.ui.module | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.content.pm.ActivityInfo | ||||
| import android.os.Bundle | ||||
| import android.view.KeyEvent | ||||
| import android.view.Menu | ||||
| import android.view.MenuInflater | ||||
| import android.view.MenuItem | ||||
| import android.view.View | ||||
| import android.view.ViewTreeObserver | ||||
| import android.widget.Toast | ||||
| import androidx.core.view.MenuProvider | ||||
| import androidx.core.view.isVisible | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.arch.BaseFragment | ||||
| import com.topjohnwu.magisk.arch.viewModel | ||||
| import com.topjohnwu.magisk.core.ktx.toast | ||||
| import com.topjohnwu.magisk.databinding.FragmentActionMd2Binding | ||||
| import com.topjohnwu.magisk.core.R as CoreR | ||||
|  | ||||
| class ActionFragment : BaseFragment<FragmentActionMd2Binding>(), MenuProvider { | ||||
|  | ||||
|     override val layoutRes = R.layout.fragment_action_md2 | ||||
|     override val viewModel by viewModel<ActionViewModel>() | ||||
|     override val snackbarView: View get() = binding.snackbarContainer | ||||
|  | ||||
|     private var defaultOrientation = -1 | ||||
|  | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         viewModel.args = ActionFragmentArgs.fromBundle(requireArguments()) | ||||
|     } | ||||
|  | ||||
|     override fun onStart() { | ||||
|         super.onStart() | ||||
|         activity?.setTitle(viewModel.args.name) | ||||
|         binding.closeBtn.setOnClickListener { | ||||
|             activity?.onBackPressed() | ||||
|         } | ||||
|  | ||||
|         viewModel.state.observe(this) { | ||||
|             if (it != ActionViewModel.State.RUNNING) { | ||||
|                 binding.closeBtn.apply { | ||||
|                     if (!this.isVisible) this.show() | ||||
|                     if (!this.isFocused) this.requestFocus() | ||||
|                 } | ||||
|             } | ||||
|             if (it != ActionViewModel.State.SUCCESS) return@observe | ||||
|             view?.viewTreeObserver?.addOnWindowFocusChangeListener( | ||||
|                 object : ViewTreeObserver.OnWindowFocusChangeListener { | ||||
|                     override fun onWindowFocusChanged(hasFocus: Boolean) { | ||||
|                         if (hasFocus) return | ||||
|                         view?.viewTreeObserver?.removeOnWindowFocusChangeListener(this) | ||||
|                         view?.context?.apply { | ||||
|                             toast( | ||||
|                                 getString(CoreR.string.done_action, viewModel.args.name), | ||||
|                                 Toast.LENGTH_SHORT | ||||
|                             ) | ||||
|                         } | ||||
|                         viewModel.back() | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onCreateMenu(menu: Menu, inflater: MenuInflater) { | ||||
|         inflater.inflate(R.menu.menu_flash, menu) | ||||
|     } | ||||
|  | ||||
|     override fun onMenuItemSelected(item: MenuItem): Boolean { | ||||
|         return viewModel.onMenuItemClicked(item) | ||||
|     } | ||||
|  | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|         super.onViewCreated(view, savedInstanceState) | ||||
|  | ||||
|         defaultOrientation = activity?.requestedOrientation ?: -1 | ||||
|         activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED | ||||
|         if (savedInstanceState == null) { | ||||
|             viewModel.startRunAction() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @SuppressLint("WrongConstant") | ||||
|     override fun onDestroyView() { | ||||
|         if (defaultOrientation != -1) { | ||||
|             activity?.requestedOrientation = defaultOrientation | ||||
|         } | ||||
|         super.onDestroyView() | ||||
|     } | ||||
|  | ||||
|     override fun onKeyEvent(event: KeyEvent): Boolean { | ||||
|         return when (event.keyCode) { | ||||
|             KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> true | ||||
|  | ||||
|             else -> false | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     override fun onBackPressed(): Boolean { | ||||
|         if (viewModel.state.value == ActionViewModel.State.RUNNING) return true | ||||
|         return super.onBackPressed() | ||||
|     } | ||||
|  | ||||
|     override fun onPreBind(binding: FragmentActionMd2Binding) = Unit | ||||
| } | ||||
| @@ -0,0 +1,88 @@ | ||||
| package com.topjohnwu.magisk.ui.module | ||||
|  | ||||
| import android.view.MenuItem | ||||
| import androidx.databinding.ObservableArrayList | ||||
| import androidx.lifecycle.LiveData | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.arch.BaseViewModel | ||||
| import com.topjohnwu.magisk.core.ktx.synchronized | ||||
| import com.topjohnwu.magisk.core.ktx.timeFormatStandard | ||||
| import com.topjohnwu.magisk.core.ktx.toTime | ||||
| import com.topjohnwu.magisk.core.utils.MediaStoreUtils | ||||
| import com.topjohnwu.magisk.core.utils.MediaStoreUtils.outputStream | ||||
| import com.topjohnwu.magisk.events.SnackbarEvent | ||||
| import com.topjohnwu.magisk.ui.flash.ConsoleItem | ||||
| import com.topjohnwu.superuser.CallbackList | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import timber.log.Timber | ||||
| import java.io.IOException | ||||
|  | ||||
| class ActionViewModel : BaseViewModel() { | ||||
|  | ||||
|     enum class State { | ||||
|         RUNNING, SUCCESS, FAILED | ||||
|     } | ||||
|  | ||||
|     private val _state = MutableLiveData(State.RUNNING) | ||||
|     val state: LiveData<State> get() = _state | ||||
|  | ||||
|     val items = ObservableArrayList<ConsoleItem>() | ||||
|     lateinit var args: ActionFragmentArgs | ||||
|  | ||||
|     private val logItems = mutableListOf<String>().synchronized() | ||||
|     private val outItems = object : CallbackList<String>() { | ||||
|         override fun onAddElement(e: String?) { | ||||
|             e ?: return | ||||
|             items.add(ConsoleItem(e)) | ||||
|             logItems.add(e) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun startRunAction() = viewModelScope.launch { | ||||
|         onResult(withContext(Dispatchers.IO) { | ||||
|             try { | ||||
|                 Shell.cmd("run_action \'${args.id}\'") | ||||
|                     .to(outItems, logItems) | ||||
|                     .exec().isSuccess | ||||
|             } catch (e: IOException) { | ||||
|                 Timber.e(e) | ||||
|                 false | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     private fun onResult(success: Boolean) { | ||||
|         _state.value = if (success) State.SUCCESS else State.FAILED | ||||
|     } | ||||
|  | ||||
|     fun onMenuItemClicked(item: MenuItem): Boolean { | ||||
|         when (item.itemId) { | ||||
|             R.id.action_save -> savePressed() | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
|  | ||||
|     private fun savePressed() = withExternalRW { | ||||
|         viewModelScope.launch(Dispatchers.IO) { | ||||
|             val name = "%s_action_log_%s.log".format( | ||||
|                 args.name, | ||||
|                 System.currentTimeMillis().toTime(timeFormatStandard) | ||||
|             ) | ||||
|             val file = MediaStoreUtils.getFile(name) | ||||
|             file.uri.outputStream().bufferedWriter().use { writer -> | ||||
|                 synchronized(logItems) { | ||||
|                     logItems.forEach { | ||||
|                         writer.write(it) | ||||
|                         writer.newLine() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             SnackbarEvent(file.toString()).publish() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -25,6 +25,7 @@ class LocalModuleRvItem( | ||||
|     override val layoutRes = R.layout.item_module_md2 | ||||
|  | ||||
|     val showNotice: Boolean | ||||
|     val showAction: Boolean | ||||
|     val noticeText: TextHolder | ||||
|  | ||||
|     init { | ||||
| @@ -35,6 +36,7 @@ class LocalModuleRvItem( | ||||
|         showNotice = zygiskUnloaded || | ||||
|             (Info.isZygiskEnabled && isRiru) || | ||||
|             (!Info.isZygiskEnabled && isZygisk) | ||||
|         showAction = item.hasAction && !showNotice | ||||
|         noticeText = | ||||
|             when { | ||||
|                 zygiskUnloaded -> CoreR.string.zygisk_module_unloaded.asText() | ||||
|   | ||||
| @@ -4,8 +4,10 @@ import android.net.Uri | ||||
| import androidx.databinding.Bindable | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| import com.topjohnwu.magisk.BR | ||||
| import com.topjohnwu.magisk.MainDirections | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.arch.AsyncLoadViewModel | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.base.ContentResultCallback | ||||
| import com.topjohnwu.magisk.core.model.module.LocalModule | ||||
| @@ -96,6 +98,10 @@ class ModuleViewModel : AsyncLoadViewModel() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun runAction(id: String, name: String) { | ||||
|         MainDirections.actionActionFragment(id, name).navigate() | ||||
|     } | ||||
|  | ||||
|     companion object { | ||||
|         private val uri = MutableLiveData<Uri?>() | ||||
|     } | ||||
|   | ||||
| @@ -147,19 +147,11 @@ object UpdateChannel : BaseSettingsItem.Selector() { | ||||
|         get() = Config.updateChannel | ||||
|         set(value) { | ||||
|             Config.updateChannel = value | ||||
|             Info.remote = Info.EMPTY_REMOTE | ||||
|             Info.resetUpdate() | ||||
|         } | ||||
|  | ||||
|     override val title = CoreR.string.settings_update_channel_title.asText() | ||||
|  | ||||
|     override val entryRes = CoreR.array.update_channel | ||||
|     override fun entries(res: Resources): Array<String> { | ||||
|         return super.entries(res).let { | ||||
|             if (!Const.APP_IS_CANARY && !BuildConfig.DEBUG) | ||||
|                 it.copyOfRange(0, Config.Value.CANARY_CHANNEL) | ||||
|             else it | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| object UpdateChannelUrl : BaseSettingsItem.Input() { | ||||
| @@ -169,7 +161,7 @@ object UpdateChannelUrl : BaseSettingsItem.Input() { | ||||
|         get() = Config.customChannelUrl | ||||
|         set(value) { | ||||
|             Config.customChannelUrl = value | ||||
|             Info.remote = Info.EMPTY_REMOTE | ||||
|             Info.resetUpdate() | ||||
|             notifyPropertyChanged(BR.description) | ||||
|         } | ||||
|  | ||||
| @@ -330,6 +322,12 @@ object Reauthenticate : BaseSettingsItem.Toggle() { | ||||
|     override var value by Config::suReAuth | ||||
|  | ||||
|     override fun refresh() { | ||||
|         isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Info.showSuperUser | ||||
|         isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O | ||||
|     } | ||||
| } | ||||
|  | ||||
| object Restrict : BaseSettingsItem.Toggle() { | ||||
|     override val title = CoreR.string.settings_su_restrict_title.asText() | ||||
|     override val description = CoreR.string.settings_su_restrict_summary.asText() | ||||
|     override var value by Config::suRestrict | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import com.topjohnwu.magisk.BR | ||||
| import com.topjohnwu.magisk.arch.BaseViewModel | ||||
| import com.topjohnwu.magisk.core.AppContext | ||||
| import com.topjohnwu.magisk.core.BuildConfig | ||||
| import com.topjohnwu.magisk.core.Config | ||||
| import com.topjohnwu.magisk.core.Const | ||||
| import com.topjohnwu.magisk.core.Info | ||||
| import com.topjohnwu.magisk.core.R | ||||
| @@ -82,6 +83,9 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler { | ||||
|                 // Can hide overlay windows on 12.0+ | ||||
|                 list.remove(Tapjack) | ||||
|             } | ||||
|             if (Const.Version.atLeast_30_1()) { | ||||
|                 list.add(Restrict) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return list | ||||
| @@ -92,7 +96,7 @@ class SettingsViewModel : BaseViewModel(), BaseSettingsItem.Handler { | ||||
|             DownloadPath -> withExternalRW(doAction) | ||||
|             UpdateChecker -> withPostNotificationPermission(doAction) | ||||
|             Authentication -> AuthEvent(doAction).publish() | ||||
|             Hide, Restore -> withInstallPermission(doAction) | ||||
|             AutomaticResponse -> if (Config.suAuth) AuthEvent(doAction).publish() else doAction() | ||||
|             else -> doAction() | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -4,11 +4,13 @@ import android.graphics.drawable.Drawable | ||||
| import androidx.databinding.Bindable | ||||
| import com.topjohnwu.magisk.BR | ||||
| import com.topjohnwu.magisk.R | ||||
| import com.topjohnwu.magisk.core.Config | ||||
| import com.topjohnwu.magisk.core.model.su.SuPolicy | ||||
| import com.topjohnwu.magisk.databinding.DiffItem | ||||
| import com.topjohnwu.magisk.databinding.ItemWrapper | ||||
| import com.topjohnwu.magisk.databinding.ObservableRvItem | ||||
| import com.topjohnwu.magisk.databinding.set | ||||
| import com.topjohnwu.magisk.core.R as CoreR | ||||
|  | ||||
| class PolicyRvItem( | ||||
|     private val viewModel: SuperuserViewModel, | ||||
| @@ -33,14 +35,34 @@ class PolicyRvItem( | ||||
|     var isExpanded = false | ||||
|         set(value) = set(value, field, { field = it }, BR.expanded) | ||||
|  | ||||
|     val showSlider = Config.suRestrict || item.policy == SuPolicy.RESTRICT | ||||
|  | ||||
|     @get:Bindable | ||||
|     var isEnabled | ||||
|         get() = item.policy == SuPolicy.ALLOW | ||||
|         get() = item.policy >= SuPolicy.ALLOW | ||||
|         set(value) = setImpl(value, isEnabled) { | ||||
|             notifyPropertyChanged(BR.enabled) | ||||
|             viewModel.togglePolicy(this, value) | ||||
|             viewModel.updatePolicy(this, if (it) SuPolicy.ALLOW else SuPolicy.DENY) | ||||
|         } | ||||
|  | ||||
|     @get:Bindable | ||||
|     var sliderValue | ||||
|         get() = item.policy | ||||
|         set(value) = setImpl(value, sliderValue) { | ||||
|             notifyPropertyChanged(BR.sliderValue) | ||||
|             notifyPropertyChanged(BR.enabled) | ||||
|             viewModel.updatePolicy(this, it) | ||||
|         } | ||||
|  | ||||
|     val sliderValueToPolicyString: (Float) -> Int = { value -> | ||||
|         when (value.toInt()) { | ||||
|             1 -> CoreR.string.deny | ||||
|             2 -> CoreR.string.restrict | ||||
|             3 -> CoreR.string.grant | ||||
|             else -> CoreR.string.deny | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @get:Bindable | ||||
|     var shouldNotify | ||||
|         get() = item.notification | ||||
|   | ||||
| @@ -78,8 +78,8 @@ class SuperuserViewModel( | ||||
|                             this@SuperuserViewModel, policy, | ||||
|                             info.packageName, | ||||
|                             info.sharedUserId != null, | ||||
|                             info.applicationInfo.loadIcon(pm), | ||||
|                             info.applicationInfo.getLabel(pm) | ||||
|                             info.applicationInfo?.loadIcon(pm) ?: pm.defaultActivityIcon, | ||||
|                             info.applicationInfo?.getLabel(pm) ?: info.packageName | ||||
|                         ) | ||||
|                     } catch (e: PackageManager.NameNotFoundException) { | ||||
|                         null | ||||
| @@ -156,15 +156,16 @@ class SuperuserViewModel( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun togglePolicy(item: PolicyRvItem, enable: Boolean) { | ||||
|     fun updatePolicy(item: PolicyRvItem, policy: Int) { | ||||
|         val items = itemsPolicies.filter { it.item.uid == item.item.uid } | ||||
|         fun updateState() { | ||||
|             viewModelScope.launch { | ||||
|                 val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny | ||||
|                 item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY | ||||
|                 val res = if (policy >= SuPolicy.ALLOW) R.string.su_snack_grant else R.string.su_snack_deny | ||||
|                 item.item.policy = policy | ||||
|                 db.update(item.item) | ||||
|                 items.forEach { | ||||
|                     it.notifyPropertyChanged(BR.enabled) | ||||
|                     it.notifyPropertyChanged(BR.sliderValue) | ||||
|                 } | ||||
|                 SnackbarEvent(res.asText(item.appName)).publish() | ||||
|             } | ||||
|   | ||||
| @@ -111,7 +111,7 @@ class SuRequestViewModel( | ||||
|             // shared UID. We have no way to know where this request comes from. | ||||
|             icon = pm.defaultActivityIcon | ||||
|             title = "[SharedUID] ${info.sharedUserId}" | ||||
|             packageName = info.sharedUserId | ||||
|             packageName = info.sharedUserId.toString() | ||||
|         } else { | ||||
|             val prefix = if (info.sharedUserId == null) "" else "[SharedUID] " | ||||
|             icon = app.loadIcon(pm) | ||||
|   | ||||
| @@ -0,0 +1,14 @@ | ||||
| package com.topjohnwu.magisk.utils | ||||
|  | ||||
| import android.content.ContentResolver | ||||
| import android.provider.Settings | ||||
|  | ||||
| class AccessibilityUtils { | ||||
|     companion object { | ||||
|         fun isAnimationEnabled(cr: ContentResolver): Boolean { | ||||
|             return !(Settings.Global.getFloat(cr, Settings.Global.ANIMATOR_DURATION_SCALE, 1.0f) == 0.0f | ||||
|                 && Settings.Global.getFloat(cr, Settings.Global.TRANSITION_ANIMATION_SCALE, 1.0f) == 0.0f | ||||
|                 && Settings.Global.getFloat(cr, Settings.Global.WINDOW_ANIMATION_SCALE, 1.0f) == 0.0f) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										5
									
								
								app/apk/src/main/res/drawable/ic_action_md2.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/apk/src/main/res/drawable/ic_action_md2.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> | ||||
|  | ||||
|     <path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/> | ||||
|  | ||||
| </vector> | ||||
							
								
								
									
										68
									
								
								app/apk/src/main/res/layout/fragment_action_md2.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/apk/src/main/res/layout/fragment_action_md2.xml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <layout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <data> | ||||
|  | ||||
|         <variable | ||||
|             name="viewModel" | ||||
|             type="com.topjohnwu.magisk.ui.module.ActionViewModel" /> | ||||
|  | ||||
|     </data> | ||||
|  | ||||
|     <androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent"> | ||||
|  | ||||
|         <HorizontalScrollView | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_marginTop="@dimen/internal_action_bar_size" | ||||
|             app:layout_fitsSystemWindowsInsets="top" | ||||
|             tools:layout_marginTop="@dimen/internal_action_bar_size"> | ||||
|  | ||||
|             <androidx.recyclerview.widget.RecyclerView | ||||
|                 android:id="@+id/flash_content" | ||||
|                 scrollToLast="@{true}" | ||||
|                 android:layout_width="wrap_content" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:clipToPadding="false" | ||||
|                 android:orientation="vertical" | ||||
|                 app:fitsSystemWindowsInsets="start|end|bottom" | ||||
|                 app:items="@{viewModel.items}" | ||||
|                 app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||||
|                 app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||||
|                 tools:listitem="@layout/item_console_md2" /> | ||||
|  | ||||
|         </HorizontalScrollView> | ||||
|  | ||||
|         <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton | ||||
|             android:id="@+id/close_btn" | ||||
|             android:visibility="gone" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_gravity="bottom|end" | ||||
|             android:layout_margin="@dimen/l1" | ||||
|             android:layout_marginBottom="@dimen/l1" | ||||
|             android:clickable="true" | ||||
|             android:enabled="true" | ||||
|             android:focusable="true" | ||||
|             android:text="@string/close" | ||||
|             android:textAllCaps="false" | ||||
|             android:textColor="?colorOnPrimary" | ||||
|             android:textStyle="bold" | ||||
|             app:backgroundTint="?colorPrimary" | ||||
|             app:icon="@drawable/ic_close_md2" | ||||
|             app:iconTint="?colorOnPrimary" | ||||
|             app:layout_fitsSystemWindowsInsets="bottom" /> | ||||
|  | ||||
|         <androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
|             android:id="@+id/snackbar_container" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             app:fitsSystemWindowsInsets="top|bottom" /> | ||||
|  | ||||
|     </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
|  | ||||
| </layout> | ||||
| @@ -16,6 +16,7 @@ | ||||
|         android:layout_height="match_parent"> | ||||
|  | ||||
|         <HorizontalScrollView | ||||
|             android:id="@+id/log_scroll_magisk" | ||||
|             gone="@{viewModel.loading}" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user