Compare commits
205 Commits
Author | SHA1 | Date |
---|---|---|
KevinX8 | 8b3f2c5e2e | |
Xinto | 7421ed9462 | |
Jeroen Van Goey | 2ded1e87dd | |
Xinto | dcc3ad9a55 | |
AmineMansoum | 42bb94bd84 | |
AmineMansoum | 0db7ddd83a | |
Xinto | fc9da62528 | |
Vaibhav Sharma | dd9ffeca18 | |
Xinto | a2720c00fd | |
AmineMansoum | a2d6c1b9de | |
Xinto | a170835b1d | |
Tiger Oakes | 9ca0fb789b | |
Xinto | eb28c6163c | |
Tiger Oakes | 368808de0c | |
Tiger Oakes | 4af47e8de0 | |
Tiger Oakes | 9a14d0f10c | |
X1nto | 25f8f800ea | |
X1nto | 851727a1c1 | |
X1nto | 7c1dbb654a | |
X1nto | 7c1392de94 | |
X1nto | 9a31f2b7e3 | |
X1nto | f887ba5d6c | |
X1nto | ec17869600 | |
X1nto | 17f3af190f | |
KevinX8 | 4eae61e7a6 | |
X1nto | 36fc660811 | |
X1nto | f882e05965 | |
X1nto | e7c6d2dc3d | |
X1nto | bbe993edf7 | |
X1nto | 9358447838 | |
KevinX8 | f110dc6a14 | |
Xinto | ad97dab04a | |
X1nto | 6ed72d3b65 | |
X1nto | ccd82f3bce | |
X1nto | e787217bf2 | |
X1nto | 0a87f0d81c | |
X1nto | 0db9faad94 | |
X1nto | a70d731ec4 | |
X1nto | ce18bb0963 | |
X1nto | a101e3a79f | |
X1nto | b1a51f837f | |
X1nto | 02f7fe62ba | |
X1nto | 4a50b27812 | |
X1nto | 465d9f4fdb | |
X1nto | 5ef967f097 | |
X1nto | 7fca85be14 | |
X1nto | 1cd0294801 | |
X1nto | 715eb9cf7a | |
X1nto | 46cb058cab | |
K0RR | 697fbf5ea7 | |
KevinX8 | bd04fddeb0 | |
Tornike Khintibidze | 1e0c187b43 | |
Sahin Deniz | d23f9a7bcf | |
Sahin Deniz | be35498e65 | |
Sahin Deniz | 6445564c85 | |
Sahin Deniz | bb100aeefd | |
Sahin Deniz | 1759c01e98 | |
KevinX8 | 956d8800e9 | |
Tornike Khintibidze | a7ee51a90b | |
cl-ement05 | f98c21ddbe | |
cl-ement05 | d7dfb27ca5 | |
cl-ement05 | b9820468c6 | |
cl-ement05 | 1ea32c75ff | |
X1nto | e6f04a95c8 | |
X1nto | 9346324ec5 | |
Tornike Khintibidze | 4167aa422e | |
A. Taha Baki | 58a0b06b92 | |
A. Taha Baki | b53d9f35cc | |
Tornike Khintibidze | 1150f5800b | |
Nautilus | cd7c05d25a | |
Nautilus | 04d3500bb0 | |
Tornike Khintibidze | 0e93d2091a | |
Nautilus | df56ee8f04 | |
Nautilus | bfd8a0e1ef | |
Nautilus | ecca7dfcba | |
Nautilus | c4ae3887fb | |
Nautilus | f15dcdfaef | |
X1nto | 81f8c7a036 | |
X1nto | c69bccfc99 | |
Tornike Khintibidze | c5a92d51b2 | |
cl-ement05 | 69e0221e82 | |
KevinX8 | 96a69144fe | |
KevinX8 | d8fc45dd39 | |
Tornike Khintibidze | ffdc76a2ca | |
KevinX8 | 88c3a3b12b | |
KevinX8 | 28346f7ae3 | |
KevinX8 | f92061f54d | |
KevinX8 | 822d7bdb78 | |
KevinX8 | a420ab2d79 | |
KevinX8 | 822b900396 | |
KevinX8 | 5a2be52767 | |
KevinX8 | f6231f809c | |
KevinX8 | 24cac1920f | |
KevinX8 | 835f84078b | |
KevinX8 | 00896b77c3 | |
KevinX8 | c40c83d91c | |
KevinX8 | 48632c3d2a | |
KevinX8 | ab6beff186 | |
KevinX8 | ac8f5f6540 | |
KevinX8 | de8c0a7ac7 | |
KevinX8 | 0a7f579063 | |
KevinX8 | 487dc15c2a | |
KevinX8 | afe6ec2f07 | |
KevinX8 | 521495eaa8 | |
KevinX8 | 10c683a66c | |
KevinX8 | 287b169bc9 | |
KevinX8 | b3db04e96e | |
KevinX8 | 48dbb7527b | |
KevinX8 | b6e53bafa4 | |
KevinX8 | a7107e4427 | |
KevinX8 | 91f40ad846 | |
KevinX8 | 2c3cdd0697 | |
KevinX8 | 606664cf39 | |
KevinX8 | 4e776e78ca | |
KevinX8 | bfb094f6b3 | |
KevinX8 | f9c40b12b2 | |
KevinX8 | 4adfb9db3c | |
KevinX8 | f248bdba62 | |
KevinX8 | cd3a419326 | |
KevinX8 | ac09ae3c58 | |
KevinX8 | e43bef60e5 | |
KevinX8 | 0a354f4e3d | |
KevinX8 | 2a1fc505ea | |
KevinX8 | 0e0ed12401 | |
KevinX8 | 431139eb6b | |
KevinX8 | 56a3850b5e | |
KevinX8 | c1cb75afe6 | |
KevinX8 | 3620438efb | |
KevinX8 | 33bfedfdf2 | |
KevinX8 | 01fadc6854 | |
KevinX8 | b0d9611056 | |
KevinX8 | b264c13623 | |
KevinX8 | 5b78a5b4b7 | |
KevinX8 | 1aff587b09 | |
KevinX8 | b89e6d01c8 | |
KevinX8 | de94258b11 | |
KevinX8 | 5f87878d94 | |
KevinX8 | 0969b8d744 | |
KevinX8 | fac5ec0f1b | |
KevinX8 | 5914932728 | |
KevinX8 | b119be8bbf | |
KevinX8 | 24424c1d52 | |
X1nto | 9db254d2f2 | |
X1nto | 9368af2187 | |
X1nto | f1b6519825 | |
X1nto | 96d01de045 | |
X1nto | 5b2c8ce671 | |
X1nto | f0f98b40b6 | |
X1nto | 135454645a | |
X1nto | 8bdeff3c83 | |
X1nto | 33d059d002 | |
X1nto | 82ba0b2615 | |
X1nto | 108ca5ae5d | |
X1nto | 4086b1150b | |
X1nto | b84a2bd930 | |
X1nto | a14eeda825 | |
X1nto | 5d63aaf510 | |
X1nto | a570aae1a1 | |
X1nto | f82dc4eecf | |
X1nto | 29158e7c9a | |
X1nto | ebf9e8031b | |
X1nto | aeaa0d445e | |
X1nto | 4b645ef1c0 | |
X1nto | 98c4b88961 | |
X1nto | 2d30eb60af | |
X1nto | e08f6513cf | |
X1nto | 65f6a1c8d3 | |
X1nto | 7550c37fb6 | |
X1nto | 39b9b981ca | |
X1nto | 4fee34b661 | |
X1nto | c1770e2217 | |
X1nto | af3937e23f | |
X1nto | 7e3edb9cb6 | |
X1nto | b5b577cf8b | |
KevinX8 | 3d8886359c | |
X1nto | c0d2dd6aa9 | |
X1nto | 1a4504de1c | |
X1nto | e2a6f056c9 | |
X1nto | 982fff9989 | |
X1nto | 0673920103 | |
X1nto | e73b56ed4d | |
X1nto | 1c3ab835a6 | |
X1nto | 16e7d1586d | |
X1nto | 140a3ebf4b | |
X1nto | f7d90b7c59 | |
X1nto | 85e2d0c5c2 | |
X1nto | 6c506c989c | |
X1nto | 38860ace07 | |
X1nto | e11d918a82 | |
X1nto | 22d67b51c3 | |
X1nto | 5583674974 | |
X1nto | ad17c6dfec | |
X1nto | ad299ed96d | |
X1nto | 3525ef88c1 | |
X1nto | 1f600430ea | |
X1nto | 18cad94748 | |
X1nto | ae37a1e406 | |
X1nto | 2e3416ef43 | |
X1nto | 4fcfe72a0f | |
X1nto | b7a5741f70 | |
X1nto | ff8177e147 | |
X1nto | c84e795d75 | |
X1nto | 365fb4aede | |
X1nto | 5c7b953fb4 | |
X1nto | fd9defcb5e |
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
name: Bug issue template
|
||||
about: Vanced Manager Bug template
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Please only report your issue here, if all points below are true**
|
||||
- I installed the App from [vancedapp.com](https://vancedapp.com), this github repository or the Vanced Discord server
|
||||
- I am using the latest version
|
||||
- This is an issue in the Vanced Manager app (NOT Youtube Vanced/Music)
|
||||
- This issue keeps re-occurring every time I try
|
||||
- For MIUI users: I disabled MIUI optimisations
|
||||
|
||||
**Phone Specifications:**
|
||||
- Brand:
|
||||
- Operating System:
|
||||
- Android Version:
|
||||
- Vanced Manager Version:
|
||||
|
||||
**Please describe the problem you are having in as much detail as possible:**
|
||||
|
||||
|
||||
**Steps to reproduce:**
|
||||
|
||||
|
||||
**Further details:**
|
|
@ -0,0 +1,41 @@
|
|||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
assignees:
|
||||
- X1nto
|
||||
body:
|
||||
- type: textarea
|
||||
id: device-info
|
||||
attributes:
|
||||
label: Device
|
||||
description: What device are you using?
|
||||
value: |
|
||||
Device:
|
||||
Operating System:
|
||||
Android Version:
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: manager-version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of Vanced Manager are you using?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: Describe the bug and how to reproduce it in as much detail as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: is-manager-bug
|
||||
attributes:
|
||||
label: Additional checks
|
||||
options:
|
||||
- label: I have checked other bug reports and this is not a duplicate.
|
||||
required: true
|
||||
- label: This is a bug in Vanced Manager and NOT YouTube Vanced/YouTube Vanced Music/Vanced microG.
|
||||
required: true
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
|
@ -0,0 +1,29 @@
|
|||
name: Feature Request
|
||||
description: Request a feature
|
||||
title: "[Feature]: "
|
||||
labels: ["enhancement"]
|
||||
assignees:
|
||||
- X1nto
|
||||
body:
|
||||
- type: textarea
|
||||
id: suggestion
|
||||
attributes:
|
||||
label: Suggestion
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: suggestion-relevancy
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: Why is this suggestion relevant?
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
id: is-manager-suggestion
|
||||
attributes:
|
||||
label: Additional checks
|
||||
options:
|
||||
- label: I have checked other feature requests and this is not a duplicate.
|
||||
required: true
|
||||
- label: This is a suggestion for Vanced Manager and NOT YouTube Vanced/YouTube Vanced Music/Vanced microG.
|
||||
required: true
|
|
@ -4,27 +4,28 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
java-version: 11
|
||||
|
||||
- name: Grant rights
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Build project with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Build Debug APK with Gradle
|
||||
- name: Build debug APK with Gradle
|
||||
run: ./gradlew assembleDebug
|
||||
|
||||
- name: Upload Debug
|
||||
|
|
|
@ -6,5 +6,5 @@ app/src/main/java/com/vanced/manager/core/base/DummyJava.java
|
|||
app/build/
|
||||
app/release
|
||||
local.properties
|
||||
/.github/
|
||||
*.iml
|
||||
.vscode/
|
||||
|
|
84
README.md
|
@ -1,34 +1,64 @@
|
|||
Pull requests should be made to the Dev branch as that is the working branch, master is for release code.
|
||||
======
|
||||
For anyone who wants to provide translations please submit them to https://crowdin.com/project/vanced-manager as we also use it for YouTube Vanced. Any issues with translations should be posted there too.
|
||||
======
|
||||
Vanced FAQ (from the faq branch) now available on the playstore! https://play.google.com/store/apps/details?id=com.vanced.faq
|
||||
|
||||
[![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg)](https://github.com/YTVanced/VancedManager/releases/latest) [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg)](https://github.com/YTVanced/VancedManager/releases/latest)
|
||||
# Vanced Manager
|
||||
Hi, when we released Vanced 15.05.54, people were upset because it used the .apks format, which was way harder to install than a traditional .apk file. Even though we wrote clear instructions on how to install the new Vanced build, people still couldn't figure it out.
|
||||
Then we thought, "why don't we make a manager for vanced, which will download, update and uninstall Vanced and MicroG, have an easy and understandable UI and be less than 10mb?" and that's how Vanced Manager was born.
|
||||
<div>
|
||||
|
||||
[![Github All Releases](https://img.shields.io/github/downloads/YTVanced/VancedManager/total.svg?style=for-the-badge)](https://github.com/YTVanced/VancedManager/releases/latest) [![Github All Releases](https://img.shields.io/github/release/YTVanced/VancedManager.svg?style=for-the-badge)](https://github.com/YTVanced/VancedManager/releases/latest)
|
||||
|
||||
</div>
|
||||
|
||||
## Introduction
|
||||
|
||||
Hi, when we released Vanced 15.05.54, people were upset because it used the .apks format, which was burdensome to install than a traditional .apk file. Even though we wrote clear instructions on how to install the new Vanced build, people still couldn't figure it out.
|
||||
|
||||
Then we thought, "why don't we make a manager for vanced, which will download, update and uninstall Vanced and MicroG, have an easy and user-friendly UI and be less than 10mb?" and that's how Vanced Manager was born.
|
||||
|
||||
After 3 months of development, we are finally ready to introduce Vanced Manager to the masses. Vanced manager can easily install and uninstall vanced and microg, has various settings for customisation and better experience. The Manager comes with an easy-to-use interface
|
||||
After 3 months of development, we are finally ready to Introduce [Vanced Manager](https://github.com/YTVanced/VancedManager) to the masses!!
|
||||
|
||||
##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity.
|
||||
## Features
|
||||
|
||||
## Vanced Developers
|
||||
- xfileFIN
|
||||
- KevinX8
|
||||
- Zanezam
|
||||
- Laura Almeida
|
||||
- Vanced manager can easily install and uninstall Vanced and MicroG.
|
||||
- It has various settings for customization and better experience.
|
||||
- The Manager comes with an easy-to-use Interface.
|
||||
|
||||
## Vanced Manager Developers
|
||||
- Xinto (X1nto)
|
||||
- Koopah (ostajic)
|
||||
</br>
|
||||
|
||||
## Contributors
|
||||
- AioiLight
|
||||
- HaliksaR
|
||||
<div class="note">
|
||||
<p><strong>NOTE: </strong>Background download/installation feature is no longer supported due to problems with some ROMs, please <b>DO NOT</b> report issues regarding background activity.</p>
|
||||
</div>
|
||||
|
||||
<!-- ##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity. -->
|
||||
|
||||
## Contributions
|
||||
Pull Requests should be made to the [Dev](https://github.com/YTVanced/VancedManager) Branch as that is the working branch, master is for Release code only.
|
||||
|
||||
For anyone who wants to provide translations please submit them to this [link](https://crowdin.com/project/vanced-manager) as we also use it for YouTube Vanced. Any issues with translations should be posted there too.
|
||||
|
||||
## TODO
|
||||
- [ ] Clean up the ViewModel and DataModel code
|
||||
- [ ] Migrate to Jetpack Compose when it's officially released
|
||||
|
||||
## Building
|
||||
|
||||
<div>
|
||||
|
||||
[![Build](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml/badge.svg?branch=dev)](https://github.com/YTVanced/VancedManager/actions/workflows/debug.yml)
|
||||
|
||||
</div>
|
||||
|
||||
## Using Android Studio
|
||||
Clone the Repository, open it in Android Studio and build the application.
|
||||
|
||||
## Google Advanced Protection Program
|
||||
If you are using this feature on your Google account, you must either disable it or log out from your Google account before installing Youtube Vanced via Vanced Manager.
|
||||
The Google Advanced Protection Program does not allow the installation of apps from unknown sources. These security measures are tied to the protected account and not the device. After the installation, you will be able to log back in or enroll again into the program.
|
||||
|
||||
## Using Command Line
|
||||
#### On Windows:
|
||||
```powershell
|
||||
.\gradlew.bat assembleDebug
|
||||
```
|
||||
#### On Linux/macOS:
|
||||
```bash
|
||||
chmod +x gradlew
|
||||
./gradlew assembleDebug
|
||||
```
|
||||
|
||||
## Credits
|
||||
- topjohnwu for his wonderful [LibSU](https://github.com/topjohnwu/libsu)
|
||||
- aefyr for [SAI](https://github.com/aefyr/SAI), which was an inspiration for our Manager
|
||||
- kittinunf for [Fuel](https://github.com/kittinunf/Fuel) HTTP client
|
||||
- cbeust for [klaxon](https://github.com/cbeust/klaxon) JSON parser
|
||||
|
|
|
@ -10,25 +10,23 @@ plugins {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion(30)
|
||||
compileSdk = 31
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.vanced.manager"
|
||||
minSdkVersion(21)
|
||||
targetSdkVersion(30)
|
||||
versionCode = 251
|
||||
versionName = "2.5.1 (Weed)"
|
||||
minSdk = 21
|
||||
targetSdk = 31
|
||||
versionCode = 262
|
||||
versionName = "2.6.2 (Crimson)"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
buildConfigField("String[]", "MANAGER_LANGUAGES", "{$languages}")
|
||||
buildConfigField("Boolean", "ENABLE_CROWDIN_AUTH", "false")
|
||||
buildConfigField("String", "CROWDIN_HASH", "\"${System.getenv("CROWDIN_HASH")}\"")
|
||||
buildConfigField("String", "CROWDIN_CLIENT_ID", "\"${System.getenv("CROWDIN_CLIENT_ID")}\"")
|
||||
buildConfigField("String", "CROWDIN_CLIENT_SECRET", "\"${System.getenv("CROWDIN_CLIENT_SECRET")}\"")
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
lint {
|
||||
disable("MissingTranslation", "ExtraTranslation")
|
||||
}
|
||||
|
||||
|
@ -45,12 +43,13 @@ android {
|
|||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
//compose = true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude("META-INF/DEPENDENCIES")
|
||||
exclude("META-INF/*.kotlin_module")
|
||||
resources {
|
||||
excludes += "META-INF/DEPENDENCIES"
|
||||
excludes += "META-INF/*.kotlin_module"
|
||||
}
|
||||
}
|
||||
|
||||
// To inline the bytecode built with JVM target 1.8 into
|
||||
|
@ -64,7 +63,6 @@ android {
|
|||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
//useIR = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -87,8 +85,6 @@ val languages: String get() {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
//val composeVersion = "1.0.0-alpha12"
|
||||
implementation(project(":core-presentation"))
|
||||
implementation(project(":core-ui"))
|
||||
|
||||
|
@ -99,60 +95,45 @@ dependencies {
|
|||
implementation(kotlin("reflect"))
|
||||
|
||||
// AndroidX
|
||||
implementation("androidx.appcompat:appcompat:1.2.0")
|
||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
||||
implementation("androidx.browser:browser:1.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||
implementation("androidx.core:core-ktx:1.3.2")
|
||||
implementation("androidx.fragment:fragment-ktx:1.3.0")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.1")
|
||||
implementation("androidx.core:core-ktx:1.6.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.3.6")
|
||||
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
|
||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.3.3")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.3.3")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.3.5")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.3.5")
|
||||
implementation("androidx.preference:preference-ktx:1.1.1")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.work:work-runtime-ktx:2.7.0-rc01")
|
||||
|
||||
|
||||
// Compose
|
||||
// implementation("androidx.compose.ui:ui:$composeVersion")
|
||||
// implementation("androidx.compose.ui:ui-tooling:$composeVersion")
|
||||
// implementation("androidx.compose.foundation:foundation:$composeVersion")
|
||||
// implementation("androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha02")
|
||||
// implementation("androidx.compose.material:material:$composeVersion")
|
||||
// implementation("androidx.compose.material:material-icons-core:$composeVersion")
|
||||
// implementation("androidx.compose.material:material-icons-extended:$composeVersion")
|
||||
// implementation("androidx.compose.runtime:runtime-livedata:$composeVersion")
|
||||
|
||||
// Appearance
|
||||
implementation("com.github.madrapps:pikolo:2.0.1")
|
||||
implementation("com.google.android.material:material:1.3.0")
|
||||
implementation("com.github.madrapps:pikolo:2.0.2")
|
||||
implementation("com.google.android.material:material:1.5.0-alpha04")
|
||||
|
||||
// JSON parser
|
||||
implementation("com.beust:klaxon:5.4")
|
||||
|
||||
// Crowdin
|
||||
implementation("com.github.crowdin.mobile-sdk-android:sdk:1.4.0")
|
||||
|
||||
// Tips
|
||||
implementation("com.github.florent37:viewtooltip:1.2.2")
|
||||
implementation("com.beust:klaxon:5.5")
|
||||
|
||||
// HTTP networking
|
||||
implementation("com.github.kittinunf.fuel:fuel:2.3.0")
|
||||
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.2.3")
|
||||
implementation("com.github.kittinunf.fuel:fuel-json:2.2.3")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
|
||||
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
|
||||
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1")
|
||||
implementation("com.github.kittinunf.fuel:fuel-json:2.3.1")
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.2")
|
||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||
|
||||
// Root permissions
|
||||
implementation("com.github.topjohnwu.libsu:core:3.1.1")
|
||||
implementation("com.github.topjohnwu.libsu:io:3.1.1")
|
||||
val libsuVersion = "3.1.2"
|
||||
implementation("com.github.topjohnwu.libsu:core:$libsuVersion")
|
||||
implementation("com.github.topjohnwu.libsu:io:$libsuVersion")
|
||||
//implementation("com.github.topjohnwu.libsu:busybox:$libsuVersion")
|
||||
|
||||
// Layout
|
||||
implementation("com.google.android:flexbox:2.0.1")
|
||||
implementation("com.google.android.flexbox:flexbox:3.0.0")
|
||||
|
||||
// Firebase
|
||||
implementation("com.google.firebase:firebase-analytics-ktx:18.0.2")
|
||||
implementation("com.google.firebase:firebase-crashlytics:17.3.1")
|
||||
implementation("com.google.firebase:firebase-messaging:21.0.1")
|
||||
implementation("com.google.firebase:firebase-perf:19.1.1")
|
||||
implementation("com.google.firebase:firebase-analytics-ktx:19.0.2")
|
||||
implementation("com.google.firebase:firebase-crashlytics:18.2.3")
|
||||
implementation("com.google.firebase:firebase-messaging:22.0.0")
|
||||
implementation("com.google.firebase:firebase-perf:20.0.3")
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile, LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
|
@ -31,13 +32,13 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:supportsRtl="true">
|
||||
|
||||
<activity
|
||||
android:name=".ui.core.SplashScreenActivity"
|
||||
android:name=".ui.SplashScreenActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/SplashTheme">
|
||||
android:theme="@style/SplashTheme"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -56,9 +57,9 @@
|
|||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:configChanges="layoutDirection|locale|keyboardHidden|orientation|screenSize"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/DarkTheme">
|
||||
android:theme="@style/DarkTheme"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -66,8 +67,7 @@
|
|||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data
|
||||
android:scheme="https"
|
||||
android:host="vancedapp.com"
|
||||
android:pathPrefix="/downloads"/>
|
||||
android:host="api.vancedapp.com"/>
|
||||
|
||||
</intent-filter>
|
||||
|
||||
|
@ -76,8 +76,8 @@
|
|||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
|
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 25 KiB |
|
@ -1,115 +0,0 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.florent37.viewtooltip.ViewTooltip
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ViewAppBinding
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.model.RootDataModel
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
class AppListAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private val viewModel: HomeViewModel,
|
||||
private val tooltip: ViewTooltip?
|
||||
) : RecyclerView.Adapter<AppListAdapter.ListViewHolder>() {
|
||||
|
||||
private val apps = mutableListOf<String>()
|
||||
private val dataModels = mutableListOf<DataModel?>()
|
||||
private val rootDataModels = mutableListOf<RootDataModel?>()
|
||||
private val prefs = getDefaultSharedPreferences(activity)
|
||||
|
||||
private val isRoot = prefs.managerVariant == "root"
|
||||
|
||||
inner class ListViewHolder(private val binding: ViewAppBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
val appCard = binding.appCard
|
||||
fun bind(position: Int) {
|
||||
val dataModel = if (isRoot) rootDataModels[position] else dataModels[position]
|
||||
with(binding) {
|
||||
appName.text = dataModel?.appName
|
||||
dataModel?.buttonTxt?.observe(activity) {
|
||||
appInstallButton.text = it
|
||||
}
|
||||
appInstallButton.setOnClickListener {
|
||||
if (dataModel?.versionName?.value != activity.getString(R.string.unavailable)) {
|
||||
viewModel.openInstallDialog(it, apps[position])
|
||||
} else {
|
||||
return@setOnClickListener
|
||||
}
|
||||
}
|
||||
appUninstall.setOnClickListener {
|
||||
dataModel?.appPkg?.let { it1 -> viewModel.uninstallPackage(it1) }
|
||||
}
|
||||
appLaunch.setOnClickListener {
|
||||
viewModel.launchApp(apps[position], isRoot)
|
||||
}
|
||||
dataModel?.isAppInstalled?.observe(activity) {
|
||||
appUninstall.isVisible = it
|
||||
appLaunch.isVisible = it
|
||||
}
|
||||
dataModel?.versionName?.observe(activity) {
|
||||
appRemoteVersion.text = it
|
||||
}
|
||||
dataModel?.installedVersionName?.observe(activity) {
|
||||
appInstalledVersion.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||
val view = ViewAppBinding.inflate(LayoutInflater.from(activity), parent, false)
|
||||
return ListViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
||||
holder.bind(position)
|
||||
val dataModel = if (isRoot) rootDataModels[position] else dataModels[position]
|
||||
holder.appCard.setOnClickListener {
|
||||
tooltip?.close()
|
||||
AppInfoDialog.newInstance(
|
||||
appName = apps[position],
|
||||
appIcon = dataModel?.appIcon,
|
||||
changelog = dataModel?.changelog?.value
|
||||
).show(activity.supportFragmentManager, "info")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = apps.size
|
||||
|
||||
init {
|
||||
|
||||
if (prefs.enableVanced) {
|
||||
if (isRoot) {
|
||||
rootDataModels.add(viewModel.vancedRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.vancedModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.vanced))
|
||||
}
|
||||
|
||||
if (prefs.enableMusic) {
|
||||
if (isRoot) {
|
||||
rootDataModels.add(viewModel.musicRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.musicModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.music))
|
||||
}
|
||||
|
||||
if (!isRoot) {
|
||||
dataModels.add(viewModel.microgModel.value)
|
||||
apps.add(activity.getString(R.string.microg))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ViewAppExpandableBinding
|
||||
import com.vanced.manager.model.ButtonTag
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.dialogs.AppUninstallDialog
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.*
|
||||
|
||||
class ExpandableAppListAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
private val viewModel: HomeViewModel
|
||||
) : RecyclerView.Adapter<ExpandableAppListAdapter.ListViewHolder>() {
|
||||
|
||||
private val apps = mutableListOf<String>()
|
||||
private val dataModels = mutableListOf<DataModel?>()
|
||||
private val prefs = getDefaultSharedPreferences(activity)
|
||||
|
||||
private val isRoot = prefs.managerVariant == "root"
|
||||
|
||||
private var isAnimationRunning = false
|
||||
|
||||
inner class ListViewHolder(private val binding: ViewAppExpandableBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
private var isExpanded = false
|
||||
|
||||
fun bind(position: Int) {
|
||||
val dataModel = dataModels[position]
|
||||
with(binding) {
|
||||
appTitle.text = dataModel?.appName
|
||||
appDescription.text = dataModel?.appDescription
|
||||
dataModel?.appIcon?.let { appIcon.setImageResource(it) }
|
||||
appClickableLayout.setOnClickListener {
|
||||
if (isAnimationRunning) return@setOnClickListener
|
||||
val rootHeight = root.measuredHeight
|
||||
val expandedViewHeight = appExpandedView.height
|
||||
val expandedTranslation = appClickableLayout.height.toFloat()
|
||||
|
||||
when (isExpanded.also { isExpanded = !isExpanded }) {
|
||||
true -> {
|
||||
appExpandedView.toggle(0f, 0.8f, -expandedTranslation)
|
||||
root.toggleCard(rootHeight - expandedViewHeight)
|
||||
appExpandArrow.rotateArrow(90f)
|
||||
}
|
||||
false -> {
|
||||
root.toggleCard(rootHeight + expandedViewHeight)
|
||||
appExpandedView.toggle(1f, 1f, expandedTranslation)
|
||||
appExpandArrow.rotateArrow(-90f)
|
||||
}
|
||||
}
|
||||
}
|
||||
appUninstall.setOnClickListener {
|
||||
AppUninstallDialog.newInstance(
|
||||
dataModel?.appName,
|
||||
dataModel?.appPkg
|
||||
).show(activity.supportFragmentManager, null)
|
||||
}
|
||||
appLaunch.setOnClickListener {
|
||||
viewModel.launchApp(apps[position], isRoot)
|
||||
}
|
||||
appInfo.setOnClickListener {
|
||||
AppInfoDialog.newInstance(
|
||||
appName = apps[position],
|
||||
appIcon = dataModel?.appIcon,
|
||||
changelog = dataModel?.changelog?.value
|
||||
).show(activity.supportFragmentManager, "info")
|
||||
}
|
||||
dataModel?.buttonTag?.observe(activity) { buttonTag ->
|
||||
appDownload.apply {
|
||||
setOnClickListener {
|
||||
viewModel.openInstallDialog(
|
||||
activity.supportFragmentManager,
|
||||
buttonTag,
|
||||
apps[position]
|
||||
)
|
||||
}
|
||||
appDownload.setIconResource(buttonTag.image)
|
||||
contentDescription = activity.getString(
|
||||
when (buttonTag) {
|
||||
ButtonTag.UPDATE -> R.string.accessibility_update
|
||||
ButtonTag.REINSTALL -> R.string.accessibility_reinstall
|
||||
else -> R.string.accessibility_download
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
dataModel?.isAppInstalled?.observe(activity) {
|
||||
appUninstall.isVisible = it
|
||||
appLaunch.isVisible = it
|
||||
}
|
||||
dataModel?.versionName?.observe(activity) {
|
||||
appVersionLatest.text = it
|
||||
appDownload.isGone = it == activity.getString(R.string.unavailable)
|
||||
}
|
||||
dataModel?.installedVersionName?.observe(activity) {
|
||||
appVersionInstalled.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||
val view = ViewAppExpandableBinding.inflate(LayoutInflater.from(activity), parent, false)
|
||||
return ListViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
|
||||
holder.bind(position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = apps.size
|
||||
|
||||
private fun ImageView.rotateArrow(degrees: Float) {
|
||||
animate().apply {
|
||||
duration = animationDuration
|
||||
rotation(degrees)
|
||||
}
|
||||
}
|
||||
|
||||
private fun View.toggle(
|
||||
alpha: Float,
|
||||
scale: Float,
|
||||
translation: Float
|
||||
) {
|
||||
animate().apply {
|
||||
duration = animationDuration
|
||||
scaleX(scale)
|
||||
scaleY(scale)
|
||||
alpha(alpha)
|
||||
translationYBy(translation)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MaterialCardView.toggleCard(resultHeight: Int) {
|
||||
ValueAnimator.ofInt(measuredHeight, resultHeight).apply {
|
||||
duration = animationDuration
|
||||
addUpdateListener { value ->
|
||||
updateLayoutParams {
|
||||
height = value.animatedValue as Int
|
||||
}
|
||||
}
|
||||
addListener(
|
||||
onStart = {
|
||||
isAnimationRunning = true
|
||||
},
|
||||
onEnd = {
|
||||
isAnimationRunning = false
|
||||
}
|
||||
)
|
||||
}.start()
|
||||
}
|
||||
|
||||
init {
|
||||
|
||||
if (prefs.enableVanced) {
|
||||
if (isRoot) {
|
||||
dataModels.add(viewModel.vancedRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.vancedModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.vanced))
|
||||
}
|
||||
|
||||
if (prefs.enableMusic) {
|
||||
if (isRoot) {
|
||||
dataModels.add(viewModel.musicRootModel.value)
|
||||
} else {
|
||||
dataModels.add(viewModel.musicModel.value)
|
||||
}
|
||||
apps.add(activity.getString(R.string.music))
|
||||
}
|
||||
|
||||
if (!isRoot) {
|
||||
dataModels.add(viewModel.microgModel.value)
|
||||
apps.add(activity.getString(R.string.microg))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val animationDuration = 250L
|
||||
}
|
||||
|
||||
}
|
|
@ -10,7 +10,8 @@ import com.vanced.manager.databinding.ViewNotificationSettingBinding
|
|||
import com.vanced.manager.model.NotifModel
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
|
||||
class GetNotifAdapter(private val context: Context) : RecyclerView.Adapter<GetNotifAdapter.GetNotifViewHolder>() {
|
||||
class GetNotifAdapter(private val context: Context) :
|
||||
RecyclerView.Adapter<GetNotifAdapter.GetNotifViewHolder>() {
|
||||
|
||||
private val prefs = context.defPrefs
|
||||
|
||||
|
@ -35,7 +36,8 @@ class GetNotifAdapter(private val context: Context) : RecyclerView.Adapter<GetNo
|
|||
|
||||
private val apps = arrayOf(vanced, music, microg)
|
||||
|
||||
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
val switch = binding.notifSwitch
|
||||
fun bind(position: Int) {
|
||||
val app = apps[position]
|
||||
|
@ -44,15 +46,21 @@ class GetNotifAdapter(private val context: Context) : RecyclerView.Adapter<GetNo
|
|||
setSummary(app.switchSummary)
|
||||
setTitle(app.switchTitle)
|
||||
setDefaultValue(true)
|
||||
with (prefs) {
|
||||
setChecked(getBoolean( "enable_" + app.key.substringBefore("_"), true) && getBoolean(app.key, true))
|
||||
with(prefs) {
|
||||
setChecked(
|
||||
getBoolean(
|
||||
"enable_" + app.key.substringBefore("_"),
|
||||
true
|
||||
) && getBoolean(app.key, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GetNotifViewHolder {
|
||||
val view = ViewNotificationSettingBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
val view =
|
||||
ViewNotificationSettingBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||
return GetNotifViewHolder(view)
|
||||
}
|
||||
|
||||
|
|
|
@ -57,13 +57,14 @@ class LinkAdapter(
|
|||
|
||||
val links = arrayOf(instagram, youtube, github, website, telegram, twitter, discord, reddit)
|
||||
|
||||
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val logo = binding.linkImage
|
||||
|
||||
fun bind(position: Int) {
|
||||
binding.linkBg.setOnClickListener {
|
||||
viewModel.openUrl(links[position].linkUrl)
|
||||
viewModel.openUrl(context, links[position].linkUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,21 +18,22 @@ class SelectAppsAdapter(private val context: Context) :
|
|||
|
||||
private val vanced = SelectAppModel(
|
||||
context.getString(R.string.vanced),
|
||||
context.getString(R.string.select_apps_vanced),
|
||||
context.getString(R.string.description_vanced),
|
||||
"vanced",
|
||||
prefs.enableVanced
|
||||
)
|
||||
|
||||
private val music = SelectAppModel(
|
||||
context.getString(R.string.music),
|
||||
context.getString(R.string.select_apps_music),
|
||||
context.getString(R.string.description_vanced_music),
|
||||
"music",
|
||||
prefs.enableMusic
|
||||
)
|
||||
|
||||
val apps = arrayOf(vanced, music)
|
||||
|
||||
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
val appName = binding.appCheckboxText
|
||||
val appDescription = binding.appCheckboxDescription
|
||||
val appCard = binding.appCheckboxBg
|
||||
|
|
|
@ -18,7 +18,10 @@ class SponsorAdapter(
|
|||
) : RecyclerView.Adapter<SponsorAdapter.LinkViewHolder>() {
|
||||
|
||||
private val brave = SponsorModel(
|
||||
if (currentTheme == LIGHT) AppCompatResources.getDrawable(context, R.drawable.ic_brave_light) else AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
||||
if (currentTheme == LIGHT) AppCompatResources.getDrawable(
|
||||
context,
|
||||
R.drawable.ic_brave_light
|
||||
) else AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
||||
"Brave",
|
||||
BRAVE
|
||||
)
|
||||
|
@ -39,7 +42,7 @@ class SponsorAdapter(
|
|||
with(binding) {
|
||||
sponsorName.text = sponsors[position].name
|
||||
cardSponsor.setOnClickListener {
|
||||
viewModel.openUrl(sponsors[position].url)
|
||||
viewModel.openUrl(context, sponsors[position].url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.CrowdinConfig
|
||||
import com.crowdin.platform.data.model.AuthConfig
|
||||
import com.crowdin.platform.data.remote.NetworkType
|
||||
import com.vanced.manager.BuildConfig.*
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.utils.loadJson
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.mutableAccentColor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
open class App: Application() {
|
||||
class App : Application() {
|
||||
|
||||
private val prefs by lazy { getDefaultSharedPreferences(this) }
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
@ -23,30 +20,15 @@ open class App: Application() {
|
|||
override fun onCreate() {
|
||||
scope.launch { loadJson(this@App) }
|
||||
super.onCreate()
|
||||
|
||||
Crowdin.init(this,
|
||||
CrowdinConfig.Builder().apply {
|
||||
withDistributionHash(CROWDIN_HASH)
|
||||
withNetworkType(NetworkType.WIFI)
|
||||
if (ENABLE_CROWDIN_AUTH) {
|
||||
if (prefs.getBoolean("crowdin_real_time", false))
|
||||
withRealTimeUpdates()
|
||||
|
||||
withSourceLanguage("en")
|
||||
withAuthConfig(AuthConfig(CROWDIN_CLIENT_ID, CROWDIN_CLIENT_SECRET, null))
|
||||
withScreenshotEnabled()
|
||||
log("test", "crowdin credentials")
|
||||
}
|
||||
}.build()
|
||||
mutableAccentColor.value = prefs.managerAccent
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder
|
||||
.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
//.setInitializers(BusyBoxInstaller::class.java) //TODO fix busybox
|
||||
.setTimeout(10)
|
||||
)
|
||||
|
||||
if (prefs.getBoolean("crowdin_upload_screenshot", false))
|
||||
Crowdin.registerScreenShotContentObserver(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
Crowdin.onConfigurationChanged()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
|
||||
/**
|
||||
* CombinedLiveData is a helper class to combine results from two LiveData sources.
|
||||
* @param combine Function reference that will be used to combine all LiveData data.
|
||||
* @param R The type of data returned after combining all LiveData data.
|
||||
* Usage:
|
||||
* CombinedLiveData(
|
||||
* getLiveData1(),
|
||||
* getLiveData2()
|
||||
* ) { data1, data2 ->
|
||||
* // Use datas[0], datas[1], ..., datas[N] to return a value
|
||||
* }
|
||||
*/
|
||||
class CombinedLiveData<R, A, B>(
|
||||
liveDataA: LiveData<A>,
|
||||
liveDataB: LiveData<B>,
|
||||
private val combine: (a: A?, b: B?) -> R
|
||||
) : MediatorLiveData<R>() {
|
||||
|
||||
private var a: A? = null
|
||||
private var b: B? = null
|
||||
|
||||
init {
|
||||
addSource(liveDataA) {
|
||||
a = it
|
||||
value = combine(a, b)
|
||||
}
|
||||
addSource(liveDataB) {
|
||||
b = it
|
||||
value = combine(a, b)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,9 @@ object MusicDownloader {
|
|||
|
||||
fun downloadMusic(context: Context, version: String? = null) {
|
||||
val prefs = context.defPrefs
|
||||
musicVersion = version ?: prefs.musicVersion?.getLatestAppVersion(musicVersions.value?.value ?: listOf(""))
|
||||
musicVersion = version ?: prefs.musicVersion?.getLatestAppVersion(
|
||||
musicVersions.value?.value ?: listOf("")
|
||||
)
|
||||
versionCode = music.value?.int("versionCode")
|
||||
variant = prefs.managerVariant
|
||||
baseurl = "$baseInstallUrl/music/v$musicVersion"
|
||||
|
@ -35,31 +37,43 @@ object MusicDownloader {
|
|||
|
||||
private fun downloadApk(context: Context, apk: String = "music") {
|
||||
val url = if (apk == "stock") "$baseurl/stock/${getArch()}.apk" else "$baseurl/$variant.apk"
|
||||
download(url, "$baseurl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||
if (variant == "root" && apk != "stock") {
|
||||
downloadApk(context, "stock")
|
||||
return@download
|
||||
}
|
||||
|
||||
when (apk) {
|
||||
"music" -> {
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, "root", hashUrl!!, context)) {
|
||||
if (downloadStockCheck(musicRootPkg, versionCode!!, context))
|
||||
downloadApk(context, "stock")
|
||||
else
|
||||
startMusicInstall(context)
|
||||
} else {
|
||||
downloadApk(context, apk)
|
||||
}
|
||||
} else
|
||||
startMusicInstall(context)
|
||||
download(
|
||||
url,
|
||||
"$baseurl/",
|
||||
folderName!!,
|
||||
getFileNameFromUrl(url),
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
if (variant == "root" && apk != "stock") {
|
||||
downloadApk(context, "stock")
|
||||
return@download
|
||||
}
|
||||
"stock" -> startMusicInstall(context)
|
||||
}
|
||||
}, onError = {
|
||||
downloadingFile.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||
})
|
||||
|
||||
when (apk) {
|
||||
"music" -> {
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, "root", hashUrl!!, context)) {
|
||||
if (downloadStockCheck(musicRootPkg, versionCode!!, context))
|
||||
downloadApk(context, "stock")
|
||||
else
|
||||
startMusicInstall(context)
|
||||
} else {
|
||||
downloadApk(context, apk)
|
||||
}
|
||||
} else
|
||||
startMusicInstall(context)
|
||||
}
|
||||
"stock" -> startMusicInstall(context)
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
getFileNameFromUrl(url)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fun startMusicInstall(context: Context) {
|
||||
|
|
|
@ -16,7 +16,7 @@ import com.vanced.manager.utils.PackageHelper.installVancedRoot
|
|||
import java.io.File
|
||||
|
||||
object VancedDownloader {
|
||||
|
||||
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var defPrefs: SharedPreferences
|
||||
private lateinit var arch: String
|
||||
|
@ -47,7 +47,9 @@ object VancedDownloader {
|
|||
lang = it.split(", ").toMutableList()
|
||||
}
|
||||
theme = prefs.theme
|
||||
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))
|
||||
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(
|
||||
vancedVersions.value?.value ?: listOf("")
|
||||
)
|
||||
themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme"
|
||||
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
|
||||
arch = getArch()
|
||||
|
@ -68,53 +70,68 @@ object VancedDownloader {
|
|||
"theme" -> "$themePath/$theme.apk"
|
||||
"arch" -> "$baseInstallUrl/apks/v$vancedVersion/$variant/Arch/split_config.$arch.apk"
|
||||
"stock" -> "$themePath/stock.apk"
|
||||
"dpi" -> "$themePath/dpi.apk"
|
||||
"dpi" -> "$themePath/dpi.apk"
|
||||
"lang" -> "$baseInstallUrl/apks/v$vancedVersion/$variant/Language/split_config.${lang[count]}.apk"
|
||||
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
|
||||
}
|
||||
|
||||
download(url, "$baseInstallUrl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, theme!!, hashUrl, context)) {
|
||||
if (downloadStockCheck(vancedRootPkg, vancedVersionCode, context))
|
||||
downloadSplits(context, "arch")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
download(
|
||||
url,
|
||||
"$baseInstallUrl/",
|
||||
folderName!!,
|
||||
getFileNameFromUrl(url),
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root") {
|
||||
if (validateTheme(downloadPath!!, theme!!, hashUrl, context)) {
|
||||
if (downloadStockCheck(vancedRootPkg, vancedVersionCode, context))
|
||||
downloadSplits(context, "arch")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
} else
|
||||
downloadSplits(context, "theme")
|
||||
} else
|
||||
downloadSplits(context, "theme")
|
||||
} else
|
||||
downloadSplits(context, "arch")
|
||||
"arch" -> if (variant == "root") downloadSplits(context, "stock") else downloadSplits(context, "lang")
|
||||
"stock" -> downloadSplits(context, "dpi")
|
||||
"dpi" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
count++
|
||||
succesfulLangCount++
|
||||
if (count < lang.size)
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
}
|
||||
|
||||
}
|
||||
}, onError = {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
when {
|
||||
count < lang.size -> downloadSplits(context, "lang")
|
||||
succesfulLangCount == 0 -> {
|
||||
lang.add("en")
|
||||
downloadSplits(context, "lang")
|
||||
downloadSplits(context, "arch")
|
||||
"arch" -> if (variant == "root") downloadSplits(
|
||||
context,
|
||||
"stock"
|
||||
) else downloadSplits(context, "lang")
|
||||
"stock" -> downloadSplits(context, "dpi")
|
||||
"dpi" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
count++
|
||||
succesfulLangCount++
|
||||
if (count < lang.size)
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
startVancedInstall(context)
|
||||
}
|
||||
else -> startVancedInstall(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
downloadingFile.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
when {
|
||||
count < lang.size -> downloadSplits(context, "lang")
|
||||
succesfulLangCount == 0 -> {
|
||||
lang.add("en")
|
||||
downloadSplits(context, "lang")
|
||||
}
|
||||
else -> startVancedInstall(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
getFileNameFromUrl(url)
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun startVancedInstall(context: Context, variant: String? = this.variant) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
|||
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||
|
||||
class AppInstallerService: Service() {
|
||||
class AppInstallerService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
|
@ -31,7 +31,7 @@ class AppInstallerService: Service() {
|
|||
else -> {
|
||||
sendCloseDialog(this)
|
||||
intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)?.let {
|
||||
sendFailure(it,this)
|
||||
sendFailure(it, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.os.IBinder
|
|||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||
|
||||
class AppUninstallerService: Service() {
|
||||
class AppUninstallerService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val pkgName = intent?.getStringExtra("pkg")
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.vanced.manager.R
|
||||
|
||||
enum class ButtonTag(@DrawableRes val image: Int) {
|
||||
INSTALL(R.drawable.ic_app_download),
|
||||
UPDATE(R.drawable.ic_app_update),
|
||||
REINSTALL(R.drawable.ic_app_reinstall)
|
||||
}
|
|
@ -1,93 +1,81 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.*
|
||||
import com.beust.klaxon.JsonObject
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.CombinedLiveData
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
|
||||
open class DataModel(
|
||||
private val jsonObject: LiveData<JsonObject?>,
|
||||
private val context: Context,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
jsonObject: LiveData<JsonObject?>,
|
||||
context: Context,
|
||||
val appPkg: String,
|
||||
val appName: String,
|
||||
val appIcon: Drawable?
|
||||
val appDescription: String,
|
||||
@DrawableRes val appIcon: Int
|
||||
) {
|
||||
|
||||
private val versionCode = MutableLiveData<Int>()
|
||||
private val installedVersionCode = MutableLiveData<Int>()
|
||||
val isAppInstalled = Transformations.map(jsonObject) { isAppInstalled(appPkg) }
|
||||
|
||||
val isAppInstalled = MutableLiveData<Boolean>()
|
||||
val versionName = MutableLiveData<String>()
|
||||
val installedVersionName = MutableLiveData<String>()
|
||||
val buttonTxt = MutableLiveData<String>()
|
||||
val changelog = MutableLiveData<String>()
|
||||
private val versionCode = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.int("versionCode") ?: 0
|
||||
}
|
||||
private val installedVersionCode = Transformations.map(isAppInstalled) {
|
||||
getPkgVersionCode(appPkg, it)
|
||||
}
|
||||
private val unavailable = context.getString(R.string.unavailable)
|
||||
private val pm = context.packageManager
|
||||
|
||||
private fun fetch() {
|
||||
val jobj = jsonObject.value
|
||||
isAppInstalled.value = isAppInstalled(appPkg)
|
||||
versionCode.value = jobj?.int("versionCode") ?: 0
|
||||
versionName.value = jobj?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||
changelog.value = jobj?.string("changelog") ?: context.getString(R.string.unavailable)
|
||||
val versionName = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.string("version") ?: unavailable
|
||||
}
|
||||
val changelog = Transformations.map(jsonObject) { jobj ->
|
||||
jobj?.string("changelog") ?: unavailable
|
||||
}
|
||||
val installedVersionName = Transformations.map(isAppInstalled) {
|
||||
getPkgVersionName(appPkg, it)
|
||||
}
|
||||
val buttonTag = CombinedLiveData(versionCode, installedVersionCode) { versionCode, installedVersionCode ->
|
||||
compareInt(installedVersionCode, versionCode)
|
||||
}
|
||||
|
||||
init {
|
||||
fetch()
|
||||
with(lifecycleOwner) {
|
||||
jsonObject.observe(this) {
|
||||
fetch()
|
||||
}
|
||||
isAppInstalled.observe(this) {
|
||||
installedVersionCode.value = getPkgVersionCode(appPkg)
|
||||
installedVersionName.value = getPkgVersionName(appPkg)
|
||||
}
|
||||
versionCode.observe(this) { versionCode ->
|
||||
installedVersionCode.observe(this) { installedVersionCode ->
|
||||
buttonTxt.value = compareInt(installedVersionCode, versionCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, pm)
|
||||
|
||||
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, context.packageManager)
|
||||
|
||||
private fun getPkgVersionName(pkg: String): String {
|
||||
val pm = context.packageManager
|
||||
return try {
|
||||
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
context.getString(R.string.unavailable)
|
||||
private fun getPkgVersionName(pkg: String, isAppInstalled: Boolean): String {
|
||||
return if (isAppInstalled) {
|
||||
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: unavailable
|
||||
} else {
|
||||
unavailable
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getPkgVersionCode(pkg: String): Int {
|
||||
val pm = context.packageManager
|
||||
return try {
|
||||
private fun getPkgVersionCode(pkg: String, isAppInstalled: Boolean): Int {
|
||||
return if (isAppInstalled) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
pm?.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt() ?: 0
|
||||
else
|
||||
pm?.getPackageInfo(pkg, 0)?.versionCode ?: 0
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareInt(int1: Int?, int2: Int?): String {
|
||||
private fun compareInt(int1: Int?, int2: Int?): ButtonTag {
|
||||
if (int2 != null && int1 != null) {
|
||||
return when {
|
||||
int1 == 0 -> context.getString(R.string.install)
|
||||
int2 > int1 -> context.getString(R.string.update)
|
||||
int2 == int1 || int1 > int2 -> context.getString(R.string.button_reinstall)
|
||||
else -> context.getString(R.string.install)
|
||||
int1 == 0 -> ButtonTag.INSTALL
|
||||
int2 > int1 -> ButtonTag.UPDATE
|
||||
int1 >= int2 -> ButtonTag.REINSTALL
|
||||
else -> ButtonTag.INSTALL
|
||||
}
|
||||
}
|
||||
return context.getString(R.string.install)
|
||||
return ButtonTag.INSTALL
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.beust.klaxon.JsonObject
|
||||
|
@ -10,18 +10,18 @@ import com.vanced.manager.utils.PackageHelper
|
|||
class RootDataModel(
|
||||
jsonObject: LiveData<JsonObject?>,
|
||||
context: Context,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
appPkg: String,
|
||||
appName: String,
|
||||
appIcon: Drawable?,
|
||||
appDescription: String,
|
||||
@DrawableRes appIcon: Int,
|
||||
//BUG THIS!
|
||||
//kotlin thinks that this value is null if we use
|
||||
//private val scriptName: String
|
||||
//Although it's impossible for it to be null.
|
||||
//Ironic, isn't it?
|
||||
private val scriptName: String?
|
||||
): DataModel(
|
||||
jsonObject, context, lifecycleOwner, appPkg, appName, appIcon
|
||||
) : DataModel(
|
||||
jsonObject, context, appPkg, appName, appDescription, appIcon
|
||||
) {
|
||||
|
||||
override fun isAppInstalled(pkg: String): Boolean {
|
||||
|
|
|
@ -3,7 +3,7 @@ package com.vanced.manager.model
|
|||
import android.graphics.drawable.Drawable
|
||||
|
||||
data class SponsorModel(
|
||||
val logo: Drawable?,
|
||||
val name: String,
|
||||
val url: String
|
||||
val logo: Drawable?,
|
||||
val name: String,
|
||||
val url: String
|
||||
)
|
|
@ -15,10 +15,7 @@ import androidx.navigation.findNavController
|
|||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.LoadingStateListener
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.vanced.manager.BuildConfig.ENABLE_CROWDIN_AUTH
|
||||
import com.vanced.manager.BuildConfig.VERSION_CODE
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.ActivityMainBinding
|
||||
|
@ -32,6 +29,7 @@ import com.vanced.manager.utils.AppUtils.currentLocale
|
|||
import com.vanced.manager.utils.AppUtils.faqpkg
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
import com.vanced.manager.utils.AppUtils.playStorePkg
|
||||
import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
@ -39,23 +37,9 @@ class MainActivity : AppCompatActivity() {
|
|||
lateinit var binding: ActivityMainBinding
|
||||
private val navHost by lazy { findNavController(R.id.nav_host) }
|
||||
|
||||
private val loadingObserver = object : LoadingStateListener {
|
||||
val tag = "VMLocalisation"
|
||||
override fun onDataChanged() {
|
||||
log(tag, "Loaded data")
|
||||
}
|
||||
|
||||
override fun onFailure(throwable: Throwable) {
|
||||
log(tag, "Failed to load data: ${throwable.stackTraceToString()}")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setFinalTheme()
|
||||
super.onCreate(savedInstanceState)
|
||||
if (ENABLE_CROWDIN_AUTH)
|
||||
authCrowdin()
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
@ -74,7 +58,7 @@ class MainActivity : AppCompatActivity() {
|
|||
initDialogs(intent.getBooleanExtra("firstLaunch", false))
|
||||
manager.observe(this) {
|
||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||
ManagerUpdateDialog.newInstance(false).show(this)
|
||||
ManagerUpdateDialog.newInstance(true).show(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,26 +69,19 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) {
|
||||
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(this, R.drawable.ic_keyboard_backspace_black_24dp) else null
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
Crowdin.unregisterDataLoadingObserver(loadingObserver)
|
||||
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(
|
||||
this,
|
||||
R.drawable.ic_keyboard_backspace_black_24dp
|
||||
) else null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
setFinalTheme()
|
||||
super.onResume()
|
||||
Crowdin.registerDataLoadingObserver(loadingObserver)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
true
|
||||
}
|
||||
R.id.toolbar_about -> {
|
||||
navHost.navigate(HomeFragmentDirections.toAboutFragment())
|
||||
true
|
||||
|
@ -117,30 +94,9 @@ class MainActivity : AppCompatActivity() {
|
|||
navHost.navigate(HomeFragmentDirections.toLogFragment())
|
||||
true
|
||||
}
|
||||
R.id.toolbar_guide -> {
|
||||
try {
|
||||
val intent = if (isPackageInstalled(faqpkg, packageManager)) {
|
||||
Intent().apply {
|
||||
component = ComponentName(faqpkg, "$faqpkg.ui.MainActivity")
|
||||
}
|
||||
} else {
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
val uriBuilder = Uri.parse("https://play.google.com/store/apps/details")
|
||||
.buildUpon()
|
||||
.appendQueryParameter("id", faqpkg)
|
||||
.appendQueryParameter("launch", "true")
|
||||
data = uriBuilder.build()
|
||||
setPackage(playStorePkg)
|
||||
}
|
||||
}
|
||||
startActivity(intent)
|
||||
true
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
R.id.toolbar_update_manager -> {
|
||||
ManagerUpdateDialog.newInstance(false).show(supportFragmentManager, "manager_update")
|
||||
ManagerUpdateDialog.newInstance(false)
|
||||
.show(supportFragmentManager, "manager_update")
|
||||
true
|
||||
}
|
||||
R.id.dev_settings -> {
|
||||
|
@ -152,14 +108,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
override fun attachBaseContext(newBase: Context) {
|
||||
super.attachBaseContext(Crowdin.wrapContext(LanguageContextWrapper.wrap(newBase)))
|
||||
}
|
||||
|
||||
//I have no idea why the fuck is super method deprecated
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
onActivityResult(requestCode)
|
||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase))
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
|
@ -173,8 +122,14 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
when (newConfig.orientation) {
|
||||
Configuration.ORIENTATION_PORTRAIT -> log("VMUI", "screen orientation changed to portrait")
|
||||
Configuration.ORIENTATION_LANDSCAPE -> log("VMUI", "screen orientation changed to landscape")
|
||||
Configuration.ORIENTATION_PORTRAIT -> log(
|
||||
"VMUI",
|
||||
"screen orientation changed to portrait"
|
||||
)
|
||||
Configuration.ORIENTATION_LANDSCAPE -> log(
|
||||
"VMUI",
|
||||
"screen orientation changed to landscape"
|
||||
)
|
||||
else -> log("VMUI", "screen orientation changed to [REDACTED]")
|
||||
}
|
||||
|
||||
|
@ -208,7 +163,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
} else {
|
||||
if (isMiuiOptimizationsEnabled) {
|
||||
DialogContainer.applyAccentMiuiDialog(this)
|
||||
DialogContainer.miuiDialog(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,7 +172,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
if (variant == "root") {
|
||||
if (PackageHelper.getPackageVersionName("com.google.android.youtube", packageManager) == "14.21.54") {
|
||||
if (PackageHelper.getPackageVersionName(vancedRootPkg, packageManager) == "14.21.54") {
|
||||
DialogContainer.basicDialog(
|
||||
getString(R.string.hold_on),
|
||||
getString(R.string.magisk_vanced),
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
||||
class SplashScreenActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
//Preheat the shell
|
||||
Shell.getShell {
|
||||
if (getDefaultSharedPreferences(this).getBoolean("firstLaunch", true)) {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
finish()
|
||||
} else {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.util.LayoutDirection
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.animation.addListener
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.vanced.manager.adapter.WelcomePageAdapter
|
||||
import com.vanced.manager.databinding.ActivityWelcomeBinding
|
||||
|
@ -12,21 +12,22 @@ import kotlin.math.abs
|
|||
|
||||
class WelcomeActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var viewPager2: ViewPager2
|
||||
private lateinit var binding: ActivityWelcomeBinding
|
||||
private var isRtl = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
viewPager2 = binding.welcomeViewpager
|
||||
viewPager2.apply {
|
||||
isRtl = resources.configuration.layoutDirection == LayoutDirection.RTL
|
||||
|
||||
binding.welcomeViewpager.apply {
|
||||
adapter = WelcomePageAdapter(this@WelcomeActivity)
|
||||
isUserInputEnabled = false
|
||||
setPageTransformer { page, position ->
|
||||
page.apply {
|
||||
val pageWidth = width
|
||||
val pageWidth = width.toFloat()
|
||||
//Thank you, fragula dev!
|
||||
when {
|
||||
position > 0 && position < 1 -> {
|
||||
|
@ -35,7 +36,7 @@ class WelcomeActivity : AppCompatActivity() {
|
|||
}
|
||||
position > -1 && position <= 0 -> {
|
||||
alpha = 1.0f - abs(position * 0.7f)
|
||||
translationX = -pageWidth * position / 1.3F
|
||||
translationX = pageWidth.rtlCompat * position / 1.3F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,21 +45,25 @@ class WelcomeActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (viewPager2.currentItem == 0) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
navigateTo(viewPager2.currentItem - 1)
|
||||
with(binding) {
|
||||
if (welcomeViewpager.currentItem == 0) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
navigateTo(welcomeViewpager.currentItem - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun navigateTo(position: Int) {
|
||||
viewPager2.currentPosition = position
|
||||
binding.welcomeViewpager.currentPosition = position
|
||||
}
|
||||
|
||||
private val Float.rtlCompat get() = if (isRtl) this else -this
|
||||
|
||||
//Shit way to implement animation duration, but at least it works
|
||||
private var ViewPager2.currentPosition: Int
|
||||
get() = currentItem
|
||||
set(value) {
|
||||
set(value) {
|
||||
val pixelsToDrag: Int = width * (value - currentItem)
|
||||
val animator = ValueAnimator.ofInt(0, pixelsToDrag)
|
||||
var previousValue = 0
|
||||
|
@ -66,16 +71,13 @@ class WelcomeActivity : AppCompatActivity() {
|
|||
addUpdateListener { valueAnimator ->
|
||||
val currentValue = valueAnimator.animatedValue as Int
|
||||
val currentPxToDrag = (currentValue - previousValue).toFloat()
|
||||
fakeDragBy(-currentPxToDrag)
|
||||
fakeDragBy(currentPxToDrag.rtlCompat)
|
||||
previousValue = currentValue
|
||||
}
|
||||
addListener(object : Animator.AnimatorListener {
|
||||
override fun onAnimationStart(animation: Animator?) { beginFakeDrag() }
|
||||
override fun onAnimationEnd(animation: Animator?) { endFakeDrag() }
|
||||
override fun onAnimationCancel(animation: Animator?) {}
|
||||
override fun onAnimationRepeat(animation: Animator?) {}
|
||||
})
|
||||
interpolator = AccelerateDecelerateInterpolator()
|
||||
addListener(
|
||||
onStart = { beginFakeDrag() },
|
||||
onEnd = { endFakeDrag() }
|
||||
)
|
||||
duration = 500
|
||||
start()
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class EmptyPreference @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
fun setSummary(newSummary: String) {
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
preferenceSummary.text = newSummary
|
||||
preferenceSummary.isVisible = true
|
||||
preferenceTitle.setPadding(0, 0, 0, 0)
|
||||
|
@ -40,10 +40,11 @@ class EmptyPreference @JvmOverloads constructor(
|
|||
|
||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||
attrs?.let { mAttrs ->
|
||||
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
||||
val typedArray =
|
||||
context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
||||
val title = typedArray.getText(R.styleable.EmptyPreference_preference_title)
|
||||
val summary = typedArray.getText(R.styleable.EmptyPreference_preference_summary)
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
if (summary != null) {
|
||||
preferenceSummary.text = summary
|
||||
} else {
|
||||
|
|
|
@ -27,7 +27,8 @@ class PreferenceCategory @JvmOverloads constructor(
|
|||
|
||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||
attrs.let { mAttrs ->
|
||||
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
||||
val typedArray =
|
||||
context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
||||
val title = typedArray.getText(R.styleable.PreferenceCategory_category_title)
|
||||
|
||||
binding.categoryTitle.text = title
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.CompoundButton
|
||||
|
@ -31,6 +32,13 @@ class PreferenceSwitch @JvmOverloads constructor(
|
|||
|
||||
private var mListener: OnCheckedListener? = null
|
||||
|
||||
private val prefListener =
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
if (key == prefKey) {
|
||||
binding.preferenceSwitch.isChecked = sharedPreferences.getBoolean(key, defValue)
|
||||
}
|
||||
}
|
||||
|
||||
private var _binding: ViewPreferenceSwitchBinding? = null
|
||||
|
||||
val binding: ViewPreferenceSwitchBinding
|
||||
|
@ -38,11 +46,7 @@ class PreferenceSwitch @JvmOverloads constructor(
|
|||
|
||||
init {
|
||||
_binding = ViewPreferenceSwitchBinding.inflate(LayoutInflater.from(context), this, true)
|
||||
prefs.registerOnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
if (key == prefKey) {
|
||||
binding.preferenceSwitch.isChecked = sharedPreferences.getBoolean(key, defValue)
|
||||
}
|
||||
}
|
||||
prefs.registerOnSharedPreferenceChangeListener(prefListener)
|
||||
attrs?.let { mAttrs ->
|
||||
with(context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceSwitch, 0, 0)) {
|
||||
val title = getText(R.styleable.PreferenceSwitch_switch_title)
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
|
||||
class SplashScreenActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (getDefaultSharedPreferences(this).getBoolean("firstLaunch", true)) {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
finish()
|
||||
} else {
|
||||
startActivity(Intent(this, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedAppCard @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MaterialCardView(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
setCardBackgroundColor(ColorStateList.valueOf(accentColor.value!!).withAlpha(35))
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedIconButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
iconTint = ColorStateList.valueOf(accentColor.value!!)
|
||||
setOnLongClickListener {
|
||||
Toast.makeText(context, contentDescription, Toast.LENGTH_SHORT).show()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -7,9 +7,7 @@ import androidx.core.graphics.ColorUtils
|
|||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedMaterialButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -18,8 +16,7 @@ class ThemedMaterialButton @JvmOverloads constructor(
|
|||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
setBgColor(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setBgColor(color.toInt())
|
||||
}
|
||||
|
|
|
@ -5,14 +5,13 @@ import android.content.res.ColorStateList
|
|||
import android.util.AttributeSet
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialCheckbox @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : MaterialCheckBox(context, attributeSet, R.attr.checkboxStyle) {
|
||||
init {
|
||||
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
||||
}
|
||||
}
|
|
@ -5,14 +5,13 @@ import android.content.res.ColorStateList
|
|||
import android.util.AttributeSet
|
||||
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialRadioButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : MaterialRadioButton(context, attributeSet, R.attr.radioButtonStyle) {
|
||||
init {
|
||||
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
||||
}
|
||||
}
|
|
@ -4,8 +4,7 @@ import android.content.Context
|
|||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedMaterialSlider @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -14,7 +13,7 @@ class ThemedMaterialSlider @JvmOverloads constructor(
|
|||
) : Slider(context, attributeSet, defStyleAttr) {
|
||||
|
||||
init {
|
||||
val accentValue = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||
val accentValue = ColorStateList.valueOf(accentColor.value!!)
|
||||
thumbTintList = accentValue
|
||||
trackActiveTintList = accentValue
|
||||
trackInactiveTintList = accentValue.withAlpha(70)
|
||||
|
|
|
@ -6,10 +6,7 @@ import android.util.AttributeSet
|
|||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
|
||||
class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -17,8 +14,7 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
|||
defStyleAttr: Int = 0
|
||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||
init {
|
||||
applyAccent(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
applyAccent(color.toInt())
|
||||
}
|
||||
|
@ -27,6 +23,9 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
|||
|
||||
private fun applyAccent(color: Int) {
|
||||
setTextColor(color)
|
||||
rippleColor = ColorStateList(arrayOf(intArrayOf()), intArrayOf(ColorUtils.setAlphaComponent(color, 50)))
|
||||
rippleColor = ColorStateList(
|
||||
arrayOf(intArrayOf()),
|
||||
intArrayOf(ColorUtils.setAlphaComponent(color, 50))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,21 +4,27 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
import com.vanced.manager.utils.accentColor
|
||||
|
||||
class ThemedSwipeRefreshlayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null
|
||||
) : SwipeRefreshLayout(context, attributeSet) {
|
||||
init {
|
||||
setColorSchemeColors(context.defPrefs.managerAccent)
|
||||
setColorSchemeColors(accentColor.value!!)
|
||||
initAttrs(context, attributeSet)
|
||||
}
|
||||
|
||||
private fun initAttrs(context: Context, attributeSet: AttributeSet?) {
|
||||
attributeSet.let {
|
||||
val typedAttrs = context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
||||
setProgressBackgroundColorSchemeColor(typedAttrs.getColor(R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor, 0))
|
||||
val typedAttrs =
|
||||
context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
||||
setProgressBackgroundColorSchemeColor(
|
||||
typedAttrs.getColor(
|
||||
R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor,
|
||||
0
|
||||
)
|
||||
)
|
||||
typedAttrs.recycle()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,20 +9,18 @@ import androidx.core.graphics.ColorUtils
|
|||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedSwitchCompat @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attributeSet: AttributeSet? = null,
|
||||
) : SwitchCompat(context, attributeSet, R.attr.switchStyle) {
|
||||
|
||||
private val states = arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
||||
private val states =
|
||||
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
||||
|
||||
init {
|
||||
setSwitchColors(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setSwitchColors(color.toInt())
|
||||
}
|
||||
|
@ -32,7 +30,13 @@ class ThemedSwitchCompat @JvmOverloads constructor(
|
|||
private fun setSwitchColors(color: Int) {
|
||||
val thumbColors = intArrayOf(Color.LTGRAY, color)
|
||||
val trackColors = intArrayOf(Color.GRAY, ColorUtils.setAlphaComponent(color, 70))
|
||||
DrawableCompat.setTintList(DrawableCompat.wrap(thumbDrawable), ColorStateList(states, thumbColors))
|
||||
DrawableCompat.setTintList(DrawableCompat.wrap(trackDrawable), ColorStateList(states, trackColors))
|
||||
DrawableCompat.setTintList(
|
||||
DrawableCompat.wrap(thumbDrawable),
|
||||
ColorStateList(states, thumbColors)
|
||||
)
|
||||
DrawableCompat.setTintList(
|
||||
DrawableCompat.wrap(trackDrawable),
|
||||
ColorStateList(states, trackColors)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,9 +4,7 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import com.vanced.manager.utils.accentColor
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.lifecycleOwner
|
||||
import com.vanced.manager.utils.managerAccent
|
||||
|
||||
class ThemedTextView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
|
@ -14,8 +12,7 @@ class ThemedTextView @JvmOverloads constructor(
|
|||
defStyleAttr: Int = 0
|
||||
) : AppCompatTextView(context, attributeSet, defStyleAttr) {
|
||||
init {
|
||||
setTextColor(context.defPrefs.managerAccent)
|
||||
context.lifecycleOwner()?.let { owner ->
|
||||
context.lifecycleOwner?.let { owner ->
|
||||
accentColor.observe(owner) { color ->
|
||||
setTextColor(color.toInt())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -72,20 +73,28 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
|||
appDownloadHeader.text = app
|
||||
if (arguments?.getBoolean(TAG_INSTALLING) == false) {
|
||||
when (app) {
|
||||
getString(R.string.vanced) -> downloadVanced(requireContext(), arguments?.getString(TAG_VERSION))
|
||||
getString(R.string.music) -> downloadMusic(requireContext(), arguments?.getString(TAG_VERSION))
|
||||
getString(R.string.vanced) -> downloadVanced(
|
||||
requireContext(),
|
||||
arguments?.getString(TAG_VERSION)
|
||||
)
|
||||
getString(R.string.music) -> downloadMusic(
|
||||
requireContext(),
|
||||
arguments?.getString(TAG_VERSION)
|
||||
)
|
||||
getString(R.string.microg) -> downloadMicrog(requireContext())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun DialogAppDownloadBinding.bindDownloadProgress() {
|
||||
downloadProgress.observe(viewLifecycleOwner) {
|
||||
appDownloadProgressbar.progress = it
|
||||
appDownloadProgress.text = "$it%"
|
||||
}
|
||||
installing.observe(viewLifecycleOwner) { installing ->
|
||||
appDownloadProgressbar.isVisible = !installing
|
||||
appDownloadProgressbarContainer.isVisible = !installing
|
||||
appInstallProgressbar.isVisible = installing
|
||||
appDownloadFile.isVisible = !installing
|
||||
appDownloadCancel.isEnabled = !installing
|
||||
|
|
|
@ -2,11 +2,10 @@ package com.vanced.manager.ui.dialogs
|
|||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogAppInfoBinding
|
||||
|
@ -21,13 +20,15 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
|||
|
||||
fun newInstance(
|
||||
appName: String?,
|
||||
appIcon: Drawable?,
|
||||
@DrawableRes appIcon: Int?,
|
||||
changelog: String?
|
||||
): AppInfoDialog = AppInfoDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(TAG_APP_NAME, appName)
|
||||
putString(TAG_CHANGELOG, changelog)
|
||||
putParcelable(TAG_APP_ICON, appIcon?.toBitmap())
|
||||
if (appIcon != null) {
|
||||
putInt(TAG_APP_ICON, appIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
|||
with(binding) {
|
||||
aboutAppName.text = getString(R.string.about_app, arguments?.getString(TAG_APP_NAME))
|
||||
aboutAppChangelog.text = arguments?.getString(TAG_CHANGELOG)
|
||||
aboutAppImage.setImageBitmap(arguments?.getParcelable(TAG_APP_ICON))
|
||||
arguments?.getInt(TAG_APP_ICON)?.let { aboutAppImage.setImageResource(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||
import com.vanced.manager.databinding.DialogAppUninstallBinding
|
||||
import com.vanced.manager.utils.PackageHelper
|
||||
|
||||
class AppUninstallDialog : BindingDialogFragment<DialogAppUninstallBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
private const val TAG_APP_NAME = "APP_NAME"
|
||||
private const val TAG_APP_PACKAGE = "APP_PACKAGE"
|
||||
|
||||
|
||||
fun newInstance(
|
||||
appName: String?,
|
||||
appPackage: String?,
|
||||
) = AppUninstallDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(TAG_APP_NAME, appName)
|
||||
putString(TAG_APP_PACKAGE, appPackage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
) = DialogAppUninstallBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun otherSetups() {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bindData()
|
||||
}
|
||||
|
||||
private fun bindData() {
|
||||
val appName = arguments?.getString(TAG_APP_NAME)
|
||||
val appPackage = arguments?.getString(TAG_APP_PACKAGE)
|
||||
with(binding) {
|
||||
appUninstallConfirm.setOnClickListener {
|
||||
if (appPackage != null) {
|
||||
PackageHelper.uninstallApk(
|
||||
pkg = appPackage,
|
||||
context = requireActivity()
|
||||
)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
appUninstallCancel.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
appUninstallMessage.text = getString(R.string.uninstall_app_text, appName)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,11 +11,12 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
|||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.formatVersion
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
|
||||
class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
class AppVersionSelectorDialog :
|
||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
|
||||
private val prefs by lazy { requireActivity().defPrefs }
|
||||
|
||||
|
@ -64,7 +65,7 @@ class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRa
|
|||
}
|
||||
dialogTitle.text = getString(R.string.version)
|
||||
dialogSave.setOnClickListener {
|
||||
val checkedTag = dialogRadiogroup.getCheckedButtonTag()
|
||||
val checkedTag = dialogRadiogroup.checkedButtonTag
|
||||
if (checkedTag != null) {
|
||||
prefs.edit { putString("${arguments?.getString(TAG_APP)}_version", checkedTag) }
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import androidx.core.content.edit
|
|||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.applyAccent
|
||||
import com.vanced.manager.utils.isMiuiOptimizationsEnabled
|
||||
import com.vanced.manager.utils.openUrl
|
||||
import com.vanced.manager.utils.showWithAccent
|
||||
|
||||
object DialogContainer {
|
||||
|
||||
|
@ -20,17 +20,17 @@ object DialogContainer {
|
|||
}
|
||||
setOnCancelListener {
|
||||
if (context.isMiuiOptimizationsEnabled) {
|
||||
applyAccentMiuiDialog(context)
|
||||
miuiDialog(context)
|
||||
}
|
||||
}
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit { putBoolean("firstLaunch", false) }
|
||||
}
|
||||
|
||||
fun applyAccentMiuiDialog(context: Context) {
|
||||
fun miuiDialog(context: Context) {
|
||||
MaterialAlertDialogBuilder(context).apply {
|
||||
setTitle(context.getString(R.string.miui_one_title))
|
||||
setMessage(context.getString(R.string.miui_one))
|
||||
|
@ -44,7 +44,7 @@ object DialogContainer {
|
|||
}
|
||||
setCancelable(false)
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ object DialogContainer {
|
|||
setMessage("So this statement is false huh? I'll go with True!")
|
||||
setPositiveButton("wut?") { dialog, _ -> dialog.dismiss() }
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
@ -68,28 +68,54 @@ object DialogContainer {
|
|||
when (msg) {
|
||||
context.getString(R.string.installation_signature) -> {
|
||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://lmgtfy.com/?q=andnixsh+apk+verification+disable", R.color.Twitter, context)
|
||||
openUrl(
|
||||
"https://lmgtfy.com/?q=andnixsh+apk+verification+disable",
|
||||
R.color.Twitter,
|
||||
context
|
||||
)
|
||||
}
|
||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
context.getString(R.string.installation_miui) -> {
|
||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://telegra.ph/How-to-install-v15-on-MIUI-02-11", R.color.Telegram, context)
|
||||
openUrl(
|
||||
"https://telegra.ph/How-to-install-v15-on-MIUI-02-11",
|
||||
R.color.Telegram,
|
||||
context
|
||||
)
|
||||
}
|
||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
if (fullMsg != null)
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
||||
basicDialog(
|
||||
context.getString(R.string.advanced),
|
||||
fullMsg,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +125,7 @@ object DialogContainer {
|
|||
setMessage(msg)
|
||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
create()
|
||||
applyAccent()
|
||||
showWithAccent()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import com.vanced.manager.databinding.DialogInstallationFilesDetectedBinding
|
|||
import com.vanced.manager.utils.defPrefs
|
||||
import com.vanced.manager.utils.managerVariant
|
||||
|
||||
class InstallationFilesDetectedDialog : BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
||||
class InstallationFilesDetectedDialog :
|
||||
BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -40,9 +41,11 @@ class InstallationFilesDetectedDialog : BindingBottomSheetDialogFragment<DialogI
|
|||
|
||||
private fun bindData() {
|
||||
with(binding) {
|
||||
val app = arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
||||
val app =
|
||||
arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
||||
installationDetectedTitle.text = getString(R.string.app_install_files_detected, app)
|
||||
installationDetectedSummary.text = getString(R.string.app_install_files_detected_summary, app)
|
||||
installationDetectedSummary.text =
|
||||
getString(R.string.app_install_files_detected_summary, app)
|
||||
installationDetectedRedownload.setOnClickListener {
|
||||
dismiss()
|
||||
when (app) {
|
||||
|
|
|
@ -50,9 +50,20 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
hexEdittext.apply {
|
||||
setText(accent.toHex(), TextView.BufferType.EDITABLE)
|
||||
addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
override fun onTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
before: Int,
|
||||
count: Int
|
||||
) {
|
||||
if (length() == 0) {
|
||||
setText("#")
|
||||
setSelection(1)
|
||||
|
@ -63,7 +74,8 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
val colorFromEditText = Color.parseColor(text.toString())
|
||||
accentPicker.setColor(colorFromEditText)
|
||||
mutableAccentColor.value = colorFromEditText
|
||||
} catch (e: IllegalArgumentException) {}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,7 +108,11 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
|||
prefs.managerAccent = colorFromEditText
|
||||
} catch (e: IllegalArgumentException) {
|
||||
log("VMTheme", getString(R.string.failed_accent))
|
||||
Toast.makeText(requireActivity(), getString(R.string.failed_accent), Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
getString(R.string.failed_accent),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import com.vanced.manager.BuildConfig.MANAGER_LANGUAGES
|
|||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerLanguageBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.getLanguageFormat
|
||||
import com.vanced.manager.utils.managerLang
|
||||
|
||||
|
@ -43,7 +43,7 @@ class ManagerLanguageDialog : BindingBottomSheetDialogFragment<DialogManagerLang
|
|||
val language = prefs.managerLang
|
||||
root.findViewWithTag<ThemedMaterialRadioButton>(language)?.isChecked = true
|
||||
languageSave.setOnClickListener {
|
||||
val newPref = binding.languageRadiogroup.getCheckedButtonTag()
|
||||
val newPref = binding.languageRadiogroup.checkedButtonTag
|
||||
if (language != newPref) {
|
||||
prefs.managerLang = newPref
|
||||
dismiss()
|
||||
|
|
|
@ -7,7 +7,7 @@ import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
|||
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerThemeBinding
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.managerTheme
|
||||
|
||||
class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBinding>() {
|
||||
|
@ -36,7 +36,7 @@ class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBi
|
|||
val theme = prefs.managerTheme
|
||||
root.findViewWithTag<MaterialRadioButton>(theme).isChecked = true
|
||||
themeSave.setOnClickListener {
|
||||
val newPref = themeRadiogroup.getCheckedButtonTag()
|
||||
val newPref = themeRadiogroup.checkedButtonTag
|
||||
if (theme != newPref) {
|
||||
prefs.managerTheme = newPref
|
||||
dismiss()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
@ -57,7 +58,8 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
bindData()
|
||||
if (arguments?.getBoolean(TAG_FORCE_UPDATE) == true) {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.please_be_patient)
|
||||
downloadManager(requireActivity())
|
||||
} else {
|
||||
checkUpdates()
|
||||
|
@ -77,10 +79,12 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun DialogManagerUpdateBinding.bindDownloadProgress() {
|
||||
downloadProgress.observe(viewLifecycleOwner) {
|
||||
managerUpdateProgressbar.progress = it
|
||||
managerUpdateProgressbar.isVisible = it != 0
|
||||
managerUpdateProgressbarContainer.isVisible = it != 0
|
||||
managerUpdateProgress.text = "$it%"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,10 +100,12 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
|||
|
||||
private fun checkUpdates() {
|
||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.please_be_patient)
|
||||
downloadManager(requireActivity())
|
||||
} else {
|
||||
binding.managerUpdatePatient.text = requireActivity().getString(R.string.update_not_found)
|
||||
binding.managerUpdatePatient.text =
|
||||
requireActivity().getString(R.string.update_not_found)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import com.google.android.material.radiobutton.MaterialRadioButton
|
|||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.databinding.DialogManagerVariantBinding
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.managerVariant
|
||||
|
||||
class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVariantBinding>() {
|
||||
|
@ -37,11 +37,11 @@ class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVaria
|
|||
val variant = prefs.managerVariant
|
||||
root.findViewWithTag<MaterialRadioButton>(variant).isChecked = true
|
||||
variantSave.setOnClickListener {
|
||||
val newPref = variantRadiogroup.getCheckedButtonTag()
|
||||
val newPref = variantRadiogroup.checkedButtonTag
|
||||
if (variant != newPref) {
|
||||
prefs.managerVariant =
|
||||
if (newPref == "root" && Shell.rootAccess()) {
|
||||
"root"
|
||||
"root"
|
||||
} else {
|
||||
"nonroot"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.vanced.manager.ui.dialogs
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
|
@ -34,8 +33,12 @@ class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPrefe
|
|||
private fun bindData() {
|
||||
with(binding) {
|
||||
val musicVersionsConv = musicVersions.value?.value?.convertToAppVersions()
|
||||
musicInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.music))
|
||||
musicVersion.text = getString(R.string.chosen_version, prefs.musicVersion?.formatVersion(requireActivity()))
|
||||
musicInstallTitle.text =
|
||||
getString(R.string.app_installation_preferences, getString(R.string.music))
|
||||
musicVersion.text = getString(
|
||||
R.string.chosen_version,
|
||||
prefs.musicVersion?.formatVersion(requireActivity())
|
||||
)
|
||||
openVersionSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
showDialog(
|
||||
|
@ -46,36 +49,12 @@ class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPrefe
|
|||
)
|
||||
}
|
||||
musicInstall.setOnClickListener {
|
||||
fun downloadMusic(version: String? = null) {
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.music),
|
||||
version = version
|
||||
)
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.music)
|
||||
)
|
||||
}
|
||||
if (prefs.managerVariant == "nonroot" && isMicrogBroken && prefs.musicVersion?.getLatestAppVersion(musicVersions.value?.value ?: listOf(""))?.replace(".", "")?.take(3)?.toIntOrNull() ?: 0 >= 411 &&
|
||||
!PackageHelper.isPackageInstalled(
|
||||
AppUtils.musicPkg,
|
||||
requireActivity().packageManager
|
||||
)
|
||||
) {
|
||||
MaterialAlertDialogBuilder(requireActivity()).apply {
|
||||
setTitle(R.string.microg_bug)
|
||||
setMessage(R.string.microg_bug_summary_music)
|
||||
setPositiveButton(R.string.auth_dialog_ok) { _, _ ->
|
||||
downloadMusic("4.07.51")
|
||||
}
|
||||
setNeutralButton(R.string.cancel) { _, _ ->
|
||||
dismiss()
|
||||
}
|
||||
create()
|
||||
}.applyAccent()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
downloadMusic()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,11 @@ class SelectAppsDialog : BindingBottomSheetDialogFragment<DialogSelectAppsBindin
|
|||
}
|
||||
selectAppsSave.setOnClickListener {
|
||||
if (ad.apps.all { app -> !app.isChecked }) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.select_at_least_one_app,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
prefs.edit {
|
||||
|
|
|
@ -32,7 +32,7 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
|||
}
|
||||
|
||||
private fun bindData() {
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
servicedSlider.value = prefs.getInt("serviced_sleep_timer", 1).toFloat()
|
||||
servicedCancel.setOnClickListener {
|
||||
dismiss()
|
||||
|
@ -41,12 +41,26 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
|||
try {
|
||||
arrayOf("vanced", "music").forEach { app ->
|
||||
if (scriptExists(app)) {
|
||||
val apkFPath = "${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
getPackageDir(requireActivity(), getPkgNameRoot(app))?.let { it1 -> requireActivity().writeServiceDScript(apkFPath, it1, app) }
|
||||
val apkFPath =
|
||||
"${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||
getPackageDir(
|
||||
requireActivity(),
|
||||
getPkgNameRoot(app)
|
||||
)?.let { it1 ->
|
||||
requireActivity().writeServiceDScript(
|
||||
apkFPath,
|
||||
it1,
|
||||
app
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(requireActivity(), R.string.script_save_failed, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.script_save_failed,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -45,11 +45,12 @@ class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() {
|
|||
TextView.BufferType.EDITABLE
|
||||
)
|
||||
urlSave.setOnClickListener {
|
||||
val finalUrl = if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
||||
urlInput.text?.removeSuffix("/").toString()
|
||||
} else {
|
||||
"https://${urlInput.text}".removeSuffix("/")
|
||||
}
|
||||
val finalUrl =
|
||||
if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
||||
urlInput.text?.removeSuffix("/").toString()
|
||||
} else {
|
||||
"https://${urlInput.text}".removeSuffix("/")
|
||||
}
|
||||
saveUrl(finalUrl)
|
||||
}
|
||||
urlReset.setOnClickListener { saveUrl(baseUrl) }
|
||||
|
|
|
@ -19,7 +19,8 @@ import com.vanced.manager.utils.lang
|
|||
import com.vanced.manager.utils.vanced
|
||||
import java.util.*
|
||||
|
||||
class VancedLanguageSelectionDialog : BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
||||
class VancedLanguageSelectionDialog :
|
||||
BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -52,7 +53,11 @@ class VancedLanguageSelectionDialog : BindingBottomSheetDialogFragment<DialogVan
|
|||
}
|
||||
}
|
||||
if (chosenLangs.isEmpty()) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_lang, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireActivity(),
|
||||
R.string.select_at_least_one_lang,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
prefs.lang = chosenLangs.joinToString()
|
||||
|
|
|
@ -3,14 +3,11 @@ package com.vanced.manager.ui.dialogs
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogVancedPreferencesBinding
|
||||
import com.vanced.manager.utils.*
|
||||
import com.vanced.manager.utils.AppUtils.vancedPkg
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
import java.util.*
|
||||
|
||||
class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() {
|
||||
|
@ -43,9 +40,16 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
|||
showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT))
|
||||
}
|
||||
val vancedVersionsConv = vancedVersions.value?.value?.convertToAppVersions()
|
||||
vancedInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
||||
vancedTheme.text = getString(R.string.chosen_theme, installPrefs.theme?.convertToAppTheme(requireActivity()))
|
||||
vancedVersion.text = getString(R.string.chosen_version, defPrefs.vancedVersion?.formatVersion(requireActivity()))
|
||||
vancedInstallTitle.text =
|
||||
getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
||||
vancedTheme.text = getString(
|
||||
R.string.chosen_theme,
|
||||
installPrefs.theme?.convertToAppTheme(requireActivity())
|
||||
)
|
||||
vancedVersion.text = getString(
|
||||
R.string.chosen_version,
|
||||
defPrefs.vancedVersion?.formatVersion(requireActivity())
|
||||
)
|
||||
vancedLang.text = getString(R.string.chosen_lang, showLang)
|
||||
openThemeSelectorLayout.setOnClickListener {
|
||||
dismiss()
|
||||
|
@ -68,33 +72,12 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
|||
if (showLang.isEmpty()) {
|
||||
installPrefs.lang = "en"
|
||||
}
|
||||
|
||||
fun downloadVanced(version: String? = null) {
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.vanced),
|
||||
version = version
|
||||
)
|
||||
dismiss()
|
||||
showDialog(
|
||||
AppDownloadDialog.newInstance(
|
||||
app = getString(R.string.vanced)
|
||||
)
|
||||
}
|
||||
|
||||
if (defPrefs.managerVariant == "nonroot" && isMicrogBroken && defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))?.take(2)?.toIntOrNull() == 16 && !isPackageInstalled(vancedPkg, requireActivity().packageManager)) {
|
||||
MaterialAlertDialogBuilder(requireActivity()).apply {
|
||||
setTitle(R.string.microg_bug)
|
||||
setMessage(R.string.microg_bug_summary)
|
||||
setPositiveButton(R.string.auth_dialog_ok) { _, _ ->
|
||||
downloadVanced("15.43.32")
|
||||
}
|
||||
setNeutralButton(R.string.cancel) { _, _ ->
|
||||
dismiss()
|
||||
}
|
||||
create()
|
||||
}.applyAccent()
|
||||
return@setOnClickListener
|
||||
}
|
||||
|
||||
downloadVanced()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,13 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
|||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||
import com.vanced.manager.utils.checkedButtonTag
|
||||
import com.vanced.manager.utils.convertToAppTheme
|
||||
import com.vanced.manager.utils.getCheckedButtonTag
|
||||
import com.vanced.manager.utils.theme
|
||||
import com.vanced.manager.utils.vanced
|
||||
|
||||
class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
class VancedThemeSelectorDialog :
|
||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -24,7 +25,12 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
}
|
||||
}
|
||||
|
||||
private val prefs by lazy { requireActivity().getSharedPreferences("installPrefs", Context.MODE_PRIVATE) }
|
||||
private val prefs by lazy {
|
||||
requireActivity().getSharedPreferences(
|
||||
"installPrefs",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
}
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -51,7 +57,7 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
tag.isChecked = true
|
||||
}
|
||||
dialogSave.setOnClickListener {
|
||||
val checkedTag = binding.dialogRadiogroup.getCheckedButtonTag()
|
||||
val checkedTag = binding.dialogRadiogroup.checkedButtonTag
|
||||
if (checkedTag != null) {
|
||||
prefs.theme = checkedTag
|
||||
}
|
||||
|
@ -60,7 +66,7 @@ class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomR
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadButtons() = vanced.value?.array<String>("themes")?.value?.map {theme ->
|
||||
private fun loadButtons() = vanced.value?.array<String>("themes")?.value?.map { theme ->
|
||||
ThemedMaterialRadioButton(requireActivity()).apply {
|
||||
text = theme.convertToAppTheme(requireActivity())
|
||||
tag = theme
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package com.vanced.manager.ui.events
|
||||
|
||||
open class Event<out T>(private val content: T) {
|
||||
|
||||
private var hasBeenHandled = false
|
||||
|
||||
/**
|
||||
* Returns the content and prevents its use again.
|
||||
*/
|
||||
fun getContentIfNotHandled(): T? {
|
||||
return if (hasBeenHandled) {
|
||||
null
|
||||
} else {
|
||||
hasBeenHandled = true
|
||||
content
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content, even if it's already been handled.
|
||||
*/
|
||||
fun peekContent(): T = content
|
||||
}
|
|
@ -6,7 +6,6 @@ import android.view.LayoutInflater
|
|||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.edit
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.preference.PreferenceManager
|
||||
|
@ -37,11 +36,11 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun dataBind() {
|
||||
requireActivity().title = getString(R.string.title_about)
|
||||
binding.root.setOnClickListener {
|
||||
binding.aboutVersionCard.setOnClickListener {
|
||||
showDialog(
|
||||
AppInfoDialog.newInstance(
|
||||
appName = getString(R.string.app_name),
|
||||
appIcon = AppCompatResources.getDrawable(requireActivity(), R.mipmap.ic_launcher),
|
||||
appIcon = R.mipmap.ic_launcher,
|
||||
changelog = manager.value?.string("changelog")
|
||||
)
|
||||
)
|
||||
|
@ -61,10 +60,18 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
|||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val devSettings = prefs.getBoolean("devSettings", false)
|
||||
if (!devSettings) {
|
||||
Toast.makeText(requireContext(), "Dev options unlocked!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Dev options unlocked!",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
prefs.edit { putBoolean("devSettings", true) }
|
||||
} else
|
||||
Toast.makeText(requireContext(), "Dev options already unlocked", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Dev options already unlocked",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
|
||||
}
|
||||
return@setOnTouchListener true
|
||||
|
|
|
@ -3,22 +3,15 @@ package com.vanced.manager.ui.fragments
|
|||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.databinding.FragmentDevSettingsBinding
|
||||
import com.vanced.manager.ui.WelcomeActivity
|
||||
import com.vanced.manager.ui.dialogs.ManagerUpdateDialog
|
||||
import com.vanced.manager.ui.dialogs.URLChangeDialog
|
||||
import com.vanced.manager.utils.authCrowdin
|
||||
|
||||
class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
||||
|
||||
|
@ -40,7 +33,6 @@ class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
|||
bindWelcomeLauncher()
|
||||
bindForceUpdate()
|
||||
bindChannelURL()
|
||||
bindCrowdin()
|
||||
bindKernelArch()
|
||||
bindAndroidVersion()
|
||||
}
|
||||
|
@ -72,31 +64,6 @@ class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun FragmentDevSettingsBinding.bindCrowdin() {
|
||||
if (BuildConfig.ENABLE_CROWDIN_AUTH) {
|
||||
val isAuthorized = Crowdin.isAuthorized()
|
||||
crowdinCategory.isVisible = true
|
||||
|
||||
crowdinAuth.isVisible = !isAuthorized
|
||||
screenshotUploading.isVisible = isAuthorized
|
||||
realTimeUpdates.isVisible = isAuthorized
|
||||
|
||||
crowdinAuth.setOnClickListener {
|
||||
requireActivity().authCrowdin()
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
if (!Settings.canDrawOverlays(requireActivity())) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
("package:" + requireActivity().packageName).toUri()
|
||||
)
|
||||
startActivityForResult(intent, 69)
|
||||
}
|
||||
|
||||
Crowdin.authorize(requireActivity())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun FragmentDevSettingsBinding.bindKernelArch() {
|
||||
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
|
||||
|
||||
|
|
|
@ -41,7 +41,12 @@ class GrantRootFragment : BindingFragment<FragmentGrantRootBinding>() {
|
|||
|
||||
private fun grantRoot() {
|
||||
if (Shell.rootAccess()) {
|
||||
getDefaultSharedPreferences(requireActivity()).edit { putString("vanced_variant", "root") }
|
||||
getDefaultSharedPreferences(requireActivity()).edit {
|
||||
putString(
|
||||
"vanced_variant",
|
||||
"root"
|
||||
)
|
||||
}
|
||||
navigateToFirstLaunch()
|
||||
} else {
|
||||
Toast.makeText(requireActivity(), R.string.root_not_granted, Toast.LENGTH_SHORT).show()
|
||||
|
|
|
@ -9,26 +9,25 @@ import android.view.LayoutInflater
|
|||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.crowdin.platform.util.inflateWithCrowdin
|
||||
import com.github.florent37.viewtooltip.ViewTooltip
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.google.android.flexbox.JustifyContent
|
||||
import com.vanced.manager.BuildConfig.VERSION_CODE
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.AppListAdapter
|
||||
import com.vanced.manager.adapter.ExpandableAppListAdapter
|
||||
import com.vanced.manager.adapter.LinkAdapter
|
||||
import com.vanced.manager.adapter.SponsorAdapter
|
||||
import com.vanced.manager.core.ui.base.BindingFragment
|
||||
import com.vanced.manager.core.ui.ext.showDialog
|
||||
import com.vanced.manager.databinding.FragmentHomeBinding
|
||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModelFactory
|
||||
import com.vanced.manager.utils.isFetching
|
||||
import com.vanced.manager.utils.manager
|
||||
|
||||
class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||
|
||||
|
@ -37,13 +36,9 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
const val REFRESH_HOME = "REFRESH_HOME"
|
||||
}
|
||||
|
||||
private val viewModel: HomeViewModel by viewModels {
|
||||
HomeViewModelFactory(requireActivity())
|
||||
}
|
||||
private val viewModel: HomeViewModel by viewModels()
|
||||
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }
|
||||
private val prefs by lazy { PreferenceManager.getDefaultSharedPreferences(requireActivity()) }
|
||||
private var tooltip: ViewTooltip? = null
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -58,29 +53,27 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
private fun bindData() {
|
||||
requireActivity().title = getString(R.string.title_home)
|
||||
setHasOptionsMenu(true)
|
||||
with (binding) {
|
||||
with(binding) {
|
||||
homeRefresh.setOnRefreshListener { viewModel.fetchData() }
|
||||
isFetching.observe(viewLifecycleOwner) { homeRefresh.isRefreshing = it }
|
||||
|
||||
if (prefs.getBoolean("show_changelog_tooltip", true)) {
|
||||
tooltip = ViewTooltip
|
||||
.on(recyclerAppList)
|
||||
.position(ViewTooltip.Position.TOP)
|
||||
.autoHide(false, 0)
|
||||
.color(ResourcesCompat.getColor(requireActivity().resources, R.color.Twitter, null))
|
||||
.withShadow(false)
|
||||
.corner(25)
|
||||
.onHide {
|
||||
prefs.edit { putBoolean("show_changelog_tooltip", false) }
|
||||
}
|
||||
.text(requireActivity().getString(R.string.app_changelog_tooltip))
|
||||
|
||||
tooltip?.show()
|
||||
}
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
if (prefs.contains("LastVersionCode")) {
|
||||
if (prefs.getInt("LastVersionCode", -1) < VERSION_CODE) {
|
||||
showDialog(
|
||||
AppInfoDialog.newInstance(
|
||||
appName = getString(R.string.app_name),
|
||||
appIcon = R.mipmap.ic_launcher,
|
||||
changelog = manager.value?.string("changelog")
|
||||
)
|
||||
)
|
||||
prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
||||
}
|
||||
} else prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
||||
|
||||
recyclerAppList.apply {
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
adapter = AppListAdapter(requireActivity(), viewModel, tooltip)
|
||||
adapter = ExpandableAppListAdapter(requireActivity(), viewModel /*, tooltip*/)
|
||||
setHasFixedSize(true)
|
||||
}
|
||||
|
||||
|
@ -103,14 +96,12 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflateWithCrowdin(R.menu.toolbar_menu, menu, resources)
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.toolbar_menu, menu)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
||||
tooltip?.close()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -121,7 +112,11 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
|||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
INSTALL_FAILED -> installAlertBuilder(intent.getStringExtra("errorMsg").toString(), intent.getStringExtra("fullErrorMsg"), requireActivity())
|
||||
INSTALL_FAILED -> installAlertBuilder(
|
||||
intent.getStringExtra("errorMsg").toString(),
|
||||
intent.getStringExtra("fullErrorMsg"),
|
||||
requireActivity()
|
||||
)
|
||||
REFRESH_HOME -> viewModel.fetchData()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,15 +38,18 @@ class LogFragment : BindingFragment<FragmentLogBinding>() {
|
|||
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||
val minute = calendar.get(Calendar.MINUTE)
|
||||
val second = calendar.get(Calendar.SECOND)
|
||||
val log = File(requireActivity().getExternalFilesDir("logs")?.path + "/$year$month${day}_$hour$minute$second.log")
|
||||
val savePath = requireActivity().getExternalFilesDir("logs")?.path + "/$year$month${day}_$hour$minute$second.log"
|
||||
val log =
|
||||
File(savePath)
|
||||
FileWriter(log).apply {
|
||||
append(logs)
|
||||
flush()
|
||||
close()
|
||||
}
|
||||
Toast.makeText(requireActivity(), R.string.logs_saved, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireActivity(), getString(R.string.logs_saved, savePath), Toast.LENGTH_SHORT).show()
|
||||
} catch (e: IOException) {
|
||||
Toast.makeText(requireActivity(), R.string.logs_not_saved, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireActivity(), R.string.logs_not_saved, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
|||
|
||||
private fun actionOnClickAppsFab() {
|
||||
if (selectAdapter.apps.all { app -> !app.isChecked }) {
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
val prefs = getDefaultSharedPreferences(requireActivity())
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.view.MenuInflater
|
|||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
|
@ -34,6 +35,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private val prefs by lazy { getDefaultSharedPreferences(requireActivity()) }
|
||||
|
||||
private lateinit var variant: String
|
||||
private lateinit var parentActivity: FragmentActivity
|
||||
|
||||
override fun binding(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -43,6 +45,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
|
||||
override fun otherSetups() {
|
||||
setHasOptionsMenu(true)
|
||||
parentActivity = requireActivity()
|
||||
bindData()
|
||||
}
|
||||
|
||||
|
@ -63,8 +66,8 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
|
||||
private fun FragmentSettingsBinding.bindRecycler() {
|
||||
notificationsRecycler.apply {
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
adapter = GetNotifAdapter(requireActivity())
|
||||
layoutManager = LinearLayoutManager(parentActivity)
|
||||
adapter = GetNotifAdapter(parentActivity)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +75,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
firebase.setOnCheckedListener { _, isChecked ->
|
||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isChecked)
|
||||
FirebasePerformance.getInstance().isPerformanceCollectionEnabled = isChecked
|
||||
FirebaseAnalytics.getInstance(requireActivity()).setAnalyticsCollectionEnabled(isChecked)
|
||||
FirebaseAnalytics.getInstance(parentActivity).setAnalyticsCollectionEnabled(isChecked)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +96,13 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private fun FragmentSettingsBinding.bindClearFiles() {
|
||||
clearFiles.setOnClickListener {
|
||||
with(requireActivity()) {
|
||||
listOf("vanced/nonroot", "vanced/root", "music/nonroot", "music/root", "microg").forEach { dir ->
|
||||
listOf(
|
||||
"vanced/nonroot",
|
||||
"vanced/root",
|
||||
"music/nonroot",
|
||||
"music/root",
|
||||
"microg"
|
||||
).forEach { dir ->
|
||||
File(getExternalFilesDir(dir)?.path.toString()).deleteRecursively()
|
||||
}
|
||||
Toast.makeText(this, getString(R.string.cleared_files), Toast.LENGTH_SHORT).show()
|
||||
|
@ -116,7 +125,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
}
|
||||
|
||||
private fun FragmentSettingsBinding.bindManagerAccentColor() {
|
||||
managerAccentColor.apply{
|
||||
managerAccentColor.apply {
|
||||
setSummary(prefs.getInt("manager_accent_color", defAccentColor).toHex())
|
||||
setOnClickListener { showDialog(ManagerAccentColorDialog()) }
|
||||
accentColor.observe(viewLifecycleOwner) {
|
||||
|
@ -128,14 +137,15 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
|||
private fun FragmentSettingsBinding.bindManagerLanguage() {
|
||||
val langPref = prefs.getString("manager_lang", "System Default")
|
||||
managerLanguage.apply {
|
||||
setSummary(getLanguageFormat(requireActivity(), requireNotNull(langPref)))
|
||||
setSummary(getLanguageFormat(parentActivity, requireNotNull(langPref)))
|
||||
setOnClickListener { showDialog(ManagerLanguageDialog()) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val devSettings = getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
||||
val devSettings =
|
||||
getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
||||
if (devSettings) {
|
||||
inflater.inflate(R.menu.dev_settings_menu, menu)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.AndroidViewModel
|
|||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.openUrl
|
||||
|
||||
class AboutViewModel(application: Application): AndroidViewModel(application) {
|
||||
class AboutViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
fun openUrl(url: String) {
|
||||
openUrl(url, R.color.GitHub, getApplication())
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.DISCORD
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.REDDIT
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TELEGRAM
|
||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TWITTER
|
||||
import com.vanced.manager.adapter.SponsorAdapter.Companion.BRAVE
|
||||
import com.vanced.manager.model.ButtonTag
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.model.RootDataModel
|
||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||
|
@ -40,10 +39,11 @@ import com.vanced.manager.utils.PackageHelper.uninstallRootApk
|
|||
import com.vanced.manager.utils.PackageHelper.vancedInstallFilesExist
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
private val prefs = getDefaultSharedPreferences(activity)
|
||||
private val prefs = getDefaultSharedPreferences(context)
|
||||
private val variant get() = prefs.getString("vanced_variant", "nonroot")
|
||||
private val context: Context get() = getApplication()
|
||||
|
||||
val vancedModel = MutableLiveData<DataModel>()
|
||||
val vancedRootModel = MutableLiveData<RootDataModel>()
|
||||
|
@ -54,14 +54,13 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
|
||||
fun fetchData() {
|
||||
viewModelScope.launch {
|
||||
loadJson(activity)
|
||||
Crowdin.forceUpdate(activity)
|
||||
loadJson(context)
|
||||
}
|
||||
}
|
||||
|
||||
private val microgToast = Toast.makeText(activity, R.string.no_microg, Toast.LENGTH_LONG)
|
||||
|
||||
fun openUrl(url: String) {
|
||||
private val microgToast = Toast.makeText(context, R.string.no_microg, Toast.LENGTH_LONG)
|
||||
|
||||
fun openUrl(context: Context, url: String) {
|
||||
val color: Int =
|
||||
when (url) {
|
||||
DISCORD -> R.color.Discord
|
||||
|
@ -71,75 +70,83 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
BRAVE -> R.color.Brave
|
||||
else -> R.color.Vanced
|
||||
}
|
||||
|
||||
openUrl(url, color, activity)
|
||||
|
||||
openUrl(url, color, context)
|
||||
}
|
||||
|
||||
fun launchApp(app: String, isRoot: Boolean) {
|
||||
val componentName = when (app) {
|
||||
activity.getString(R.string.vanced) -> if (isRoot) ComponentName(vancedRootPkg, "$vancedRootPkg.HomeActivity") else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
|
||||
activity.getString(R.string.music) -> if (isRoot) ComponentName(musicRootPkg, "$musicRootPkg.activities.MusicActivity") else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
|
||||
activity.getString(R.string.microg) -> ComponentName(microgPkg, "org.microg.gms.ui.SettingsActivity")
|
||||
context.getString(R.string.vanced) -> if (isRoot) ComponentName(
|
||||
vancedRootPkg,
|
||||
"$vancedRootPkg.HomeActivity"
|
||||
) else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
|
||||
context.getString(R.string.music) -> if (isRoot) ComponentName(
|
||||
musicRootPkg,
|
||||
"$musicRootPkg.activities.MusicActivity"
|
||||
) else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
|
||||
context.getString(R.string.microg) -> ComponentName(
|
||||
microgPkg,
|
||||
"org.microg.gms.ui.SettingsActivity"
|
||||
)
|
||||
else -> throw IllegalArgumentException("Can't open this app")
|
||||
}
|
||||
try {
|
||||
activity.startActivity(Intent().setComponent(componentName))
|
||||
context.startActivity(Intent().setComponent(componentName))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
log("VMHMV", e.toString())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun openInstallDialog(view: View, app: String) {
|
||||
if (variant == "nonroot" && app != activity.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
||||
fun openInstallDialog(fragmentManager: FragmentManager, buttonTag: ButtonTag?, app: String) {
|
||||
if (variant == "nonroot" && app != context.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
||||
microgToast.show()
|
||||
return
|
||||
}
|
||||
|
||||
if ((view as MaterialButton).text == activity.getString(R.string.update)) {
|
||||
if (buttonTag == ButtonTag.UPDATE) {
|
||||
when (app) {
|
||||
activity.getString(R.string.vanced) -> VancedPreferencesDialog().show(activity)
|
||||
activity.getString(R.string.music) -> MusicPreferencesDialog().show(activity)
|
||||
else -> AppDownloadDialog.newInstance(app).show(activity)
|
||||
context.getString(R.string.vanced) -> VancedPreferencesDialog().show(fragmentManager)
|
||||
context.getString(R.string.music) -> MusicPreferencesDialog().show(fragmentManager)
|
||||
else -> AppDownloadDialog.newInstance(app).show(fragmentManager)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
when (app) {
|
||||
activity.getString(R.string.vanced) -> {
|
||||
context.getString(R.string.vanced) -> {
|
||||
when (variant) {
|
||||
"nonroot" -> {
|
||||
if (vancedInstallFilesExist(activity)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
if (vancedInstallFilesExist(context)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
VancedPreferencesDialog().show(activity)
|
||||
VancedPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
"root" -> {
|
||||
VancedPreferencesDialog().show(activity)
|
||||
VancedPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.getString(R.string.music) -> {
|
||||
context.getString(R.string.music) -> {
|
||||
when (variant) {
|
||||
"nonroot" -> {
|
||||
if (musicApkExists(activity)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
if (musicApkExists(context)) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
MusicPreferencesDialog().show(activity)
|
||||
MusicPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
"root" -> {
|
||||
MusicPreferencesDialog().show(activity)
|
||||
MusicPreferencesDialog().show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
activity.getString(R.string.microg) -> {
|
||||
if (apkExist(activity, "microg.apk")) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||
context.getString(R.string.microg) -> {
|
||||
if (apkExist(context, "microg.apk")) {
|
||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
||||
} else {
|
||||
AppDownloadDialog.newInstance(app).show(activity)
|
||||
AppDownloadDialog.newInstance(app).show(fragmentManager)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,23 +155,67 @@ open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
|||
|
||||
fun uninstallPackage(pkg: String) {
|
||||
if (variant == "root" && uninstallRootApk(pkg)) {
|
||||
viewModelScope.launch { loadJson(activity) }
|
||||
viewModelScope.launch { loadJson(context) }
|
||||
} else {
|
||||
uninstallApk(pkg, activity)
|
||||
uninstallApk(pkg, context)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
with (activity) {
|
||||
with(context) {
|
||||
if (variant == "root") {
|
||||
vancedRootModel.value = RootDataModel(vanced, this, this, vancedRootPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced), "vanced")
|
||||
musicRootModel.value = RootDataModel(music, this, this, musicRootPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music), "music")
|
||||
vancedRootModel.value = RootDataModel(
|
||||
vanced,
|
||||
this,
|
||||
vancedRootPkg,
|
||||
this.getString(R.string.vanced),
|
||||
this.getString(R.string.description_vanced),
|
||||
R.drawable.ic_vanced,
|
||||
"vanced"
|
||||
)
|
||||
musicRootModel.value = RootDataModel(
|
||||
music,
|
||||
this,
|
||||
musicRootPkg,
|
||||
this.getString(R.string.music),
|
||||
this.getString(R.string.description_vanced_music),
|
||||
R.drawable.ic_music,
|
||||
"music"
|
||||
)
|
||||
} else {
|
||||
vancedModel.value = DataModel(vanced, this, this, vancedPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced))
|
||||
musicModel.value = DataModel(music, this, this, musicPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music))
|
||||
microgModel.value = DataModel(microg, this, this, microgPkg, this.getString(R.string.microg), AppCompatResources.getDrawable(this, R.drawable.ic_microg))
|
||||
vancedModel.value = DataModel(
|
||||
vanced,
|
||||
this,
|
||||
vancedPkg,
|
||||
this.getString(R.string.vanced),
|
||||
this.getString(R.string.description_vanced),
|
||||
R.drawable.ic_vanced
|
||||
)
|
||||
musicModel.value = DataModel(
|
||||
music,
|
||||
this,
|
||||
musicPkg,
|
||||
this.getString(R.string.music),
|
||||
this.getString(R.string.description_vanced_music),
|
||||
R.drawable.ic_music
|
||||
)
|
||||
microgModel.value = DataModel(
|
||||
microg,
|
||||
this,
|
||||
microgPkg,
|
||||
this.getString(R.string.microg),
|
||||
this.getString(R.string.description_microg),
|
||||
R.drawable.ic_microg
|
||||
)
|
||||
}
|
||||
managerModel.value = DataModel(manager, this, this, managerPkg, this.getString(R.string.app_name), AppCompatResources.getDrawable(this, R.mipmap.ic_launcher))
|
||||
managerModel.value = DataModel(
|
||||
manager,
|
||||
this,
|
||||
managerPkg,
|
||||
this.getString(R.string.app_name),
|
||||
"Just manager meh",
|
||||
R.mipmap.ic_launcher
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
class HomeViewModelFactory(private val activity: FragmentActivity) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return HomeViewModel(activity) as T
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@ import android.text.style.ForegroundColorSpan
|
|||
import android.text.style.StyleSpan
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.BuildConfig.APPLICATION_ID
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||
|
@ -20,7 +21,7 @@ import java.io.IOException
|
|||
import java.security.MessageDigest
|
||||
import java.util.*
|
||||
|
||||
object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
object AppUtils : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||
|
||||
const val vancedPkg = "com.vanced.android.youtube"
|
||||
const val vancedRootPkg = "com.google.android.youtube"
|
||||
|
@ -40,16 +41,24 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
SpannableString("$tag: $message\n").apply {
|
||||
setSpan(ForegroundColorSpan(Color.parseColor("#2e73ff")), 0, tag.length + 1, 0)
|
||||
setSpan(StyleSpan(Typeface.BOLD), 0, tag.length + 1, 0)
|
||||
setSpan(ForegroundColorSpan(Color.MAGENTA), tag.length + 2, tag.length + message.length + 2, 0)
|
||||
setSpan(
|
||||
ForegroundColorSpan(Color.MAGENTA),
|
||||
tag.length + 2,
|
||||
tag.length + message.length + 2,
|
||||
0
|
||||
)
|
||||
}
|
||||
)
|
||||
Log.d(tag, message)
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.d(tag, message)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendRefresh(context: Context): Job {
|
||||
return launch {
|
||||
delay(700)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
LocalBroadcastManager.getInstance(context)
|
||||
.sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,11 +66,12 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
return launch {
|
||||
delay(700)
|
||||
installing.postValue(false)
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
||||
LocalBroadcastManager.getInstance(context)
|
||||
.sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
||||
}
|
||||
}
|
||||
|
||||
fun sendFailure(error: MutableList<String>, context: Context) {
|
||||
fun sendFailure(error: List<String>, context: Context) {
|
||||
sendFailure(error.joinToString(" "), context)
|
||||
}
|
||||
|
||||
|
@ -92,7 +102,7 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
private fun printableHexString(data: ByteArray): String {
|
||||
// Create Hex String
|
||||
val hexString: StringBuilder = StringBuilder()
|
||||
for (aMessageDigest:Byte in data) {
|
||||
for (aMessageDigest: Byte in data) {
|
||||
var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt())
|
||||
while (h.length < 2)
|
||||
h = "0$h"
|
||||
|
@ -127,12 +137,9 @@ object AppUtils: CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
status.contains("ModApk_Missing") -> context.getString(R.string.modapk_missing)
|
||||
status.contains("Files_Missing_VA") -> context.getString(R.string.files_missing_va)
|
||||
status.contains("Path_Missing") -> context.getString(R.string.path_missing)
|
||||
status.contains("INSTALL_FAILED_INTERNAL_ERROR: Permission Denied") -> {
|
||||
if (context.isMiuiOptimizationsEnabled)
|
||||
context.getString(R.string.installation_miui)
|
||||
else
|
||||
context.getString(R.string.installation_blocked)
|
||||
}
|
||||
status.contains("INSTALL_FAILED_INTERNAL_ERROR: Permission Denied") -> context.getString(
|
||||
R.string.installation_miui
|
||||
)
|
||||
else -> context.getString(R.string.installation_failed)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.os.Build
|
||||
|
||||
fun getArch(): String = when {
|
||||
Build.SUPPORTED_ABIS.contains("x86") -> "x86"
|
||||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||
else -> "armeabi_v7a"
|
||||
}
|
||||
}
|
||||
|
||||
val intentFlags =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
||||
PendingIntent.FLAG_MUTABLE
|
||||
else
|
||||
0
|
|
@ -49,12 +49,20 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
|
||||
if (response.isSuccessful) {
|
||||
launch {
|
||||
if (response.body()?.let { writeFile(it, context.getExternalFilesDir(fileFolder)?.path + "/" + fileName) } == true) {
|
||||
if (response.body()?.let {
|
||||
writeFile(
|
||||
it,
|
||||
context.getExternalFilesDir(fileFolder)?.path + "/" + fileName
|
||||
)
|
||||
} == true) {
|
||||
onDownloadComplete()
|
||||
} else {
|
||||
onError("Could not save file")
|
||||
downloadProgress.postValue(0)
|
||||
log("VMDownloader", "Failed to save file: $url\n${response.errorBody()}")
|
||||
log(
|
||||
"VMDownloader",
|
||||
"Failed to save file: $url\n${response.errorBody()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -111,33 +119,40 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
|||
|
||||
fun downloadManager(context: Context) {
|
||||
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
|
||||
download(url,"https://github.com/YTVanced/VancedManager/", "manager", "manager.apk", context, onDownloadComplete = {
|
||||
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
||||
val uri =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
FileProvider.getUriForFile(context, "${context.packageName}.provider", apk)
|
||||
else
|
||||
Uri.fromFile(apk)
|
||||
download(
|
||||
url,
|
||||
"https://github.com/YTVanced/VancedManager/",
|
||||
"manager",
|
||||
"manager.apk",
|
||||
context,
|
||||
onDownloadComplete = {
|
||||
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
||||
val uri =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
FileProvider.getUriForFile(context, "${context.packageName}.provider", apk)
|
||||
else
|
||||
Uri.fromFile(apk)
|
||||
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
log("VMDownloader", e.stackTraceToString())
|
||||
} finally {
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
}, onError = {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
"manager.apk"
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
log("VMDownloader", e.stackTraceToString())
|
||||
} finally {
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
downloadingFile.postValue(
|
||||
context.getString(
|
||||
R.string.error_downloading,
|
||||
"manager.apk"
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.widget.RadioGroup
|
|||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
|
@ -17,57 +18,58 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.utils.AppUtils.log
|
||||
import java.util.*
|
||||
|
||||
fun RadioGroup.getCheckedButtonTag(): String? {
|
||||
return findViewById<MaterialRadioButton>(checkedRadioButtonId)?.tag?.toString()
|
||||
}
|
||||
val RadioGroup.checkedButtonTag: String?
|
||||
get() = findViewById<MaterialRadioButton>(
|
||||
checkedRadioButtonId
|
||||
)?.tag?.toString()
|
||||
|
||||
fun DialogFragment.show(activity: FragmentActivity) {
|
||||
fun DialogFragment.show(fragmentManager: FragmentManager) {
|
||||
try {
|
||||
show(activity.supportFragmentManager, "")
|
||||
show(fragmentManager, "")
|
||||
} catch (e: Exception) {
|
||||
log("VMUI", e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
|
||||
fun DialogFragment.show(activity: FragmentActivity) {
|
||||
show(activity.supportFragmentManager)
|
||||
}
|
||||
|
||||
fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
|
||||
|
||||
fun String.formatVersion(context: Context): String = if (this == "latest") context.getString(R.string.install_latest) else this
|
||||
fun String.formatVersion(context: Context): String =
|
||||
if (this == "latest") context.getString(R.string.install_latest) else this
|
||||
|
||||
fun String.convertToAppTheme(context: Context): String {
|
||||
return with(context) {
|
||||
getString(R.string.light_plus_other, if (this@convertToAppTheme == "dark") getString(R.string.vanced_dark) else getString(R.string.vanced_black))
|
||||
}
|
||||
fun String.convertToAppTheme(context: Context): String = with(context) {
|
||||
getString(
|
||||
R.string.light_plus_other,
|
||||
if (this@convertToAppTheme == "dark") getString(R.string.vanced_dark) else getString(R.string.vanced_black)
|
||||
)
|
||||
}
|
||||
|
||||
fun String.getLatestAppVersion(versions: List<String>): String = if (this == "latest") versions.reversed()[0] else this
|
||||
fun String.getLatestAppVersion(versions: List<String>): String =
|
||||
if (this == "latest") versions.reversed()[0] else this
|
||||
|
||||
fun Context.lifecycleOwner(): LifecycleOwner? {
|
||||
var curContext = this
|
||||
var maxDepth = 20
|
||||
while (maxDepth-- > 0 && curContext !is LifecycleOwner) {
|
||||
curContext = (curContext as ContextWrapper).baseContext
|
||||
val Context.lifecycleOwner: LifecycleOwner?
|
||||
get() = when (this) {
|
||||
is LifecycleOwner -> this
|
||||
!is LifecycleOwner -> (this as ContextWrapper).baseContext as LifecycleOwner
|
||||
else -> null
|
||||
}
|
||||
return if (curContext is LifecycleOwner) {
|
||||
curContext
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Int.toHex(): String = java.lang.String.format("#%06X", 0xFFFFFF and this)
|
||||
|
||||
//Material team decided to keep their LinearProgressIndicator final
|
||||
//At least extension methods exist
|
||||
fun LinearProgressIndicator.applyAccent() {
|
||||
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||
with(accentColor.value!!) {
|
||||
setIndicatorColor(this)
|
||||
trackColor = ColorUtils.setAlphaComponent(this, 70)
|
||||
}
|
||||
}
|
||||
|
||||
fun MaterialAlertDialogBuilder.applyAccent() {
|
||||
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||
fun MaterialAlertDialogBuilder.showWithAccent() {
|
||||
with(accentColor.value!!) {
|
||||
show().apply {
|
||||
getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(this@with)
|
||||
getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(this@with)
|
||||
|
@ -86,5 +88,5 @@ fun Context.writeServiceDScript(apkFPath: String, path: String, app: String) {
|
|||
chcon u:object_r:apk_data_file:s0 $apkFPath
|
||||
mount -o bind $apkFPath $path
|
||||
""".trimIndent()
|
||||
SuFileOutputStream.open(shellFileZ).use { out -> out.write(script.toByteArray())}
|
||||
SuFileOutputStream.open(shellFileZ).use { out -> out.write(script.toByteArray()) }
|
||||
}
|
||||
|
|
|
@ -4,7 +4,11 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
|
||||
val Context.installPrefs: SharedPreferences get() = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val Context.installPrefs: SharedPreferences
|
||||
get() = getSharedPreferences(
|
||||
"installPrefs",
|
||||
Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
var SharedPreferences.lang
|
||||
get() = getString("lang", getDefaultVancedLanguages())
|
||||
|
|
|
@ -32,8 +32,8 @@ val music = MutableLiveData<JsonObject?>()
|
|||
val microg = MutableLiveData<JsonObject?>()
|
||||
val manager = MutableLiveData<JsonObject?>()
|
||||
|
||||
val vancedVersions = MutableLiveData<JsonArray<String>>()
|
||||
val musicVersions = MutableLiveData<JsonArray<String>>()
|
||||
val vancedVersions = MutableLiveData<JsonArray<String>?>()
|
||||
val musicVersions = MutableLiveData<JsonArray<String>?>()
|
||||
|
||||
val isFetching = MutableLiveData<Boolean>()
|
||||
|
||||
|
@ -43,16 +43,23 @@ var baseInstallUrl = ""
|
|||
|
||||
fun openUrl(url: String, color: Int, context: Context) {
|
||||
try {
|
||||
val customTabPrefs = getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
||||
val customTabPrefs =
|
||||
getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
||||
if (customTabPrefs) {
|
||||
val builder = CustomTabsIntent.Builder()
|
||||
val params = CustomTabColorSchemeParams.Builder().setToolbarColor(ContextCompat.getColor(context, color))
|
||||
val params = CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, color))
|
||||
builder.setDefaultColorSchemeParams(params.build())
|
||||
val customTabsIntent = builder.build()
|
||||
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
customTabsIntent.launchUrl(context, url.toUri())
|
||||
} else
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||
context.startActivity(
|
||||
Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
url.toUri()
|
||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
||||
|
@ -103,7 +110,7 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
|
|||
val versions = getJson("$baseInstallUrl/versions.json?$fetchTime")
|
||||
isMicrogBroken = latest?.boolean("is_microg_broken") ?: false
|
||||
vanced.postValue(latest?.obj("vanced"))
|
||||
vancedVersions.postValue(versions?.array("vanced") )
|
||||
vancedVersions.postValue(versions?.array("vanced"))
|
||||
music.postValue(latest?.obj("music"))
|
||||
musicVersions.postValue(versions?.array("music"))
|
||||
microg.postValue(latest?.obj("microg"))
|
||||
|
@ -137,4 +144,4 @@ fun checkSHA256(sha256: String, updateFile: File): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
const val baseUrl = "https://vancedapp.com/api/v1"
|
||||
const val baseUrl = "https://api.vancedapp.com/api/v1"
|
|
@ -23,7 +23,10 @@ class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
|||
val sysLocale = Resources.getSystem().configuration.locale
|
||||
val locale = when {
|
||||
pref == "System Default" -> Locale(sysLocale.language, sysLocale.country)
|
||||
pref?.length!! > 2 -> Locale(pref.substring(0, pref.length - 3), pref.substring(pref.length - 2))
|
||||
pref?.length!! > 2 -> Locale(
|
||||
pref.substring(0, pref.length - 3),
|
||||
pref.substring(pref.length - 2)
|
||||
)
|
||||
else -> Locale(pref)
|
||||
}
|
||||
currentLocale = locale
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.LocaleList
|
||||
import android.provider.Settings
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.vanced.manager.R
|
||||
import java.util.*
|
||||
|
||||
|
@ -34,7 +29,10 @@ fun getLanguageFormat(context: Context, language: String): String {
|
|||
@Suppress("DEPRECATION")
|
||||
fun getDefaultVancedLanguages(): String {
|
||||
val serverLangs = vanced.value?.array("langs") ?: mutableListOf("")
|
||||
val sysLocales = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Resources.getSystem().configuration.locales.toLangTags() else arrayOf(Resources.getSystem().configuration.locale.language)
|
||||
val sysLocales =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Resources.getSystem().configuration.locales.toLangTags() else arrayOf(
|
||||
Resources.getSystem().configuration.locale.language
|
||||
)
|
||||
val finalLangs = mutableListOf<String>()
|
||||
sysLocales.forEach { sysLocale ->
|
||||
when {
|
||||
|
@ -53,26 +51,4 @@ fun LocaleList.toLangTags(): Array<String> {
|
|||
langTags[i] = langTags[i].substring(0, 2)
|
||||
}
|
||||
return langTags
|
||||
}
|
||||
|
||||
fun Activity.authCrowdin() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (!Settings.canDrawOverlays(this)) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:$packageName")
|
||||
)
|
||||
startActivityForResult(intent, 69)
|
||||
return
|
||||
}
|
||||
Crowdin.authorize(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.onActivityResult(requestCode: Int) {
|
||||
if (requestCode == 69 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
Crowdin.authorize(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,4 +5,8 @@ import android.provider.Settings
|
|||
|
||||
private const val MIUI_OPTIMIZATION = "miui_optimization"
|
||||
|
||||
val Context.isMiuiOptimizationsEnabled: Boolean get() = Settings.Secure.getString(contentResolver, MIUI_OPTIMIZATION) == "1"
|
||||
val Context.isMiuiOptimizationsEnabled: Boolean
|
||||
get() = Settings.Secure.getString(
|
||||
contentResolver,
|
||||
MIUI_OPTIMIZATION
|
||||
) == "1"
|
|
@ -9,7 +9,6 @@ import android.content.pm.PackageManager
|
|||
import android.os.Build
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.core.installer.AppInstallerService
|
||||
import com.vanced.manager.core.installer.AppUninstallerService
|
||||
import com.vanced.manager.utils.AppUtils.log
|
||||
|
@ -22,23 +21,18 @@ import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
object PackageHelper {
|
||||
|
||||
const val apkInstallPath = "/data/adb"
|
||||
private const val INSTALLER_TAG = "VMInstall"
|
||||
private val vancedThemes = vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
|
||||
|
||||
init {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
.setTimeout(10)
|
||||
)
|
||||
}
|
||||
private val vancedThemes =
|
||||
vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
|
||||
|
||||
private fun getAppNameRoot(pkg: String): String {
|
||||
return when (pkg) {
|
||||
|
@ -64,6 +58,7 @@ object PackageHelper {
|
|||
else -> ""
|
||||
}
|
||||
}
|
||||
|
||||
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
||||
return try {
|
||||
packageManager.getPackageInfo(packageName, 0)
|
||||
|
@ -81,7 +76,7 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
fun getPkgVerCode(pkg: String, pm:PackageManager): Int? {
|
||||
fun getPkgVerCode(pkg: String, pm: PackageManager): Int? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
pm.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
||||
else
|
||||
|
@ -122,9 +117,15 @@ object PackageHelper {
|
|||
if (files?.isNotEmpty() == true) {
|
||||
for (file in files) {
|
||||
when {
|
||||
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add("base")
|
||||
file.name.matches(Regex("split_config\\.(..)\\.apk")) && !splitFiles.contains("lang") -> splitFiles.add("lang")
|
||||
(file.name.startsWith("split_config.arm") || file.name.startsWith("split_config.x86")) && !splitFiles.contains("arch") -> splitFiles.add("arch")
|
||||
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add(
|
||||
"base"
|
||||
)
|
||||
file.name.matches(Regex("split_config\\.(..)\\.apk")) && !splitFiles.contains(
|
||||
"lang"
|
||||
) -> splitFiles.add("lang")
|
||||
(file.name.startsWith("split_config.arm") || file.name.startsWith("split_config.x86")) && !splitFiles.contains(
|
||||
"arch"
|
||||
) -> splitFiles.add("arch")
|
||||
}
|
||||
|
||||
if (splitFiles.size == 3) {
|
||||
|
@ -148,7 +149,7 @@ object PackageHelper {
|
|||
fun uninstallApk(pkg: String, context: Context) {
|
||||
val callbackIntent = Intent(context, AppUninstallerService::class.java)
|
||||
callbackIntent.putExtra("pkg", pkg)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
try {
|
||||
context.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
} catch (e: Exception) {
|
||||
|
@ -158,9 +159,13 @@ object PackageHelper {
|
|||
|
||||
fun install(path: String, context: Context) {
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val params =
|
||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
params.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||
}
|
||||
val sessionId: Int
|
||||
var session: PackageInstaller.Session? = null
|
||||
try {
|
||||
|
@ -191,49 +196,56 @@ object PackageHelper {
|
|||
if (apk.name != "root.apk") {
|
||||
val newPath = "/data/local/tmp/${apk.name}"
|
||||
|
||||
//moving apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("mv ${apk.path} $newPath").exec()
|
||||
val command = Shell.su("pm install $newPath").exec()
|
||||
//Copy apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("cp ${apk.path} $newPath").exec()
|
||||
val command = Shell.su("pm install -r $newPath").exec()
|
||||
Shell.su("rm $newPath").exec()
|
||||
if (command.isSuccess) {
|
||||
return true
|
||||
} else {
|
||||
if (!command.isSuccess) {
|
||||
sendFailure(command.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
private fun installRootApp(context: Context, app: String, appVerCode: Int?, pkg: String, modApkBool: (fileName: String) -> Boolean) = CoroutineScope(Dispatchers.IO).launch {
|
||||
Shell.getShell {
|
||||
val apkFilesPath = context.getExternalFilesDir("$app/root")?.path
|
||||
val files = File(apkFilesPath.toString()).listFiles()?.toList()
|
||||
if (files != null) {
|
||||
val modApk: File? = files.lastOrNull { modApkBool(it.name) }
|
||||
if (modApk != null) {
|
||||
if (appVerCode != null) {
|
||||
if (overwriteBase(modApk, files, appVerCode, pkg, app, context)) {
|
||||
setInstallerPackage(context, pkg, playStorePkg)
|
||||
log(INSTALLER_TAG, "Finished installation")
|
||||
sendRefresh(context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
} else {
|
||||
sendFailure(listOf("appVerCode is null").toMutableList(), context)
|
||||
private fun installRootApp(
|
||||
context: Context,
|
||||
app: String,
|
||||
appVerCode: Int?,
|
||||
pkg: String,
|
||||
modApkBool: (fileName: String) -> Boolean
|
||||
) = CoroutineScope(Dispatchers.IO).launch {
|
||||
if (!isMagiskInstalled()) {
|
||||
sendFailure("NO_MAGISK", context)
|
||||
sendCloseDialog(context)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val apkFilesPath = context.getExternalFilesDir("$app/root")?.path
|
||||
val files = File(apkFilesPath.toString()).listFiles()?.toList()
|
||||
if (files != null) {
|
||||
val modApk: File? = files.lastOrNull { modApkBool(it.name) }
|
||||
if (modApk != null) {
|
||||
if (appVerCode != null) {
|
||||
if (overwriteBase(modApk, files, appVerCode, pkg, app, context)) {
|
||||
setInstallerPackage(context, pkg, playStorePkg)
|
||||
log(INSTALLER_TAG, "Finished installation")
|
||||
sendRefresh(context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
} else {
|
||||
sendFailure(listOf("ModApk_Missing").toMutableList(), context)
|
||||
sendFailure("appVerCode is null", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
} else {
|
||||
sendFailure(listOf("Files_Missing_VA").toMutableList(), context)
|
||||
sendFailure("ModApk_Missing", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
|
||||
} else {
|
||||
sendFailure("Files_Missing_VA", context)
|
||||
sendCloseDialog(context)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -268,9 +280,13 @@ object PackageHelper {
|
|||
val folder = File(context.getExternalFilesDir("$appName/nonroot")?.path.toString())
|
||||
var session: PackageInstaller.Session? = null
|
||||
val sessionId: Int
|
||||
val sessionParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
val sessionParams =
|
||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
sessionParams.setRequireUserAction(PackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||
}
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
||||
try {
|
||||
sessionId = packageInstaller.createSession(sessionParams)
|
||||
session = packageInstaller.openSession(sessionId)
|
||||
|
@ -296,16 +312,25 @@ object PackageHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private fun installSplitApkFilesRoot(apkFiles: List<File>?, context: Context) : Boolean {
|
||||
private fun installSplitApkFilesRoot(apkFiles: List<File>?, context: Context): Boolean {
|
||||
val filenames = arrayOf("black.apk", "dark.apk", "blue.apk", "pink.apk", "hash.json")
|
||||
log(INSTALLER_TAG, "installing split apk files: ${apkFiles?.map { it.name }}")
|
||||
val sessionId = Shell.su("pm install-create").exec().out.joinToString(" ").filter { it.isDigit() }.toInt()
|
||||
val sessionId =
|
||||
Shell.su("pm install-create -r").exec().out.joinToString(" ").filter { it.isDigit() }
|
||||
.toIntOrNull()
|
||||
|
||||
if (sessionId == null) {
|
||||
sendFailure("Session ID is null", context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
apkFiles?.filter { !filenames.contains(it.name) }?.forEach { apkFile ->
|
||||
val apkName = apkFile.name
|
||||
log(INSTALLER_TAG, "installing APK: $apkName")
|
||||
val newPath = "/data/local/tmp/$apkName"
|
||||
// Moving apk to avoid permission denials
|
||||
Shell.su("mv ${apkFile.path} $newPath").exec()
|
||||
//Copy apk to tmp folder in order to avoid permission denials
|
||||
Shell.su("cp ${apkFile.path} $newPath").exec()
|
||||
val command = Shell.su("pm install-write $sessionId $apkName $newPath").exec()
|
||||
Shell.su("rm $newPath").exec()
|
||||
if (!command.isSuccess) {
|
||||
|
@ -316,12 +341,12 @@ object PackageHelper {
|
|||
}
|
||||
log(INSTALLER_TAG, "committing...")
|
||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||
if (installResult.isSuccess) {
|
||||
return true
|
||||
if (!installResult.isSuccess) {
|
||||
sendFailure(installResult.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
sendFailure(installResult.out, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
@ -353,12 +378,18 @@ object PackageHelper {
|
|||
return false
|
||||
}
|
||||
|
||||
private fun setupScript(apkFPath: String, path: String, app: String, pkg: String, context: Context): Boolean
|
||||
{
|
||||
private fun setupScript(
|
||||
apkFPath: String,
|
||||
path: String,
|
||||
app: String,
|
||||
pkg: String,
|
||||
context: Context
|
||||
): Boolean {
|
||||
try {
|
||||
log(INSTALLER_TAG, "Setting up script")
|
||||
context.writeServiceDScript(apkFPath, path, app)
|
||||
Shell.su("""echo "#!/system/bin/sh\nwhile read line; do echo \${"$"}{line} | grep $pkg | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts" > /data/adb/post-fs-data.d/$app.sh""").exec()
|
||||
Shell.su("""echo "#!/system/bin/sh\nwhile read line; do echo \${"$"}{line} | grep $pkg | awk '{print \${'$'}2}' | xargs umount -l; done< /proc/mounts" > /data/adb/post-fs-data.d/$app.sh""")
|
||||
.exec()
|
||||
return Shell.su("chmod 744 /data/adb/service.d/$app.sh").exec().isSuccess
|
||||
} catch (e: IOException) {
|
||||
sendFailure(e.stackTraceToString(), context)
|
||||
|
@ -371,7 +402,8 @@ object PackageHelper {
|
|||
private fun linkApp(apkFPath: String, pkg: String, path: String): Boolean {
|
||||
log(INSTALLER_TAG, "Linking app")
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """).exec()
|
||||
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """)
|
||||
.exec()
|
||||
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
||||
Thread.sleep(500)
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
@ -383,12 +415,17 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//check version and perform action based on result
|
||||
private fun checkVersion(versionCode: Int, baseApkFiles: List<File>, pkg: String, context: Context): Boolean {
|
||||
private fun checkVersion(
|
||||
versionCode: Int,
|
||||
baseApkFiles: List<File>,
|
||||
pkg: String,
|
||||
context: Context
|
||||
): Boolean {
|
||||
log(INSTALLER_TAG, "Checking stock version")
|
||||
val path = getPackageDir(context, pkg)
|
||||
if (path != null) {
|
||||
if (path.contains("/data/app/")) {
|
||||
when (getVersionNumber(pkg, context)?.let { compareVersion(it,versionCode) } ) {
|
||||
when (getVersionNumber(pkg, context)?.let { compareVersion(it, versionCode) }) {
|
||||
1 -> return fixHigherVer(baseApkFiles, pkg, context)
|
||||
-1 -> return installStock(baseApkFiles, pkg, context)
|
||||
}
|
||||
|
@ -402,7 +439,7 @@ object PackageHelper {
|
|||
private fun getPkgInfo(pkg: String, context: Context): PackageInfo? {
|
||||
return try {
|
||||
context.packageManager.getPackageInfo(pkg, 0)
|
||||
} catch (e:Exception) {
|
||||
} catch (e: Exception) {
|
||||
log(INSTALLER_TAG, "Unable to get package info")
|
||||
null
|
||||
}
|
||||
|
@ -417,10 +454,13 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
//uninstall current update and install base that works with patch
|
||||
private fun fixHigherVer(apkFiles: List<File>, pkg: String, context: Context) : Boolean {
|
||||
private fun fixHigherVer(apkFiles: List<File>, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Downgrading stock")
|
||||
if (uninstallRootApk(pkg)) {
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(apkFiles, context) else installRootMusic(apkFiles, context)
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
||||
apkFiles,
|
||||
context
|
||||
) else installRootMusic(apkFiles, context)
|
||||
}
|
||||
sendFailure(listOf("Failed_Uninstall").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
|
@ -430,77 +470,68 @@ object PackageHelper {
|
|||
//install stock youtube matching vanced version
|
||||
private fun installStock(baseApkFiles: List<File>, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Installing stock")
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(baseApkFiles, context) else installRootMusic(baseApkFiles, context)
|
||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
||||
baseApkFiles,
|
||||
context
|
||||
) else installRootMusic(baseApkFiles, context)
|
||||
}
|
||||
|
||||
private fun isMagiskInstalled() = Shell.su("magisk -c").exec().isSuccess
|
||||
|
||||
//set chcon to apk_data_file
|
||||
private fun chConV(apkFPath: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Running chcon")
|
||||
val response = Shell.su("chcon u:object_r:apk_data_file:s0 $apkFPath").exec()
|
||||
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
|
||||
return if (response.isSuccess) {
|
||||
true
|
||||
} else {
|
||||
if (!response.isSuccess) {
|
||||
sendFailure(response.out, context)
|
||||
sendCloseDialog(context)
|
||||
false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//move patch to data/app
|
||||
private fun moveAPK(apkFile: String, path: String, pkg: String, context: Context) : Boolean {
|
||||
private fun moveAPK(apkFile: String, path: String, pkg: String, context: Context): Boolean {
|
||||
log(INSTALLER_TAG, "Moving app")
|
||||
val apkinF = SuFile.open(apkFile)
|
||||
val apkoutF = SuFile.open(path)
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
||||
if(apkinF.exists()) {
|
||||
try {
|
||||
Shell.su("am force-stop $pkg").exec()
|
||||
|
||||
//Shell.su("rm -r SuFile.open(path).parent")
|
||||
|
||||
copy(apkinF,apkoutF)
|
||||
Shell.su("chmod 644 $path").exec().isSuccess
|
||||
return if(Shell.su("chown system:system $path").exec().isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(listOf("Chown_Fail").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
catch (e: IOException)
|
||||
{
|
||||
sendFailure(listOf("${e.message}").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
log(INSTALLER_TAG, e.stackTraceToString())
|
||||
return false
|
||||
}
|
||||
val mv = Shell.su("cp $apkFile $path").exec()
|
||||
if (!mv.isSuccess) {
|
||||
sendFailure(mv.out.apply { add(0, "MV_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
sendFailure(listOf("IFile_Missing").toMutableList(), context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
val chmod = Shell.su("chmod 644 $path").exec()
|
||||
if (!chmod.isSuccess) {
|
||||
sendFailure(chmod.out.apply { add(0, "Chmod_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun copy(src: File, dst: File) {
|
||||
val cmd = Shell.su("mv ${src.absolutePath} ${dst.absolutePath}").exec().isSuccess
|
||||
log("ZLog", cmd.toString())
|
||||
val chown = Shell.su("chown system:system $path").exec()
|
||||
if (!chown.isSuccess) {
|
||||
sendFailure(chown.out.apply { add(0, "Chown_Fail") }, context)
|
||||
sendCloseDialog(context)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
private fun getVersionNumber(pkg: String, context: Context): Int? {
|
||||
try {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(0xFFFFFFFF).toInt()
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(
|
||||
0xFFFFFFFF
|
||||
).toInt()
|
||||
else
|
||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).versionCode
|
||||
}
|
||||
catch (e : Exception) {
|
||||
} catch (e: Exception) {
|
||||
val execRes = Shell.su("dumpsys package $pkg | grep versionCode").exec()
|
||||
if(execRes.isSuccess) {
|
||||
if (execRes.isSuccess) {
|
||||
val result = execRes.out
|
||||
var version = 0
|
||||
result
|
||||
|
@ -524,9 +555,8 @@ object PackageHelper {
|
|||
val execRes = Shell.su("dumpsys package $pkg | grep codePath").exec()
|
||||
if (execRes.isSuccess) {
|
||||
val result = execRes.out
|
||||
for (line in result)
|
||||
{
|
||||
if(line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
for (line in result) {
|
||||
if (line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
}
|
||||
}
|
||||
null
|
||||
|
@ -538,7 +568,8 @@ object PackageHelper {
|
|||
try {
|
||||
log(INSTALLER_TAG, "Setting installer package to $installer for $target")
|
||||
val installerUid = context.packageManager.getPackageUid(installer, 0)
|
||||
val res = Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
||||
val res =
|
||||
Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
||||
if (res.out.any { line -> line.contains("Success") }) {
|
||||
log(INSTALLER_TAG, "Installer package successfully set")
|
||||
return
|
||||
|
|
|
@ -7,15 +7,15 @@ import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
|||
|
||||
val Context.defPrefs: SharedPreferences get() = getDefaultSharedPreferences(this)
|
||||
|
||||
var SharedPreferences.managerTheme
|
||||
var SharedPreferences.managerTheme
|
||||
get() = getString("manager_theme", "System Default")
|
||||
set(value) = edit { putString("manager_theme", value) }
|
||||
|
||||
var SharedPreferences.managerAccent
|
||||
var SharedPreferences.managerAccent
|
||||
get() = getInt("manager_accent_color", defAccentColor)
|
||||
set(value) = edit { putInt("manager_accent_color", value) }
|
||||
|
||||
var SharedPreferences.managerVariant
|
||||
var SharedPreferences.managerVariant
|
||||
get() = getString("vanced_variant", "nonroot")
|
||||
set(value) = edit { putString("vanced_variant", value) }
|
||||
|
||||
|
@ -23,11 +23,11 @@ var SharedPreferences.managerLang
|
|||
get() = getString("manager_lang", "System Default")
|
||||
set(value) = edit { putString("manager_lang", value) }
|
||||
|
||||
var SharedPreferences.installUrl
|
||||
var SharedPreferences.installUrl
|
||||
get() = getString("install_url", baseUrl)
|
||||
set(value) = edit { putString("install_url", value) }
|
||||
|
||||
var SharedPreferences.vancedVersion
|
||||
var SharedPreferences.vancedVersion
|
||||
get() = getString("vanced_version", "latest")
|
||||
set(value) = edit { putString("vanced_version", value) }
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ const val LIGHT = "Light"
|
|||
const val DARK = "Dark"
|
||||
const val SYSTEM_DEFAULT = "System Default"
|
||||
|
||||
val mutableAccentColor = MutableLiveData<Int>()
|
||||
val mutableAccentColor = MutableLiveData(defAccentColor)
|
||||
val accentColor: LiveData<Int> = mutableAccentColor
|
||||
|
||||
var currentTheme = ""
|
||||
|
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 438 B After Width: | Height: | Size: 355 B |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 319 B After Width: | Height: | Size: 271 B |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 576 B After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 636 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<size
|
||||
android:width="48dp"
|
||||
android:height="48dp" />
|
||||
|
||||
<solid
|
||||
android:color="?colorSurface" />
|
||||
|
||||
<corners
|
||||
android:radius="12dp" />
|
||||
|
||||
</shape>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zM5,18h14v2L5,20z"/>
|
||||
</vector>
|
|
@ -1,9 +0,0 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="25dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|