diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8aa7aaeff..001a071c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,12 @@ jobs: with: java-version: '17' distribution: temurin + - uses: actions/cache@v4 + with: + path: gh-cache/ + key: "prefix-${{ hashFiles('buildscripts/**.sh') }}" + restore-keys: "prefix-" + enableCrossOsArchive: true - name: Build the release artifacts run: | @@ -32,5 +38,8 @@ jobs: ./download.sh > /dev/null ./buildall.sh --arch ${{ matrix.architecture }} mpv ./buildall.sh -n - env: - GRADLE_OPTS: -Xmx4G + + - uses: actions/upload-artifact@v4 + with: + name: aniyomi-mpv-lib-${{ matrix.architecture }} + path: app/build/outputs/aar/app-default-release.aar diff --git a/app/build.gradle b/app/build.gradle index 29ea96c5e..6a80f435e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { minSdkVersion 21 targetSdkVersion 33 - versionCode 33 + versionCode 35 versionName "1.14.n" vectorDrawables.useSupportLibrary = true } diff --git a/app/src/main/assets/cacert.pem b/app/src/main/assets/cacert.pem index 6b93dc34f..f78a6101a 100644 --- a/app/src/main/assets/cacert.pem +++ b/app/src/main/assets/cacert.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Tue May 30 03:12:04 2023 GMT +## Certificate data from Mozilla as of: Mon Mar 11 15:25:27 2024 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.29. -## SHA256: c47475103fb05bb562bbadff0d1e72346b03236154e1448a6ca191b740f83507 +## SHA256: 4d96bd539f4719e9ace493757afbe4a23ee8579de1c97fbebc50bba3c12e8c1e ## @@ -200,27 +200,6 @@ vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- -Security Communication Root CA -============================== ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -HhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMP -U0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw -8yl89f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJDKaVv0uM -DPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9Ms+k2Y7CI9eNqPPYJayX -5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/NQV3Is00qVUarH9oe4kA92819uZKAnDfd -DJZkndwi92SL32HeFZRSFaB9UslLqCHJxrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2 -JChzAgMBAAGjPzA9MB0GA1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYw -DwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vGkl3g -0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfrUj94nK9NrvjVT8+a -mCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5Bw+SUEmK3TGXX8npN6o7WWWXlDLJ -s58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJUJRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ -6rBK+1YWc26sTfcioU+tHXotRSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAi -FL39vmwLAw== ------END CERTIFICATE----- - XRamp Global CA Root ==================== -----BEGIN CERTIFICATE----- @@ -669,39 +648,6 @@ YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r kpeDMdmztcpHWD9f -----END CERTIFICATE----- -Autoridad de Certificacion Firmaprofesional CIF A62634068 -========================================================= ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UEBhMCRVMxQjBA -BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 -MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEyMzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIw -QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB -NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD -Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P -B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY -7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH -ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI -plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX -MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX -LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK -bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU -vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1Ud -EwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNH -DhpkLzCBpgYDVR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp -cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBvACAAZABlACAA -bABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBlAGwAbwBuAGEAIAAwADgAMAAx -ADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx -51tkljYyGOylMnfX40S2wBEqgLk9am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qk -R71kMrv2JYSiJ0L1ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaP -T481PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS3a/DTg4f -Jl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5kSeTy36LssUzAKh3ntLFl -osS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF3dvd6qJ2gHN99ZwExEWN57kci57q13XR -crHedUTnQn3iV2t93Jm8PYMo6oCTjcVMZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoR -saS8I8nkvof/uZS2+F0gStRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTD -KCOM/iczQ0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQBjLMi -6Et8Vcad+qMUu2WFbm5PEn4KPJ2V ------END CERTIFICATE----- - Izenpe.com ========== -----BEGIN CERTIFICATE----- @@ -3222,55 +3168,6 @@ AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR -----END CERTIFICATE----- -E-Tugra Global Root CA RSA v3 -============================= ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- - -E-Tugra Global Root CA ECC v3 -============================= ------BEGIN CERTIFICATE----- -MIICpTCCAiqgAwIBAgIUJkYZdzHhT28oNt45UYbm1JeIIsEwCgYIKoZIzj0EAwMwgYAxCzAJBgNV -BAYTAlRSMQ8wDQYDVQQHEwZBbmthcmExGTAXBgNVBAoTEEUtVHVncmEgRUJHIEEuUy4xHTAbBgNV -BAsTFEUtVHVncmEgVHJ1c3QgQ2VudGVyMSYwJAYDVQQDEx1FLVR1Z3JhIEdsb2JhbCBSb290IENB -IEVDQyB2MzAeFw0yMDAzMTgwOTQ2NThaFw00NTAzMTIwOTQ2NThaMIGAMQswCQYDVQQGEwJUUjEP -MA0GA1UEBxMGQW5rYXJhMRkwFwYDVQQKExBFLVR1Z3JhIEVCRyBBLlMuMR0wGwYDVQQLExRFLVR1 -Z3JhIFRydXN0IENlbnRlcjEmMCQGA1UEAxMdRS1UdWdyYSBHbG9iYWwgUm9vdCBDQSBFQ0MgdjMw -djAQBgcqhkjOPQIBBgUrgQQAIgNiAASOmCm/xxAeJ9urA8woLNheSBkQKczLWYHMjLiSF4mDKpL2 -w6QdTGLVn9agRtwcvHbB40fQWxPa56WzZkjnIZpKT4YKfWzqTTKACrJ6CZtpS5iB4i7sAnCWH/31 -Rs7K3IKjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU/4Ixcj75xGZsrTie0bBRiKWQ -zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO -PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W -Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3 ------END CERTIFICATE----- - Security Communication RootCA3 ============================== -----BEGIN CERTIFICATE----- @@ -3361,3 +3258,324 @@ SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== -----END CERTIFICATE----- + +Sectigo Public Server Authentication Root E46 +============================================= +-----BEGIN CERTIFICATE----- +MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQswCQYDVQQGEwJH +QjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBTZXJ2 +ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5 +WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0 +aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUr +gQQAIgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccCWvkEN/U0 +NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+6xnOQ6OjQjBAMB0GA1Ud +DgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB +/zAKBggqhkjOPQQDAwNnADBkAjAn7qRaqCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RH +lAFWovgzJQxC36oCMB3q4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21U +SAGKcw== +-----END CERTIFICATE----- + +Sectigo Public Server Authentication Root R46 +============================================= +-----BEGIN CERTIFICATE----- +MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBfMQswCQYDVQQG +EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT +ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1 +OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T +ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3 +DQEBAQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDaef0rty2k +1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnzSDBh+oF8HqcIStw+Kxwf +GExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xfiOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMP +FF1bFOdLvt30yNoDN9HWOaEhUTCDsG3XME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vu +ZDCQOc2TZYEhMbUjUDM3IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5Qaz +Yw6A3OASVYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgESJ/A +wSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu+Zd4KKTIRJLpfSYF +plhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt8uaZFURww3y8nDnAtOFr94MlI1fZ +EoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+LHaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW +6aWWrL3DkJiy4Pmi1KZHQ3xtzwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWI +IUkwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c +mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQYKlJfp/imTYp +E0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52gDY9hAaLMyZlbcp+nv4fjFg4 +exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZAFv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M +0ejf5lG5Nkc/kLnHvALcWxxPDkjBJYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI +84HxZmduTILA7rpXDhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9m +pFuiTdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5dHn5Hrwd +Vw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65LvKRRFHQV80MNNVIIb/b +E/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmm +J1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAYQqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL +-----END CERTIFICATE----- + +SSL.com TLS RSA Root CA 2022 +============================ +-----BEGIN CERTIFICATE----- +MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQG +EwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBSU0Eg +Um9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloXDTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMC +VVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJv +b3QgQ0EgMjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u +9nTPL3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OYt6/wNr/y +7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0insS657Lb85/bRi3pZ7Qcac +oOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3PnxEX4MN8/HdIGkWCVDi1FW24IBydm5M +R7d1VVm0U3TZlMZBrViKMWYPHqIbKUBOL9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDG +D6C1vBdOSHtRwvzpXGk3R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEW +TO6Af77wdr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS+YCk +8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYSd66UNHsef8JmAOSq +g+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoGAtUjHBPW6dvbxrB6y3snm/vg1UYk +7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2fgTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1Ud +EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsu +N+7jhHonLs0ZNbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt +hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsMQtfhWsSWTVTN +j8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvfR4iyrT7gJ4eLSYwfqUdYe5by +iB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJDPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjU +o3KUQyxi4U5cMj29TH0ZR6LDSeeWP4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqo +ENjwuSfr98t67wVylrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7Egkaib +MOlqbLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2wAgDHbICi +vRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3qr5nsLFR+jM4uElZI7xc7 +P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sjiMho6/4UIyYOf8kpIEFR3N+2ivEC+5BB0 +9+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= +-----END CERTIFICATE----- + +SSL.com TLS ECC Root CA 2022 +============================ +-----BEGIN CERTIFICATE----- +MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV +UzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBFQ0MgUm9v +dCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMx +GDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3Qg +Q0EgMjAyMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWy +JGYmacCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFNSeR7T5v1 +5wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSJjy+j6CugFFR7 +81a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NWuCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGG +MAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w +7deedWo1dlJF4AIxAMeNb0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5 +Zn6g6g== +-----END CERTIFICATE----- + +Atos TrustedRoot Root CA ECC TLS 2021 +===================================== +-----BEGIN CERTIFICATE----- +MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4wLAYDVQQDDCVB +dG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQswCQYD +VQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3Mg +VHJ1c3RlZFJvb3QgUm9vdCBDQSBFQ0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYT +AkRFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6K +DP/XtXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4AjJn8ZQS +b+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2KCXWfeBmmnoJsmo7jjPX +NtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwW5kp85wxtolrbNa9d+F851F+ +uDrNozZffPc8dz7kUK2o59JZDCaOMDtuCCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGY +a3cpetskz2VAv9LcjBHo9H1/IISpQuQo +-----END CERTIFICATE----- + +Atos TrustedRoot Root CA RSA TLS 2021 +===================================== +-----BEGIN CERTIFICATE----- +MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBMMS4wLAYDVQQD +DCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQsw +CQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0 +b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNV +BAYTAkRFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BB +l01Z4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYvYe+W/CBG +vevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZkmGbzSoXfduP9LVq6hdK +ZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDsGY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt +0xU6kGpn8bRrZtkh68rZYnxGEFzedUlnnkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVK +PNe0OwANwI8f4UDErmwh3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMY +sluMWuPD0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzygeBY +Br3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8ANSbhqRAvNncTFd+ +rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezBc6eUWsuSZIKmAMFwoW4sKeFYV+xa +fJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lIpw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUdEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0G +CSqGSIb3DQEBDAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS +4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPso0UvFJ/1TCpl +Q3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJqM7F78PRreBrAwA0JrRUITWX +AdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuywxfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9G +slA9hGCZcbUztVdF5kJHdWoOsAgMrr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2Vkt +afcxBPTy+av5EzH4AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9q +TFsR0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuYo7Ey7Nmj +1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5dDTedk+SKlOxJTnbPP/l +PqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcEoji2jbDwN/zIIX8/syQbPYtuzE2wFg2W +HYMfRsCbvUOZ58SWLs5fyQ== +-----END CERTIFICATE----- + +TrustAsia Global Root CA G3 +=========================== +-----BEGIN CERTIFICATE----- +MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEMBQAwWjELMAkG +A1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMM +G1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAeFw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEw +MTlaMFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMu +MSQwIgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNST1QY4Sxz +lZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqKAtCWHwDNBSHvBm3dIZwZ +Q0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/V +P68czH5GX6zfZBCK70bwkPAPLfSIC7Epqq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1Ag +dB4SQXMeJNnKziyhWTXAyB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm +9WAPzJMshH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gXzhqc +D0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAvkV34PmVACxmZySYg +WmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msTf9FkPz2ccEblooV7WIQn3MSAPmea +mseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jAuPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCF +TIcQcf+eQxuulXUtgQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj +7zjKsK5Xf/IhMBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E +BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4wM8zAQLpw6o1 +D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2XFNFV1pF1AWZLy4jVe5jaN/T +G3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNj +duMNhXJEIlU/HHzp/LgV6FL6qj6jITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstl +cHboCoWASzY9M/eVVHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys ++TIxxHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1onAX1daBli +2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d7XB4tmBZrOFdRWOPyN9y +aFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2NtjjgKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsAS +ZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFR +JQJ6+N1rZdVtTTDIZbpoFGWsJwt0ivKH +-----END CERTIFICATE----- + +TrustAsia Global Root CA G4 +=========================== +-----BEGIN CERTIFICATE----- +MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMwWjELMAkGA1UE +BhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMMG1Ry +dXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0yMTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJa +MFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQw +IgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi +AATxs8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbwLxYI+hW8 +m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJijYzBhMA8GA1UdEwEB/wQF +MAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mDpm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/ +pDHel4NZg6ZvccveMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AA +bbd+NvBNEU/zy4k6LHiRUKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xk +dUfFVZDj/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== +-----END CERTIFICATE----- + +CommScope Public Trust ECC Root-01 +================================== +-----BEGIN CERTIFICATE----- +MIICHTCCAaOgAwIBAgIUQ3CCd89NXTTxyq4yLzf39H91oJ4wCgYIKoZIzj0EAwMwTjELMAkGA1UE +BhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBUcnVz +dCBFQ0MgUm9vdC0wMTAeFw0yMTA0MjgxNzM1NDNaFw00NjA0MjgxNzM1NDJaME4xCzAJBgNVBAYT +AlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3Qg +RUNDIFJvb3QtMDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARLNumuV16ocNfQj3Rid8NeeqrltqLx +eP0CflfdkXmcbLlSiFS8LwS+uM32ENEp7LXQoMPwiXAZu1FlxUOcw5tjnSCDPgYLpkJEhRGnSjot +6dZoL0hOUysHP029uax3OVejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSOB2LAUN3GGQYARnQE9/OufXVNMDAKBggqhkjOPQQDAwNoADBlAjEAnDPfQeMjqEI2 +Jpc1XHvr20v4qotzVRVcrHgpD7oh2MSg2NED3W3ROT3Ek2DS43KyAjB8xX6I01D1HiXo+k515liW +pDVfG2XqYZpwI7UNo5uSUm9poIyNStDuiw7LR47QjRE= +-----END CERTIFICATE----- + +CommScope Public Trust ECC Root-02 +================================== +-----BEGIN CERTIFICATE----- +MIICHDCCAaOgAwIBAgIUKP2ZYEFHpgE6yhR7H+/5aAiDXX0wCgYIKoZIzj0EAwMwTjELMAkGA1UE +BhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBUcnVz +dCBFQ0MgUm9vdC0wMjAeFw0yMTA0MjgxNzQ0NTRaFw00NjA0MjgxNzQ0NTNaME4xCzAJBgNVBAYT +AlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1c3Qg +RUNDIFJvb3QtMDIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR4MIHoYx7l63FRD/cHB8o5mXxO1Q/M +MDALj2aTPs+9xYa9+bG3tD60B8jzljHz7aRP+KNOjSkVWLjVb3/ubCK1sK9IRQq9qEmUv4RDsNuE +SgMjGWdqb8FuvAY5N9GIIvejQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBTmGHX/72DehKT1RsfeSlXjMjZ59TAKBggqhkjOPQQDAwNnADBkAjAmc0l6tqvmSfR9 +Uj/UQQSugEODZXW5hYA4O9Zv5JOGq4/nich/m35rChJVYaoR4HkCMHfoMXGsPHED1oQmHhS48zs7 +3u1Z/GtMMH9ZzkXpc2AVmkzw5l4lIhVtwodZ0LKOag== +-----END CERTIFICATE----- + +CommScope Public Trust RSA Root-01 +================================== +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUPgNJgXUWdDGOTKvVxZAplsU5EN0wDQYJKoZIhvcNAQELBQAwTjELMAkG +A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBU +cnVzdCBSU0EgUm9vdC0wMTAeFw0yMTA0MjgxNjQ1NTRaFw00NjA0MjgxNjQ1NTNaME4xCzAJBgNV +BAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1 +c3QgUlNBIFJvb3QtMDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwSGWjDR1C45Ft +nYSkYZYSwu3D2iM0GXb26v1VWvZVAVMP8syMl0+5UMuzAURWlv2bKOx7dAvnQmtVzslhsuitQDy6 +uUEKBU8bJoWPQ7VAtYXR1HHcg0Hz9kXHgKKEUJdGzqAMxGBWBB0HW0alDrJLpA6lfO741GIDuZNq +ihS4cPgugkY4Iw50x2tBt9Apo52AsH53k2NC+zSDO3OjWiE260f6GBfZumbCk6SP/F2krfxQapWs +vCQz0b2If4b19bJzKo98rwjyGpg/qYFlP8GMicWWMJoKz/TUyDTtnS+8jTiGU+6Xn6myY5QXjQ/c +Zip8UlF1y5mO6D1cv547KI2DAg+pn3LiLCuz3GaXAEDQpFSOm117RTYm1nJD68/A6g3czhLmfTif +BSeolz7pUcZsBSjBAg/pGG3svZwG1KdJ9FQFa2ww8esD1eo9anbCyxooSU1/ZOD6K9pzg4H/kQO9 +lLvkuI6cMmPNn7togbGEW682v3fuHX/3SZtS7NJ3Wn2RnU3COS3kuoL4b/JOHg9O5j9ZpSPcPYeo +KFgo0fEbNttPxP/hjFtyjMcmAyejOQoBqsCyMWCDIqFPEgkBEa801M/XrmLTBQe0MXXgDW1XT2mH ++VepuhX2yFJtocucH+X8eKg1mp9BFM6ltM6UCBwJrVbl2rZJmkrqYxhTnCwuwwIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUN12mmnQywsL5x6YVEFm4 +5P3luG0wDQYJKoZIhvcNAQELBQADggIBAK+nz97/4L1CjU3lIpbfaOp9TSp90K09FlxD533Ahuh6 +NWPxzIHIxgvoLlI1pKZJkGNRrDSsBTtXAOnTYtPZKdVUvhwQkZyybf5Z/Xn36lbQnmhUQo8mUuJM +3y+Xpi/SB5io82BdS5pYV4jvguX6r2yBS5KPQJqTRlnLX3gWsWc+QgvfKNmwrZggvkN80V4aCRck +jXtdlemrwWCrWxhkgPut4AZ9HcpZuPN4KWfGVh2vtrV0KnahP/t1MJ+UXjulYPPLXAziDslg+Mkf +Foom3ecnf+slpoq9uC02EJqxWE2aaE9gVOX2RhOOiKy8IUISrcZKiX2bwdgt6ZYD9KJ0DLwAHb/W +NyVntHKLr4W96ioDj8z7PEQkguIBpQtZtjSNMgsSDesnwv1B10A8ckYpwIzqug/xBpMu95yo9GA+ +o/E4Xo4TwbM6l4c/ksp4qRyv0LAbJh6+cOx69TOY6lz/KwsETkPdY34Op054A5U+1C0wlREQKC6/ +oAI+/15Z0wUOlV9TRe9rh9VIzRamloPh37MG88EU26fsHItdkJANclHnYfkUyq+Dj7+vsQpZXdxc +1+SWrVtgHdqul7I52Qb1dgAT+GhMIbA1xNxVssnBQVocicCMb3SgazNNtQEo/a2tiRc7ppqEvOuM +6sRxJKi6KfkIsidWNTJf6jn7MZrVGczw +-----END CERTIFICATE----- + +CommScope Public Trust RSA Root-02 +================================== +-----BEGIN CERTIFICATE----- +MIIFbDCCA1SgAwIBAgIUVBa/O345lXGN0aoApYYNK496BU4wDQYJKoZIhvcNAQELBQAwTjELMAkG +A1UEBhMCVVMxEjAQBgNVBAoMCUNvbW1TY29wZTErMCkGA1UEAwwiQ29tbVNjb3BlIFB1YmxpYyBU +cnVzdCBSU0EgUm9vdC0wMjAeFw0yMTA0MjgxNzE2NDNaFw00NjA0MjgxNzE2NDJaME4xCzAJBgNV +BAYTAlVTMRIwEAYDVQQKDAlDb21tU2NvcGUxKzApBgNVBAMMIkNvbW1TY29wZSBQdWJsaWMgVHJ1 +c3QgUlNBIFJvb3QtMDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDh+g77aAASyE3V +rCLENQE7xVTlWXZjpX/rwcRqmL0yjReA61260WI9JSMZNRTpf4mnG2I81lDnNJUDMrG0kyI9p+Kx +7eZ7Ti6Hmw0zdQreqjXnfuU2mKKuJZ6VszKWpCtYHu8//mI0SFHRtI1CrWDaSWqVcN3SAOLMV2MC +e5bdSZdbkk6V0/nLKR8YSvgBKtJjCW4k6YnS5cciTNxzhkcAqg2Ijq6FfUrpuzNPDlJwnZXjfG2W +Wy09X6GDRl224yW4fKcZgBzqZUPckXk2LHR88mcGyYnJ27/aaL8j7dxrrSiDeS/sOKUNNwFnJ5rp +M9kzXzehxfCrPfp4sOcsn/Y+n2Dg70jpkEUeBVF4GiwSLFworA2iI540jwXmojPOEXcT1A6kHkIf +hs1w/tkuFT0du7jyU1fbzMZ0KZwYszZ1OC4PVKH4kh+Jlk+71O6d6Ts2QrUKOyrUZHk2EOH5kQMr +eyBUzQ0ZGshBMjTRsJnhkB4BQDa1t/qp5Xd1pCKBXbCL5CcSD1SIxtuFdOa3wNemKfrb3vOTlycE +VS8KbzfFPROvCgCpLIscgSjX74Yxqa7ybrjKaixUR9gqiC6vwQcQeKwRoi9C8DfF8rhW3Q5iLc4t +Vn5V8qdE9isy9COoR+jUKgF4z2rDN6ieZdIs5fq6M8EGRPbmz6UNp2YINIos8wIDAQABo0IwQDAP +BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUR9DnsSL/nSz12Vdgs7Gx +cJXvYXowDQYJKoZIhvcNAQELBQADggIBAIZpsU0v6Z9PIpNojuQhmaPORVMbc0RTAIFhzTHjCLqB +KCh6krm2qMhDnscTJk3C2OVVnJJdUNjCK9v+5qiXz1I6JMNlZFxHMaNlNRPDk7n3+VGXu6TwYofF +1gbTl4MgqX67tiHCpQ2EAOHyJxCDut0DgdXdaMNmEMjRdrSzbymeAPnCKfWxkxlSaRosTKCL4BWa +MS/TiJVZbuXEs1DIFAhKm4sTg7GkcrI7djNB3NyqpgdvHSQSn8h2vS/ZjvQs7rfSOBAkNlEv41xd +gSGn2rtO/+YHqP65DSdsu3BaVXoT6fEqSWnHX4dXTEN5bTpl6TBcQe7rd6VzEojov32u5cSoHw2O +HG1QAk8mGEPej1WFsQs3BWDJVTkSBKEqz3EWnzZRSb9wO55nnPt7eck5HHisd5FUmrh1CoFSl+Nm +YWvtPjgelmFV4ZFUjO2MJB+ByRCac5krFk5yAD9UG/iNuovnFNa2RU9g7Jauwy8CTl2dlklyALKr +dVwPaFsdZcJfMw8eD/A7hvWwTruc9+olBdytoptLFwG+Qt81IR2tq670v64fG9PiO/yzcnMcmyiQ +iRM9HcEARwmWmjgb3bHPDcK0RPOWlc4yOo80nOAXx17Org3bhzjlP1v9mxnhMUF6cKojawHhRUzN +lM47ni3niAIi9G7oyOzWPPO5std3eqx7 +-----END CERTIFICATE----- + +Telekom Security TLS ECC Root 2020 +================================== +-----BEGIN CERTIFICATE----- +MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQGEwJE +RTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJUZWxl +a29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIwMB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIz +NTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkg +R21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqG +SM49AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/OtdKPD/M1 +2kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDPf8iAC8GXs7s1J8nCG6NC +MEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6fMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P +AQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZ +Mo7k+5Dck2TOrbRBR2Diz6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdU +ga/sf+Rn27iQ7t0l +-----END CERTIFICATE----- + +Telekom Security TLS RSA Root 2023 +================================== +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/app/src/main/java/is/xyz/filepicker/AbstractFilePickerFragment.java b/app/src/main/java/is/xyz/filepicker/AbstractFilePickerFragment.java index 8181e5d68..d4d843b6f 100644 --- a/app/src/main/java/is/xyz/filepicker/AbstractFilePickerFragment.java +++ b/app/src/main/java/is/xyz/filepicker/AbstractFilePickerFragment.java @@ -227,7 +227,7 @@ public void onLoadFinished(@NonNull final Loader> loader, mFiles = data; mAdapter.setList(data); onChangePath(mCurrentPath); - String key = mCurrentPath.toString(); + String key = pathToString(mCurrentPath); if (mPositionMap.containsKey(key)) layoutManager.scrollToPositionWithOffset(mPositionMap.get(key), 0); else @@ -314,6 +314,13 @@ public void onClickHeader(@NonNull View view, @NonNull HeaderViewHolder viewHold goUp(); } + /** + * Returns the directory currently being viewed. + */ + public T getCurrentDir() { + return mCurrentPath; + } + /** * Browses to the parent directory from the current directory. For example, if the current * directory is /foo/bar/, then goUp() will change the current directory to /foo/. It is up to @@ -321,7 +328,7 @@ public void onClickHeader(@NonNull View view, @NonNull HeaderViewHolder viewHold *

*/ public void goUp() { - String key = mCurrentPath.toString(); + String key = pathToString(mCurrentPath); mPositionMap.remove(key); goToDir(getParent(mCurrentPath), false); } @@ -334,7 +341,7 @@ public void goUp() { */ public void onClickDir(@NonNull View view, @NonNull DirViewHolder viewHolder) { if (isDir(viewHolder.file)) { - String key = mCurrentPath.toString(); + String key = pathToString(mCurrentPath); mPositionMap.put(key, layoutManager.findFirstVisibleItemPosition()); goToDir(viewHolder.file, false); } diff --git a/app/src/main/java/is/xyz/filepicker/DividerItemDecoration.java b/app/src/main/java/is/xyz/filepicker/DividerItemDecoration.java index 07af3defa..0173a2b6a 100644 --- a/app/src/main/java/is/xyz/filepicker/DividerItemDecoration.java +++ b/app/src/main/java/is/xyz/filepicker/DividerItemDecoration.java @@ -11,7 +11,7 @@ /** * Basic ItemDecoration which loads a drawable as a divider. */ -public class DividerItemDecoration extends RecyclerView.ItemDecoration { +class DividerItemDecoration extends RecyclerView.ItemDecoration { private final Drawable mDivider; public DividerItemDecoration(Drawable divider) { diff --git a/app/src/main/java/is/xyz/filepicker/DocumentPickerFragment.java b/app/src/main/java/is/xyz/filepicker/DocumentPickerFragment.java index 746983dc2..44b5dc639 100644 --- a/app/src/main/java/is/xyz/filepicker/DocumentPickerFragment.java +++ b/app/src/main/java/is/xyz/filepicker/DocumentPickerFragment.java @@ -6,6 +6,7 @@ import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; +import android.util.Log; import androidx.annotation.NonNull; import androidx.loader.content.AsyncTaskLoader; @@ -18,7 +19,7 @@ /** * An implementation of the picker that operates on a document tree. - * + *
* See also: * - https://developer.android.com/training/data-storage/shared/documents-files * - https://developer.android.com/reference/android/provider/DocumentsContract.Document#MIME_TYPE_DIR @@ -31,10 +32,13 @@ public class DocumentPickerFragment extends AbstractFilePickerFragment { // grab additional info for free afterwards. This is not the case with the documents API so we // have to work around it. final HashMap mLastRead; + // maps document ID of directories to parent path + final HashMap mParents; public DocumentPickerFragment(@NonNull Uri root) { mRoot = root; mLastRead = new HashMap<>(); + mParents = new HashMap<>(); } /** @@ -106,32 +110,26 @@ public String getName(@NonNull Uri path) { return ret; } - @NonNull - @Override - public Uri toUri(@NonNull Uri path) { - return path; - } - @NonNull @Override public Uri getParent(@NonNull Uri from) { - Document doc = mLastRead.get(from); - if (doc != null) { - return doc.parent; - } - // This is not supposed to happen + String docId = DocumentsContract.getDocumentId(from); + Uri parent = mParents.get(docId); + if (parent != null) + return parent; + Log.e(TAG, "getParent() has not seen this document before"); return getRoot(); } @NonNull @Override - public String getFullPath(@NonNull Uri path) { + public String pathToString(@NonNull Uri path) { return path.toString(); } @NonNull @Override - public Uri getPath(@NonNull String path) { + public Uri pathFromString(@NonNull String path) { return Uri.parse(path); } @@ -148,7 +146,7 @@ public Loader> getLoader() { final Uri currentPath = mCurrentPath; // totally makes sense! - final String docId = mCurrentPath.equals(root) ? DocumentsContract.getTreeDocumentId(currentPath) : + final String docId = currentPath.equals(root) ? DocumentsContract.getTreeDocumentId(currentPath) : DocumentsContract.getDocumentId(currentPath); final Uri childUri = DocumentsContract.buildChildDocumentsUriUsingTree(root, docId); @@ -157,7 +155,7 @@ public Loader> getLoader() { DocumentsContract.Document.COLUMN_MIME_TYPE, DocumentsContract.Document.COLUMN_DISPLAY_NAME, }; - return new AsyncTaskLoader>(requireContext()) { + return new AsyncTaskLoader<>(requireContext()) { @Override public List loadInBackground() { final ContentResolver contentResolver = getContext().getContentResolver(); @@ -170,12 +168,19 @@ public List loadInBackground() { final int i1 = c.getColumnIndex(cols[0]), i2 = c.getColumnIndex(cols[1]), i3 = c.getColumnIndex(cols[2]); while (c.moveToNext()) { // TODO later: support FileFilter equivalent here + final String docId = c.getString(i1); + final boolean isDir = c.getString(i2).equals(DocumentsContract.Document.MIME_TYPE_DIR); files.add(new Document( - DocumentsContract.buildDocumentUriUsingTree(root, c.getString(i1)), - currentPath, - c.getString(i2).equals(DocumentsContract.Document.MIME_TYPE_DIR), + DocumentsContract.buildDocumentUriUsingTree(root, docId), + isDir, c.getString(i3) )); + // There is no generic way to get a parent directory for another directory and this + // can't be solved via mLastRead either, since by the time someone asks getParent() + // we're already inside the new directory. Not to mention that this would be insufficient + // when going back multiple times. + if (isDir) + mParents.put(docId, currentPath); } c.close(); @@ -206,13 +211,11 @@ protected void onStartLoading() { */ private static class Document implements Comparable { private final @NonNull Uri uri; - private final @NonNull Uri parent; private final boolean isDir; private final @NonNull String displayName; - private Document(@NonNull Uri uri, @NonNull Uri parent, boolean dir, @NonNull String name) { + private Document(@NonNull Uri uri, boolean dir, @NonNull String name) { this.uri = uri; - this.parent = parent; isDir = dir; displayName = name; } @@ -228,13 +231,11 @@ public boolean equals(Object o) { // Sort directories before files, alphabetically otherwise @Override public int compareTo(Document other) { - if (isDir && !other.isDir) { - return -1; - } else if (other.isDir && !isDir) { - return 1; - } else { - return displayName.compareToIgnoreCase(other.displayName); - } + if (isDir != other.isDir) + return other.isDir ? 1 : -1; + return displayName.compareToIgnoreCase(other.displayName); } } + + private static final String TAG = "mpv"; } diff --git a/app/src/main/java/is/xyz/filepicker/FileItemAdapter.java b/app/src/main/java/is/xyz/filepicker/FileItemAdapter.java index f50c384a3..1756d81db 100644 --- a/app/src/main/java/is/xyz/filepicker/FileItemAdapter.java +++ b/app/src/main/java/is/xyz/filepicker/FileItemAdapter.java @@ -17,7 +17,7 @@ * A simple adapter which also inserts a header item ".." to handle going up to the parent folder. * @param the type which is used, for example a normal java File object. */ -public class FileItemAdapter extends RecyclerView.Adapter { +class FileItemAdapter extends RecyclerView.Adapter { protected final LogicHandler mLogic; protected List mList = null; diff --git a/app/src/main/java/is/xyz/filepicker/FilePickerFragment.java b/app/src/main/java/is/xyz/filepicker/FilePickerFragment.java index ac9be5069..4d05b4b5b 100644 --- a/app/src/main/java/is/xyz/filepicker/FilePickerFragment.java +++ b/app/src/main/java/is/xyz/filepicker/FilePickerFragment.java @@ -214,7 +214,7 @@ public File getParent(@NonNull final File from) { */ @NonNull @Override - public File getPath(@NonNull final String path) { + public File pathFromString(@NonNull final String path) { return new File(path); } @@ -224,7 +224,7 @@ public File getPath(@NonNull final String path) { */ @NonNull @Override - public String getFullPath(@NonNull final File path) { + public String pathToString(@NonNull final File path) { return path.getPath(); } @@ -239,18 +239,6 @@ public File getRoot() { return new File("/"); } - /** - * Convert the path to a URI for the return intent - * - * @param file either a file or directory - * @return a Uri - */ - @NonNull - @Override - public Uri toUri(@NonNull final File file) { - return Uri.fromFile(file); - } - /** * Get a loader that lists the Files in the current path, * and monitors changes. @@ -258,8 +246,7 @@ public Uri toUri(@NonNull final File file) { @NonNull @Override public Loader> getLoader() { - return new AsyncTaskLoader>(getActivity()) { - + return new AsyncTaskLoader<>(requireContext()) { FileObserver fileObserver; @Override @@ -271,7 +258,6 @@ public List loadInBackground() { } ArrayList files = new ArrayList<>(listFiles.length); - for (File f : listFiles) { if (f.isHidden() && !areHiddenItemsShown()) continue; @@ -280,12 +266,7 @@ public List loadInBackground() { files.add(f); } - Collections.sort(files, new Comparator() { - @Override - public int compare(File lhs, File rhs) { - return compareFiles(lhs, rhs); - } - }); + Collections.sort(files, (lhs, rhs) -> compareFiles(lhs, rhs)); return files; } @@ -349,13 +330,10 @@ protected void onReset() { * and 1 if rhs should be placed before lhs */ protected int compareFiles(@NonNull File lhs, @NonNull File rhs) { - if (lhs.isDirectory() && !rhs.isDirectory()) { - return -1; - } else if (rhs.isDirectory() && !lhs.isDirectory()) { - return 1; - } else { - return lhs.getName().compareToIgnoreCase(rhs.getName()); - } + final boolean ldir = lhs.isDirectory(), rdir = rhs.isDirectory(); + if (ldir != rdir) + return rdir ? 1 : -1; + return lhs.getName().compareToIgnoreCase(rhs.getName()); } private static final String TAG = "mpv"; diff --git a/app/src/main/java/is/xyz/filepicker/LogicHandler.java b/app/src/main/java/is/xyz/filepicker/LogicHandler.java index c871253a3..216dfc5c4 100644 --- a/app/src/main/java/is/xyz/filepicker/LogicHandler.java +++ b/app/src/main/java/is/xyz/filepicker/LogicHandler.java @@ -6,7 +6,6 @@ package is.xyz.filepicker; -import android.net.Uri; import androidx.annotation.NonNull; import androidx.loader.content.Loader; import androidx.recyclerview.widget.RecyclerView; @@ -37,38 +36,26 @@ public interface LogicHandler { @NonNull String getName(@NonNull final T path); - /** - * Convert the path to a URI for the return intent - * - * @param path - * @return a Uri - */ - @NonNull - Uri toUri(@NonNull final T path); - /** * Return the path to the parent directory. Should return the root if * from is root. * - * @param from + * @param from path to a directory */ @NonNull T getParent(@NonNull final T from); /** - * @param path - * @return the full path to the file + * Convert path to a string representation. */ @NonNull - String getFullPath(@NonNull final T path); + String pathToString(@NonNull final T path); /** - * Convert the path to the type used. - * - * @param path + * Convert string representation back to a path. */ @NonNull - T getPath(@NonNull final String path); + T pathFromString(@NonNull final String path); /** * Get the root path (lowest allowed). diff --git a/app/src/main/java/is/xyz/mpv/DecimalPickerDialog.kt b/app/src/main/java/is/xyz/mpv/DecimalPickerDialog.kt index 877db8544..71fa2bca7 100644 --- a/app/src/main/java/is/xyz/mpv/DecimalPickerDialog.kt +++ b/app/src/main/java/is/xyz/mpv/DecimalPickerDialog.kt @@ -6,7 +6,7 @@ import android.text.TextWatcher import android.view.LayoutInflater import android.view.View -class DecimalPickerDialog( +internal class DecimalPickerDialog( private val rangeMin: Double, private val rangeMax: Double ) : PickerDialog { private lateinit var binding: DialogDecimalBinding @@ -14,6 +14,11 @@ class DecimalPickerDialog( override fun buildView(layoutInflater: LayoutInflater): View { binding = DialogDecimalBinding.inflate(layoutInflater) + // hide extranous UI parts + arrayOf(binding.label1, binding.label2, binding.rowSecondary).forEach { + it.visibility = View.GONE + } + binding.editText.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} @@ -25,14 +30,12 @@ class DecimalPickerDialog( binding.editText.setText(valueBounded.toString()) } }) - binding.btnMinus.setOnClickListener { - val value = this.number ?: 0.0 - this.number = (value - STEP).coerceIn(rangeMin, rangeMax) - } - binding.btnPlus.setOnClickListener { + val onClick = { delta: Double -> val value = this.number ?: 0.0 - this.number = (value + STEP).coerceIn(rangeMin, rangeMax) + this.number = (value - delta).coerceIn(rangeMin, rangeMax) } + binding.btnMinus.setOnClickListener { onClick(-STEP) } + binding.btnPlus.setOnClickListener { onClick(STEP) } return binding.root } diff --git a/app/src/main/java/is/xyz/mpv/FilePickerActivity.kt b/app/src/main/java/is/xyz/mpv/FilePickerActivity.kt index a4133828d..de6906118 100644 --- a/app/src/main/java/is/xyz/mpv/FilePickerActivity.kt +++ b/app/src/main/java/is/xyz/mpv/FilePickerActivity.kt @@ -1,7 +1,6 @@ package `is`.xyz.mpv import `is`.xyz.filepicker.AbstractFilePickerFragment -import android.Manifest import android.app.UiModeManager import android.content.Intent import android.content.pm.PackageManager @@ -33,7 +32,7 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil private var documentOpener = registerForActivityResult(ActivityResultContracts.OpenDocument()) { it?.let { uri -> - onDocumentPicked(uri, false) + finishWithResult(RESULT_OK, uri.toString()) } } @@ -189,12 +188,12 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil } var defaultPathStr = intent.getStringExtra("default_path") - if (defaultPathStr == null) { + if (defaultPathStr.isNullOrEmpty()) { // TODO: rework or remove this setting defaultPathStr = sharedPrefs.getString("default_file_manager_path", Environment.getExternalStorageDirectory().path) } - val defaultPath = File(defaultPathStr) + val defaultPath = File(defaultPathStr!!) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // check that the preferred path is inside a storage volume @@ -252,6 +251,14 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil Log.v(TAG, "FilePickerActivity: showing document picker at \"$root\"") assert(fragment2 == null) fragment2 = MPVDocumentPickerFragment(root) + + val defaultPathStr = intent.getStringExtra("default_path") + if (!defaultPathStr.isNullOrEmpty()) { + fragment2!!.apply { + goToDir(pathFromString(defaultPathStr)) + } + } + with (supportFragmentManager.beginTransaction()) { setReorderingAllowed(true) add(R.id.fragment_container_view, fragment2!!, null) @@ -290,14 +297,20 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil } private fun finishWithResult(code: Int, path: String? = null) { + val result = Intent() + fragment?.apply { + result.putExtra("last_path", pathToString(currentDir)) + } + fragment2?.apply { + result.putExtra("last_path", pathToString(currentDir)) + } if (path != null) { - val result = Intent() result.putExtra("path", path) - setResult(code, result) - Log.v(TAG, "FilePickerActivity: file picked \"$path\"") + Log.v(TAG, "FilePickerActivity: picked \"$path\"") } else { - setResult(code) + Log.v(TAG, "FilePickerActivity: nothing picked") } + setResult(code, result) finish() } @@ -306,8 +319,9 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil override fun onDirPicked(dir: File) = finishWithResult(RESULT_OK, dir.absolutePath) override fun onDocumentPicked(uri: Uri, isDir: Boolean) { + assert(fragment2 != null) if (!isDir) - finishWithResult(RESULT_OK, uri.toString()) + finishWithResult(RESULT_OK, fragment2!!.pathToString(uri)) } override fun onCancelled() = finishWithResult(RESULT_CANCELED) @@ -355,7 +369,7 @@ class FilePickerActivity : AppCompatActivity(), AbstractFilePickerFragment.OnFil // filter hidden files due to stuff like ".thumbnails" contents.filterNot { it.startsWith('.') }.any() } else { - Utils.MEDIA_EXTENSIONS.contains(file.extension.toLowerCase()) + Utils.MEDIA_EXTENSIONS.contains(file.extension.lowercase()) } } diff --git a/app/src/main/java/is/xyz/mpv/KeyMapping.java b/app/src/main/java/is/xyz/mpv/KeyMapping.java index 8e68927bf..3c360a4a8 100644 --- a/app/src/main/java/is/xyz/mpv/KeyMapping.java +++ b/app/src/main/java/is/xyz/mpv/KeyMapping.java @@ -3,10 +3,9 @@ // Mapping between Android and mpv keycodes (special keys) import android.util.SparseArray; - import static android.view.KeyEvent.*; -public class KeyMapping { +class KeyMapping { public static final SparseArray map = new SparseArray<>(); static { @@ -50,6 +49,8 @@ public class KeyMapping { map.put(KEYCODE_MEDIA_RECORD, "RECORD"); map.put(KEYCODE_CHANNEL_UP, "CHANNEL_UP"); map.put(KEYCODE_CHANNEL_DOWN, "CHANNEL_DOWN"); + map.put(KEYCODE_ZOOM_IN, "ZOOMIN"); + map.put(KEYCODE_ZOOM_OUT, "ZOOMOUT"); map.put(KEYCODE_F1, "F1"); map.put(KEYCODE_F2, "F2"); diff --git a/app/src/main/java/is/xyz/mpv/MPVActivity.kt b/app/src/main/java/is/xyz/mpv/MPVActivity.kt index d48e237aa..a8c34f065 100644 --- a/app/src/main/java/is/xyz/mpv/MPVActivity.kt +++ b/app/src/main/java/is/xyz/mpv/MPVActivity.kt @@ -37,6 +37,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import java.io.File import java.lang.IllegalArgumentException @@ -60,9 +61,10 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse private var activityIsForeground = true private var didResumeBackgroundPlayback = false - private var userIsOperatingSeekbar = false + private var toast: Toast? = null + private var audioManager: AudioManager? = null private var audioFocusRestore: () -> Unit = {} @@ -70,7 +72,6 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse private var mediaSession: MediaSessionCompat? = null private lateinit var binding: PlayerBinding - private lateinit var toast: Toast private lateinit var gestures: TouchGestures // convenience alias @@ -183,8 +184,10 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse private var controlsAtBottom = true private var showMediaTitle = false + private var useTimeRemaining = false private var ignoreAudioFocus = false + private var playlistExitWarning = true private var smoothSeekGesture = false /* * */ @@ -202,6 +205,11 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse topPiPBtn.setOnClickListener { goIntoPiP() } topMenuBtn.setOnClickListener { openTopMenu() } unlockBtn.setOnClickListener { unlockUI() } + playbackDurationTxt.setOnClickListener { + useTimeRemaining = !useTimeRemaining + updatePlaybackPos(psc.position_s) + updatePlaybackDuration(psc.duration_s) + } cycleAudioBtn.setOnLongClickListener { pickAudio(); true } cycleSpeedBtn.setOnLongClickListener { pickSpeed(); true } @@ -226,12 +234,6 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse } } - @SuppressLint("ShowToast") - private fun initMessageToast() { - toast = Toast.makeText(this, "This totally shouldn't be seen", Toast.LENGTH_SHORT) - toast.setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 0) - } - private var playbackHasStarted = false private var onloadCommands = mutableListOf>() @@ -253,13 +255,10 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse // Initialize listeners for the player view initListeners() - // Initialize toast used for short messages - initMessageToast() - gestures = TouchGestures(this) // set up initial UI state - syncSettings() + readSettings() onConfigurationChanged(resources.configuration) run { // edge-to-edge & immersive mode @@ -427,6 +426,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse } else if (!shouldBackground) { player.paused = true } + writeSettings() super.onPause() didResumeBackgroundPlayback = shouldBackground @@ -441,7 +441,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse } } - private fun syncSettings() { + private fun readSettings() { // FIXME: settings should be in their own class completely val prefs = getDefaultSharedPreferences(applicationContext) val getString: (String, Int) -> String = { key, defaultRes -> @@ -462,10 +462,21 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse this.autoRotationMode = getString("auto_rotation", R.string.pref_auto_rotation_default) this.controlsAtBottom = prefs.getBoolean("bottom_controls", true) this.showMediaTitle = prefs.getBoolean("display_media_title", false) + this.useTimeRemaining = prefs.getBoolean("use_time_remaining", false) this.ignoreAudioFocus = prefs.getBoolean("ignore_audio_focus", false) + this.playlistExitWarning = prefs.getBoolean("playlist_exit_warning", true) this.smoothSeekGesture = prefs.getBoolean("seek_gesture_smooth", false) } + private fun writeSettings() { + val prefs = getDefaultSharedPreferences(applicationContext) + + with (prefs.edit()) { + putBoolean("use_time_remaining", useTimeRemaining) + commit() + } + } + override fun onStart() { super.onStart() activityIsStopped = false @@ -490,7 +501,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse // Init controls to be hidden and view fullscreen hideControls() - syncSettings() + readSettings() activityIsForeground = true // stop background service with a delay @@ -514,7 +525,8 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse // UI - private var btnSelected = -1 // dpad navigation + /** dpad navigation */ + private var btnSelected = -1 private var mightWantToToggleControls = false @@ -645,7 +657,6 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse (ev.action == KeyEvent.ACTION_DOWN && interceptKeyDown(ev)) || player.onKey(ev) if (handled) { - showControls() return true } return super.dispatchKeyEvent(ev) @@ -688,52 +699,64 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse return true } + /** + * Returns views eligible for dpad button navigation + */ + private fun dpadButtons(): Sequence { + val groups = arrayOf(binding.controlsButtonGroup, binding.topControls) + return sequence { + for (g in groups) { + for (i in 0 until g.childCount) { + val view = g.getChildAt(i) + if (view.isEnabled && view.isVisible && view.isFocusable) + yield(view) + } + } + } + } + private fun interceptDpad(ev: KeyEvent): Boolean { if (btnSelected == -1) { // UP and DOWN are always grabbed and overriden when (ev.keyCode) { KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN -> { if (ev.action == KeyEvent.ACTION_DOWN) { // activate dpad navigation btnSelected = 0 - updateShowBtnSelected() + updateSelectedDpadButton() + showControls() } return true } } return false } - // only used when dpad navigation is active - val group1 = binding.controlsButtonGroup - val group2 = binding.topControls - val childCountTotal = group1.childCount + group2.childCount + // this runs when dpad nagivation is active: when (ev.keyCode) { KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN -> { if (ev.action == KeyEvent.ACTION_DOWN) { // deactivate dpad navigation btnSelected = -1 - updateShowBtnSelected() + updateSelectedDpadButton() + hideControlsDelayed() } return true } KeyEvent.KEYCODE_DPAD_RIGHT -> { if (ev.action == KeyEvent.ACTION_DOWN) { - btnSelected = (btnSelected + 1) % childCountTotal - updateShowBtnSelected() + btnSelected = (btnSelected + 1) % dpadButtons().count() + updateSelectedDpadButton() } return true } KeyEvent.KEYCODE_DPAD_LEFT -> { if (ev.action == KeyEvent.ACTION_DOWN) { - btnSelected = (childCountTotal + btnSelected - 1) % childCountTotal - updateShowBtnSelected() + val count = dpadButtons().count() + btnSelected = (count + btnSelected - 1) % count + updateSelectedDpadButton() } return true } KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_DPAD_CENTER -> { - val childCount = group1.childCount - val view = if (btnSelected < childCount) - group1.getChildAt(btnSelected) - else - group2.getChildAt(btnSelected - childCount) if (ev.action == KeyEvent.ACTION_UP) { + val view = dpadButtons().elementAtOrNull(btnSelected) // 500ms appears to be the standard if (ev.eventTime - ev.downTime > 500L) view?.performLongClick() @@ -746,27 +769,16 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse return false } - private fun updateShowBtnSelected() { + private fun updateSelectedDpadButton() { val colorFocused = ContextCompat.getColor(this, R.color.tint_btn_bg_focused) val colorNoFocus = ContextCompat.getColor(this, R.color.tint_btn_bg_nofocus) - val group1 = binding.controlsButtonGroup - val group2 = binding.topControls - val childCount = group1.childCount - for (i in 0 until childCount) { - val child = group1.getChildAt(i) + dpadButtons().forEachIndexed { i, child -> if (i == btnSelected) child.setBackgroundColor(colorFocused) else child.setBackgroundColor(colorNoFocus) } - for (i in 0 until group2.childCount) { - val child = group2.getChildAt(i) - if (i == btnSelected - childCount) - child.setBackgroundColor(colorFocused) - else - child.setBackgroundColor(colorNoFocus) - } } private fun interceptKeyDown(event: KeyEvent): Boolean { @@ -804,7 +816,7 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse return showUnlockControls() val notYetPlayed = psc.playlistCount - psc.playlistPos - 1 - if (notYetPlayed <= 0) { + if (notYetPlayed <= 0 || !playlistExitWarning) { finishWithResult(RESULT_OK, true) return } @@ -828,10 +840,14 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse super.onConfigurationChanged(newConfig) val isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE - // TODO: figure out if this should be replaced by WindowManager.getCurrentWindowMetrics() - val dm = DisplayMetrics() - windowManager.defaultDisplay.getRealMetrics(dm) - gestures.setMetrics(dm.widthPixels.toFloat(), dm.heightPixels.toFloat()) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val wm = windowManager.currentWindowMetrics + gestures.setMetrics(wm.bounds.width().toFloat(), wm.bounds.height().toFloat()) + } else { + val dm = DisplayMetrics() + windowManager.defaultDisplay.getRealMetrics(dm) + gestures.setMetrics(dm.widthPixels.toFloat(), dm.heightPixels.toFloat()) + } // Adjust control margins binding.controls.updateLayoutParams { @@ -873,9 +889,11 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse private fun showToast(msg: String, cancel: Boolean = false) { if (cancel) - toast.cancel() - toast.setText(msg) - toast.show() + toast?.cancel() + toast = Toast.makeText(this, msg, Toast.LENGTH_SHORT).apply { + setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 0) + show() + } } // Intent/Uri parsing @@ -1017,7 +1035,8 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse } dialog = with (AlertDialog.Builder(this)) { - setView(impl.buildView(layoutInflater)) + val inflater = LayoutInflater.from(context) + setView(impl.buildView(inflater)) setOnDismissListener { restore() } create() } @@ -1059,7 +1078,8 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse } dialog = with (AlertDialog.Builder(this)) { - setView(impl.buildView(layoutInflater)) + val inflater = LayoutInflater.from(context) + setView(impl.buildView(inflater)) setOnDismissListener { restore() } create() } @@ -1089,7 +1109,6 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse private fun cycleSpeed() { player.cycleSpeed() - updateSpeedButton() } private fun pickSpeed() { @@ -1098,7 +1117,6 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse val restore = pauseForDialog() genericPickerDialog(picker, R.string.title_speed_dialog, "speed") { - updateSpeedButton() restore() } } @@ -1126,7 +1144,9 @@ class MPVActivity : AppCompatActivity(), MPVLib.EventObserver, TouchGesturesObse @LayoutRes layoutRes: Int, buttons: List, hiddenButtons: Set, restoreState: StateRestoreCallback) { lateinit var dialog: AlertDialog - val dialogView = layoutInflater.inflate(layoutRes, null) + + val builder = AlertDialog.Builder(this) + val dialogView = LayoutInflater.from(builder.context).inflate(layoutRes, null) for (button in buttons) { val buttonView = dialogView.findViewById