mirror of
https://github.com/YTVanced/VancedManager
synced 2024-11-18 01:05:10 +00:00
Compare commits
No commits in common. "dev" and "v2.3.0" have entirely different histories.
241 changed files with 4354 additions and 6390 deletions
29
.github/ISSUE_TEMPLATE/bug-issue-template.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug-issue-template.md
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
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)
|
||||||
|
- This issue keeps re-occurring every time I try
|
||||||
|
- For MIUI users: I disabled MIUI optimisation
|
||||||
|
|
||||||
|
**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:**
|
41
.github/ISSUE_TEMPLATE/bug-issue-template.yml
vendored
41
.github/ISSUE_TEMPLATE/bug-issue-template.yml
vendored
|
@ -1,41 +0,0 @@
|
||||||
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
|
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
|
@ -1 +0,0 @@
|
||||||
blank_issues_enabled: false
|
|
|
@ -1,29 +0,0 @@
|
||||||
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
|
|
13
.github/workflows/debug.yml
vendored
13
.github/workflows/debug.yml
vendored
|
@ -4,28 +4,27 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
paths-ignore:
|
|
||||||
- '**.md'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: set up JDK 11
|
- name: set up JDK 1.8
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 1.8
|
||||||
|
|
||||||
- name: Grant rights
|
- name: Grant rights
|
||||||
run: chmod +x ./gradlew
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
- name: Build debug APK with Gradle
|
- name: Build project with Gradle
|
||||||
|
run: ./gradlew build
|
||||||
|
|
||||||
|
- name: Build Debug APK with Gradle
|
||||||
run: ./gradlew assembleDebug
|
run: ./gradlew assembleDebug
|
||||||
|
|
||||||
- name: Upload Debug
|
- name: Upload Debug
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,5 +6,5 @@ app/src/main/java/com/vanced/manager/core/base/DummyJava.java
|
||||||
app/build/
|
app/build/
|
||||||
app/release
|
app/release
|
||||||
local.properties
|
local.properties
|
||||||
|
/.github/
|
||||||
*.iml
|
*.iml
|
||||||
.vscode/
|
|
||||||
|
|
85
README.md
85
README.md
|
@ -1,64 +1,35 @@
|
||||||
|
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
|
||||||
|
## The FAQ app has just been suspended due to "Impersonating Vanced", an appeal has been filed, reporting other apps which impersonate Vanced on the play store is appreciated.
|
||||||
|
|
||||||
|
[![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
|
# Vanced Manager
|
||||||
<div>
|
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.
|
||||||
|
|
||||||
[![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)
|
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
|
||||||
|
|
||||||
</div>
|
##### Background download/installation feature is no longer supported due to problems with some ROMs, please do NOT report issues regarding background activity.
|
||||||
|
|
||||||
## Introduction
|
## Vanced Developers
|
||||||
|
- xfileFIN
|
||||||
|
- KevinX8
|
||||||
|
- Zanezam
|
||||||
|
- Laura Almeida
|
||||||
|
|
||||||
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.
|
## Vanced Manager Developers
|
||||||
|
- Xinto (X1nto)
|
||||||
|
- Koopah (ostajic)
|
||||||
|
|
||||||
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.
|
## Contributors
|
||||||
|
- AioiLight
|
||||||
After 3 months of development, we are finally ready to Introduce [Vanced Manager](https://github.com/YTVanced/VancedManager) to the masses!!
|
- HaliksaR
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
</br>
|
|
||||||
|
|
||||||
<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
|
||||||
|
|
|
@ -48,3 +48,6 @@ Vanced Manager sucks 100% of your CPU to mine Bitcoins, this is a new technique
|
||||||
![Zanezam](https://i.imgur.com/QVcXA6q.png)
|
![Zanezam](https://i.imgur.com/QVcXA6q.png)
|
||||||
- Laura Almeida
|
- Laura Almeida
|
||||||
![Laura Almeida](https://i.imgur.com/ovVD939.png)
|
![Laura Almeida](https://i.imgur.com/ovVD939.png)
|
||||||
|
|
||||||
|
###### If someone is reading this pls help me, KevinX8 is bullying me and forces me to develop manager. please send bobs and veganas and call 911
|
||||||
|
|
||||||
|
|
|
@ -6,27 +6,28 @@ plugins {
|
||||||
id("com.google.firebase.crashlytics")
|
id("com.google.firebase.crashlytics")
|
||||||
id("com.google.firebase.firebase-perf")
|
id("com.google.firebase.firebase-perf")
|
||||||
id("androidx.navigation.safeargs.kotlin")
|
id("androidx.navigation.safeargs.kotlin")
|
||||||
id("kotlin-android")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk = 31
|
compileSdkVersion(30)
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.vanced.manager"
|
applicationId = "com.vanced.manager"
|
||||||
minSdk = 21
|
minSdkVersion(21)
|
||||||
targetSdk = 31
|
targetSdkVersion(30)
|
||||||
versionCode = 262
|
versionCode = 230
|
||||||
versionName = "2.6.2 (Crimson)"
|
versionName = "2.3.0 (MicroShitMoment)"
|
||||||
|
|
||||||
vectorDrawables {
|
vectorDrawables.useSupportLibrary = true
|
||||||
useSupportLibrary = true
|
|
||||||
|
buildConfigField("String[]", "MANAGER_LANGUAGES", "{" + getLanguages() + "}")
|
||||||
|
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")}\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConfigField("String[]", "MANAGER_LANGUAGES", "{$languages}")
|
lintOptions {
|
||||||
}
|
|
||||||
|
|
||||||
lint {
|
|
||||||
disable("MissingTranslation", "ExtraTranslation")
|
disable("MissingTranslation", "ExtraTranslation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,14 +43,13 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
|
dataBinding = true // ObservableField migrate to flow or liveData
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
}
|
}
|
||||||
|
|
||||||
packagingOptions {
|
packagingOptions {
|
||||||
resources {
|
exclude("META-INF/DEPENDENCIES")
|
||||||
excludes += "META-INF/DEPENDENCIES"
|
exclude("META-INF/*.kotlin_module")
|
||||||
excludes += "META-INF/*.kotlin_module"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// To inline the bytecode built with JVM target 1.8 into
|
// To inline the bytecode built with JVM target 1.8 into
|
||||||
|
@ -68,72 +68,75 @@ android {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val languages: String get() {
|
fun getLanguages(): String {
|
||||||
val langs = arrayListOf("en", "bn_BD", "bn_IN", "pa_IN", "pa_PK", "pt_BR", "pt_PT", "zh_CN", "zh_TW")
|
val langs = arrayListOf("en", "bn_BD", "bn_IN", "pa_IN", "pa_PK", "pt_BR", "pt_PT", "zh_CN", "zh_TW")
|
||||||
val exceptions = arrayOf("bn", "pa", "pt", "zh")
|
val exceptions = arrayOf("bn", "pa", "pt", "zh")
|
||||||
|
|
||||||
File("$projectDir/src/main/res").listFiles()?.filter {
|
File("$projectDir/src/main/res").listFiles()?.forEach { dir ->
|
||||||
val name = it.name
|
if (dir.name.startsWith("values-") && !dir.name.contains("v23")) {
|
||||||
name.startsWith("values-") && !name.contains("v23")
|
|
||||||
}?.forEach { dir ->
|
|
||||||
val dirname = dir.name.substringAfter("-").substringBefore("-")
|
val dirname = dir.name.substringAfter("-").substringBefore("-")
|
||||||
if (!exceptions.contains(dirname)) {
|
if (!exceptions.any { dirname == it }) {
|
||||||
langs.add(dirname)
|
langs.add(dirname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return langs.joinToString(", ") { "\"$it\"" }
|
return langs.joinToString(", ") { "\"$it\"" }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation(project(":core-presentation"))
|
implementation(project(":core-presentation"))
|
||||||
implementation(project(":core-ui"))
|
implementation(project(":core-ui"))
|
||||||
|
|
||||||
implementation(project(":library-network"))
|
implementation(project(":library-network"))
|
||||||
|
|
||||||
// Kotlin
|
// Kotlin
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
implementation(kotlin("reflect"))
|
implementation(kotlin("reflect"))
|
||||||
|
|
||||||
// AndroidX
|
// AndroidX
|
||||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
implementation("androidx.appcompat:appcompat:1.2.0")
|
||||||
implementation("androidx.browser:browser:1.3.0")
|
implementation("androidx.browser:browser:1.3.0")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.1")
|
implementation("androidx.constraintlayout:constraintlayout:2.0.4")
|
||||||
implementation("androidx.core:core-ktx:1.6.0")
|
implementation("androidx.core:core-ktx:1.3.2")
|
||||||
implementation("androidx.fragment:fragment-ktx:1.3.6")
|
implementation("androidx.fragment:fragment-ktx:1.2.5")
|
||||||
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.3.1")
|
implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.2.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0")
|
||||||
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.0.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.3.5")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.3.2")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.3.5")
|
implementation("androidx.navigation:navigation-ui-ktx:2.3.2")
|
||||||
implementation("androidx.preference:preference-ktx:1.1.1")
|
implementation("androidx.preference:preference-ktx:1.1.1")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.work:work-runtime-ktx:2.7.0-rc01")
|
|
||||||
|
|
||||||
implementation("com.github.madrapps:pikolo:2.0.2")
|
//Appearance
|
||||||
implementation("com.google.android.material:material:1.5.0-alpha04")
|
implementation("com.github.madrapps:pikolo:2.0.1")
|
||||||
|
implementation("com.google.android.material:material:1.3.0-rc01")
|
||||||
|
|
||||||
// JSON parser
|
// JSON parser
|
||||||
implementation("com.beust:klaxon:5.5")
|
implementation("com.beust:klaxon:5.4")
|
||||||
|
|
||||||
|
// Crowdin
|
||||||
|
implementation("com.crowdin.platform:mobile-sdk:1.2.0")
|
||||||
|
|
||||||
|
// Tips
|
||||||
|
implementation("com.github.florent37:viewtooltip:1.2.2")
|
||||||
|
|
||||||
// HTTP networking
|
// HTTP networking
|
||||||
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
|
implementation("com.github.kittinunf.fuel:fuel:2.3.0")
|
||||||
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1")
|
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.2.3")
|
||||||
implementation("com.github.kittinunf.fuel:fuel-json:2.3.1")
|
implementation("com.github.kittinunf.fuel:fuel-json:2.2.3")
|
||||||
implementation("com.squareup.okhttp3:logging-interceptor:4.9.2")
|
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
|
||||||
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
|
||||||
|
|
||||||
// Root permissions
|
// Root permissions
|
||||||
val libsuVersion = "3.1.2"
|
implementation("com.github.topjohnwu.libsu:core:3.0.2")
|
||||||
implementation("com.github.topjohnwu.libsu:core:$libsuVersion")
|
implementation("com.github.topjohnwu.libsu:io:3.0.2")
|
||||||
implementation("com.github.topjohnwu.libsu:io:$libsuVersion")
|
|
||||||
//implementation("com.github.topjohnwu.libsu:busybox:$libsuVersion")
|
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
implementation("com.google.android.flexbox:flexbox:3.0.0")
|
implementation("com.google.android:flexbox:2.0.1")
|
||||||
|
|
||||||
// Firebase
|
// Firebase
|
||||||
implementation("com.google.firebase:firebase-analytics-ktx:19.0.2")
|
implementation("com.google.firebase:firebase-analytics-ktx:18.0.1")
|
||||||
implementation("com.google.firebase:firebase-crashlytics:18.2.3")
|
implementation("com.google.firebase:firebase-crashlytics:17.3.0")
|
||||||
implementation("com.google.firebase:firebase-messaging:22.0.0")
|
implementation("com.google.firebase:firebase-messaging:21.0.1")
|
||||||
implementation("com.google.firebase:firebase-perf:20.0.3")
|
implementation("com.google.firebase:firebase-perf:19.1.0")
|
||||||
}
|
}
|
||||||
|
|
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
# Uncomment this to preserve the line number information for
|
||||||
# debugging stack traces.
|
# debugging stack traces.
|
||||||
-keepattributes SourceFile, LineNumberTable
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<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_DELETE_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_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.SYSTEM_ALERT_WINDOW" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<!-- is required for some Android 5.x devices -->
|
<!-- is required for some Android 5.x devices -->
|
||||||
<uses-permission
|
<uses-permission
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
<package android:name="com.vanced.android.apps.youtube.music" />
|
<package android:name="com.vanced.android.apps.youtube.music" />
|
||||||
<package android:name="com.google.android.apps.youtube.music" />
|
<package android:name="com.google.android.apps.youtube.music" />
|
||||||
<package android:name="com.mgoogle.android.gms" />
|
<package android:name="com.mgoogle.android.gms" />
|
||||||
<package android:name="com.vanced.faq" />
|
|
||||||
<package android:name="com.android.vending" />
|
<package android:name="com.android.vending" />
|
||||||
</queries>
|
</queries>
|
||||||
|
|
||||||
|
@ -32,13 +31,13 @@
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true">
|
android:supportsRtl="true"
|
||||||
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SplashScreenActivity"
|
android:name=".ui.core.SplashScreenActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/SplashTheme"
|
android:theme="@style/SplashTheme">
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
@ -56,10 +55,10 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:configChanges="layoutDirection|locale|keyboardHidden|orientation|screenSize"
|
android:configChanges="layoutDirection|locale"
|
||||||
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/DarkTheme"
|
android:theme="@style/DarkTheme">
|
||||||
android:exported="true">
|
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
@ -67,7 +66,8 @@
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data
|
<data
|
||||||
android:scheme="https"
|
android:scheme="https"
|
||||||
android:host="api.vancedapp.com"/>
|
android:host="vancedapp.com"
|
||||||
|
android:pathPrefix="/downloads"/>
|
||||||
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
@ -76,8 +76,8 @@
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
android:grantUriPermissions="true"
|
android:exported="false"
|
||||||
android:exported="false">
|
android:grantUriPermissions="true">
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 29 KiB |
123
app/src/main/java/com/vanced/manager/adapter/AppListAdapter.kt
Normal file
123
app/src/main/java/com/vanced/manager/adapter/AppListAdapter.kt
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
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.lifecycle.LifecycleOwner
|
||||||
|
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.enableMusic
|
||||||
|
import com.vanced.manager.utils.enableVanced
|
||||||
|
import com.vanced.manager.utils.managerVariant
|
||||||
|
|
||||||
|
class AppListAdapter(
|
||||||
|
private val context: FragmentActivity,
|
||||||
|
private val viewModel: HomeViewModel,
|
||||||
|
private val lifecycleOwner: LifecycleOwner,
|
||||||
|
private val tooltip: ViewTooltip
|
||||||
|
) : RecyclerView.Adapter<AppListAdapter.ListViewHolder>() {
|
||||||
|
|
||||||
|
val apps = mutableListOf<String>()
|
||||||
|
private val dataModels = mutableListOf<DataModel?>()
|
||||||
|
private val rootDataModels = mutableListOf<RootDataModel?>()
|
||||||
|
private val prefs = getDefaultSharedPreferences(context)
|
||||||
|
private var itemCount = 0
|
||||||
|
|
||||||
|
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(lifecycleOwner) {
|
||||||
|
appInstallButton.text = it
|
||||||
|
}
|
||||||
|
appInstallButton.setOnClickListener {
|
||||||
|
viewModel.openInstallDialog(it, apps[position])
|
||||||
|
}
|
||||||
|
appUninstall.setOnClickListener {
|
||||||
|
dataModel?.appPkg?.let { it1 -> viewModel.uninstallPackage(it1) }
|
||||||
|
}
|
||||||
|
appLaunch.setOnClickListener {
|
||||||
|
viewModel.launchApp(apps[position], isRoot)
|
||||||
|
}
|
||||||
|
with(dataModel?.isAppInstalled?.value) {
|
||||||
|
appUninstall.isVisible = this == true
|
||||||
|
appLaunch.isVisible = this == true
|
||||||
|
}
|
||||||
|
dataModel?.isAppInstalled?.observe(lifecycleOwner) {
|
||||||
|
appUninstall.isVisible = it
|
||||||
|
appLaunch.isVisible = it
|
||||||
|
}
|
||||||
|
dataModel?.versionName?.observe(lifecycleOwner) {
|
||||||
|
appRemoteVersion.text = it
|
||||||
|
}
|
||||||
|
dataModel?.installedVersionName?.observe(lifecycleOwner) {
|
||||||
|
appInstalledVersion.text = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
|
||||||
|
val view = ViewAppBinding.inflate(LayoutInflater.from(context), 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(context.supportFragmentManager, "info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = itemCount
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
if (prefs.enableVanced) {
|
||||||
|
if (isRoot) {
|
||||||
|
rootDataModels.add(viewModel.vancedRootModel.value)
|
||||||
|
} else {
|
||||||
|
dataModels.add(viewModel.vancedModel.value)
|
||||||
|
}
|
||||||
|
apps.add(context.getString(R.string.vanced))
|
||||||
|
itemCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefs.enableMusic) {
|
||||||
|
if (isRoot) {
|
||||||
|
rootDataModels.add(viewModel.musicRootModel.value)
|
||||||
|
} else {
|
||||||
|
dataModels.add(viewModel.musicModel.value)
|
||||||
|
}
|
||||||
|
apps.add(context.getString(R.string.music))
|
||||||
|
itemCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isRoot) {
|
||||||
|
dataModels.add(viewModel.microgModel.value)
|
||||||
|
apps.add(context.getString(R.string.microg))
|
||||||
|
itemCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,199 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -8,13 +8,10 @@ import com.google.firebase.messaging.FirebaseMessaging
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.databinding.ViewNotificationSettingBinding
|
import com.vanced.manager.databinding.ViewNotificationSettingBinding
|
||||||
import com.vanced.manager.model.NotifModel
|
import com.vanced.manager.model.NotifModel
|
||||||
import com.vanced.manager.utils.defPrefs
|
|
||||||
|
|
||||||
class GetNotifAdapter(private val context: Context) :
|
class GetNotifAdapter(private val context: Context) :
|
||||||
RecyclerView.Adapter<GetNotifAdapter.GetNotifViewHolder>() {
|
RecyclerView.Adapter<GetNotifAdapter.GetNotifViewHolder>() {
|
||||||
|
|
||||||
private val prefs = context.defPrefs
|
|
||||||
|
|
||||||
private val vanced = NotifModel(
|
private val vanced = NotifModel(
|
||||||
"Vanced-Update",
|
"Vanced-Update",
|
||||||
context.getString(R.string.push_notifications, context.getString(R.string.vanced)),
|
context.getString(R.string.push_notifications, context.getString(R.string.vanced)),
|
||||||
|
@ -36,24 +33,15 @@ class GetNotifAdapter(private val context: Context) :
|
||||||
|
|
||||||
private val apps = arrayOf(vanced, music, microg)
|
private val apps = arrayOf(vanced, music, microg)
|
||||||
|
|
||||||
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) :
|
inner class GetNotifViewHolder(val binding: ViewNotificationSettingBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
val switch = binding.notifSwitch
|
val switch = binding.notifSwitch
|
||||||
|
|
||||||
fun bind(position: Int) {
|
fun bind(position: Int) {
|
||||||
val app = apps[position]
|
|
||||||
with(binding.notifSwitch) {
|
with(binding.notifSwitch) {
|
||||||
setKey(app.key)
|
setKey(apps[position].key)
|
||||||
setSummary(app.switchSummary)
|
setSummary(apps[position].switchSummary)
|
||||||
setTitle(app.switchTitle)
|
setTitle(apps[position].switchTitle)
|
||||||
setDefaultValue(true)
|
setDefaultValue(true)
|
||||||
with(prefs) {
|
|
||||||
setChecked(
|
|
||||||
getBoolean(
|
|
||||||
"enable_" + app.key.substringBefore("_"),
|
|
||||||
true
|
|
||||||
) && getBoolean(app.key, true)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,14 +57,13 @@ class LinkAdapter(
|
||||||
|
|
||||||
val links = arrayOf(instagram, youtube, github, website, telegram, twitter, discord, reddit)
|
val links = arrayOf(instagram, youtube, github, website, telegram, twitter, discord, reddit)
|
||||||
|
|
||||||
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) :
|
inner class LinkViewHolder(private val binding: ViewSocialLinkBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
val logo = binding.linkImage
|
val logo = binding.linkImage
|
||||||
|
|
||||||
fun bind(position: Int) {
|
fun bind(position: Int) {
|
||||||
binding.linkBg.setOnClickListener {
|
binding.linkBg.setOnClickListener {
|
||||||
viewModel.openUrl(context, links[position].linkUrl)
|
viewModel.openUrl(links[position].linkUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,22 +18,21 @@ class SelectAppsAdapter(private val context: Context) :
|
||||||
|
|
||||||
private val vanced = SelectAppModel(
|
private val vanced = SelectAppModel(
|
||||||
context.getString(R.string.vanced),
|
context.getString(R.string.vanced),
|
||||||
context.getString(R.string.description_vanced),
|
context.getString(R.string.select_apps_vanced),
|
||||||
"vanced",
|
"vanced",
|
||||||
prefs.enableVanced
|
prefs.enableVanced
|
||||||
)
|
)
|
||||||
|
|
||||||
private val music = SelectAppModel(
|
private val music = SelectAppModel(
|
||||||
context.getString(R.string.music),
|
context.getString(R.string.music),
|
||||||
context.getString(R.string.description_vanced_music),
|
context.getString(R.string.select_apps_music),
|
||||||
"music",
|
"music",
|
||||||
prefs.enableMusic
|
prefs.enableMusic
|
||||||
)
|
)
|
||||||
|
|
||||||
val apps = arrayOf(vanced, music)
|
val apps = arrayOf(vanced, music)
|
||||||
|
|
||||||
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) :
|
inner class SelectAppsViewHolder(binding: ViewAppCheckboxBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
val appName = binding.appCheckboxText
|
val appName = binding.appCheckboxText
|
||||||
val appDescription = binding.appCheckboxDescription
|
val appDescription = binding.appCheckboxDescription
|
||||||
val appCard = binding.appCheckboxBg
|
val appCard = binding.appCheckboxBg
|
||||||
|
|
|
@ -9,19 +9,15 @@ import com.vanced.manager.R
|
||||||
import com.vanced.manager.databinding.ViewSponsorBinding
|
import com.vanced.manager.databinding.ViewSponsorBinding
|
||||||
import com.vanced.manager.model.SponsorModel
|
import com.vanced.manager.model.SponsorModel
|
||||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||||
import com.vanced.manager.utils.LIGHT
|
|
||||||
import com.vanced.manager.utils.currentTheme
|
|
||||||
|
|
||||||
class SponsorAdapter(
|
class SponsorAdapter(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val viewModel: HomeViewModel
|
private val viewModel: HomeViewModel,
|
||||||
|
//private val json: ObservableField<JsonObject?>
|
||||||
) : RecyclerView.Adapter<SponsorAdapter.LinkViewHolder>() {
|
) : RecyclerView.Adapter<SponsorAdapter.LinkViewHolder>() {
|
||||||
|
|
||||||
private val brave = SponsorModel(
|
private val brave = SponsorModel(
|
||||||
if (currentTheme == LIGHT) AppCompatResources.getDrawable(
|
AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
||||||
context,
|
|
||||||
R.drawable.ic_brave_light
|
|
||||||
) else AppCompatResources.getDrawable(context, R.drawable.ic_brave),
|
|
||||||
"Brave",
|
"Brave",
|
||||||
BRAVE
|
BRAVE
|
||||||
)
|
)
|
||||||
|
@ -42,7 +38,7 @@ class SponsorAdapter(
|
||||||
with(binding) {
|
with(binding) {
|
||||||
sponsorName.text = sponsors[position].name
|
sponsorName.text = sponsors[position].name
|
||||||
cardSponsor.setOnClickListener {
|
cardSponsor.setOnClickListener {
|
||||||
viewModel.openUrl(context, sponsors[position].url)
|
viewModel.openUrl(sponsors[position].url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
package com.vanced.manager.adapter
|
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import com.vanced.manager.ui.fragments.GrantRootFragment
|
|
||||||
import com.vanced.manager.ui.fragments.SelectAppsFragment
|
|
||||||
import com.vanced.manager.ui.fragments.WelcomeFragment
|
|
||||||
|
|
||||||
class WelcomePageAdapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = 3
|
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
|
||||||
return when (position) {
|
|
||||||
0 -> WelcomeFragment()
|
|
||||||
1 -> SelectAppsFragment()
|
|
||||||
2 -> GrantRootFragment()
|
|
||||||
else -> throw IllegalArgumentException("Unknown fragment")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,18 +1,21 @@
|
||||||
package com.vanced.manager.core
|
package com.vanced.manager.core
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.util.Log
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.crowdin.platform.Crowdin
|
||||||
import com.vanced.manager.BuildConfig
|
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.loadJson
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class App : Application() {
|
open class App: Application() {
|
||||||
|
|
||||||
private val prefs by lazy { getDefaultSharedPreferences(this) }
|
private val prefs by lazy { getDefaultSharedPreferences(this) }
|
||||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
@ -20,15 +23,30 @@ class App : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
scope.launch { loadJson(this@App) }
|
scope.launch { loadJson(this@App) }
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
mutableAccentColor.value = prefs.managerAccent
|
|
||||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
Crowdin.init(this,
|
||||||
Shell.setDefaultBuilder(
|
CrowdinConfig.Builder().apply {
|
||||||
Shell.Builder
|
withDistributionHash(CROWDIN_HASH)
|
||||||
.create()
|
withNetworkType(NetworkType.WIFI)
|
||||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
if (ENABLE_CROWDIN_AUTH) {
|
||||||
//.setInitializers(BusyBoxInstaller::class.java) //TODO fix busybox
|
if (prefs.getBoolean("crowdin_real_time", false))
|
||||||
.setTimeout(10)
|
withRealTimeUpdates()
|
||||||
|
|
||||||
|
withSourceLanguage("en")
|
||||||
|
withAuthConfig(AuthConfig(CROWDIN_CLIENT_ID, CROWDIN_CLIENT_SECRET, null))
|
||||||
|
withScreenshotEnabled()
|
||||||
|
Log.d("test", "crowdin credentials")
|
||||||
|
}
|
||||||
|
}.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (prefs.getBoolean("crowdin_upload_screenshot", false))
|
||||||
|
Crowdin.registerScreenShotContentObserver(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
super.onConfigurationChanged(newConfig)
|
||||||
|
Crowdin.onConfigurationChanged()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,9 +2,11 @@ package com.vanced.manager.core.downloader
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.*
|
|
||||||
import com.vanced.manager.utils.DownloadHelper.download
|
import com.vanced.manager.utils.DownloadHelper.download
|
||||||
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
import com.vanced.manager.utils.PackageHelper.install
|
import com.vanced.manager.utils.PackageHelper.install
|
||||||
|
import com.vanced.manager.utils.baseInstallUrl
|
||||||
|
import com.vanced.manager.utils.microg
|
||||||
|
|
||||||
object MicrogDownloader {
|
object MicrogDownloader {
|
||||||
|
|
||||||
|
@ -16,14 +18,14 @@ object MicrogDownloader {
|
||||||
download(url, "$baseInstallUrl/", folderName, fileName, context, onDownloadComplete = {
|
download(url, "$baseInstallUrl/", folderName, fileName, context, onDownloadComplete = {
|
||||||
startMicrogInstall(context)
|
startMicrogInstall(context)
|
||||||
}, onError = {
|
}, onError = {
|
||||||
downloadingFile.postValue(context.getString(R.string.error_downloading, fileName))
|
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, fileName))
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startMicrogInstall(context: Context) {
|
fun startMicrogInstall(context: Context) {
|
||||||
installing.postValue(true)
|
downloadProgress.value?.installing?.postValue(true)
|
||||||
postReset()
|
downloadProgress.value?.postReset()
|
||||||
install("${context.getExternalFilesDir(folderName)}/$fileName", context)
|
install("${context.getExternalFilesDir(folderName)}/$fileName", context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import com.vanced.manager.utils.*
|
||||||
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
||||||
import com.vanced.manager.utils.AppUtils.validateTheme
|
import com.vanced.manager.utils.AppUtils.validateTheme
|
||||||
import com.vanced.manager.utils.DownloadHelper.download
|
import com.vanced.manager.utils.DownloadHelper.download
|
||||||
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
||||||
import com.vanced.manager.utils.PackageHelper.install
|
import com.vanced.manager.utils.PackageHelper.install
|
||||||
import com.vanced.manager.utils.PackageHelper.installMusicRoot
|
import com.vanced.manager.utils.PackageHelper.installMusicRoot
|
||||||
|
@ -13,21 +14,19 @@ import com.vanced.manager.utils.PackageHelper.installMusicRoot
|
||||||
object MusicDownloader {
|
object MusicDownloader {
|
||||||
|
|
||||||
private var variant: String? = null
|
private var variant: String? = null
|
||||||
private var musicVersion: String? = null
|
private var version: String? = null
|
||||||
private var versionCode: Int? = null
|
private var versionCode: Int? = null
|
||||||
private var baseurl = ""
|
private var baseurl = ""
|
||||||
private var folderName: String? = null
|
private var folderName: String? = null
|
||||||
private var downloadPath: String? = null
|
private var downloadPath: String? = null
|
||||||
private var hashUrl: String? = null
|
private var hashUrl: String? = null
|
||||||
|
|
||||||
fun downloadMusic(context: Context, version: String? = null) {
|
fun downloadMusic(context: Context) {
|
||||||
val prefs = context.defPrefs
|
val prefs = context.defPrefs
|
||||||
musicVersion = version ?: prefs.musicVersion?.getLatestAppVersion(
|
version = prefs.musicVersion?.getLatestAppVersion(musicVersions.value?.value ?: listOf(""))
|
||||||
musicVersions.value?.value ?: listOf("")
|
|
||||||
)
|
|
||||||
versionCode = music.value?.int("versionCode")
|
versionCode = music.value?.int("versionCode")
|
||||||
variant = prefs.managerVariant
|
variant = prefs.managerVariant
|
||||||
baseurl = "$baseInstallUrl/music/v$musicVersion"
|
baseurl = "$baseInstallUrl/music/v$version"
|
||||||
folderName = "music/$variant"
|
folderName = "music/$variant"
|
||||||
downloadPath = context.getExternalFilesDir(folderName)?.path
|
downloadPath = context.getExternalFilesDir(folderName)?.path
|
||||||
hashUrl = "$baseurl/hash.json"
|
hashUrl = "$baseurl/hash.json"
|
||||||
|
@ -37,13 +36,7 @@ object MusicDownloader {
|
||||||
|
|
||||||
private fun downloadApk(context: Context, apk: String = "music") {
|
private fun downloadApk(context: Context, apk: String = "music") {
|
||||||
val url = if (apk == "stock") "$baseurl/stock/${getArch()}.apk" else "$baseurl/$variant.apk"
|
val url = if (apk == "stock") "$baseurl/stock/${getArch()}.apk" else "$baseurl/$variant.apk"
|
||||||
download(
|
download(url, "$baseurl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||||
url,
|
|
||||||
"$baseurl/",
|
|
||||||
folderName!!,
|
|
||||||
getFileNameFromUrl(url),
|
|
||||||
context,
|
|
||||||
onDownloadComplete = {
|
|
||||||
if (variant == "root" && apk != "stock") {
|
if (variant == "root" && apk != "stock") {
|
||||||
downloadApk(context, "stock")
|
downloadApk(context, "stock")
|
||||||
return@download
|
return@download
|
||||||
|
@ -65,20 +58,14 @@ object MusicDownloader {
|
||||||
}
|
}
|
||||||
"stock" -> startMusicInstall(context)
|
"stock" -> startMusicInstall(context)
|
||||||
}
|
}
|
||||||
},
|
}, onError = {
|
||||||
onError = {
|
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||||
downloadingFile.postValue(
|
|
||||||
context.getString(
|
|
||||||
R.string.error_downloading,
|
|
||||||
getFileNameFromUrl(url)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startMusicInstall(context: Context) {
|
fun startMusicInstall(context: Context) {
|
||||||
installing.postValue(true)
|
downloadProgress.value?.installing?.postValue(true)
|
||||||
postReset()
|
downloadProgress.value?.postReset()
|
||||||
if (variant == "root")
|
if (variant == "root")
|
||||||
installMusicRoot(context)
|
installMusicRoot(context)
|
||||||
else
|
else
|
||||||
|
|
|
@ -2,16 +2,17 @@ package com.vanced.manager.core.downloader
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
import com.google.firebase.analytics.FirebaseAnalytics
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
import com.google.firebase.analytics.ktx.logEvent
|
import com.google.firebase.analytics.ktx.logEvent
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.*
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
import com.vanced.manager.utils.AppUtils.validateTheme
|
import com.vanced.manager.utils.AppUtils.validateTheme
|
||||||
import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
||||||
import com.vanced.manager.utils.DownloadHelper.download
|
import com.vanced.manager.utils.DownloadHelper.download
|
||||||
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
import com.vanced.manager.utils.PackageHelper.downloadStockCheck
|
||||||
import com.vanced.manager.utils.PackageHelper.installSplitApkFiles
|
import com.vanced.manager.utils.PackageHelper.installVanced
|
||||||
import com.vanced.manager.utils.PackageHelper.installVancedRoot
|
import com.vanced.manager.utils.PackageHelper.installVancedRoot
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
@ -47,9 +48,7 @@ object VancedDownloader {
|
||||||
lang = it.split(", ").toMutableList()
|
lang = it.split(", ").toMutableList()
|
||||||
}
|
}
|
||||||
theme = prefs.theme
|
theme = prefs.theme
|
||||||
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(
|
vancedVersion = version ?: defPrefs.vancedVersion?.getLatestAppVersion(vancedVersions.value?.value ?: listOf(""))
|
||||||
vancedVersions.value?.value ?: listOf("")
|
|
||||||
)
|
|
||||||
themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme"
|
themePath = "$baseInstallUrl/apks/v$vancedVersion/$variant/Theme"
|
||||||
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
|
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
|
||||||
arch = getArch()
|
arch = getArch()
|
||||||
|
@ -59,8 +58,8 @@ object VancedDownloader {
|
||||||
try {
|
try {
|
||||||
downloadSplits(context)
|
downloadSplits(context)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log("VMDownloader", e.stackTraceToString())
|
Log.d("VMDownloader", e.stackTraceToString())
|
||||||
downloadingFile.postValue(context.getString(R.string.error_downloading, "Vanced"))
|
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, "Vanced"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,13 +74,7 @@ object VancedDownloader {
|
||||||
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
|
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
|
||||||
}
|
}
|
||||||
|
|
||||||
download(
|
download(url, "$baseInstallUrl/", folderName!!, getFileNameFromUrl(url), context, onDownloadComplete = {
|
||||||
url,
|
|
||||||
"$baseInstallUrl/",
|
|
||||||
folderName!!,
|
|
||||||
getFileNameFromUrl(url),
|
|
||||||
context,
|
|
||||||
onDownloadComplete = {
|
|
||||||
when (type) {
|
when (type) {
|
||||||
"theme" ->
|
"theme" ->
|
||||||
if (variant == "root") {
|
if (variant == "root") {
|
||||||
|
@ -94,10 +87,7 @@ object VancedDownloader {
|
||||||
downloadSplits(context, "theme")
|
downloadSplits(context, "theme")
|
||||||
} else
|
} else
|
||||||
downloadSplits(context, "arch")
|
downloadSplits(context, "arch")
|
||||||
"arch" -> if (variant == "root") downloadSplits(
|
"arch" -> if (variant == "root") downloadSplits(context, "stock") else downloadSplits(context, "lang")
|
||||||
context,
|
|
||||||
"stock"
|
|
||||||
) else downloadSplits(context, "lang")
|
|
||||||
"stock" -> downloadSplits(context, "dpi")
|
"stock" -> downloadSplits(context, "dpi")
|
||||||
"dpi" -> downloadSplits(context, "lang")
|
"dpi" -> downloadSplits(context, "lang")
|
||||||
"lang" -> {
|
"lang" -> {
|
||||||
|
@ -110,8 +100,7 @@ object VancedDownloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
}, onError = {
|
||||||
onError = {
|
|
||||||
if (type == "lang") {
|
if (type == "lang") {
|
||||||
count++
|
count++
|
||||||
when {
|
when {
|
||||||
|
@ -124,19 +113,14 @@ object VancedDownloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
downloadingFile.postValue(
|
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.error_downloading, getFileNameFromUrl(url)))
|
||||||
context.getString(
|
|
||||||
R.string.error_downloading,
|
|
||||||
getFileNameFromUrl(url)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startVancedInstall(context: Context, variant: String? = this.variant) {
|
fun startVancedInstall(context: Context, variant: String? = this.variant) {
|
||||||
installing.postValue(true)
|
downloadProgress.value?.installing?.postValue(true)
|
||||||
postReset()
|
downloadProgress.value?.postReset()
|
||||||
FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
|
FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
|
||||||
variant?.let { param("vanced_variant", it) }
|
variant?.let { param("vanced_variant", it) }
|
||||||
theme?.let { param("vanced_theme", it) }
|
theme?.let { param("vanced_theme", it) }
|
||||||
|
@ -144,6 +128,6 @@ object VancedDownloader {
|
||||||
if (variant == "root")
|
if (variant == "root")
|
||||||
installVancedRoot(context)
|
installVancedRoot(context)
|
||||||
else
|
else
|
||||||
installSplitApkFiles(context, "vanced")
|
installVanced(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
package com.vanced.manager.core.firebase
|
package com.vanced.manager.core.firebase
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
|
|
||||||
class CloudMessaging : FirebaseMessagingService() {
|
class CloudMessaging : FirebaseMessagingService() {
|
||||||
|
|
||||||
override fun onNewToken(p0: String) {
|
override fun onNewToken(p0: String) {
|
||||||
log("VMC", "Generated new token: $p0")
|
super.onNewToken(p0)
|
||||||
|
Log.d("VMC", "Generated new token: $p0")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,35 +4,33 @@ import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
import android.util.Log
|
||||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||||
import com.vanced.manager.utils.AppUtils.sendFailure
|
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||||
|
|
||||||
class AppInstallerService : Service() {
|
class AppInstallerService: Service() {
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||||
log(TAG, "Requesting user confirmation for installation")
|
Log.d(TAG, "Requesting user confirmation for installation")
|
||||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
try {
|
try {
|
||||||
startActivity(confirmationIntent)
|
startActivity(confirmationIntent)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log("VMInstall", "Unable to start installation")
|
Log.d("VMInstall", "Unable to start installation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PackageInstaller.STATUS_SUCCESS -> {
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
log(TAG, "Installation succeed")
|
Log.d(TAG, "Installation succeed")
|
||||||
sendCloseDialog(this)
|
sendCloseDialog(this)
|
||||||
sendRefresh(this)
|
sendRefresh(this)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
sendCloseDialog(this)
|
sendCloseDialog(this)
|
||||||
intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)?.let {
|
sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE),this)
|
||||||
sendFailure(it, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stopSelf()
|
stopSelf()
|
||||||
|
|
|
@ -4,16 +4,16 @@ import android.app.Service
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
import android.util.Log
|
||||||
import com.vanced.manager.utils.AppUtils.sendRefresh
|
import com.vanced.manager.utils.AppUtils.sendRefresh
|
||||||
|
|
||||||
class AppUninstallerService : Service() {
|
class AppUninstallerService: Service() {
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
val pkgName = intent?.getStringExtra("pkg")
|
val pkgName = intent?.getStringExtra("pkg")
|
||||||
when (intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
when (intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||||
log(AppInstallerService.TAG, "Requesting user confirmation for uninstallation")
|
Log.d(AppInstallerService.TAG, "Requesting user confirmation for uninstallation")
|
||||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
try {
|
try {
|
||||||
|
@ -24,11 +24,11 @@ class AppUninstallerService : Service() {
|
||||||
//Delay broadcast until activity (and fragment) show up on the screen
|
//Delay broadcast until activity (and fragment) show up on the screen
|
||||||
PackageInstaller.STATUS_SUCCESS -> {
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
sendRefresh(this)
|
sendRefresh(this)
|
||||||
log("VMpm", "Successfully uninstalled $pkgName")
|
Log.d("VMpm", "Successfully uninstalled $pkgName")
|
||||||
}
|
}
|
||||||
PackageInstaller.STATUS_FAILURE -> {
|
PackageInstaller.STATUS_FAILURE -> {
|
||||||
sendRefresh(this)
|
sendRefresh(this)
|
||||||
log("VMpm", "Failed to uninstall $pkgName")
|
Log.d("VMpm", "Failed to uninstall $pkgName")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stopSelf()
|
stopSelf()
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -3,79 +3,88 @@ package com.vanced.manager.model
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.*
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.beust.klaxon.JsonObject
|
import com.beust.klaxon.JsonObject
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.CombinedLiveData
|
|
||||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||||
|
|
||||||
open class DataModel(
|
open class DataModel(
|
||||||
jsonObject: LiveData<JsonObject?>,
|
private val jsonObject: LiveData<JsonObject?>,
|
||||||
context: Context,
|
private val context: Context,
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
val appPkg: String,
|
val appPkg: String,
|
||||||
val appName: String,
|
val appName: String,
|
||||||
val appDescription: String,
|
val appIcon: Drawable?,
|
||||||
@DrawableRes val appIcon: Int
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val isAppInstalled = Transformations.map(jsonObject) { isAppInstalled(appPkg) }
|
private val versionCode = MutableLiveData<Int>()
|
||||||
|
private val installedVersionCode = MutableLiveData<Int>()
|
||||||
|
|
||||||
private val versionCode = Transformations.map(jsonObject) { jobj ->
|
val isAppInstalled = MutableLiveData<Boolean>()
|
||||||
jobj?.int("versionCode") ?: 0
|
val versionName = MutableLiveData<String>()
|
||||||
}
|
val installedVersionName = MutableLiveData<String>()
|
||||||
private val installedVersionCode = Transformations.map(isAppInstalled) {
|
val buttonTxt = MutableLiveData<String>()
|
||||||
getPkgVersionCode(appPkg, it)
|
val changelog = MutableLiveData<String>()
|
||||||
}
|
|
||||||
private val unavailable = context.getString(R.string.unavailable)
|
|
||||||
private val pm = context.packageManager
|
|
||||||
|
|
||||||
val versionName = Transformations.map(jsonObject) { jobj ->
|
private fun fetch() {
|
||||||
jobj?.string("version") ?: unavailable
|
val jobj = jsonObject.value
|
||||||
}
|
isAppInstalled.value = isAppInstalled(appPkg)
|
||||||
val changelog = Transformations.map(jsonObject) { jobj ->
|
versionCode.value = jobj?.int("versionCode") ?: 0
|
||||||
jobj?.string("changelog") ?: unavailable
|
versionName.value = jobj?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||||
}
|
changelog.value = jobj?.string("changelog") ?: context.getString(R.string.unavailable)
|
||||||
val installedVersionName = Transformations.map(isAppInstalled) {
|
|
||||||
getPkgVersionName(appPkg, it)
|
|
||||||
}
|
|
||||||
val buttonTag = CombinedLiveData(versionCode, installedVersionCode) { versionCode, installedVersionCode ->
|
|
||||||
compareInt(installedVersionCode, versionCode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, pm)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getPkgVersionName(pkg: String, isAppInstalled: Boolean): String {
|
open fun isAppInstalled(pkg: String): Boolean = isPackageInstalled(pkg, context.packageManager)
|
||||||
return if (isAppInstalled) {
|
|
||||||
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: unavailable
|
private fun getPkgVersionName(pkg: String): String {
|
||||||
|
val pm = context.packageManager
|
||||||
|
return if (isAppInstalled.value == true) {
|
||||||
|
pm?.getPackageInfo(pkg, 0)?.versionName?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)
|
||||||
} else {
|
} else {
|
||||||
unavailable
|
context.getString(R.string.unavailable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun getPkgVersionCode(pkg: String, isAppInstalled: Boolean): Int {
|
private fun getPkgVersionCode(pkg: String): Int {
|
||||||
return if (isAppInstalled) {
|
val pm = context.packageManager
|
||||||
|
return if (isAppInstalled.value == true) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||||
pm?.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt() ?: 0
|
pm?.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt() ?: 0
|
||||||
else
|
else
|
||||||
pm?.getPackageInfo(pkg, 0)?.versionCode ?: 0
|
pm?.getPackageInfo(pkg, 0)?.versionCode ?: 0
|
||||||
|
} else 0
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun compareInt(int1: Int?, int2: Int?): ButtonTag {
|
private fun compareInt(int1: Int?, int2: Int?): String {
|
||||||
if (int2 != null && int1 != null) {
|
if (int2 != null && int1 != null) {
|
||||||
return when {
|
return when {
|
||||||
int1 == 0 -> ButtonTag.INSTALL
|
int1 == 0 -> context.getString(R.string.install)
|
||||||
int2 > int1 -> ButtonTag.UPDATE
|
int2 > int1 -> context.getString(R.string.update)
|
||||||
int1 >= int2 -> ButtonTag.REINSTALL
|
int2 == int1 || int1 > int2 -> context.getString(R.string.button_reinstall)
|
||||||
else -> ButtonTag.INSTALL
|
else -> context.getString(R.string.install)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ButtonTag.INSTALL
|
return context.getString(R.string.install)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,5 +4,5 @@ import android.graphics.drawable.Drawable
|
||||||
|
|
||||||
data class LinkModel(
|
data class LinkModel(
|
||||||
val linkIcon: Drawable?,
|
val linkIcon: Drawable?,
|
||||||
val linkUrl: String
|
val linkUrl: String,
|
||||||
)
|
)
|
30
app/src/main/java/com/vanced/manager/model/ProgressModel.kt
Normal file
30
app/src/main/java/com/vanced/manager/model/ProgressModel.kt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package com.vanced.manager.model
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import okhttp3.ResponseBody
|
||||||
|
import retrofit2.Call
|
||||||
|
|
||||||
|
open class ProgressModel {
|
||||||
|
|
||||||
|
val downloadProgress = MutableLiveData<Int>()
|
||||||
|
val downloadingFile = MutableLiveData<String>()
|
||||||
|
val installing = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
var currentDownload: Call<ResponseBody>? = null
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
downloadProgress.value = 0
|
||||||
|
downloadingFile.value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun postReset() {
|
||||||
|
downloadProgress.postValue(0)
|
||||||
|
downloadingFile.postValue("")
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
installing.postValue(false)
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package com.vanced.manager.model
|
package com.vanced.manager.model
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.DrawableRes
|
import android.graphics.drawable.Drawable
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.beust.klaxon.JsonObject
|
import com.beust.klaxon.JsonObject
|
||||||
|
@ -10,18 +10,18 @@ import com.vanced.manager.utils.PackageHelper
|
||||||
class RootDataModel(
|
class RootDataModel(
|
||||||
jsonObject: LiveData<JsonObject?>,
|
jsonObject: LiveData<JsonObject?>,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
lifecycleOwner: LifecycleOwner,
|
||||||
appPkg: String,
|
appPkg: String,
|
||||||
appName: String,
|
appName: String,
|
||||||
appDescription: String,
|
appIcon: Drawable?,
|
||||||
@DrawableRes appIcon: Int,
|
|
||||||
//BUG THIS!
|
//BUG THIS!
|
||||||
//kotlin thinks that this value is null if we use
|
//kotlin thinks that this value is null if we use
|
||||||
//private val scriptName: String
|
//private val scriptName: String
|
||||||
//Although it's impossible for it to be null.
|
//Although it's impossible for it to be null.
|
||||||
//Ironic, isn't it?
|
//Ironic, isn't it?
|
||||||
private val scriptName: String?
|
private val scriptName: String?
|
||||||
) : DataModel(
|
): DataModel(
|
||||||
jsonObject, context, appPkg, appName, appDescription, appIcon
|
jsonObject, context, lifecycleOwner, appPkg, appName, appIcon
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun isAppInstalled(pkg: String): Boolean {
|
override fun isAppInstalled(pkg: String): Boolean {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package com.vanced.manager.ui
|
package com.vanced.manager.ui
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -15,7 +13,10 @@ import androidx.navigation.findNavController
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||||
|
import com.crowdin.platform.Crowdin
|
||||||
|
import com.crowdin.platform.LoadingStateListener
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
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.BuildConfig.VERSION_CODE
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.databinding.ActivityMainBinding
|
import com.vanced.manager.databinding.ActivityMainBinding
|
||||||
|
@ -25,21 +26,30 @@ import com.vanced.manager.ui.dialogs.URLChangeDialog
|
||||||
import com.vanced.manager.ui.fragments.HomeFragmentDirections
|
import com.vanced.manager.ui.fragments.HomeFragmentDirections
|
||||||
import com.vanced.manager.ui.fragments.SettingsFragmentDirections
|
import com.vanced.manager.ui.fragments.SettingsFragmentDirections
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.*
|
||||||
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() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
lateinit var binding: ActivityMainBinding
|
lateinit var binding: ActivityMainBinding
|
||||||
private val navHost by lazy { findNavController(R.id.nav_host) }
|
private val navHost by lazy { findNavController(R.id.nav_host) }
|
||||||
|
|
||||||
|
private val loadingObserver = object : LoadingStateListener {
|
||||||
|
val tag = "VMLocalisation"
|
||||||
|
override fun onDataChanged() {
|
||||||
|
Log.d(tag, "Loaded data")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(throwable: Throwable) {
|
||||||
|
Log.d(tag, "Failed to load data: $throwable")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setFinalTheme()
|
setFinalTheme()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
if (ENABLE_CROWDIN_AUTH)
|
||||||
|
authCrowdin()
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
@ -58,7 +68,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
initDialogs(intent.getBooleanExtra("firstLaunch", false))
|
initDialogs(intent.getBooleanExtra("firstLaunch", false))
|
||||||
manager.observe(this) {
|
manager.observe(this) {
|
||||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||||
ManagerUpdateDialog.newInstance(true).show(this)
|
ManagerUpdateDialog.newInstance(false).show(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,35 +79,36 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) {
|
private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) {
|
||||||
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(
|
binding.toolbar.navigationIcon = if (isNeeded) ContextCompat.getDrawable(this, R.drawable.ic_keyboard_backspace_black_24dp) else null
|
||||||
this,
|
}
|
||||||
R.drawable.ic_keyboard_backspace_black_24dp
|
|
||||||
) else null
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
Crowdin.unregisterDataLoadingObserver(loadingObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
setFinalTheme()
|
setFinalTheme()
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
Crowdin.registerDataLoadingObserver(loadingObserver)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return when (item.itemId) {
|
when (item.itemId) {
|
||||||
|
android.R.id.home -> {
|
||||||
|
onBackPressedDispatcher.onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
R.id.toolbar_about -> {
|
R.id.toolbar_about -> {
|
||||||
navHost.navigate(HomeFragmentDirections.toAboutFragment())
|
navHost.navigate(HomeFragmentDirections.toAboutFragment())
|
||||||
true
|
return true
|
||||||
}
|
}
|
||||||
R.id.toolbar_settings -> {
|
R.id.toolbar_settings -> {
|
||||||
navHost.navigate(HomeFragmentDirections.toSettingsFragment())
|
navHost.navigate(HomeFragmentDirections.toSettingsFragment())
|
||||||
true
|
return true
|
||||||
}
|
|
||||||
R.id.toolbar_log -> {
|
|
||||||
navHost.navigate(HomeFragmentDirections.toLogFragment())
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
R.id.toolbar_update_manager -> {
|
R.id.toolbar_update_manager -> {
|
||||||
ManagerUpdateDialog.newInstance(false)
|
ManagerUpdateDialog.newInstance(false).show(supportFragmentManager, "manager_update")
|
||||||
.show(supportFragmentManager, "manager_update")
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
R.id.dev_settings -> {
|
R.id.dev_settings -> {
|
||||||
navHost.navigate(SettingsFragmentDirections.toDevSettingsFragment())
|
navHost.navigate(SettingsFragmentDirections.toDevSettingsFragment())
|
||||||
|
@ -105,34 +116,22 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun attachBaseContext(newBase: Context) {
|
override fun attachBaseContext(newBase: Context) {
|
||||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase))
|
super.attachBaseContext(Crowdin.wrapContext(LanguageContextWrapper.wrap(newBase)))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
onActivityResult(requestCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
super.onConfigurationChanged(newConfig)
|
||||||
|
recreate() //restarting activity to recreate viewmodels, otherwise some text won't update
|
||||||
//update manager language when system language is changed
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
if (newConfig.locale != currentLocale) {
|
|
||||||
recreate() //restarting activity in order to recreate viewmodels, otherwise some text won't update
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when (newConfig.orientation) {
|
|
||||||
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]")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun recreate() {
|
override fun recreate() {
|
||||||
|
@ -154,25 +153,21 @@ class MainActivity : AppCompatActivity() {
|
||||||
urldialog.show(this)
|
urldialog.show(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstLaunch) {
|
when {
|
||||||
|
firstLaunch -> {
|
||||||
DialogContainer.showSecurityDialog(this)
|
DialogContainer.showSecurityDialog(this)
|
||||||
with(FirebaseMessaging.getInstance()) {
|
with(FirebaseMessaging.getInstance()) {
|
||||||
subscribeToTopic("Vanced-Update")
|
subscribeToTopic("Vanced-Update")
|
||||||
subscribeToTopic("Music-Update")
|
subscribeToTopic("Music-Update")
|
||||||
subscribeToTopic("MicroG-Update")
|
subscribeToTopic("MicroG-Update")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (isMiuiOptimizationsEnabled) {
|
|
||||||
DialogContainer.miuiDialog(this)
|
|
||||||
}
|
}
|
||||||
}
|
!prefs.getBoolean("statement", true) -> DialogContainer.statementFalse(this)
|
||||||
|
variant == "root" -> {
|
||||||
if (!prefs.getBoolean("statement", true)) {
|
if (PackageHelper.getPackageVersionName(
|
||||||
DialogContainer.statementFalse(this)
|
"com.google.android.youtube",
|
||||||
}
|
packageManager
|
||||||
|
) == "14.21.54")
|
||||||
if (variant == "root") {
|
|
||||||
if (PackageHelper.getPackageVersionName(vancedRootPkg, packageManager) == "14.21.54") {
|
|
||||||
DialogContainer.basicDialog(
|
DialogContainer.basicDialog(
|
||||||
getString(R.string.hold_on),
|
getString(R.string.hold_on),
|
||||||
getString(R.string.magisk_vanced),
|
getString(R.string.magisk_vanced),
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
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,86 +1,21 @@
|
||||||
package com.vanced.manager.ui
|
package com.vanced.manager.ui
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.LayoutDirection
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.animation.addListener
|
import androidx.navigation.findNavController
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.adapter.WelcomePageAdapter
|
|
||||||
import com.vanced.manager.databinding.ActivityWelcomeBinding
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
class WelcomeActivity : AppCompatActivity() {
|
class WelcomeActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var binding: ActivityWelcomeBinding
|
private val navHost by lazy { findNavController(R.id.welcome_navhost) }
|
||||||
private var isRtl = false
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
setContentView(R.layout.activity_welcome)
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
isRtl = resources.configuration.layoutDirection == LayoutDirection.RTL
|
|
||||||
|
|
||||||
binding.welcomeViewpager.apply {
|
|
||||||
adapter = WelcomePageAdapter(this@WelcomeActivity)
|
|
||||||
isUserInputEnabled = false
|
|
||||||
setPageTransformer { page, position ->
|
|
||||||
page.apply {
|
|
||||||
val pageWidth = width.toFloat()
|
|
||||||
//Thank you, fragula dev!
|
|
||||||
when {
|
|
||||||
position > 0 && position < 1 -> {
|
|
||||||
alpha = 1f
|
|
||||||
translationX = 0f
|
|
||||||
}
|
|
||||||
position > -1 && position <= 0 -> {
|
|
||||||
alpha = 1.0f - abs(position * 0.7f)
|
|
||||||
translationX = pageWidth.rtlCompat * position / 1.3F
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
with(binding) {
|
if (!navHost.popBackStack())
|
||||||
if (welcomeViewpager.currentItem == 0) {
|
finish()
|
||||||
super.onBackPressed()
|
|
||||||
} else {
|
|
||||||
navigateTo(welcomeViewpager.currentItem - 1)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun navigateTo(position: Int) {
|
|
||||||
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) {
|
|
||||||
val pixelsToDrag: Int = width * (value - currentItem)
|
|
||||||
val animator = ValueAnimator.ofInt(0, pixelsToDrag)
|
|
||||||
var previousValue = 0
|
|
||||||
animator.apply {
|
|
||||||
addUpdateListener { valueAnimator ->
|
|
||||||
val currentValue = valueAnimator.animatedValue as Int
|
|
||||||
val currentPxToDrag = (currentValue - previousValue).toFloat()
|
|
||||||
fakeDragBy(currentPxToDrag.rtlCompat)
|
|
||||||
previousValue = currentValue
|
|
||||||
}
|
|
||||||
addListener(
|
|
||||||
onStart = { beginFakeDrag() },
|
|
||||||
onEnd = { endFakeDrag() }
|
|
||||||
)
|
|
||||||
duration = 500
|
|
||||||
start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
package com.vanced.manager.ui.compose
|
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
package com.vanced.manager.ui.compose
|
|
||||||
|
|
||||||
//import androidx.compose.foundation.clickable
|
|
||||||
//import androidx.compose.foundation.layout.Column
|
|
||||||
//import androidx.compose.foundation.layout.ColumnScope
|
|
||||||
//import androidx.compose.foundation.layout.padding
|
|
||||||
//import androidx.compose.material.Switch
|
|
||||||
//import androidx.compose.material.Text
|
|
||||||
//import androidx.compose.runtime.*
|
|
||||||
//import androidx.compose.ui.Modifier
|
|
||||||
//import androidx.compose.ui.graphics.Color
|
|
||||||
//import androidx.compose.ui.platform.LocalContext
|
|
||||||
//import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
//import androidx.compose.ui.unit.dp
|
|
||||||
//import androidx.compose.ui.unit.em
|
|
||||||
//import androidx.compose.ui.unit.sp
|
|
||||||
//import androidx.constraintlayout.compose.ConstraintLayout
|
|
||||||
//import androidx.core.content.edit
|
|
||||||
//import androidx.preference.PreferenceManager
|
|
||||||
//
|
|
||||||
//@Composable
|
|
||||||
//@Preview
|
|
||||||
//inline fun PreferenceCategory(
|
|
||||||
// categoryTitle: String,
|
|
||||||
// content: @Composable ColumnScope.() -> Unit
|
|
||||||
//) {
|
|
||||||
// Column {
|
|
||||||
// Text(
|
|
||||||
// categoryTitle,
|
|
||||||
// letterSpacing = 0.15.em,
|
|
||||||
// color = Color(LocalContext.current.accentColor)
|
|
||||||
// )
|
|
||||||
// content()
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//@Composable
|
|
||||||
//@Preview
|
|
||||||
//inline fun SwitchPreference(
|
|
||||||
// preferenceTitle: String,
|
|
||||||
// preferenceDescription: String? = null,
|
|
||||||
// preferenceKey: String,
|
|
||||||
// defValue: Boolean = true,
|
|
||||||
// crossinline onCheckedChange: (Boolean) -> Unit = {}
|
|
||||||
//) {
|
|
||||||
// val prefs = PreferenceManager.getDefaultSharedPreferences(LocalContext.current)
|
|
||||||
// val isChecked = remember { mutableStateOf(prefs.getBoolean(preferenceKey, defValue)) }
|
|
||||||
// ConstraintLayout(modifier = Modifier.padding(12.dp, 4.dp).clickable {
|
|
||||||
// isChecked.value = !isChecked.value
|
|
||||||
// }) {
|
|
||||||
// val (title, description, switch) = createRefs()
|
|
||||||
// Text(preferenceTitle, fontSize = 16.sp, modifier = Modifier.constrainAs(title) {
|
|
||||||
// top.linkTo(parent.top)
|
|
||||||
// start.linkTo(parent.start)
|
|
||||||
// end.linkTo(switch.start, 4.dp)
|
|
||||||
// if (preferenceDescription != null) {
|
|
||||||
// bottom.linkTo(description.top)
|
|
||||||
// } else {
|
|
||||||
// bottom.linkTo(parent.bottom)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// if (preferenceDescription != null) {
|
|
||||||
// Text(preferenceDescription, fontSize = 13.sp, modifier = Modifier.constrainAs(description) {
|
|
||||||
// top.linkTo(title.bottom)
|
|
||||||
// start.linkTo(parent.start)
|
|
||||||
// end.linkTo(switch.start, 8.dp)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// Switch(
|
|
||||||
// isChecked.value,
|
|
||||||
// onCheckedChange = {
|
|
||||||
// prefs.edit { putBoolean(preferenceKey, it) }
|
|
||||||
// onCheckedChange(it)
|
|
||||||
// },
|
|
||||||
// modifier = Modifier.clickable(false) {}
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//@Composable
|
|
||||||
//@Preview
|
|
||||||
//fun Preference(
|
|
||||||
// preferenceTitle: String,
|
|
||||||
// preferenceDescription: String? = null,
|
|
||||||
// onClick: () -> Unit
|
|
||||||
//) {
|
|
||||||
// ConstraintLayout(modifier = Modifier.padding(12.dp, 4.dp).clickable(onClick = onClick)) {
|
|
||||||
// val (title, description, switch) = createRefs()
|
|
||||||
// Text(preferenceTitle, fontSize = 16.sp, modifier = Modifier.constrainAs(title) {
|
|
||||||
// top.linkTo(parent.top)
|
|
||||||
// start.linkTo(parent.start)
|
|
||||||
// end.linkTo(switch.start, 4.dp)
|
|
||||||
// if (preferenceDescription != null) {
|
|
||||||
// bottom.linkTo(description.top)
|
|
||||||
// } else {
|
|
||||||
// bottom.linkTo(parent.bottom)
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// if (preferenceDescription != null) {
|
|
||||||
// Text(preferenceDescription, fontSize = 13.sp, modifier = Modifier.constrainAs(description) {
|
|
||||||
// top.linkTo(title.bottom)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package com.vanced.manager.ui.compose
|
|
||||||
|
|
||||||
//import android.content.Context
|
|
||||||
//import androidx.compose.foundation.isSystemInDarkTheme
|
|
||||||
//import androidx.compose.material.MaterialTheme
|
|
||||||
//import androidx.compose.material.darkColors
|
|
||||||
//import androidx.compose.material.lightColors
|
|
||||||
//import androidx.compose.runtime.Composable
|
|
||||||
//import androidx.compose.runtime.MutableState
|
|
||||||
//import androidx.compose.runtime.mutableStateOf
|
|
||||||
//import androidx.compose.runtime.remember
|
|
||||||
//import androidx.compose.ui.graphics.Color
|
|
||||||
//import androidx.compose.ui.platform.LocalContext
|
|
||||||
//import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
|
||||||
//import com.vanced.manager.utils.mutableAccentColor
|
|
||||||
//
|
|
||||||
//const val Dark = "Dark"
|
|
||||||
//const val SystemDefault = "System Default"
|
|
||||||
//
|
|
||||||
//const val defAccentColor: Int = -13732865
|
|
||||||
//
|
|
||||||
//val Context.accentColor get() = mutableAccentColor.value ?: getDefaultSharedPreferences(this).getInt("manager_accent_color", defAccentColor)
|
|
||||||
//
|
|
||||||
//enum class Theme {
|
|
||||||
// DARK, LIGHT
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//val lightColors = lightColors(
|
|
||||||
// primary = Color(defAccentColor)
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//val darkColors = darkColors(
|
|
||||||
// primary = Color(defAccentColor)
|
|
||||||
//)
|
|
||||||
//
|
|
||||||
//fun Context.retrieveTheme(): Theme = when (getDefaultSharedPreferences(this).getString("manager_theme", SystemDefault)) {
|
|
||||||
// SystemDefault -> if (isSystemInDarkTheme()) Theme.DARK else Theme.LIGHT
|
|
||||||
// Dark -> Theme.DARK
|
|
||||||
// else -> Theme.LIGHT
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//val Context.isDarkTheme: Boolean
|
|
||||||
// get() = retrieveTheme() == Theme.DARK
|
|
||||||
//
|
|
||||||
//fun Context.ManagerTheme(
|
|
||||||
// content: @Composable () -> Unit
|
|
||||||
//) {
|
|
||||||
// MaterialTheme(
|
|
||||||
// colors = if (isDarkTheme) darkColors else lightColors,
|
|
||||||
// content = content
|
|
||||||
// )
|
|
||||||
//}
|
|
|
@ -31,7 +31,7 @@ class EmptyPreference @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSummary(newSummary: String) {
|
fun setSummary(newSummary: String) {
|
||||||
with(binding) {
|
with (binding) {
|
||||||
preferenceSummary.text = newSummary
|
preferenceSummary.text = newSummary
|
||||||
preferenceSummary.isVisible = true
|
preferenceSummary.isVisible = true
|
||||||
preferenceTitle.setPadding(0, 0, 0, 0)
|
preferenceTitle.setPadding(0, 0, 0, 0)
|
||||||
|
@ -40,11 +40,10 @@ class EmptyPreference @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||||
attrs?.let { mAttrs ->
|
attrs?.let { mAttrs ->
|
||||||
val typedArray =
|
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
||||||
context.obtainStyledAttributes(mAttrs, R.styleable.EmptyPreference, 0, 0)
|
|
||||||
val title = typedArray.getText(R.styleable.EmptyPreference_preference_title)
|
val title = typedArray.getText(R.styleable.EmptyPreference_preference_title)
|
||||||
val summary = typedArray.getText(R.styleable.EmptyPreference_preference_summary)
|
val summary = typedArray.getText(R.styleable.EmptyPreference_preference_summary)
|
||||||
with(binding) {
|
with (binding) {
|
||||||
if (summary != null) {
|
if (summary != null) {
|
||||||
preferenceSummary.text = summary
|
preferenceSummary.text = summary
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import com.vanced.manager.databinding.ViewPreferenceCategoryBinding
|
||||||
class PreferenceCategory @JvmOverloads constructor(
|
class PreferenceCategory @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyle: Int = 0
|
defStyle: Int = 0,
|
||||||
) : LinearLayout(context, attrs, defStyle) {
|
) : LinearLayout(context, attrs, defStyle) {
|
||||||
|
|
||||||
private var _binding: ViewPreferenceCategoryBinding? = null
|
private var _binding: ViewPreferenceCategoryBinding? = null
|
||||||
|
@ -27,8 +27,7 @@ class PreferenceCategory @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
private fun initAttrs(context: Context, attrs: AttributeSet?) {
|
||||||
attrs.let { mAttrs ->
|
attrs.let { mAttrs ->
|
||||||
val typedArray =
|
val typedArray = context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
||||||
context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceCategory, 0, 0)
|
|
||||||
val title = typedArray.getText(R.styleable.PreferenceCategory_category_title)
|
val title = typedArray.getText(R.styleable.PreferenceCategory_category_title)
|
||||||
|
|
||||||
binding.categoryTitle.text = title
|
binding.categoryTitle.text = title
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.vanced.manager.ui.core
|
package com.vanced.manager.ui.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
|
@ -32,13 +31,6 @@ class PreferenceSwitch @JvmOverloads constructor(
|
||||||
|
|
||||||
private var mListener: OnCheckedListener? = null
|
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
|
private var _binding: ViewPreferenceSwitchBinding? = null
|
||||||
|
|
||||||
val binding: ViewPreferenceSwitchBinding
|
val binding: ViewPreferenceSwitchBinding
|
||||||
|
@ -46,7 +38,6 @@ class PreferenceSwitch @JvmOverloads constructor(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
_binding = ViewPreferenceSwitchBinding.inflate(LayoutInflater.from(context), this, true)
|
_binding = ViewPreferenceSwitchBinding.inflate(LayoutInflater.from(context), this, true)
|
||||||
prefs.registerOnSharedPreferenceChangeListener(prefListener)
|
|
||||||
attrs?.let { mAttrs ->
|
attrs?.let { mAttrs ->
|
||||||
with(context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceSwitch, 0, 0)) {
|
with(context.obtainStyledAttributes(mAttrs, R.styleable.PreferenceSwitch, 0, 0)) {
|
||||||
val title = getText(R.styleable.PreferenceSwitch_switch_title)
|
val title = getText(R.styleable.PreferenceSwitch_switch_title)
|
||||||
|
@ -94,8 +85,4 @@ class PreferenceSwitch @JvmOverloads constructor(
|
||||||
defValue = newVal
|
defValue = newVal
|
||||||
binding.preferenceSwitch.isChecked = prefs.getBoolean(prefKey, newVal)
|
binding.preferenceSwitch.isChecked = prefs.getBoolean(prefKey, newVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setChecked(checked: Boolean) {
|
|
||||||
binding.preferenceSwitch.isChecked = checked
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.vanced.manager.ui.core
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
|
||||||
|
open class SlidingConstraintLayout : ConstraintLayout {
|
||||||
|
|
||||||
|
constructor(context: Context) : super(context)
|
||||||
|
constructor(context: Context, attrs: AttributeSet?) : super(
|
||||||
|
context,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
|
||||||
|
var xFraction: Float
|
||||||
|
get() {
|
||||||
|
val width = width
|
||||||
|
return if (width != 0)
|
||||||
|
x / getWidth()
|
||||||
|
else
|
||||||
|
x
|
||||||
|
}
|
||||||
|
set(xFraction) {
|
||||||
|
val width = width
|
||||||
|
val newWidth =
|
||||||
|
if (width > 0)
|
||||||
|
xFraction * width
|
||||||
|
else
|
||||||
|
(1).toFloat()
|
||||||
|
x = newWidth
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package com.vanced.manager.ui.core
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
|
||||||
|
open class SlidingLinearLayout: LinearLayout {
|
||||||
|
|
||||||
|
constructor(context: Context?) : super(context)
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super(
|
||||||
|
context,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
|
||||||
|
var yFraction: Float
|
||||||
|
get() {
|
||||||
|
val height = height
|
||||||
|
return if (height != 0)
|
||||||
|
y / height
|
||||||
|
else
|
||||||
|
y
|
||||||
|
}
|
||||||
|
set(yFraction) {
|
||||||
|
val height = height
|
||||||
|
val newHeight =
|
||||||
|
if (height > 0)
|
||||||
|
yFraction * height
|
||||||
|
else
|
||||||
|
(1).toFloat()
|
||||||
|
y = newHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
var xFraction: Float
|
||||||
|
get() {
|
||||||
|
val width = width
|
||||||
|
return if (width != 0)
|
||||||
|
x / getWidth()
|
||||||
|
else
|
||||||
|
x
|
||||||
|
}
|
||||||
|
set(xFraction) {
|
||||||
|
val width = width
|
||||||
|
val newWidth =
|
||||||
|
if (width > 0)
|
||||||
|
xFraction * width
|
||||||
|
else
|
||||||
|
(1).toFloat()
|
||||||
|
x = newWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.vanced.manager.ui.core
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
|
||||||
|
open class SlidingSwipeRefreshLayout : SwipeRefreshLayout {
|
||||||
|
|
||||||
|
constructor(context: Context?) : super(context!!)
|
||||||
|
constructor(context: Context?, attrs: AttributeSet?) : super(
|
||||||
|
context!!,
|
||||||
|
attrs
|
||||||
|
)
|
||||||
|
|
||||||
|
var xFraction: Float
|
||||||
|
get() {
|
||||||
|
val width = width
|
||||||
|
return if (width != 0)
|
||||||
|
x / getWidth()
|
||||||
|
else
|
||||||
|
x
|
||||||
|
}
|
||||||
|
set(xFraction) {
|
||||||
|
val width = width
|
||||||
|
val newWidth =
|
||||||
|
if (width > 0)
|
||||||
|
xFraction * width
|
||||||
|
else
|
||||||
|
(1).toFloat()
|
||||||
|
x = newWidth
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
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,7 +7,9 @@ import androidx.core.graphics.ColorUtils
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.accentColor
|
||||||
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.lifecycleOwner
|
import com.vanced.manager.utils.lifecycleOwner
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedMaterialButton @JvmOverloads constructor(
|
class ThemedMaterialButton @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -16,7 +18,8 @@ class ThemedMaterialButton @JvmOverloads constructor(
|
||||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
context.lifecycleOwner?.let { owner ->
|
setBgColor(context.defPrefs.managerAccent)
|
||||||
|
context.lifecycleOwner()?.let { owner ->
|
||||||
accentColor.observe(owner) { color ->
|
accentColor.observe(owner) { color ->
|
||||||
setBgColor(color.toInt())
|
setBgColor(color.toInt())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ import android.content.res.ColorStateList
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.google.android.material.checkbox.MaterialCheckBox
|
import com.google.android.material.checkbox.MaterialCheckBox
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.defPrefs
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedMaterialCheckbox @JvmOverloads constructor(
|
class ThemedMaterialCheckbox @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null,
|
attributeSet: AttributeSet? = null,
|
||||||
) : MaterialCheckBox(context, attributeSet, R.attr.checkboxStyle) {
|
) : MaterialCheckBox(context, attributeSet, R.attr.checkboxStyle) {
|
||||||
init {
|
init {
|
||||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,13 +5,14 @@ import android.content.res.ColorStateList
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.google.android.material.radiobutton.MaterialRadioButton
|
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.defPrefs
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedMaterialRadioButton @JvmOverloads constructor(
|
class ThemedMaterialRadioButton @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null,
|
attributeSet: AttributeSet? = null,
|
||||||
) : MaterialRadioButton(context, attributeSet, R.attr.radioButtonStyle) {
|
) : MaterialRadioButton(context, attributeSet, R.attr.radioButtonStyle) {
|
||||||
init {
|
init {
|
||||||
buttonTintList = ColorStateList.valueOf(accentColor.value!!)
|
buttonTintList = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,20 +4,17 @@ import android.content.Context
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.defPrefs
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedMaterialSlider @JvmOverloads constructor(
|
class ThemedMaterialSlider@JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null,
|
attributeSet: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : Slider(context, attributeSet, defStyleAttr) {
|
) : Slider(context, attributeSet, defStyleAttr) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val accentValue = ColorStateList.valueOf(accentColor.value!!)
|
thumbStrokeColor = ColorStateList.valueOf(context.defPrefs.managerAccent)
|
||||||
thumbTintList = accentValue
|
|
||||||
trackActiveTintList = accentValue
|
|
||||||
trackInactiveTintList = accentValue.withAlpha(70)
|
|
||||||
haloTintList = accentValue.withAlpha(60)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,7 +6,10 @@ import android.util.AttributeSet
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.accentColor
|
||||||
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.lifecycleOwner
|
import com.vanced.manager.utils.lifecycleOwner
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
|
|
||||||
class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -14,7 +17,8 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
) : MaterialButton(context, attributeSet, defStyleAttr) {
|
||||||
init {
|
init {
|
||||||
context.lifecycleOwner?.let { owner ->
|
applyAccent(context.defPrefs.managerAccent)
|
||||||
|
context.lifecycleOwner()?.let { owner ->
|
||||||
accentColor.observe(owner) { color ->
|
accentColor.observe(owner) { color ->
|
||||||
applyAccent(color.toInt())
|
applyAccent(color.toInt())
|
||||||
}
|
}
|
||||||
|
@ -23,9 +27,6 @@ class ThemedOutlinedMaterialButton @JvmOverloads constructor(
|
||||||
|
|
||||||
private fun applyAccent(color: Int) {
|
private fun applyAccent(color: Int) {
|
||||||
setTextColor(color)
|
setTextColor(color)
|
||||||
rippleColor = ColorStateList(
|
rippleColor = ColorStateList(arrayOf(intArrayOf()), intArrayOf(ColorUtils.setAlphaComponent(color, 50)))
|
||||||
arrayOf(intArrayOf()),
|
|
||||||
intArrayOf(ColorUtils.setAlphaComponent(color, 50))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,27 +4,21 @@ import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.defPrefs
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedSwipeRefreshlayout @JvmOverloads constructor(
|
class ThemedSwipeRefreshlayout @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null
|
attributeSet: AttributeSet? = null
|
||||||
) : SwipeRefreshLayout(context, attributeSet) {
|
) : SwipeRefreshLayout(context, attributeSet) {
|
||||||
init {
|
init {
|
||||||
setColorSchemeColors(accentColor.value!!)
|
setColorSchemeColors(context.defPrefs.managerAccent)
|
||||||
initAttrs(context, attributeSet)
|
initAttrs(context, attributeSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initAttrs(context: Context, attributeSet: AttributeSet?) {
|
private fun initAttrs(context: Context, attributeSet: AttributeSet?) {
|
||||||
attributeSet.let {
|
attributeSet.let {
|
||||||
val typedAttrs =
|
val typedAttrs = context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
||||||
context.obtainStyledAttributes(it, R.styleable.ThemedSwipeRefreshlayout, 0, 0)
|
setProgressBackgroundColorSchemeColor(typedAttrs.getColor(R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor, 0))
|
||||||
setProgressBackgroundColorSchemeColor(
|
|
||||||
typedAttrs.getColor(
|
|
||||||
R.styleable.ThemedSwipeRefreshlayout_progressBackgroundColor,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
typedAttrs.recycle()
|
typedAttrs.recycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,18 +9,20 @@ import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.graphics.drawable.DrawableCompat
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.accentColor
|
||||||
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.lifecycleOwner
|
import com.vanced.manager.utils.lifecycleOwner
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedSwitchCompat @JvmOverloads constructor(
|
class ThemedSwitchCompat @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
attributeSet: AttributeSet? = null,
|
attributeSet: AttributeSet? = null,
|
||||||
) : SwitchCompat(context, attributeSet, R.attr.switchStyle) {
|
) : SwitchCompat(context, attributeSet, R.attr.switchStyle) {
|
||||||
|
|
||||||
private val states =
|
private val states = arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
||||||
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked))
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
context.lifecycleOwner?.let { owner ->
|
setSwitchColors(context.defPrefs.managerAccent)
|
||||||
|
context.lifecycleOwner()?.let { owner ->
|
||||||
accentColor.observe(owner) { color ->
|
accentColor.observe(owner) { color ->
|
||||||
setSwitchColors(color.toInt())
|
setSwitchColors(color.toInt())
|
||||||
}
|
}
|
||||||
|
@ -30,13 +32,7 @@ class ThemedSwitchCompat @JvmOverloads constructor(
|
||||||
private fun setSwitchColors(color: Int) {
|
private fun setSwitchColors(color: Int) {
|
||||||
val thumbColors = intArrayOf(Color.LTGRAY, color)
|
val thumbColors = intArrayOf(Color.LTGRAY, color)
|
||||||
val trackColors = intArrayOf(Color.GRAY, ColorUtils.setAlphaComponent(color, 70))
|
val trackColors = intArrayOf(Color.GRAY, ColorUtils.setAlphaComponent(color, 70))
|
||||||
DrawableCompat.setTintList(
|
DrawableCompat.setTintList(DrawableCompat.wrap(thumbDrawable), ColorStateList(states, thumbColors))
|
||||||
DrawableCompat.wrap(thumbDrawable),
|
DrawableCompat.setTintList(DrawableCompat.wrap(trackDrawable), ColorStateList(states, trackColors))
|
||||||
ColorStateList(states, thumbColors)
|
|
||||||
)
|
|
||||||
DrawableCompat.setTintList(
|
|
||||||
DrawableCompat.wrap(trackDrawable),
|
|
||||||
ColorStateList(states, trackColors)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,9 @@ import android.content.Context
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import com.vanced.manager.utils.accentColor
|
import com.vanced.manager.utils.accentColor
|
||||||
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.lifecycleOwner
|
import com.vanced.manager.utils.lifecycleOwner
|
||||||
|
import com.vanced.manager.utils.managerAccent
|
||||||
|
|
||||||
class ThemedTextView @JvmOverloads constructor(
|
class ThemedTextView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -12,7 +14,8 @@ class ThemedTextView @JvmOverloads constructor(
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : AppCompatTextView(context, attributeSet, defStyleAttr) {
|
) : AppCompatTextView(context, attributeSet, defStyleAttr) {
|
||||||
init {
|
init {
|
||||||
context.lifecycleOwner?.let { owner ->
|
setTextColor(context.defPrefs.managerAccent)
|
||||||
|
context.lifecycleOwner()?.let { owner ->
|
||||||
accentColor.observe(owner) { color ->
|
accentColor.observe(owner) { color ->
|
||||||
setTextColor(color.toInt())
|
setTextColor(color.toInt())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.vanced.manager.ui.dialogs
|
package com.vanced.manager.ui.dialogs
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -18,7 +17,8 @@ import com.vanced.manager.core.downloader.MusicDownloader.downloadMusic
|
||||||
import com.vanced.manager.core.downloader.VancedDownloader.downloadVanced
|
import com.vanced.manager.core.downloader.VancedDownloader.downloadVanced
|
||||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogAppDownloadBinding
|
import com.vanced.manager.databinding.DialogAppDownloadBinding
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
|
import com.vanced.manager.utils.applyAccent
|
||||||
|
|
||||||
class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
||||||
|
|
||||||
|
@ -73,28 +73,22 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
||||||
appDownloadHeader.text = app
|
appDownloadHeader.text = app
|
||||||
if (arguments?.getBoolean(TAG_INSTALLING) == false) {
|
if (arguments?.getBoolean(TAG_INSTALLING) == false) {
|
||||||
when (app) {
|
when (app) {
|
||||||
getString(R.string.vanced) -> downloadVanced(
|
getString(R.string.vanced) -> downloadVanced(requireContext(), arguments?.getString(TAG_VERSION))
|
||||||
requireContext(),
|
getString(R.string.music) -> downloadMusic(requireContext())
|
||||||
arguments?.getString(TAG_VERSION)
|
|
||||||
)
|
|
||||||
getString(R.string.music) -> downloadMusic(
|
|
||||||
requireContext(),
|
|
||||||
arguments?.getString(TAG_VERSION)
|
|
||||||
)
|
|
||||||
getString(R.string.microg) -> downloadMicrog(requireContext())
|
getString(R.string.microg) -> downloadMicrog(requireContext())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
private fun DialogAppDownloadBinding.bindDownloadProgress() {
|
private fun DialogAppDownloadBinding.bindDownloadProgress() {
|
||||||
downloadProgress.observe(viewLifecycleOwner) {
|
with(downloadProgress) {
|
||||||
|
observe(viewLifecycleOwner) { progressModel ->
|
||||||
|
progressModel.downloadProgress.observe(viewLifecycleOwner) {
|
||||||
appDownloadProgressbar.progress = it
|
appDownloadProgressbar.progress = it
|
||||||
appDownloadProgress.text = "$it%"
|
|
||||||
}
|
}
|
||||||
installing.observe(viewLifecycleOwner) { installing ->
|
progressModel.installing.observe(viewLifecycleOwner) { installing ->
|
||||||
appDownloadProgressbarContainer.isVisible = !installing
|
appDownloadProgressbar.isVisible = !installing
|
||||||
appInstallProgressbar.isVisible = installing
|
appInstallProgressbar.isVisible = installing
|
||||||
appDownloadFile.isVisible = !installing
|
appDownloadFile.isVisible = !installing
|
||||||
appDownloadCancel.isEnabled = !installing
|
appDownloadCancel.isEnabled = !installing
|
||||||
|
@ -102,15 +96,17 @@ class AppDownloadDialog : BindingDialogFragment<DialogAppDownloadBinding>() {
|
||||||
if (installing) {
|
if (installing) {
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
currentDownload?.cancel()
|
progressModel.currentDownload?.cancel()
|
||||||
downloadProgress.value = 0
|
progressModel.downloadProgress.value = 0
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
downloadingFile.observe(viewLifecycleOwner) {
|
progressModel.downloadingFile.observe(viewLifecycleOwner) {
|
||||||
appDownloadFile.text = it
|
appDownloadFile.text = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
|
@ -2,10 +2,11 @@ package com.vanced.manager.ui.dialogs
|
||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogAppInfoBinding
|
import com.vanced.manager.databinding.DialogAppInfoBinding
|
||||||
|
@ -20,15 +21,13 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
appName: String?,
|
appName: String?,
|
||||||
@DrawableRes appIcon: Int?,
|
appIcon: Drawable?,
|
||||||
changelog: String?
|
changelog: String?
|
||||||
): AppInfoDialog = AppInfoDialog().apply {
|
): AppInfoDialog = AppInfoDialog().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putString(TAG_APP_NAME, appName)
|
putString(TAG_APP_NAME, appName)
|
||||||
putString(TAG_CHANGELOG, changelog)
|
putString(TAG_CHANGELOG, changelog)
|
||||||
if (appIcon != null) {
|
putParcelable(TAG_APP_ICON, appIcon?.toBitmap())
|
||||||
putInt(TAG_APP_ICON, appIcon)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +47,7 @@ class AppInfoDialog : BindingDialogFragment<DialogAppInfoBinding>() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
aboutAppName.text = getString(R.string.about_app, arguments?.getString(TAG_APP_NAME))
|
aboutAppName.text = getString(R.string.about_app, arguments?.getString(TAG_APP_NAME))
|
||||||
aboutAppChangelog.text = arguments?.getString(TAG_CHANGELOG)
|
aboutAppChangelog.text = arguments?.getString(TAG_CHANGELOG)
|
||||||
arguments?.getInt(TAG_APP_ICON)?.let { aboutAppImage.setImageResource(it) }
|
aboutAppImage.setImageBitmap(arguments?.getParcelable(TAG_APP_ICON))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,62 +0,0 @@
|
||||||
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,12 +11,10 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.core.ui.ext.showDialog
|
import com.vanced.manager.core.ui.ext.showDialog
|
||||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||||
import com.vanced.manager.utils.checkedButtonTag
|
|
||||||
import com.vanced.manager.utils.defPrefs
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.formatVersion
|
import com.vanced.manager.utils.getCheckedButtonTag
|
||||||
|
|
||||||
class AppVersionSelectorDialog :
|
class AppVersionSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
|
||||||
|
|
||||||
private val prefs by lazy { requireActivity().defPrefs }
|
private val prefs by lazy { requireActivity().defPrefs }
|
||||||
|
|
||||||
|
@ -65,7 +63,7 @@ class AppVersionSelectorDialog :
|
||||||
}
|
}
|
||||||
dialogTitle.text = getString(R.string.version)
|
dialogTitle.text = getString(R.string.version)
|
||||||
dialogSave.setOnClickListener {
|
dialogSave.setOnClickListener {
|
||||||
val checkedTag = dialogRadiogroup.checkedButtonTag
|
val checkedTag = dialogRadiogroup.getCheckedButtonTag()
|
||||||
if (checkedTag != null) {
|
if (checkedTag != null) {
|
||||||
prefs.edit { putString("${arguments?.getString(TAG_APP)}_version", checkedTag) }
|
prefs.edit { putString("${arguments?.getString(TAG_APP)}_version", checkedTag) }
|
||||||
}
|
}
|
||||||
|
@ -77,7 +75,7 @@ class AppVersionSelectorDialog :
|
||||||
private fun loadBoxes() =
|
private fun loadBoxes() =
|
||||||
arguments?.getStringArrayList(TAG_VERSIONS)?.map { version ->
|
arguments?.getStringArrayList(TAG_VERSIONS)?.map { version ->
|
||||||
ThemedMaterialRadioButton(requireActivity()).apply {
|
ThemedMaterialRadioButton(requireActivity()).apply {
|
||||||
text = version.formatVersion(requireActivity())
|
text = version
|
||||||
tag = version
|
tag = version
|
||||||
textSize = 18f
|
textSize = 18f
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import androidx.core.content.edit
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.isMiuiOptimizationsEnabled
|
import com.vanced.manager.utils.applyAccent
|
||||||
|
import com.vanced.manager.utils.isMiui
|
||||||
import com.vanced.manager.utils.openUrl
|
import com.vanced.manager.utils.openUrl
|
||||||
import com.vanced.manager.utils.showWithAccent
|
|
||||||
|
|
||||||
object DialogContainer {
|
object DialogContainer {
|
||||||
|
|
||||||
|
@ -16,21 +16,26 @@ object DialogContainer {
|
||||||
setTitle(context.resources.getString(R.string.welcome))
|
setTitle(context.resources.getString(R.string.welcome))
|
||||||
setMessage(context.resources.getString(R.string.security_context))
|
setMessage(context.resources.getString(R.string.security_context))
|
||||||
setPositiveButton(context.resources.getString(R.string.close)) { dialog, _ ->
|
setPositiveButton(context.resources.getString(R.string.close)) { dialog, _ ->
|
||||||
dialog.cancel()
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
setOnDismissListener {
|
||||||
|
if (isMiui()) {
|
||||||
|
applyAccentMiuiDialog(context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setOnCancelListener {
|
setOnCancelListener {
|
||||||
if (context.isMiuiOptimizationsEnabled) {
|
if (isMiui()) {
|
||||||
miuiDialog(context)
|
applyAccentMiuiDialog(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
create()
|
create()
|
||||||
showWithAccent()
|
applyAccent()
|
||||||
}
|
}
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
prefs.edit { putBoolean("firstLaunch", false) }
|
prefs.edit { putBoolean("firstLaunch", false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun miuiDialog(context: Context) {
|
private fun applyAccentMiuiDialog(context: Context) {
|
||||||
MaterialAlertDialogBuilder(context).apply {
|
MaterialAlertDialogBuilder(context).apply {
|
||||||
setTitle(context.getString(R.string.miui_one_title))
|
setTitle(context.getString(R.string.miui_one_title))
|
||||||
setMessage(context.getString(R.string.miui_one))
|
setMessage(context.getString(R.string.miui_one))
|
||||||
|
@ -44,7 +49,7 @@ object DialogContainer {
|
||||||
}
|
}
|
||||||
setCancelable(false)
|
setCancelable(false)
|
||||||
create()
|
create()
|
||||||
showWithAccent()
|
applyAccent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +59,7 @@ object DialogContainer {
|
||||||
setMessage("So this statement is false huh? I'll go with True!")
|
setMessage("So this statement is false huh? I'll go with True!")
|
||||||
setPositiveButton("wut?") { dialog, _ -> dialog.dismiss() }
|
setPositiveButton("wut?") { dialog, _ -> dialog.dismiss() }
|
||||||
create()
|
create()
|
||||||
showWithAccent()
|
applyAccent()
|
||||||
}
|
}
|
||||||
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
@ -68,54 +73,28 @@ object DialogContainer {
|
||||||
when (msg) {
|
when (msg) {
|
||||||
context.getString(R.string.installation_signature) -> {
|
context.getString(R.string.installation_signature) -> {
|
||||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||||
openUrl(
|
openUrl("https://lmgtfy.com/?q=andnixsh+apk+verification+disable", R.color.Twitter, context)
|
||||||
"https://lmgtfy.com/?q=andnixsh+apk+verification+disable",
|
|
||||||
R.color.Twitter,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||||
if (fullMsg != null)
|
if (fullMsg != null)
|
||||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||||
basicDialog(
|
|
||||||
context.getString(R.string.advanced),
|
|
||||||
fullMsg,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
context.getString(R.string.installation_miui) -> {
|
context.getString(R.string.installation_miui) -> {
|
||||||
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
setPositiveButton(context.getString(R.string.guide)) { _, _ ->
|
||||||
openUrl(
|
openUrl("https://telegra.ph/How-to-install-v15-on-MIUI-02-11", R.color.Telegram, context)
|
||||||
"https://telegra.ph/How-to-install-v15-on-MIUI-02-11",
|
|
||||||
R.color.Telegram,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
setNeutralButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||||
if (fullMsg != null)
|
if (fullMsg != null)
|
||||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||||
basicDialog(
|
|
||||||
context.getString(R.string.advanced),
|
|
||||||
fullMsg,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||||
if (fullMsg != null)
|
if (fullMsg != null)
|
||||||
setNegativeButton(context.getString(R.string.advanced)) { _, _ ->
|
setNegativeButton(context.getString(R.string.advanced)) { _, _ -> basicDialog(context.getString(R.string.advanced), fullMsg, context) }
|
||||||
basicDialog(
|
|
||||||
context.getString(R.string.advanced),
|
|
||||||
fullMsg,
|
|
||||||
context
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
create()
|
create()
|
||||||
showWithAccent()
|
applyAccent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +104,7 @@ object DialogContainer {
|
||||||
setMessage(msg)
|
setMessage(msg)
|
||||||
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||||
create()
|
create()
|
||||||
showWithAccent()
|
applyAccent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,7 @@ import com.vanced.manager.databinding.DialogInstallationFilesDetectedBinding
|
||||||
import com.vanced.manager.utils.defPrefs
|
import com.vanced.manager.utils.defPrefs
|
||||||
import com.vanced.manager.utils.managerVariant
|
import com.vanced.manager.utils.managerVariant
|
||||||
|
|
||||||
class InstallationFilesDetectedDialog :
|
class InstallationFilesDetectedDialog : BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
||||||
BindingBottomSheetDialogFragment<DialogInstallationFilesDetectedBinding>() {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -41,17 +40,15 @@ class InstallationFilesDetectedDialog :
|
||||||
|
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
val app =
|
val app = arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
||||||
arguments?.getString(TAG_APP) ?: throw IllegalArgumentException("app name is null")
|
|
||||||
installationDetectedTitle.text = getString(R.string.app_install_files_detected, app)
|
installationDetectedTitle.text = getString(R.string.app_install_files_detected, app)
|
||||||
installationDetectedSummary.text =
|
installationDetectedSummary.text = getString(R.string.app_install_files_detected_summary, app)
|
||||||
getString(R.string.app_install_files_detected_summary, app)
|
|
||||||
installationDetectedRedownload.setOnClickListener {
|
installationDetectedRedownload.setOnClickListener {
|
||||||
dismiss()
|
dismiss()
|
||||||
when (app) {
|
if (app == getString(R.string.vanced))
|
||||||
getString(R.string.vanced) -> showDialog(VancedPreferencesDialog())
|
showDialog(VancedPreferencesDialog())
|
||||||
getString(R.string.music) -> showDialog(MusicPreferencesDialog())
|
else {
|
||||||
else -> showDialog(AppDownloadDialog.newInstance(app))
|
showDialog(AppDownloadDialog.newInstance(app))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
installationDetectedInstall.setOnClickListener {
|
installationDetectedInstall.setOnClickListener {
|
||||||
|
|
|
@ -2,10 +2,10 @@ package com.vanced.manager.ui.dialogs
|
||||||
|
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.drawable.ColorDrawable
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -16,7 +16,6 @@ import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogManagerAccentColorBinding
|
import com.vanced.manager.databinding.DialogManagerAccentColorBinding
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.*
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
|
|
||||||
class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorBinding>() {
|
class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorBinding>() {
|
||||||
|
|
||||||
|
@ -35,7 +34,6 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
||||||
) = DialogManagerAccentColorBinding.inflate(inflater, container, false)
|
) = DialogManagerAccentColorBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
override fun otherSetups() {
|
override fun otherSetups() {
|
||||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
|
||||||
bindData()
|
bindData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,20 +48,9 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
||||||
hexEdittext.apply {
|
hexEdittext.apply {
|
||||||
setText(accent.toHex(), TextView.BufferType.EDITABLE)
|
setText(accent.toHex(), TextView.BufferType.EDITABLE)
|
||||||
addTextChangedListener(object : TextWatcher {
|
addTextChangedListener(object : TextWatcher {
|
||||||
override fun beforeTextChanged(
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
s: CharSequence?,
|
|
||||||
start: Int,
|
|
||||||
count: Int,
|
|
||||||
after: Int
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTextChanged(
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
s: CharSequence?,
|
|
||||||
start: Int,
|
|
||||||
before: Int,
|
|
||||||
count: Int
|
|
||||||
) {
|
|
||||||
if (length() == 0) {
|
if (length() == 0) {
|
||||||
setText("#")
|
setText("#")
|
||||||
setSelection(1)
|
setSelection(1)
|
||||||
|
@ -74,8 +61,7 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
||||||
val colorFromEditText = Color.parseColor(text.toString())
|
val colorFromEditText = Color.parseColor(text.toString())
|
||||||
accentPicker.setColor(colorFromEditText)
|
accentPicker.setColor(colorFromEditText)
|
||||||
mutableAccentColor.value = colorFromEditText
|
mutableAccentColor.value = colorFromEditText
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,12 +93,8 @@ class ManagerAccentColorDialog : BindingDialogFragment<DialogManagerAccentColorB
|
||||||
mutableAccentColor.value = colorFromEditText
|
mutableAccentColor.value = colorFromEditText
|
||||||
prefs.managerAccent = colorFromEditText
|
prefs.managerAccent = colorFromEditText
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
log("VMTheme", getString(R.string.failed_accent))
|
Log.d("VMTheme", getString(R.string.failed_accent))
|
||||||
Toast.makeText(
|
Toast.makeText(requireActivity(), getString(R.string.failed_accent), Toast.LENGTH_SHORT).show()
|
||||||
requireActivity(),
|
|
||||||
getString(R.string.failed_accent),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
return@setOnClickListener
|
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.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogManagerLanguageBinding
|
import com.vanced.manager.databinding.DialogManagerLanguageBinding
|
||||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||||
import com.vanced.manager.utils.checkedButtonTag
|
import com.vanced.manager.utils.getCheckedButtonTag
|
||||||
import com.vanced.manager.utils.getLanguageFormat
|
import com.vanced.manager.utils.getLanguageFormat
|
||||||
import com.vanced.manager.utils.managerLang
|
import com.vanced.manager.utils.managerLang
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class ManagerLanguageDialog : BindingBottomSheetDialogFragment<DialogManagerLang
|
||||||
val language = prefs.managerLang
|
val language = prefs.managerLang
|
||||||
root.findViewWithTag<ThemedMaterialRadioButton>(language)?.isChecked = true
|
root.findViewWithTag<ThemedMaterialRadioButton>(language)?.isChecked = true
|
||||||
languageSave.setOnClickListener {
|
languageSave.setOnClickListener {
|
||||||
val newPref = binding.languageRadiogroup.checkedButtonTag
|
val newPref = binding.languageRadiogroup.getCheckedButtonTag()
|
||||||
if (language != newPref) {
|
if (language != newPref) {
|
||||||
prefs.managerLang = newPref
|
prefs.managerLang = newPref
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||||
import com.google.android.material.radiobutton.MaterialRadioButton
|
import com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogManagerThemeBinding
|
import com.vanced.manager.databinding.DialogManagerThemeBinding
|
||||||
import com.vanced.manager.utils.checkedButtonTag
|
import com.vanced.manager.utils.getCheckedButtonTag
|
||||||
import com.vanced.manager.utils.managerTheme
|
import com.vanced.manager.utils.managerTheme
|
||||||
|
|
||||||
class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBinding>() {
|
class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBinding>() {
|
||||||
|
@ -36,7 +36,7 @@ class ManagerThemeDialog : BindingBottomSheetDialogFragment<DialogManagerThemeBi
|
||||||
val theme = prefs.managerTheme
|
val theme = prefs.managerTheme
|
||||||
root.findViewWithTag<MaterialRadioButton>(theme).isChecked = true
|
root.findViewWithTag<MaterialRadioButton>(theme).isChecked = true
|
||||||
themeSave.setOnClickListener {
|
themeSave.setOnClickListener {
|
||||||
val newPref = themeRadiogroup.checkedButtonTag
|
val newPref = themeRadiogroup.getCheckedButtonTag()
|
||||||
if (theme != newPref) {
|
if (theme != newPref) {
|
||||||
prefs.managerTheme = newPref
|
prefs.managerTheme = newPref
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.vanced.manager.ui.dialogs
|
package com.vanced.manager.ui.dialogs
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
@ -17,9 +16,8 @@ import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
import com.vanced.manager.core.ui.base.BindingDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogManagerUpdateBinding
|
import com.vanced.manager.databinding.DialogManagerUpdateBinding
|
||||||
import com.vanced.manager.utils.DownloadHelper.downloadManager
|
import com.vanced.manager.utils.DownloadHelper.downloadManager
|
||||||
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
import com.vanced.manager.utils.applyAccent
|
import com.vanced.manager.utils.applyAccent
|
||||||
import com.vanced.manager.utils.currentDownload
|
|
||||||
import com.vanced.manager.utils.downloadProgress
|
|
||||||
import com.vanced.manager.utils.manager
|
import com.vanced.manager.utils.manager
|
||||||
|
|
||||||
class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>() {
|
class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>() {
|
||||||
|
@ -58,8 +56,7 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
||||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||||
bindData()
|
bindData()
|
||||||
if (arguments?.getBoolean(TAG_FORCE_UPDATE) == true) {
|
if (arguments?.getBoolean(TAG_FORCE_UPDATE) == true) {
|
||||||
binding.managerUpdatePatient.text =
|
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||||
requireActivity().getString(R.string.please_be_patient)
|
|
||||||
downloadManager(requireActivity())
|
downloadManager(requireActivity())
|
||||||
} else {
|
} else {
|
||||||
checkUpdates()
|
checkUpdates()
|
||||||
|
@ -71,20 +68,24 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
||||||
isCancelable = false
|
isCancelable = false
|
||||||
managerUpdateProgressbar.applyAccent()
|
managerUpdateProgressbar.applyAccent()
|
||||||
managerUpdateCancel.setOnClickListener {
|
managerUpdateCancel.setOnClickListener {
|
||||||
downloadProgress.value = 0
|
with(downloadProgress.value) {
|
||||||
currentDownload?.cancel()
|
this?.downloadProgress?.value = 0
|
||||||
|
this?.currentDownload?.cancel()
|
||||||
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
bindDownloadProgress()
|
bindDownloadProgress()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
private fun DialogManagerUpdateBinding.bindDownloadProgress() {
|
private fun DialogManagerUpdateBinding.bindDownloadProgress() {
|
||||||
downloadProgress.observe(viewLifecycleOwner) {
|
with(downloadProgress) {
|
||||||
|
observe(viewLifecycleOwner) { progressModel ->
|
||||||
|
progressModel.downloadProgress.observe(viewLifecycleOwner) {
|
||||||
managerUpdateProgressbar.progress = it
|
managerUpdateProgressbar.progress = it
|
||||||
managerUpdateProgressbarContainer.isVisible = it != 0
|
managerUpdateProgressbar.isVisible = it != 0
|
||||||
managerUpdateProgress.text = "$it%"
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,19 +94,12 @@ class ManagerUpdateDialog : BindingDialogFragment<DialogManagerUpdateBinding>()
|
||||||
registerReceiver()
|
registerReceiver()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkUpdates() {
|
private fun checkUpdates() {
|
||||||
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
if (manager.value?.int("versionCode") ?: 0 > VERSION_CODE) {
|
||||||
binding.managerUpdatePatient.text =
|
binding.managerUpdatePatient.text = requireActivity().getString(R.string.please_be_patient)
|
||||||
requireActivity().getString(R.string.please_be_patient)
|
|
||||||
downloadManager(requireActivity())
|
downloadManager(requireActivity())
|
||||||
} else {
|
} else {
|
||||||
binding.managerUpdatePatient.text =
|
binding.managerUpdatePatient.text = requireActivity().getString(R.string.update_not_found)
|
||||||
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.topjohnwu.superuser.Shell
|
||||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.databinding.DialogManagerVariantBinding
|
import com.vanced.manager.databinding.DialogManagerVariantBinding
|
||||||
import com.vanced.manager.utils.checkedButtonTag
|
import com.vanced.manager.utils.getCheckedButtonTag
|
||||||
import com.vanced.manager.utils.managerVariant
|
import com.vanced.manager.utils.managerVariant
|
||||||
|
|
||||||
class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVariantBinding>() {
|
class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVariantBinding>() {
|
||||||
|
@ -37,7 +37,7 @@ class ManagerVariantDialog : BindingBottomSheetDialogFragment<DialogManagerVaria
|
||||||
val variant = prefs.managerVariant
|
val variant = prefs.managerVariant
|
||||||
root.findViewWithTag<MaterialRadioButton>(variant).isChecked = true
|
root.findViewWithTag<MaterialRadioButton>(variant).isChecked = true
|
||||||
variantSave.setOnClickListener {
|
variantSave.setOnClickListener {
|
||||||
val newPref = variantRadiogroup.checkedButtonTag
|
val newPref = variantRadiogroup.getCheckedButtonTag()
|
||||||
if (variant != newPref) {
|
if (variant != newPref) {
|
||||||
prefs.managerVariant =
|
prefs.managerVariant =
|
||||||
if (newPref == "root" && Shell.rootAccess()) {
|
if (newPref == "root" && Shell.rootAccess()) {
|
||||||
|
|
|
@ -7,7 +7,9 @@ import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.core.ui.ext.showDialog
|
import com.vanced.manager.core.ui.ext.showDialog
|
||||||
import com.vanced.manager.databinding.DialogMusicPreferencesBinding
|
import com.vanced.manager.databinding.DialogMusicPreferencesBinding
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.convertToAppVersions
|
||||||
|
import com.vanced.manager.utils.defPrefs
|
||||||
|
import com.vanced.manager.utils.musicVersions
|
||||||
|
|
||||||
class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPreferencesBinding>() {
|
class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPreferencesBinding>() {
|
||||||
|
|
||||||
|
@ -33,13 +35,9 @@ class MusicPreferencesDialog : BindingBottomSheetDialogFragment<DialogMusicPrefe
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
val musicVersionsConv = musicVersions.value?.value?.convertToAppVersions()
|
val musicVersionsConv = musicVersions.value?.value?.convertToAppVersions()
|
||||||
musicInstallTitle.text =
|
musicInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.music))
|
||||||
getString(R.string.app_installation_preferences, getString(R.string.music))
|
musicVersion.text = getString(R.string.chosen_version, prefs.getString("music_version", "latest"))
|
||||||
musicVersion.text = getString(
|
openVersionSelector.setOnClickListener {
|
||||||
R.string.chosen_version,
|
|
||||||
prefs.musicVersion?.formatVersion(requireActivity())
|
|
||||||
)
|
|
||||||
openVersionSelectorLayout.setOnClickListener {
|
|
||||||
dismiss()
|
dismiss()
|
||||||
showDialog(
|
showDialog(
|
||||||
AppVersionSelectorDialog.newInstance(
|
AppVersionSelectorDialog.newInstance(
|
||||||
|
|
|
@ -43,17 +43,12 @@ class SelectAppsDialog : BindingBottomSheetDialogFragment<DialogSelectAppsBindin
|
||||||
}
|
}
|
||||||
selectAppsSave.setOnClickListener {
|
selectAppsSave.setOnClickListener {
|
||||||
if (ad.apps.all { app -> !app.isChecked }) {
|
if (ad.apps.all { app -> !app.isChecked }) {
|
||||||
Toast.makeText(
|
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||||
requireActivity(),
|
|
||||||
R.string.select_at_least_one_app,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
ad.apps.forEach { app ->
|
ad.apps.forEach { app ->
|
||||||
putBoolean("enable_${app.tag}", app.isChecked)
|
putBoolean("enable_${app.tag}", app.isChecked)
|
||||||
putBoolean("${app.tag}_notifs", app.isChecked)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dismiss()
|
dismiss()
|
||||||
|
|
|
@ -32,7 +32,7 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
with(binding) {
|
with (binding) {
|
||||||
servicedSlider.value = prefs.getInt("serviced_sleep_timer", 1).toFloat()
|
servicedSlider.value = prefs.getInt("serviced_sleep_timer", 1).toFloat()
|
||||||
servicedCancel.setOnClickListener {
|
servicedCancel.setOnClickListener {
|
||||||
dismiss()
|
dismiss()
|
||||||
|
@ -41,26 +41,12 @@ class ServiceDTimerDialog : BindingDialogFragment<DialogServicedTimerBinding>()
|
||||||
try {
|
try {
|
||||||
arrayOf("vanced", "music").forEach { app ->
|
arrayOf("vanced", "music").forEach { app ->
|
||||||
if (scriptExists(app)) {
|
if (scriptExists(app)) {
|
||||||
val apkFPath =
|
val apkFPath = "${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
||||||
"${PackageHelper.apkInstallPath}/${app.capitalize(Locale.ROOT)}/base.apk"
|
getPackageDir(requireActivity(), getPkgNameRoot(app))?.let { it1 -> requireActivity().writeServiceDScript(apkFPath, it1, app) }
|
||||||
getPackageDir(
|
|
||||||
requireActivity(),
|
|
||||||
getPkgNameRoot(app)
|
|
||||||
)?.let { it1 ->
|
|
||||||
requireActivity().writeServiceDScript(
|
|
||||||
apkFPath,
|
|
||||||
it1,
|
|
||||||
app
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Toast.makeText(
|
Toast.makeText(requireActivity(), R.string.script_save_failed, Toast.LENGTH_SHORT).show()
|
||||||
requireActivity(),
|
|
||||||
R.string.script_save_failed,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,7 @@ class URLChangeDialog : BindingDialogFragment<DialogCustomUrlBinding>() {
|
||||||
TextView.BufferType.EDITABLE
|
TextView.BufferType.EDITABLE
|
||||||
)
|
)
|
||||||
urlSave.setOnClickListener {
|
urlSave.setOnClickListener {
|
||||||
val finalUrl =
|
val finalUrl = if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
||||||
if (urlInput.text?.startsWith("https://") == true || urlInput.text?.startsWith("http://") == true) {
|
|
||||||
urlInput.text?.removeSuffix("/").toString()
|
urlInput.text?.removeSuffix("/").toString()
|
||||||
} else {
|
} else {
|
||||||
"https://${urlInput.text}".removeSuffix("/")
|
"https://${urlInput.text}".removeSuffix("/")
|
||||||
|
|
|
@ -19,8 +19,7 @@ import com.vanced.manager.utils.lang
|
||||||
import com.vanced.manager.utils.vanced
|
import com.vanced.manager.utils.vanced
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class VancedLanguageSelectionDialog :
|
class VancedLanguageSelectionDialog : BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
||||||
BindingBottomSheetDialogFragment<DialogVancedLanguageSelectionBinding>() {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -53,11 +52,7 @@ class VancedLanguageSelectionDialog :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chosenLangs.isEmpty()) {
|
if (chosenLangs.isEmpty()) {
|
||||||
Toast.makeText(
|
Toast.makeText(requireActivity(), R.string.select_at_least_one_lang, Toast.LENGTH_SHORT).show()
|
||||||
requireActivity(),
|
|
||||||
R.string.select_at_least_one_lang,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
prefs.lang = chosenLangs.joinToString()
|
prefs.lang = chosenLangs.joinToString()
|
||||||
|
|
|
@ -3,11 +3,14 @@ package com.vanced.manager.ui.dialogs
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.core.ui.ext.showDialog
|
import com.vanced.manager.core.ui.ext.showDialog
|
||||||
import com.vanced.manager.databinding.DialogVancedPreferencesBinding
|
import com.vanced.manager.databinding.DialogVancedPreferencesBinding
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.*
|
||||||
|
import com.vanced.manager.utils.AppUtils.vancedPkg
|
||||||
|
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() {
|
class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPreferencesBinding>() {
|
||||||
|
@ -35,27 +38,20 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
val showLang = mutableListOf<String>()
|
val showLang = mutableListOf<String>()
|
||||||
installPrefs.lang?.split(", ")?.forEach { lang ->
|
installPrefs.lang?.split(", ")?.toTypedArray()?.forEach { lang ->
|
||||||
val loc = Locale(lang)
|
val loc = Locale(lang)
|
||||||
showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT))
|
showLang.add(loc.getDisplayLanguage(loc).capitalize(Locale.ROOT))
|
||||||
}
|
}
|
||||||
val vancedVersionsConv = vancedVersions.value?.value?.convertToAppVersions()
|
val vancedVersionsConv = vancedVersions.value?.value?.convertToAppVersions()
|
||||||
vancedInstallTitle.text =
|
vancedInstallTitle.text = getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
||||||
getString(R.string.app_installation_preferences, getString(R.string.vanced))
|
vancedTheme.text = getString(R.string.chosen_theme, installPrefs.getString("theme", "dark")?.convertToAppTheme(requireActivity()))
|
||||||
vancedTheme.text = getString(
|
vancedVersion.text = getString(R.string.chosen_version, defPrefs.getString("vanced_version", "latest"))
|
||||||
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)
|
vancedLang.text = getString(R.string.chosen_lang, showLang)
|
||||||
openThemeSelectorLayout.setOnClickListener {
|
openThemeSelector.setOnClickListener {
|
||||||
dismiss()
|
dismiss()
|
||||||
showDialog(VancedThemeSelectorDialog())
|
showDialog(VancedThemeSelectorDialog())
|
||||||
}
|
}
|
||||||
openVersionSelectorLayout.setOnClickListener {
|
openVersionSelector.setOnClickListener {
|
||||||
dismiss()
|
dismiss()
|
||||||
showDialog(
|
showDialog(
|
||||||
AppVersionSelectorDialog.newInstance(
|
AppVersionSelectorDialog.newInstance(
|
||||||
|
@ -64,7 +60,7 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
openLanguageSelectorLayout.setOnClickListener {
|
openLanguageSelector.setOnClickListener {
|
||||||
dismiss()
|
dismiss()
|
||||||
showDialog(VancedLanguageSelectionDialog())
|
showDialog(VancedLanguageSelectionDialog())
|
||||||
}
|
}
|
||||||
|
@ -72,13 +68,34 @@ class VancedPreferencesDialog : BindingBottomSheetDialogFragment<DialogVancedPre
|
||||||
if (showLang.isEmpty()) {
|
if (showLang.isEmpty()) {
|
||||||
installPrefs.lang = "en"
|
installPrefs.lang = "en"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun downloadVanced(version: String? = null) {
|
||||||
dismiss()
|
dismiss()
|
||||||
showDialog(
|
showDialog(
|
||||||
AppDownloadDialog.newInstance(
|
AppDownloadDialog.newInstance(
|
||||||
app = getString(R.string.vanced)
|
app = getString(R.string.vanced),
|
||||||
|
version = version
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defPrefs.managerVariant == "nonroot" && isMicrogBroken && installPrefs.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,13 +10,12 @@ import com.vanced.manager.core.ui.base.BindingBottomSheetDialogFragment
|
||||||
import com.vanced.manager.core.ui.ext.showDialog
|
import com.vanced.manager.core.ui.ext.showDialog
|
||||||
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
import com.vanced.manager.databinding.DialogBottomRadioButtonBinding
|
||||||
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
import com.vanced.manager.ui.core.ThemedMaterialRadioButton
|
||||||
import com.vanced.manager.utils.checkedButtonTag
|
|
||||||
import com.vanced.manager.utils.convertToAppTheme
|
import com.vanced.manager.utils.convertToAppTheme
|
||||||
|
import com.vanced.manager.utils.getCheckedButtonTag
|
||||||
import com.vanced.manager.utils.theme
|
import com.vanced.manager.utils.theme
|
||||||
import com.vanced.manager.utils.vanced
|
import com.vanced.manager.utils.vanced
|
||||||
|
|
||||||
class VancedThemeSelectorDialog :
|
class VancedThemeSelectorDialog : BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
||||||
BindingBottomSheetDialogFragment<DialogBottomRadioButtonBinding>() {
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -25,12 +24,7 @@ class VancedThemeSelectorDialog :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val prefs by lazy {
|
private val prefs by lazy { requireActivity().getSharedPreferences("installPrefs", Context.MODE_PRIVATE) }
|
||||||
requireActivity().getSharedPreferences(
|
|
||||||
"installPrefs",
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun binding(
|
override fun binding(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -57,7 +51,7 @@ class VancedThemeSelectorDialog :
|
||||||
tag.isChecked = true
|
tag.isChecked = true
|
||||||
}
|
}
|
||||||
dialogSave.setOnClickListener {
|
dialogSave.setOnClickListener {
|
||||||
val checkedTag = binding.dialogRadiogroup.checkedButtonTag
|
val checkedTag = binding.dialogRadiogroup.getCheckedButtonTag()
|
||||||
if (checkedTag != null) {
|
if (checkedTag != null) {
|
||||||
prefs.theme = checkedTag
|
prefs.theme = checkedTag
|
||||||
}
|
}
|
||||||
|
@ -66,7 +60,7 @@ class VancedThemeSelectorDialog :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
ThemedMaterialRadioButton(requireActivity()).apply {
|
||||||
text = theme.convertToAppTheme(requireActivity())
|
text = theme.convertToAppTheme(requireActivity())
|
||||||
tag = theme
|
tag = theme
|
||||||
|
|
23
app/src/main/java/com/vanced/manager/ui/events/Event.kt
Normal file
23
app/src/main/java/com/vanced/manager/ui/events/Event.kt
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
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,6 +6,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
|
@ -36,11 +37,11 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun dataBind() {
|
private fun dataBind() {
|
||||||
requireActivity().title = getString(R.string.title_about)
|
requireActivity().title = getString(R.string.title_about)
|
||||||
binding.aboutVersionCard.setOnClickListener {
|
binding.aboutHeader.root.setOnClickListener {
|
||||||
showDialog(
|
showDialog(
|
||||||
AppInfoDialog.newInstance(
|
AppInfoDialog.newInstance(
|
||||||
appName = getString(R.string.app_name),
|
appName = getString(R.string.app_name),
|
||||||
appIcon = R.mipmap.ic_launcher,
|
appIcon = AppCompatResources.getDrawable(requireActivity(), R.mipmap.ic_launcher),
|
||||||
changelog = manager.value?.string("changelog")
|
changelog = manager.value?.string("changelog")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -60,25 +61,17 @@ class AboutFragment : BindingFragment<FragmentAboutBinding>() {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
val devSettings = prefs.getBoolean("devSettings", false)
|
val devSettings = prefs.getBoolean("devSettings", false)
|
||||||
if (!devSettings) {
|
if (!devSettings) {
|
||||||
Toast.makeText(
|
Toast.makeText(requireContext(), "Dev options unlocked!", Toast.LENGTH_SHORT).show()
|
||||||
requireContext(),
|
|
||||||
"Dev options unlocked!",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
prefs.edit { putBoolean("devSettings", true) }
|
prefs.edit { putBoolean("devSettings", true) }
|
||||||
} else
|
} else
|
||||||
Toast.makeText(
|
Toast.makeText(requireContext(), "Dev options already unlocked", Toast.LENGTH_SHORT).show()
|
||||||
requireContext(),
|
|
||||||
"Dev options already unlocked",
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return@setOnTouchListener true
|
return@setOnTouchListener true
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
binding.aboutGithubButton.setOnClickListener { viewModel.openUrl("https://github.com/YTVanced/VancedInstaller") }
|
binding.aboutSources.aboutGithubButton.setOnClickListener { viewModel.openUrl("https://github.com/YTVanced/VancedInstaller") }
|
||||||
binding.aboutLicenseButton.setOnClickListener { viewModel.openUrl("https://raw.githubusercontent.com/YTVanced/VancedInstaller/dev/LICENSE") }
|
binding.aboutSources.aboutLicenseButton.setOnClickListener { viewModel.openUrl("https://raw.githubusercontent.com/YTVanced/VancedInstaller/dev/LICENSE") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,22 @@ package com.vanced.manager.ui.fragments
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
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.core.ui.base.BindingFragment
|
||||||
import com.vanced.manager.databinding.FragmentDevSettingsBinding
|
import com.vanced.manager.databinding.FragmentDevSettingsBinding
|
||||||
import com.vanced.manager.ui.WelcomeActivity
|
import com.vanced.manager.ui.WelcomeActivity
|
||||||
import com.vanced.manager.ui.dialogs.ManagerUpdateDialog
|
import com.vanced.manager.ui.dialogs.ManagerUpdateDialog
|
||||||
import com.vanced.manager.ui.dialogs.URLChangeDialog
|
import com.vanced.manager.ui.dialogs.URLChangeDialog
|
||||||
|
import com.vanced.manager.utils.authCrowdin
|
||||||
|
|
||||||
class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
||||||
|
|
||||||
|
@ -33,6 +40,7 @@ class DevSettingsFragment : BindingFragment<FragmentDevSettingsBinding>() {
|
||||||
bindWelcomeLauncher()
|
bindWelcomeLauncher()
|
||||||
bindForceUpdate()
|
bindForceUpdate()
|
||||||
bindChannelURL()
|
bindChannelURL()
|
||||||
|
bindCrowdin()
|
||||||
bindKernelArch()
|
bindKernelArch()
|
||||||
bindAndroidVersion()
|
bindAndroidVersion()
|
||||||
}
|
}
|
||||||
|
@ -64,6 +72,31 @@ 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() {
|
private fun FragmentDevSettingsBinding.bindKernelArch() {
|
||||||
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
|
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
|
||||||
|
|
||||||
|
|
|
@ -41,12 +41,7 @@ class GrantRootFragment : BindingFragment<FragmentGrantRootBinding>() {
|
||||||
|
|
||||||
private fun grantRoot() {
|
private fun grantRoot() {
|
||||||
if (Shell.rootAccess()) {
|
if (Shell.rootAccess()) {
|
||||||
getDefaultSharedPreferences(requireActivity()).edit {
|
getDefaultSharedPreferences(requireActivity()).edit { putString("vanced_variant", "root") }
|
||||||
putString(
|
|
||||||
"vanced_variant",
|
|
||||||
"root"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
navigateToFirstLaunch()
|
navigateToFirstLaunch()
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(requireActivity(), R.string.root_not_granted, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireActivity(), R.string.root_not_granted, Toast.LENGTH_SHORT).show()
|
||||||
|
|
|
@ -9,36 +9,41 @@ import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
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.FlexboxLayoutManager
|
||||||
import com.google.android.flexbox.JustifyContent
|
import com.google.android.flexbox.JustifyContent
|
||||||
import com.vanced.manager.BuildConfig.VERSION_CODE
|
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.adapter.ExpandableAppListAdapter
|
import com.vanced.manager.adapter.AppListAdapter
|
||||||
import com.vanced.manager.adapter.LinkAdapter
|
import com.vanced.manager.adapter.LinkAdapter
|
||||||
import com.vanced.manager.adapter.SponsorAdapter
|
import com.vanced.manager.adapter.SponsorAdapter
|
||||||
import com.vanced.manager.core.ui.base.BindingFragment
|
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.databinding.FragmentHomeBinding
|
||||||
import com.vanced.manager.ui.dialogs.AppInfoDialog
|
|
||||||
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
||||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
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.isFetching
|
||||||
import com.vanced.manager.utils.manager
|
|
||||||
|
|
||||||
class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
open class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val INSTALL_FAILED = "INSTALL_FAILED"
|
const val INSTALL_FAILED = "INSTALL_FAILED"
|
||||||
const val REFRESH_HOME = "REFRESH_HOME"
|
const val REFRESH_HOME = "REFRESH_HOME"
|
||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel: HomeViewModel by viewModels()
|
private val viewModel: HomeViewModel by viewModels {
|
||||||
|
HomeViewModelFactory(requireActivity())
|
||||||
|
}
|
||||||
|
|
||||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }
|
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }
|
||||||
|
private val prefs by lazy { PreferenceManager.getDefaultSharedPreferences(requireActivity()) }
|
||||||
|
private lateinit var tooltip: ViewTooltip
|
||||||
|
|
||||||
override fun binding(
|
override fun binding(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -53,27 +58,28 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
requireActivity().title = getString(R.string.title_home)
|
requireActivity().title = getString(R.string.title_home)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
with(binding) {
|
with (binding) {
|
||||||
homeRefresh.setOnRefreshListener { viewModel.fetchData() }
|
homeRefresh.setOnRefreshListener { viewModel.fetchData() }
|
||||||
isFetching.observe(viewLifecycleOwner) { homeRefresh.isRefreshing = it }
|
isFetching.observe(viewLifecycleOwner) { homeRefresh.isRefreshing = it }
|
||||||
|
tooltip = ViewTooltip
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
.on(recyclerAppList)
|
||||||
if (prefs.contains("LastVersionCode")) {
|
.position(ViewTooltip.Position.TOP)
|
||||||
if (prefs.getInt("LastVersionCode", -1) < VERSION_CODE) {
|
.autoHide(false, 0)
|
||||||
showDialog(
|
.color(ResourcesCompat.getColor(requireActivity().resources, R.color.Twitter, null))
|
||||||
AppInfoDialog.newInstance(
|
.withShadow(false)
|
||||||
appName = getString(R.string.app_name),
|
.corner(25)
|
||||||
appIcon = R.mipmap.ic_launcher,
|
.onHide {
|
||||||
changelog = manager.value?.string("changelog")
|
prefs.edit { putBoolean("show_changelog_tooltip", false) }
|
||||||
)
|
}
|
||||||
)
|
.text(requireActivity().getString(R.string.app_changelog_tooltip))
|
||||||
prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
|
||||||
|
if (prefs.getBoolean("show_changelog_tooltip", true)) {
|
||||||
|
tooltip.show()
|
||||||
}
|
}
|
||||||
} else prefs.edit().putInt("LastVersionCode", VERSION_CODE).apply()
|
|
||||||
|
|
||||||
recyclerAppList.apply {
|
recyclerAppList.apply {
|
||||||
layoutManager = LinearLayoutManager(requireActivity())
|
layoutManager = LinearLayoutManager(requireActivity())
|
||||||
adapter = ExpandableAppListAdapter(requireActivity(), viewModel /*, tooltip*/)
|
adapter = AppListAdapter(requireActivity(), viewModel, viewLifecycleOwner, tooltip)
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,12 +102,14 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
inflater.inflate(R.menu.toolbar_menu, menu)
|
inflater.inflateWithCrowdin(R.menu.toolbar_menu, menu, resources)
|
||||||
|
super.onCreateOptionsMenu(menu, inflater)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
localBroadcastManager.unregisterReceiver(broadcastReceiver)
|
||||||
|
tooltip.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -112,11 +120,7 @@ class HomeFragment : BindingFragment<FragmentHomeBinding>() {
|
||||||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
INSTALL_FAILED -> installAlertBuilder(
|
INSTALL_FAILED -> installAlertBuilder(intent.getStringExtra("errorMsg").toString(), intent.getStringExtra("fullErrorMsg"), requireActivity())
|
||||||
intent.getStringExtra("errorMsg").toString(),
|
|
||||||
intent.getStringExtra("fullErrorMsg"),
|
|
||||||
requireActivity()
|
|
||||||
)
|
|
||||||
REFRESH_HOME -> viewModel.fetchData()
|
REFRESH_HOME -> viewModel.fetchData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package com.vanced.manager.ui.fragments
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.Toast
|
|
||||||
import com.vanced.manager.R
|
|
||||||
import com.vanced.manager.core.ui.base.BindingFragment
|
|
||||||
import com.vanced.manager.databinding.FragmentLogBinding
|
|
||||||
import com.vanced.manager.utils.AppUtils.logs
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.IOException
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
class LogFragment : BindingFragment<FragmentLogBinding>() {
|
|
||||||
|
|
||||||
override fun binding(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
) = FragmentLogBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
override fun otherSetups() {
|
|
||||||
binding.bindData()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun FragmentLogBinding.bindData() {
|
|
||||||
val logs = TextUtils.concat(*logs.toTypedArray())
|
|
||||||
logText.text = logs
|
|
||||||
logSave.setOnClickListener {
|
|
||||||
try {
|
|
||||||
val calendar = Calendar.getInstance()
|
|
||||||
val year = calendar.get(Calendar.YEAR)
|
|
||||||
val month = calendar.get(Calendar.MONTH)
|
|
||||||
val day = calendar.get(Calendar.DAY_OF_MONTH)
|
|
||||||
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
|
||||||
val minute = calendar.get(Calendar.MINUTE)
|
|
||||||
val second = calendar.get(Calendar.SECOND)
|
|
||||||
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(), 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -5,13 +5,13 @@ import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.adapter.SelectAppsAdapter
|
import com.vanced.manager.adapter.SelectAppsAdapter
|
||||||
import com.vanced.manager.core.ui.base.BindingFragment
|
import com.vanced.manager.core.ui.base.BindingFragment
|
||||||
import com.vanced.manager.databinding.FragmentSelectAppsBinding
|
import com.vanced.manager.databinding.FragmentSelectAppsBinding
|
||||||
import com.vanced.manager.ui.WelcomeActivity
|
|
||||||
|
|
||||||
class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
||||||
|
|
||||||
|
@ -45,14 +45,13 @@ class SelectAppsFragment : BindingFragment<FragmentSelectAppsBinding>() {
|
||||||
|
|
||||||
private fun actionOnClickAppsFab() {
|
private fun actionOnClickAppsFab() {
|
||||||
if (selectAdapter.apps.all { app -> !app.isChecked }) {
|
if (selectAdapter.apps.all { app -> !app.isChecked }) {
|
||||||
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT)
|
Toast.makeText(requireActivity(), R.string.select_at_least_one_app, Toast.LENGTH_SHORT).show()
|
||||||
.show()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val prefs = getDefaultSharedPreferences(requireActivity())
|
val prefs = getDefaultSharedPreferences(requireActivity())
|
||||||
selectAdapter.apps.forEach { app ->
|
selectAdapter.apps.forEach { app ->
|
||||||
prefs.edit { putBoolean("enable_${app.tag}", app.isChecked) }
|
prefs.edit { putBoolean("enable_${app.tag}", app.isChecked) }
|
||||||
}
|
}
|
||||||
(requireActivity() as WelcomeActivity).navigateTo(2)
|
findNavController().navigate(SelectAppsFragmentDirections.selectAppsToGrantRoot())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,6 @@ import android.view.MenuInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.firebase.analytics.FirebaseAnalytics
|
import com.google.firebase.analytics.FirebaseAnalytics
|
||||||
|
@ -35,7 +34,6 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
private val prefs by lazy { getDefaultSharedPreferences(requireActivity()) }
|
private val prefs by lazy { getDefaultSharedPreferences(requireActivity()) }
|
||||||
|
|
||||||
private lateinit var variant: String
|
private lateinit var variant: String
|
||||||
private lateinit var parentActivity: FragmentActivity
|
|
||||||
|
|
||||||
override fun binding(
|
override fun binding(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -45,7 +43,6 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
|
|
||||||
override fun otherSetups() {
|
override fun otherSetups() {
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
parentActivity = requireActivity()
|
|
||||||
bindData()
|
bindData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,8 +63,8 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
|
|
||||||
private fun FragmentSettingsBinding.bindRecycler() {
|
private fun FragmentSettingsBinding.bindRecycler() {
|
||||||
notificationsRecycler.apply {
|
notificationsRecycler.apply {
|
||||||
layoutManager = LinearLayoutManager(parentActivity)
|
layoutManager = LinearLayoutManager(requireActivity())
|
||||||
adapter = GetNotifAdapter(parentActivity)
|
adapter = GetNotifAdapter(requireActivity())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +72,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
firebase.setOnCheckedListener { _, isChecked ->
|
firebase.setOnCheckedListener { _, isChecked ->
|
||||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isChecked)
|
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(isChecked)
|
||||||
FirebasePerformance.getInstance().isPerformanceCollectionEnabled = isChecked
|
FirebasePerformance.getInstance().isPerformanceCollectionEnabled = isChecked
|
||||||
FirebaseAnalytics.getInstance(parentActivity).setAnalyticsCollectionEnabled(isChecked)
|
FirebaseAnalytics.getInstance(requireActivity()).setAnalyticsCollectionEnabled(isChecked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +93,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
private fun FragmentSettingsBinding.bindClearFiles() {
|
private fun FragmentSettingsBinding.bindClearFiles() {
|
||||||
clearFiles.setOnClickListener {
|
clearFiles.setOnClickListener {
|
||||||
with(requireActivity()) {
|
with(requireActivity()) {
|
||||||
listOf(
|
listOf("vanced/nonroot", "vanced/root", "music/nonroot", "music/root", "microg").forEach { dir ->
|
||||||
"vanced/nonroot",
|
|
||||||
"vanced/root",
|
|
||||||
"music/nonroot",
|
|
||||||
"music/root",
|
|
||||||
"microg"
|
|
||||||
).forEach { dir ->
|
|
||||||
File(getExternalFilesDir(dir)?.path.toString()).deleteRecursively()
|
File(getExternalFilesDir(dir)?.path.toString()).deleteRecursively()
|
||||||
}
|
}
|
||||||
Toast.makeText(this, getString(R.string.cleared_files), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.cleared_files), Toast.LENGTH_SHORT).show()
|
||||||
|
@ -125,7 +116,7 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun FragmentSettingsBinding.bindManagerAccentColor() {
|
private fun FragmentSettingsBinding.bindManagerAccentColor() {
|
||||||
managerAccentColor.apply {
|
managerAccentColor.apply{
|
||||||
setSummary(prefs.getInt("manager_accent_color", defAccentColor).toHex())
|
setSummary(prefs.getInt("manager_accent_color", defAccentColor).toHex())
|
||||||
setOnClickListener { showDialog(ManagerAccentColorDialog()) }
|
setOnClickListener { showDialog(ManagerAccentColorDialog()) }
|
||||||
accentColor.observe(viewLifecycleOwner) {
|
accentColor.observe(viewLifecycleOwner) {
|
||||||
|
@ -137,15 +128,14 @@ class SettingsFragment : BindingFragment<FragmentSettingsBinding>() {
|
||||||
private fun FragmentSettingsBinding.bindManagerLanguage() {
|
private fun FragmentSettingsBinding.bindManagerLanguage() {
|
||||||
val langPref = prefs.getString("manager_lang", "System Default")
|
val langPref = prefs.getString("manager_lang", "System Default")
|
||||||
managerLanguage.apply {
|
managerLanguage.apply {
|
||||||
setSummary(getLanguageFormat(parentActivity, requireNotNull(langPref)))
|
setSummary(getLanguageFormat(requireActivity(), requireNotNull(langPref)))
|
||||||
setOnClickListener { showDialog(ManagerLanguageDialog()) }
|
setOnClickListener { showDialog(ManagerLanguageDialog()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
val devSettings =
|
val devSettings = getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
||||||
getDefaultSharedPreferences(requireActivity()).getBoolean("devSettings", false)
|
|
||||||
if (devSettings) {
|
if (devSettings) {
|
||||||
inflater.inflate(R.menu.dev_settings_menu, menu)
|
inflater.inflate(R.menu.dev_settings_menu, menu)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package com.vanced.manager.ui.fragments
|
|
||||||
|
|
||||||
//import android.os.Bundle
|
|
||||||
//import android.view.LayoutInflater
|
|
||||||
//import android.view.View
|
|
||||||
//import android.view.ViewGroup
|
|
||||||
//import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
//import androidx.compose.ui.platform.ComposeView
|
|
||||||
//import androidx.fragment.app.Fragment
|
|
||||||
//import com.vanced.manager.R
|
|
||||||
//import com.vanced.manager.ui.compose.Preference
|
|
||||||
//import com.vanced.manager.ui.compose.PreferenceCategory
|
|
||||||
//import com.vanced.manager.ui.compose.SwitchPreference
|
|
||||||
//
|
|
||||||
//class SettingsFragmentCompose : Fragment() {
|
|
||||||
//
|
|
||||||
// override fun onCreateView(
|
|
||||||
// inflater: LayoutInflater,
|
|
||||||
// container: ViewGroup?,
|
|
||||||
// savedInstanceState: Bundle?
|
|
||||||
// ): View {
|
|
||||||
// return ComposeView(requireActivity()).apply {
|
|
||||||
// setContent {
|
|
||||||
// LazyColumn {
|
|
||||||
// // use `item` for separate elements like headers
|
|
||||||
// // and `items` for lists of identical elements
|
|
||||||
// item {
|
|
||||||
// PreferenceCategory(
|
|
||||||
// categoryTitle = getString(R.string.category_behaviour)
|
|
||||||
// ) {
|
|
||||||
// SwitchPreference(
|
|
||||||
// preferenceTitle = getString(R.string.use_custom_tabs),
|
|
||||||
// preferenceDescription = getString(R.string.link_custom_tabs),
|
|
||||||
// preferenceKey = "use_custom_tabs"
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// item {
|
|
||||||
// PreferenceCategory(
|
|
||||||
// categoryTitle = getString(R.string.category_appearance)
|
|
||||||
// ) {
|
|
||||||
// Preference(
|
|
||||||
// preferenceTitle = "test",
|
|
||||||
// preferenceDescription = "test",
|
|
||||||
// ) {}
|
|
||||||
// Preference(
|
|
||||||
// preferenceTitle = "test"
|
|
||||||
// ) {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
|
@ -3,9 +3,9 @@ package com.vanced.manager.ui.fragments
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.vanced.manager.core.ui.base.BindingFragment
|
import com.vanced.manager.core.ui.base.BindingFragment
|
||||||
import com.vanced.manager.databinding.FragmentWelcomeBinding
|
import com.vanced.manager.databinding.FragmentWelcomeBinding
|
||||||
import com.vanced.manager.ui.WelcomeActivity
|
|
||||||
|
|
||||||
class WelcomeFragment : BindingFragment<FragmentWelcomeBinding>() {
|
class WelcomeFragment : BindingFragment<FragmentWelcomeBinding>() {
|
||||||
|
|
||||||
|
@ -20,8 +20,10 @@ class WelcomeFragment : BindingFragment<FragmentWelcomeBinding>() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindData() {
|
private fun bindData() {
|
||||||
binding.welcomeGetStarted.setOnClickListener {
|
binding.welcomeGetStarted.setOnClickListener { navigateToWelcome() }
|
||||||
(requireActivity() as WelcomeActivity).navigateTo(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun navigateToWelcome() {
|
||||||
|
findNavController().navigate(WelcomeFragmentDirections.welcomeToSelectApps())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.AndroidViewModel
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.openUrl
|
import com.vanced.manager.utils.openUrl
|
||||||
|
|
||||||
class AboutViewModel(application: Application) : AndroidViewModel(application) {
|
class AboutViewModel(application: Application): AndroidViewModel(application) {
|
||||||
|
|
||||||
fun openUrl(url: String) {
|
fun openUrl(url: String) {
|
||||||
openUrl(url, R.color.GitHub, getApplication())
|
openUrl(url, R.color.GitHub, getApplication())
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
package com.vanced.manager.ui.viewmodels
|
package com.vanced.manager.ui.viewmodels
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
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.R
|
||||||
import com.vanced.manager.adapter.LinkAdapter.Companion.DISCORD
|
import com.vanced.manager.adapter.LinkAdapter.Companion.DISCORD
|
||||||
import com.vanced.manager.adapter.LinkAdapter.Companion.REDDIT
|
import com.vanced.manager.adapter.LinkAdapter.Companion.REDDIT
|
||||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TELEGRAM
|
import com.vanced.manager.adapter.LinkAdapter.Companion.TELEGRAM
|
||||||
import com.vanced.manager.adapter.LinkAdapter.Companion.TWITTER
|
import com.vanced.manager.adapter.LinkAdapter.Companion.TWITTER
|
||||||
import com.vanced.manager.adapter.SponsorAdapter.Companion.BRAVE
|
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.DataModel
|
||||||
import com.vanced.manager.model.RootDataModel
|
import com.vanced.manager.model.RootDataModel
|
||||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||||
|
@ -25,7 +27,6 @@ import com.vanced.manager.ui.dialogs.InstallationFilesDetectedDialog
|
||||||
import com.vanced.manager.ui.dialogs.MusicPreferencesDialog
|
import com.vanced.manager.ui.dialogs.MusicPreferencesDialog
|
||||||
import com.vanced.manager.ui.dialogs.VancedPreferencesDialog
|
import com.vanced.manager.ui.dialogs.VancedPreferencesDialog
|
||||||
import com.vanced.manager.utils.*
|
import com.vanced.manager.utils.*
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
import com.vanced.manager.utils.AppUtils.managerPkg
|
import com.vanced.manager.utils.AppUtils.managerPkg
|
||||||
import com.vanced.manager.utils.AppUtils.microgPkg
|
import com.vanced.manager.utils.AppUtils.microgPkg
|
||||||
import com.vanced.manager.utils.AppUtils.musicPkg
|
import com.vanced.manager.utils.AppUtils.musicPkg
|
||||||
|
@ -39,11 +40,10 @@ import com.vanced.manager.utils.PackageHelper.uninstallRootApk
|
||||||
import com.vanced.manager.utils.PackageHelper.vancedInstallFilesExist
|
import com.vanced.manager.utils.PackageHelper.vancedInstallFilesExist
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
open class HomeViewModel(private val activity: FragmentActivity): ViewModel() {
|
||||||
|
|
||||||
private val prefs = getDefaultSharedPreferences(context)
|
private val prefs = getDefaultSharedPreferences(activity)
|
||||||
private val variant get() = prefs.getString("vanced_variant", "nonroot")
|
private val variant get() = prefs.getString("vanced_variant", "nonroot")
|
||||||
private val context: Context get() = getApplication()
|
|
||||||
|
|
||||||
val vancedModel = MutableLiveData<DataModel>()
|
val vancedModel = MutableLiveData<DataModel>()
|
||||||
val vancedRootModel = MutableLiveData<RootDataModel>()
|
val vancedRootModel = MutableLiveData<RootDataModel>()
|
||||||
|
@ -54,13 +54,14 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
fun fetchData() {
|
fun fetchData() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
loadJson(context)
|
loadJson(activity)
|
||||||
|
Crowdin.forceUpdate(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val microgToast = Toast.makeText(context, R.string.no_microg, Toast.LENGTH_LONG)
|
private val microgToast = Toast.makeText(activity, R.string.no_microg, Toast.LENGTH_LONG)
|
||||||
|
|
||||||
fun openUrl(context: Context, url: String) {
|
fun openUrl(url: String) {
|
||||||
val color: Int =
|
val color: Int =
|
||||||
when (url) {
|
when (url) {
|
||||||
DISCORD -> R.color.Discord
|
DISCORD -> R.color.Discord
|
||||||
|
@ -71,82 +72,74 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
else -> R.color.Vanced
|
else -> R.color.Vanced
|
||||||
}
|
}
|
||||||
|
|
||||||
openUrl(url, color, context)
|
openUrl(url, color, activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun launchApp(app: String, isRoot: Boolean) {
|
fun launchApp(app: String, isRoot: Boolean) {
|
||||||
val componentName = when (app) {
|
val componentName = when (app) {
|
||||||
context.getString(R.string.vanced) -> if (isRoot) ComponentName(
|
activity.getString(R.string.vanced) -> if (isRoot) ComponentName(vancedRootPkg, "$vancedRootPkg.HomeActivity") else ComponentName(vancedPkg, "$vancedRootPkg.HomeActivity")
|
||||||
vancedRootPkg,
|
activity.getString(R.string.music) -> if (isRoot) ComponentName(musicRootPkg, "$musicRootPkg.activities.MusicActivity") else ComponentName(musicPkg, "$musicRootPkg.activities.MusicActivity")
|
||||||
"$vancedRootPkg.HomeActivity"
|
activity.getString(R.string.microg) -> ComponentName(microgPkg, "org.microg.gms.ui.SettingsActivity")
|
||||||
) 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")
|
else -> throw IllegalArgumentException("Can't open this app")
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
context.startActivity(Intent().setComponent(componentName))
|
activity.startActivity(Intent().setComponent(componentName))
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
log("VMHMV", e.toString())
|
Log.d("VMHMV", e.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openInstallDialog(fragmentManager: FragmentManager, buttonTag: ButtonTag?, app: String) {
|
fun openInstallDialog(view: View, app: String) {
|
||||||
if (variant == "nonroot" && app != context.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
if (variant == "nonroot" && app != activity.getString(R.string.microg) && !microgModel.value?.isAppInstalled?.value!!) {
|
||||||
microgToast.show()
|
microgToast.show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonTag == ButtonTag.UPDATE) {
|
if ((view as MaterialButton).text == activity.getString(R.string.update)) {
|
||||||
when (app) {
|
when (app) {
|
||||||
context.getString(R.string.vanced) -> VancedPreferencesDialog().show(fragmentManager)
|
activity.getString(R.string.vanced) -> VancedPreferencesDialog().show(activity)
|
||||||
context.getString(R.string.music) -> MusicPreferencesDialog().show(fragmentManager)
|
activity.getString(R.string.music) -> MusicPreferencesDialog().show(activity)
|
||||||
else -> AppDownloadDialog.newInstance(app).show(fragmentManager)
|
else -> AppDownloadDialog.newInstance(app).show(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
when (app) {
|
when (app) {
|
||||||
context.getString(R.string.vanced) -> {
|
activity.getString(R.string.vanced) -> {
|
||||||
when (variant) {
|
when (variant) {
|
||||||
"nonroot" -> {
|
"nonroot" -> {
|
||||||
if (vancedInstallFilesExist(context)) {
|
if (vancedInstallFilesExist(activity)) {
|
||||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||||
} else {
|
} else {
|
||||||
VancedPreferencesDialog().show(fragmentManager)
|
VancedPreferencesDialog().show(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"root" -> {
|
"root" -> {
|
||||||
VancedPreferencesDialog().show(fragmentManager)
|
VancedPreferencesDialog().show(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.getString(R.string.music) -> {
|
activity.getString(R.string.music) -> {
|
||||||
when (variant) {
|
when (variant) {
|
||||||
"nonroot" -> {
|
"nonroot" -> {
|
||||||
if (musicApkExists(context)) {
|
if (musicApkExists(activity)) {
|
||||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||||
} else {
|
} else {
|
||||||
MusicPreferencesDialog().show(fragmentManager)
|
MusicPreferencesDialog().show(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"root" -> {
|
"root" -> {
|
||||||
MusicPreferencesDialog().show(fragmentManager)
|
MusicPreferencesDialog().show(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.getString(R.string.microg) -> {
|
activity.getString(R.string.microg) -> {
|
||||||
if (apkExist(context, "microg.apk")) {
|
if (apkExist(activity, "microg.apk")) {
|
||||||
InstallationFilesDetectedDialog.newInstance(app).show(fragmentManager)
|
InstallationFilesDetectedDialog.newInstance(app).show(activity)
|
||||||
} else {
|
} else {
|
||||||
AppDownloadDialog.newInstance(app).show(fragmentManager)
|
AppDownloadDialog.newInstance(app).show(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,67 +148,23 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
fun uninstallPackage(pkg: String) {
|
fun uninstallPackage(pkg: String) {
|
||||||
if (variant == "root" && uninstallRootApk(pkg)) {
|
if (variant == "root" && uninstallRootApk(pkg)) {
|
||||||
viewModelScope.launch { loadJson(context) }
|
viewModelScope.launch { loadJson(activity) }
|
||||||
} else {
|
} else {
|
||||||
uninstallApk(pkg, context)
|
uninstallApk(pkg, activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
with(context) {
|
with (activity) {
|
||||||
if (variant == "root") {
|
if (variant == "root") {
|
||||||
vancedRootModel.value = RootDataModel(
|
vancedRootModel.value = RootDataModel(vanced, this, this, vancedRootPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced), "vanced")
|
||||||
vanced,
|
musicRootModel.value = RootDataModel(music, this, this, musicRootPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music), "music")
|
||||||
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 {
|
} else {
|
||||||
vancedModel.value = DataModel(
|
vancedModel.value = DataModel(vanced, this, this, vancedPkg, this.getString(R.string.vanced), AppCompatResources.getDrawable(this, R.drawable.ic_vanced))
|
||||||
vanced,
|
musicModel.value = DataModel(music, this, this, musicPkg, this.getString(R.string.music), AppCompatResources.getDrawable(this, R.drawable.ic_music))
|
||||||
this,
|
microgModel.value = DataModel(microg, this, this, microgPkg, this.getString(R.string.microg), AppCompatResources.getDrawable(this, R.drawable.ic_microg))
|
||||||
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(
|
managerModel.value = DataModel(manager, this, this, managerPkg, this.getString(R.string.app_name), AppCompatResources.getDrawable(this, R.mipmap.ic_launcher))
|
||||||
manager,
|
|
||||||
this,
|
|
||||||
managerPkg,
|
|
||||||
this.getString(R.string.app_name),
|
|
||||||
"Just manager meh",
|
|
||||||
R.mipmap.ic_launcher
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,81 +2,65 @@ package com.vanced.manager.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.content.pm.PackageInstaller
|
||||||
import android.graphics.Typeface
|
|
||||||
import android.text.Spannable
|
|
||||||
import android.text.SpannableString
|
|
||||||
import android.text.style.ForegroundColorSpan
|
|
||||||
import android.text.style.StyleSpan
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import com.vanced.manager.BuildConfig
|
|
||||||
import com.vanced.manager.BuildConfig.APPLICATION_ID
|
import com.vanced.manager.BuildConfig.APPLICATION_ID
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
import com.vanced.manager.ui.dialogs.AppDownloadDialog
|
||||||
import com.vanced.manager.ui.fragments.HomeFragment
|
import com.vanced.manager.ui.fragments.HomeFragment
|
||||||
|
import com.vanced.manager.utils.DownloadHelper.downloadProgress
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.security.MessageDigest
|
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 vancedPkg = "com.vanced.android.youtube"
|
||||||
const val vancedRootPkg = "com.google.android.youtube"
|
const val vancedRootPkg = "com.google.android.youtube"
|
||||||
const val musicPkg = "com.vanced.android.apps.youtube.music"
|
const val musicPkg = "com.vanced.android.apps.youtube.music"
|
||||||
const val musicRootPkg = "com.google.android.apps.youtube.music"
|
const val musicRootPkg = "com.google.android.apps.youtube.music"
|
||||||
const val microgPkg = "com.mgoogle.android.gms"
|
const val microgPkg = "com.mgoogle.android.gms"
|
||||||
const val faqpkg = "com.vanced.faq"
|
|
||||||
const val managerPkg = APPLICATION_ID
|
const val managerPkg = APPLICATION_ID
|
||||||
const val playStorePkg = "com.android.vending"
|
const val playStorePkg = "com.android.vending"
|
||||||
|
|
||||||
val logs = mutableListOf<Spannable>()
|
|
||||||
|
|
||||||
var currentLocale: Locale? = null
|
|
||||||
|
|
||||||
fun log(tag: String, message: String) {
|
|
||||||
logs.add(
|
|
||||||
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(tag, message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendRefresh(context: Context): Job {
|
fun sendRefresh(context: Context): Job {
|
||||||
return launch {
|
return launch {
|
||||||
delay(700)
|
delay(700)
|
||||||
LocalBroadcastManager.getInstance(context)
|
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||||
.sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendCloseDialog(context: Context): Job {
|
fun sendCloseDialog(context: Context): Job {
|
||||||
return launch {
|
return launch {
|
||||||
delay(700)
|
delay(700)
|
||||||
installing.postValue(false)
|
downloadProgress.value?.installing?.postValue(false)
|
||||||
LocalBroadcastManager.getInstance(context)
|
LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
||||||
.sendBroadcast(Intent(AppDownloadDialog.CLOSE_DIALOG))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendFailure(error: List<String>, context: Context) {
|
fun sendFailure(status: Int, fullError: String?, context: Context): Job {
|
||||||
sendFailure(error.joinToString(" "), context)
|
//Delay error broadcast until activity (and fragment) get back to the screen
|
||||||
|
return launch {
|
||||||
|
delay(700)
|
||||||
|
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||||
|
intent.putExtra("errorMsg", getErrorMessage(status, context))
|
||||||
|
intent.putExtra("fullErrorMsg", fullError)
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sendFailure(error: MutableList<String>, context: Context): Job {
|
||||||
|
return launch {
|
||||||
|
delay(700)
|
||||||
|
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||||
|
intent.putExtra("errorMsg", getErrorMessage(error.joinToString(), context))
|
||||||
|
intent.putExtra("fullErrorMsg", error.joinToString(" "))
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendFailure(error: String, context: Context): Job {
|
fun sendFailure(error: String, context: Context): Job {
|
||||||
//Delay error broadcast until activity (and fragment) get back to the screen
|
|
||||||
return launch {
|
return launch {
|
||||||
delay(700)
|
delay(700)
|
||||||
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
val intent = Intent(HomeFragment.INSTALL_FAILED)
|
||||||
|
@ -102,7 +86,7 @@ object AppUtils : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
private fun printableHexString(data: ByteArray): String {
|
private fun printableHexString(data: ByteArray): String {
|
||||||
// Create Hex String
|
// Create Hex String
|
||||||
val hexString: StringBuilder = StringBuilder()
|
val hexString: StringBuilder = StringBuilder()
|
||||||
for (aMessageDigest: Byte in data) {
|
for (aMessageDigest:Byte in data) {
|
||||||
var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt())
|
var h: String = Integer.toHexString(0xFF and aMessageDigest.toInt())
|
||||||
while (h.length < 2)
|
while (h.length < 2)
|
||||||
h = "0$h"
|
h = "0$h"
|
||||||
|
@ -122,7 +106,6 @@ object AppUtils : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getErrorMessage(status: String, context: Context): String {
|
private fun getErrorMessage(status: String, context: Context): String {
|
||||||
log("VMInstall", status)
|
|
||||||
return when {
|
return when {
|
||||||
status.contains("INSTALL_FAILED_ABORTED") -> context.getString(R.string.installation_aborted)
|
status.contains("INSTALL_FAILED_ABORTED") -> context.getString(R.string.installation_aborted)
|
||||||
status.contains("INSTALL_FAILED_ALREADY_EXISTS") -> context.getString(R.string.installation_conflict)
|
status.contains("INSTALL_FAILED_ALREADY_EXISTS") -> context.getString(R.string.installation_conflict)
|
||||||
|
@ -137,10 +120,27 @@ object AppUtils : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
status.contains("ModApk_Missing") -> context.getString(R.string.modapk_missing)
|
status.contains("ModApk_Missing") -> context.getString(R.string.modapk_missing)
|
||||||
status.contains("Files_Missing_VA") -> context.getString(R.string.files_missing_va)
|
status.contains("Files_Missing_VA") -> context.getString(R.string.files_missing_va)
|
||||||
status.contains("Path_Missing") -> context.getString(R.string.path_missing)
|
status.contains("Path_Missing") -> context.getString(R.string.path_missing)
|
||||||
status.contains("INSTALL_FAILED_INTERNAL_ERROR: Permission Denied") -> context.getString(
|
else ->
|
||||||
R.string.installation_miui
|
if (isMiui())
|
||||||
)
|
context.getString(R.string.installation_miui)
|
||||||
else -> context.getString(R.string.installation_failed)
|
else
|
||||||
|
context.getString(R.string.installation_failed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getErrorMessage(status: Int, context: Context): String {
|
||||||
|
return when (status) {
|
||||||
|
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(R.string.installation_aborted)
|
||||||
|
PackageInstaller.STATUS_FAILURE_BLOCKED -> context.getString(R.string.installation_blocked)
|
||||||
|
PackageInstaller.STATUS_FAILURE_CONFLICT -> context.getString(R.string.installation_conflict)
|
||||||
|
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> context.getString(R.string.installation_incompatible)
|
||||||
|
PackageInstaller.STATUS_FAILURE_INVALID -> context.getString(R.string.installation_invalid)
|
||||||
|
PackageInstaller.STATUS_FAILURE_STORAGE -> context.getString(R.string.installation_storage)
|
||||||
|
else ->
|
||||||
|
if (isMiui())
|
||||||
|
context.getString(R.string.installation_miui)
|
||||||
|
else
|
||||||
|
context.getString(R.string.installation_failed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package com.vanced.manager.utils
|
package com.vanced.manager.utils
|
||||||
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
|
||||||
fun getArch(): String = when {
|
fun getArch(): String = when {
|
||||||
|
@ -8,9 +7,3 @@ fun getArch(): String = when {
|
||||||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||||
else -> "armeabi_v7a"
|
else -> "armeabi_v7a"
|
||||||
}
|
}
|
||||||
|
|
||||||
val intentFlags =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
|
|
||||||
PendingIntent.FLAG_MUTABLE
|
|
||||||
else
|
|
||||||
0
|
|
|
@ -1,14 +1,15 @@
|
||||||
package com.vanced.manager.utils
|
package com.vanced.manager.utils
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.library.network.providers.createService
|
import com.vanced.manager.library.network.providers.createService
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
import com.vanced.manager.model.ProgressModel
|
||||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -38,49 +39,40 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
fileFolder: String,
|
fileFolder: String,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
context: Context,
|
context: Context,
|
||||||
onDownloadComplete: () -> Unit = {},
|
onDownloadComplete: () -> Unit,
|
||||||
onError: (error: String) -> Unit = {}
|
onError: (error: String) -> Unit
|
||||||
) {
|
) {
|
||||||
downloadingFile.postValue(context.getString(R.string.downloading_file, fileName))
|
downloadProgress.value?.downloadingFile?.postValue(context.getString(R.string.downloading_file, fileName))
|
||||||
val downloadInterface = createService(DownloadHelper::class, baseUrl)
|
val downloadInterface = createService(DownloadHelper::class, baseUrl)
|
||||||
val download = downloadInterface.download(url)
|
val download = downloadInterface.download(url)
|
||||||
currentDownload = download
|
downloadProgress.value?.currentDownload = download
|
||||||
download.enqueue(object : Callback<ResponseBody> {
|
download.enqueue(object : Callback<ResponseBody> {
|
||||||
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
|
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
|
||||||
if (response.isSuccessful) {
|
if (response.isSuccessful) {
|
||||||
launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
if (response.body()?.let {
|
if (response.body()?.let { writeFile(it, context.getExternalFilesDir(fileFolder)?.path + "/" + fileName) } == true) {
|
||||||
writeFile(
|
|
||||||
it,
|
|
||||||
context.getExternalFilesDir(fileFolder)?.path + "/" + fileName
|
|
||||||
)
|
|
||||||
} == true) {
|
|
||||||
onDownloadComplete()
|
onDownloadComplete()
|
||||||
} else {
|
} else {
|
||||||
onError("Could not save file")
|
onError("Could not save file")
|
||||||
downloadProgress.postValue(0)
|
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||||
log(
|
Log.d("VMDownloader", "Failed to save file: $url")
|
||||||
"VMDownloader",
|
|
||||||
"Failed to save file: $url\n${response.errorBody()}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val errorBody = response.errorBody().toString()
|
onError(response.errorBody().toString())
|
||||||
onError(errorBody)
|
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||||
downloadProgress.postValue(0)
|
Log.d("VMDownloader", "Failed to download file: $url")
|
||||||
log("VMDownloader", "Failed to download file: $url\n$errorBody")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
|
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
|
||||||
if (call.isCanceled) {
|
if (call.isCanceled) {
|
||||||
log("VMDownloader", "Download canceled")
|
Log.d("VMDownloader", "Download canceled")
|
||||||
downloadProgress.postValue(0)
|
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||||
} else {
|
} else {
|
||||||
onError(t.stackTraceToString())
|
onError(t.stackTraceToString())
|
||||||
downloadProgress.postValue(0)
|
downloadProgress.value?.downloadProgress?.postValue(0)
|
||||||
log("VMDownloader", "Failed to download file: $url")
|
Log.d("VMDownloader", "Failed to download file: $url")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +94,7 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
while (inputStream.read(fileReader).also { read = it } != -1) {
|
while (inputStream.read(fileReader).also { read = it } != -1) {
|
||||||
outputStream.write(fileReader, 0, read)
|
outputStream.write(fileReader, 0, read)
|
||||||
downloadedBytes += read.toLong()
|
downloadedBytes += read.toLong()
|
||||||
downloadProgress.postValue((downloadedBytes * 100 / totalBytes).toInt())
|
downloadProgress.value?.downloadProgress?.postValue((downloadedBytes * 100 / totalBytes).toInt())
|
||||||
}
|
}
|
||||||
outputStream.flush()
|
outputStream.flush()
|
||||||
true
|
true
|
||||||
|
@ -117,15 +109,15 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val downloadProgress = MutableLiveData<ProgressModel>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
downloadProgress.value = ProgressModel()
|
||||||
|
}
|
||||||
|
|
||||||
fun downloadManager(context: Context) {
|
fun downloadManager(context: Context) {
|
||||||
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
|
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
|
||||||
download(
|
download(url,"https://github.com/YTVanced/VancedManager/", "manager", "manager.apk", context, onDownloadComplete = {
|
||||||
url,
|
|
||||||
"https://github.com/YTVanced/VancedManager/",
|
|
||||||
"manager",
|
|
||||||
"manager.apk",
|
|
||||||
context,
|
|
||||||
onDownloadComplete = {
|
|
||||||
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
val apk = File("${context.getExternalFilesDir("manager")?.path}/manager.apk")
|
||||||
val uri =
|
val uri =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||||
|
@ -137,16 +129,10 @@ object DownloadHelper : CoroutineScope by CoroutineScope(Dispatchers.IO) {
|
||||||
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
intent.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
try {
|
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
log("VMDownloader", e.stackTraceToString())
|
|
||||||
} finally {
|
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
}
|
}, onError = {
|
||||||
},
|
downloadProgress.value?.downloadingFile?.postValue(
|
||||||
onError = {
|
|
||||||
downloadingFile.postValue(
|
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.error_downloading,
|
R.string.error_downloading,
|
||||||
"manager.apk"
|
"manager.apk"
|
||||||
|
|
|
@ -3,11 +3,11 @@ package com.vanced.manager.utils
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.util.Log
|
||||||
import android.widget.RadioGroup
|
import android.widget.RadioGroup
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.FragmentManager
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||||
|
@ -15,61 +15,55 @@ import com.google.android.material.radiobutton.MaterialRadioButton
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import com.topjohnwu.superuser.io.SuFileOutputStream
|
import com.topjohnwu.superuser.io.SuFileOutputStream
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
val RadioGroup.checkedButtonTag: String?
|
fun RadioGroup.getCheckedButtonTag(): String? {
|
||||||
get() = findViewById<MaterialRadioButton>(
|
return findViewById<MaterialRadioButton>(checkedRadioButtonId)?.tag?.toString()
|
||||||
checkedRadioButtonId
|
|
||||||
)?.tag?.toString()
|
|
||||||
|
|
||||||
fun DialogFragment.show(fragmentManager: FragmentManager) {
|
|
||||||
try {
|
|
||||||
show(fragmentManager, "")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
log("VMUI", e.stackTraceToString())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun DialogFragment.show(activity: FragmentActivity) {
|
fun DialogFragment.show(activity: FragmentActivity) {
|
||||||
show(activity.supportFragmentManager)
|
try {
|
||||||
|
show(activity.supportFragmentManager, "")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d("VMUI", e.stackTraceToString())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
|
fun List<String>.convertToAppVersions(): List<String> = listOf("latest") + reversed()
|
||||||
|
|
||||||
fun String.formatVersion(context: Context): String =
|
fun String.convertToAppTheme(context: Context): String {
|
||||||
if (this == "latest") context.getString(R.string.install_latest) else this
|
return context.getString(R.string.light_plus_other, this.capitalize(Locale.ROOT))
|
||||||
|
|
||||||
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 =
|
fun String.getLatestAppVersion(versions: List<String>): String = if (this == "latest") versions.reversed()[0] else this
|
||||||
if (this == "latest") versions.reversed()[0] else this
|
|
||||||
|
|
||||||
val Context.lifecycleOwner: LifecycleOwner?
|
fun Context.lifecycleOwner(): LifecycleOwner? {
|
||||||
get() = when (this) {
|
var curContext = this
|
||||||
is LifecycleOwner -> this
|
var maxDepth = 20
|
||||||
!is LifecycleOwner -> (this as ContextWrapper).baseContext as LifecycleOwner
|
while (maxDepth-- > 0 && curContext !is LifecycleOwner) {
|
||||||
else -> null
|
curContext = (curContext as ContextWrapper).baseContext
|
||||||
}
|
}
|
||||||
|
return if (curContext is LifecycleOwner) {
|
||||||
|
curContext
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun Int.toHex(): String = java.lang.String.format("#%06X", 0xFFFFFF and this)
|
fun Int.toHex(): String = java.lang.String.format("#%06X", 0xFFFFFF and this)
|
||||||
|
|
||||||
//Material team decided to keep their LinearProgressIndicator final
|
//Material team decided to keep their LinearProgressIndicator final
|
||||||
//At least extension methods exist
|
//At least extension methods exist
|
||||||
fun LinearProgressIndicator.applyAccent() {
|
fun LinearProgressIndicator.applyAccent() {
|
||||||
with(accentColor.value!!) {
|
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||||
setIndicatorColor(this)
|
setIndicatorColor(this)
|
||||||
trackColor = ColorUtils.setAlphaComponent(this, 70)
|
trackColor = ColorUtils.setAlphaComponent(this, 70)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MaterialAlertDialogBuilder.showWithAccent() {
|
fun MaterialAlertDialogBuilder.applyAccent() {
|
||||||
with(accentColor.value!!) {
|
with(accentColor.value ?: context.defPrefs.managerAccent) {
|
||||||
show().apply {
|
show().apply {
|
||||||
getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(this@with)
|
getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(this@with)
|
||||||
getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(this@with)
|
getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(this@with)
|
||||||
|
@ -81,12 +75,6 @@ fun MaterialAlertDialogBuilder.showWithAccent() {
|
||||||
fun Context.writeServiceDScript(apkFPath: String, path: String, app: String) {
|
fun Context.writeServiceDScript(apkFPath: String, path: String, app: String) {
|
||||||
val shellFileZ = SuFile.open("/data/adb/service.d/$app.sh")
|
val shellFileZ = SuFile.open("/data/adb/service.d/$app.sh")
|
||||||
shellFileZ.createNewFile()
|
shellFileZ.createNewFile()
|
||||||
val script = """
|
val code = """#!/system/bin/sh${"\n"}while [ "`getprop sys.boot_completed | tr -d '\r' `" != "1" ]; do sleep ${defPrefs.serviceDSleepTimer}; done${"\n"}chcon u:object_r:apk_data_file:s0 $apkFPath${"\n"}mount -o bind $apkFPath $path"""
|
||||||
#!/system/bin/sh
|
SuFileOutputStream(shellFileZ).use { out -> out.write(code.toByteArray())}
|
||||||
while [ "$(getprop sys.boot_completed | tr -d '\r')" != "1" ]; do sleep 1; done
|
|
||||||
sleep ${defPrefs.serviceDSleepTimer}
|
|
||||||
chcon u:object_r:apk_data_file:s0 $apkFPath
|
|
||||||
mount -o bind $apkFPath $path
|
|
||||||
""".trimIndent()
|
|
||||||
SuFileOutputStream.open(shellFileZ).use { out -> out.write(script.toByteArray()) }
|
|
||||||
}
|
}
|
||||||
|
|
14
app/src/main/java/com/vanced/manager/utils/FileInfo.kt
Normal file
14
app/src/main/java/com/vanced/manager/utils/FileInfo.kt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package com.vanced.manager.utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
|
open class FileInfo(val name: String, val fileSize: Long, val file: File? = null) {
|
||||||
|
|
||||||
|
open fun getInputStream(): InputStream =
|
||||||
|
if (file!= null)
|
||||||
|
FileInputStream(file)
|
||||||
|
else
|
||||||
|
throw NotImplementedError("need some way to create InputStream")
|
||||||
|
}
|
|
@ -4,11 +4,7 @@ import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
|
||||||
val Context.installPrefs: SharedPreferences
|
val Context.installPrefs: SharedPreferences get() = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||||
get() = getSharedPreferences(
|
|
||||||
"installPrefs",
|
|
||||||
Context.MODE_PRIVATE
|
|
||||||
)
|
|
||||||
|
|
||||||
var SharedPreferences.lang
|
var SharedPreferences.lang
|
||||||
get() = getString("lang", getDefaultVancedLanguages())
|
get() = getString("lang", getDefaultVancedLanguages())
|
||||||
|
|
|
@ -15,7 +15,6 @@ import com.beust.klaxon.JsonArray
|
||||||
import com.beust.klaxon.JsonObject
|
import com.beust.klaxon.JsonObject
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import com.vanced.manager.utils.AppUtils.generateChecksum
|
import com.vanced.manager.utils.AppUtils.generateChecksum
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -32,8 +31,8 @@ val music = MutableLiveData<JsonObject?>()
|
||||||
val microg = MutableLiveData<JsonObject?>()
|
val microg = MutableLiveData<JsonObject?>()
|
||||||
val manager = MutableLiveData<JsonObject?>()
|
val manager = MutableLiveData<JsonObject?>()
|
||||||
|
|
||||||
val vancedVersions = MutableLiveData<JsonArray<String>?>()
|
val vancedVersions = MutableLiveData<JsonArray<String>>()
|
||||||
val musicVersions = MutableLiveData<JsonArray<String>?>()
|
val musicVersions = MutableLiveData<JsonArray<String>>()
|
||||||
|
|
||||||
val isFetching = MutableLiveData<Boolean>()
|
val isFetching = MutableLiveData<Boolean>()
|
||||||
|
|
||||||
|
@ -43,28 +42,19 @@ var baseInstallUrl = ""
|
||||||
|
|
||||||
fun openUrl(url: String, color: Int, context: Context) {
|
fun openUrl(url: String, color: Int, context: Context) {
|
||||||
try {
|
try {
|
||||||
val customTabPrefs =
|
val customTabPrefs = getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
||||||
getDefaultSharedPreferences(context).getBoolean("use_custom_tabs", true)
|
|
||||||
if (customTabPrefs) {
|
if (customTabPrefs) {
|
||||||
val builder = CustomTabsIntent.Builder()
|
val builder = CustomTabsIntent.Builder()
|
||||||
val params = CustomTabColorSchemeParams.Builder()
|
val params = CustomTabColorSchemeParams.Builder().setToolbarColor(ContextCompat.getColor(context, color))
|
||||||
.setToolbarColor(ContextCompat.getColor(context, color))
|
|
||||||
builder.setDefaultColorSchemeParams(params.build())
|
builder.setDefaultColorSchemeParams(params.build())
|
||||||
val customTabsIntent = builder.build()
|
val customTabsIntent = builder.build()
|
||||||
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
customTabsIntent.launchUrl(context, url.toUri())
|
customTabsIntent.launchUrl(context, url.toUri())
|
||||||
} else
|
} else
|
||||||
context.startActivity(
|
context.startActivity(Intent(Intent.ACTION_VIEW, url.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
||||||
Intent(
|
|
||||||
Intent.ACTION_VIEW,
|
|
||||||
url.toUri()
|
|
||||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
)
|
|
||||||
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (e: ActivityNotFoundException) {
|
||||||
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
||||||
} catch (e: SecurityException) {
|
|
||||||
Toast.makeText(context, R.string.error, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,17 +78,17 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
|
||||||
connect()
|
connect()
|
||||||
}
|
}
|
||||||
if (connection.responseCode != 200) {
|
if (connection.responseCode != 200) {
|
||||||
log(TAG, latestbaseUrl + ": " + connection.responseCode.toString())
|
Log.d(TAG, latestbaseUrl + ": " + connection.responseCode.toString())
|
||||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||||
} catch (e: SocketTimeoutException) {
|
} catch (e: SocketTimeoutException) {
|
||||||
log(TAG, "connection timed out")
|
Log.d(TAG, "connection timed out")
|
||||||
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
baseInstallUrl = "https://mirror.codebucket.de/vanced/api/v1"
|
||||||
}
|
}
|
||||||
|
|
||||||
log(TAG, "Fetching using URL: $baseInstallUrl")
|
Log.d(TAG, "Fetching using URL: $baseInstallUrl")
|
||||||
|
|
||||||
val calendar = Calendar.getInstance()
|
val calendar = Calendar.getInstance()
|
||||||
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
val hour = calendar.get(Calendar.HOUR_OF_DAY)
|
||||||
|
@ -110,7 +100,7 @@ suspend fun loadJson(context: Context) = withContext(Dispatchers.IO) {
|
||||||
val versions = getJson("$baseInstallUrl/versions.json?$fetchTime")
|
val versions = getJson("$baseInstallUrl/versions.json?$fetchTime")
|
||||||
isMicrogBroken = latest?.boolean("is_microg_broken") ?: false
|
isMicrogBroken = latest?.boolean("is_microg_broken") ?: false
|
||||||
vanced.postValue(latest?.obj("vanced"))
|
vanced.postValue(latest?.obj("vanced"))
|
||||||
vancedVersions.postValue(versions?.array("vanced"))
|
vancedVersions.postValue(versions?.array("vanced") )
|
||||||
music.postValue(latest?.obj("music"))
|
music.postValue(latest?.obj("music"))
|
||||||
musicVersions.postValue(versions?.array("music"))
|
musicVersions.postValue(versions?.array("music"))
|
||||||
microg.postValue(latest?.obj("microg"))
|
microg.postValue(latest?.obj("microg"))
|
||||||
|
@ -144,4 +134,4 @@ fun checkSHA256(sha256: String, updateFile: File): Boolean {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val baseUrl = "https://api.vancedapp.com/api/v1"
|
const val baseUrl = "https://vancedapp.com/api/v1"
|
|
@ -4,7 +4,8 @@ import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import com.vanced.manager.utils.AppUtils.currentLocale
|
import androidx.preference.PreferenceManager
|
||||||
|
import com.crowdin.platform.Crowdin
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||||
|
@ -14,22 +15,20 @@ class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
|
||||||
fun wrap(context: Context): ContextWrapper {
|
fun wrap(context: Context): ContextWrapper {
|
||||||
val config: Configuration = context.resources.configuration
|
val config: Configuration = context.resources.configuration
|
||||||
context.createConfigurationContext(setLocale(config, context))
|
context.createConfigurationContext(setLocale(config, context))
|
||||||
|
Crowdin.wrapContext(context)
|
||||||
return LanguageContextWrapper(context)
|
return LanguageContextWrapper(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun setLocale(config: Configuration, context: Context): Configuration {
|
private fun setLocale(config: Configuration, context: Context): Configuration {
|
||||||
val pref = context.defPrefs.managerLang
|
val pref = PreferenceManager.getDefaultSharedPreferences(context).getString("manager_lang", "System Default")
|
||||||
val sysLocale = Resources.getSystem().configuration.locale
|
val sysLocale = Resources.getSystem().configuration.locale
|
||||||
val locale = when {
|
val locale =
|
||||||
|
when {
|
||||||
pref == "System Default" -> Locale(sysLocale.language, sysLocale.country)
|
pref == "System Default" -> Locale(sysLocale.language, sysLocale.country)
|
||||||
pref?.length!! > 2 -> Locale(
|
pref?.length!! > 2 -> Locale(pref.substring(0, pref.length - 3), pref.substring(pref.length - 2))
|
||||||
pref.substring(0, pref.length - 3),
|
|
||||||
pref.substring(pref.length - 2)
|
|
||||||
)
|
|
||||||
else -> Locale(pref)
|
else -> Locale(pref)
|
||||||
}
|
}
|
||||||
currentLocale = locale
|
|
||||||
Locale.setDefault(locale)
|
Locale.setDefault(locale)
|
||||||
config.setLocale(locale)
|
config.setLocale(locale)
|
||||||
return config
|
return config
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
package com.vanced.manager.utils
|
package com.vanced.manager.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.LocaleList
|
import android.os.LocaleList
|
||||||
|
import android.provider.Settings
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.crowdin.platform.Crowdin
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
@ -29,10 +34,7 @@ fun getLanguageFormat(context: Context, language: String): String {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
fun getDefaultVancedLanguages(): String {
|
fun getDefaultVancedLanguages(): String {
|
||||||
val serverLangs = vanced.value?.array("langs") ?: mutableListOf("")
|
val serverLangs = vanced.value?.array("langs") ?: mutableListOf("")
|
||||||
val sysLocales =
|
val sysLocales = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) Resources.getSystem().configuration.locales.toLangTags() else arrayOf(Resources.getSystem().configuration.locale.language)
|
||||||
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>()
|
val finalLangs = mutableListOf<String>()
|
||||||
sysLocales.forEach { sysLocale ->
|
sysLocales.forEach { sysLocale ->
|
||||||
when {
|
when {
|
||||||
|
@ -52,3 +54,25 @@ fun LocaleList.toLangTags(): Array<String> {
|
||||||
}
|
}
|
||||||
return langTags
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,22 @@
|
||||||
package com.vanced.manager.utils
|
package com.vanced.manager.utils
|
||||||
|
|
||||||
import android.content.Context
|
import java.io.BufferedReader
|
||||||
import android.provider.Settings
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
private const val MIUI_OPTIMIZATION = "miui_optimization"
|
private const val MIUI_PROP_NAME = "ro.miui.ui.version.name"
|
||||||
|
|
||||||
val Context.isMiuiOptimizationsEnabled: Boolean
|
fun isMiui(): Boolean = !getSystemProps(MIUI_PROP_NAME).isNullOrEmpty()
|
||||||
get() = Settings.Secure.getString(
|
|
||||||
contentResolver,
|
private fun getSystemProps(propname: String): String? {
|
||||||
MIUI_OPTIMIZATION
|
var input: BufferedReader? = null
|
||||||
) == "1"
|
return try {
|
||||||
|
val process = Runtime.getRuntime().exec("getprop $propname")
|
||||||
|
input = BufferedReader(InputStreamReader(process.inputStream), 1024)
|
||||||
|
input.readLine()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
null
|
||||||
|
} finally {
|
||||||
|
input?.close()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,11 +7,12 @@ import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.io.SuFile
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import com.vanced.manager.BuildConfig
|
||||||
import com.vanced.manager.core.installer.AppInstallerService
|
import com.vanced.manager.core.installer.AppInstallerService
|
||||||
import com.vanced.manager.core.installer.AppUninstallerService
|
import com.vanced.manager.core.installer.AppUninstallerService
|
||||||
import com.vanced.manager.utils.AppUtils.log
|
|
||||||
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
import com.vanced.manager.utils.AppUtils.musicRootPkg
|
||||||
import com.vanced.manager.utils.AppUtils.playStorePkg
|
import com.vanced.manager.utils.AppUtils.playStorePkg
|
||||||
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
import com.vanced.manager.utils.AppUtils.sendCloseDialog
|
||||||
|
@ -21,18 +22,27 @@ import com.vanced.manager.utils.AppUtils.vancedRootPkg
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.*
|
||||||
import java.io.FileInputStream
|
import java.text.SimpleDateFormat
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
object PackageHelper {
|
object PackageHelper {
|
||||||
|
|
||||||
const val apkInstallPath = "/data/adb"
|
const val apkInstallPath = "/data/adb"
|
||||||
private const val INSTALLER_TAG = "VMInstall"
|
private const val INSTALLER_TAG = "VMInstall"
|
||||||
private val vancedThemes =
|
private val vancedThemes = vanced.value?.array<String>("themes")?.value ?: listOf("black", "dark", "pink", "blue")
|
||||||
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 fun getAppNameRoot(pkg: String): String {
|
private fun getAppNameRoot(pkg: String): String {
|
||||||
return when (pkg) {
|
return when (pkg) {
|
||||||
|
@ -51,6 +61,7 @@ object PackageHelper {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getPkgNameRoot(app: String): String {
|
fun getPkgNameRoot(app: String): String {
|
||||||
return when (app) {
|
return when (app) {
|
||||||
"vanced" -> vancedRootPkg
|
"vanced" -> vancedRootPkg
|
||||||
|
@ -58,7 +69,6 @@ object PackageHelper {
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
||||||
return try {
|
return try {
|
||||||
packageManager.getPackageInfo(packageName, 0)
|
packageManager.getPackageInfo(packageName, 0)
|
||||||
|
@ -76,7 +86,7 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@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)
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||||
pm.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
pm.getPackageInfo(pkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
||||||
else
|
else
|
||||||
|
@ -117,15 +127,9 @@ object PackageHelper {
|
||||||
if (files?.isNotEmpty() == true) {
|
if (files?.isNotEmpty() == true) {
|
||||||
for (file in files) {
|
for (file in files) {
|
||||||
when {
|
when {
|
||||||
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add(
|
vancedThemes.any { file.name == "$it.apk" } && !splitFiles.contains("base") -> splitFiles.add("base")
|
||||||
"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")
|
||||||
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) {
|
if (splitFiles.size == 3) {
|
||||||
|
@ -149,7 +153,7 @@ object PackageHelper {
|
||||||
fun uninstallApk(pkg: String, context: Context) {
|
fun uninstallApk(pkg: String, context: Context) {
|
||||||
val callbackIntent = Intent(context, AppUninstallerService::class.java)
|
val callbackIntent = Intent(context, AppUninstallerService::class.java)
|
||||||
callbackIntent.putExtra("pkg", pkg)
|
callbackIntent.putExtra("pkg", pkg)
|
||||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||||
try {
|
try {
|
||||||
context.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
context.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -158,94 +162,73 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun install(path: String, context: Context) {
|
fun install(path: String, context: Context) {
|
||||||
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
|
||||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, intentFlags)
|
|
||||||
val packageInstaller = context.packageManager.packageInstaller
|
|
||||||
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 {
|
try {
|
||||||
sessionId = packageInstaller.createSession(params)
|
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||||
session = packageInstaller.openSession(sessionId)
|
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||||
|
val packageInstaller = context.packageManager.packageInstaller
|
||||||
|
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
|
val sessionId = packageInstaller.createSession(params)
|
||||||
|
val session = packageInstaller.openSession(sessionId)
|
||||||
val inputStream: InputStream = FileInputStream(path)
|
val inputStream: InputStream = FileInputStream(path)
|
||||||
val outputStream = session.openWrite("install", 0, -1)
|
val outputStream = session.openWrite("install", 0, -1)
|
||||||
val buffer = ByteArray(65536)
|
val buffer = ByteArray(65536)
|
||||||
var length: Int
|
var c: Int
|
||||||
while (inputStream.read(buffer).also { length = it } > 0) {
|
while (inputStream.read(buffer).also { c = it } != -1) {
|
||||||
outputStream.write(buffer, 0, length)
|
outputStream.write(buffer, 0, c)
|
||||||
}
|
}
|
||||||
session.fsync(outputStream)
|
session.fsync(outputStream)
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
outputStream.close()
|
outputStream.close()
|
||||||
session.commit(pendingIntent.intentSender)
|
session.commit(pendingIntent.intentSender)
|
||||||
} catch (e: Exception) {
|
} catch (e: IOException) {
|
||||||
log(INSTALLER_TAG, e.stackTraceToString())
|
Log.d(INSTALLER_TAG, e.stackTraceToString())
|
||||||
sendFailure(e.stackTraceToString(), context)
|
|
||||||
sendCloseDialog(context)
|
|
||||||
} finally {
|
|
||||||
session?.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installRootMusic(files: List<File>, context: Context): Boolean {
|
}
|
||||||
|
|
||||||
|
private fun installRootMusic(files: ArrayList<FileInfo>, context: Context): Boolean {
|
||||||
files.forEach { apk ->
|
files.forEach { apk ->
|
||||||
if (apk.name != "root.apk") {
|
if (apk.name != "root.apk") {
|
||||||
val newPath = "/data/local/tmp/${apk.name}"
|
val newPath = "/data/local/tmp/${apk.file?.name}"
|
||||||
|
|
||||||
//Copy apk to tmp folder in order to avoid permission denials
|
//moving apk to tmp folder in order to avoid permission denials
|
||||||
Shell.su("cp ${apk.path} $newPath").exec()
|
Shell.su("mv ${apk.file?.path} $newPath").exec()
|
||||||
val command = Shell.su("pm install -r $newPath").exec()
|
val command = Shell.su("pm install $newPath").exec()
|
||||||
Shell.su("rm $newPath").exec()
|
Shell.su("rm $newPath").exec()
|
||||||
if (!command.isSuccess) {
|
if (command.isSuccess) {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
sendFailure(command.out, context)
|
sendFailure(command.out, context)
|
||||||
sendCloseDialog(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 {
|
|
||||||
if (!isMagiskInstalled()) {
|
|
||||||
sendFailure("NO_MAGISK", context)
|
|
||||||
sendCloseDialog(context)
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 apkFilesPath = context.getExternalFilesDir("$app/root")?.path
|
||||||
val files = File(apkFilesPath.toString()).listFiles()?.toList()
|
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }
|
||||||
if (files != null) {
|
if (fileInfoList != null) {
|
||||||
val modApk: File? = files.lastOrNull { modApkBool(it.name) }
|
val modApk: FileInfo? = fileInfoList.lastOrNull { modApkBool(it.name) }
|
||||||
if (modApk != null) {
|
if (modApk != null) {
|
||||||
if (appVerCode != null) {
|
if (overwriteBase(modApk, fileInfoList, appVerCode, pkg, app, context)) {
|
||||||
if (overwriteBase(modApk, files, appVerCode, pkg, app, context)) {
|
|
||||||
setInstallerPackage(context, pkg, playStorePkg)
|
setInstallerPackage(context, pkg, playStorePkg)
|
||||||
log(INSTALLER_TAG, "Finished installation")
|
Log.d(INSTALLER_TAG, "Finished installation")
|
||||||
sendRefresh(context)
|
sendRefresh(context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendFailure("appVerCode is null", context)
|
sendFailure(listOf("ModApk_Missing").toMutableList(), context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendFailure("ModApk_Missing", context)
|
sendFailure(listOf("Files_Missing_VA").toMutableList(), context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
sendFailure("Files_Missing_VA", context)
|
|
||||||
sendCloseDialog(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -254,7 +237,7 @@ object PackageHelper {
|
||||||
installRootApp(
|
installRootApp(
|
||||||
context,
|
context,
|
||||||
"music",
|
"music",
|
||||||
music.value?.int("versionCode"),
|
music.value?.int("versionCode")!!,
|
||||||
musicRootPkg
|
musicRootPkg
|
||||||
) {
|
) {
|
||||||
it == "root.apk"
|
it == "root.apk"
|
||||||
|
@ -265,95 +248,202 @@ object PackageHelper {
|
||||||
installRootApp(
|
installRootApp(
|
||||||
context,
|
context,
|
||||||
"vanced",
|
"vanced",
|
||||||
vanced.value?.int("versionCode"),
|
vanced.value?.int("versionCode")!!,
|
||||||
vancedRootPkg
|
vancedRootPkg
|
||||||
) { fileName ->
|
) { fileName ->
|
||||||
vancedThemes.any { fileName == "$it.apk" }
|
vancedThemes.any { fileName == "$it.apk" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installSplitApkFiles(
|
fun installVanced(context: Context): Int {
|
||||||
context: Context,
|
val apkFolderPath = context.getExternalFilesDir("vanced/nonroot")?.path.toString() + "/"
|
||||||
appName: String
|
val nameSizeMap = HashMap<String, Long>()
|
||||||
) {
|
var totalSize: Long = 0
|
||||||
val packageInstaller = context.packageManager.packageInstaller
|
var sessionId = 0
|
||||||
val folder = File(context.getExternalFilesDir("$appName/nonroot")?.path.toString())
|
val folder = File(apkFolderPath)
|
||||||
var session: PackageInstaller.Session? = null
|
val listOfFiles = folder.listFiles()
|
||||||
val sessionId: Int
|
|
||||||
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, intentFlags)
|
|
||||||
try {
|
try {
|
||||||
sessionId = packageInstaller.createSession(sessionParams)
|
for (listOfFile in listOfFiles!!) {
|
||||||
session = packageInstaller.openSession(sessionId)
|
if (listOfFile.isFile) {
|
||||||
folder.listFiles()?.forEach { apk ->
|
Log.d(INSTALLER_TAG, "installApk: " + listOfFile.name)
|
||||||
val inputStream = FileInputStream(apk)
|
nameSizeMap[listOfFile.name] = listOfFile.length()
|
||||||
val outputStream = session.openWrite(apk.name, 0, apk.length())
|
totalSize += listOfFile.length()
|
||||||
val buffer = ByteArray(65536)
|
|
||||||
var length: Int
|
|
||||||
while (inputStream.read(buffer).also { length = it } > 0) {
|
|
||||||
outputStream.write(buffer, 0, length)
|
|
||||||
}
|
}
|
||||||
session.fsync(outputStream)
|
|
||||||
inputStream.close()
|
|
||||||
outputStream.close()
|
|
||||||
}
|
}
|
||||||
session.commit(pendingIntent.intentSender)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
log(INSTALLER_TAG, e.stackTraceToString())
|
e.printStackTrace()
|
||||||
sendFailure(e.stackTraceToString(), context)
|
return -1
|
||||||
sendCloseDialog(context)
|
}
|
||||||
|
val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
|
installParams.setSize(totalSize)
|
||||||
|
try {
|
||||||
|
sessionId = context.packageManager.packageInstaller.createSession(installParams)
|
||||||
|
Log.d(INSTALLER_TAG,"Success: created install session [$sessionId]")
|
||||||
|
for ((key, value) in nameSizeMap) {
|
||||||
|
doWriteSession(sessionId, apkFolderPath + key, value, key, context)
|
||||||
|
}
|
||||||
|
doCommitSession(sessionId, context)
|
||||||
|
Log.d(INSTALLER_TAG,"Success")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return sessionId
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doWriteSession(sessionId: Int, inPath: String?, sizeBytes: Long, splitName: String, context: Context): Int {
|
||||||
|
var inPathToUse = inPath
|
||||||
|
var sizeBytesToUse = sizeBytes
|
||||||
|
if ("-" == inPathToUse) {
|
||||||
|
inPathToUse = null
|
||||||
|
} else if (inPathToUse != null) {
|
||||||
|
val file = File(inPathToUse)
|
||||||
|
if (file.isFile)
|
||||||
|
sizeBytesToUse = file.length()
|
||||||
|
}
|
||||||
|
var session: PackageInstaller.Session? = null
|
||||||
|
var inputStream: InputStream? = null
|
||||||
|
var out: OutputStream? = null
|
||||||
|
try {
|
||||||
|
session = context.packageManager.packageInstaller.openSession(sessionId)
|
||||||
|
if (inPathToUse != null) {
|
||||||
|
inputStream = FileInputStream(inPathToUse)
|
||||||
|
}
|
||||||
|
out = session.openWrite(splitName, 0, sizeBytesToUse)
|
||||||
|
var total = 0
|
||||||
|
val buffer = ByteArray(65536)
|
||||||
|
var c: Int
|
||||||
|
while (true) {
|
||||||
|
c = inputStream!!.read(buffer)
|
||||||
|
if (c == -1)
|
||||||
|
break
|
||||||
|
total += c
|
||||||
|
out.write(buffer, 0, c)
|
||||||
|
}
|
||||||
|
session.fsync(out)
|
||||||
|
Log.d(INSTALLER_TAG, "Success: streamed $total bytes")
|
||||||
|
return PackageInstaller.STATUS_SUCCESS
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(INSTALLER_TAG, "Error: failed to write; " + e.message)
|
||||||
|
return PackageInstaller.STATUS_FAILURE
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
out?.close()
|
||||||
|
inputStream?.close()
|
||||||
|
session?.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun doCommitSession(sessionId: Int, context: Context) {
|
||||||
|
var session: PackageInstaller.Session? = null
|
||||||
|
try {
|
||||||
|
session = context.packageManager.packageInstaller.openSession(sessionId)
|
||||||
|
val callbackIntent = Intent(context, AppInstallerService::class.java)
|
||||||
|
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||||
|
session.commit(pendingIntent.intentSender)
|
||||||
|
session.close()
|
||||||
|
Log.d(INSTALLER_TAG, "install request sent")
|
||||||
|
Log.d(INSTALLER_TAG, "doCommitSession: " + context.packageManager.packageInstaller.mySessions)
|
||||||
|
Log.d(INSTALLER_TAG, "doCommitSession: after session commit ")
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
} finally {
|
} finally {
|
||||||
session?.close()
|
session?.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installSplitApkFilesRoot(apkFiles: List<File>?, context: Context): Boolean {
|
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>, context: Context) : Boolean {
|
||||||
|
var sessionId: Int?
|
||||||
val filenames = arrayOf("black.apk", "dark.apk", "blue.apk", "pink.apk", "hash.json")
|
val filenames = arrayOf("black.apk", "dark.apk", "blue.apk", "pink.apk", "hash.json")
|
||||||
log(INSTALLER_TAG, "installing split apk files: ${apkFiles?.map { it.name }}")
|
Log.d(INSTALLER_TAG, "installing split apk files: $apkFiles")
|
||||||
val sessionId =
|
run {
|
||||||
Shell.su("pm install-create -r").exec().out.joinToString(" ").filter { it.isDigit() }
|
val sessionIdResult = Shell.su("pm install-create -r -t").exec().out
|
||||||
.toIntOrNull()
|
val sessionIdPattern = Pattern.compile("(\\d+)")
|
||||||
|
val sessionIdMatcher = sessionIdPattern.matcher(sessionIdResult[0])
|
||||||
|
sessionIdMatcher.find()
|
||||||
|
sessionId = Integer.parseInt(sessionIdMatcher.group(1)!!)
|
||||||
|
}
|
||||||
|
apkFiles.forEach { apkFile ->
|
||||||
|
if (!filenames.any { apkFile.name == it }) {
|
||||||
|
Log.d(INSTALLER_TAG, "installing APK: ${apkFile.name} ${apkFile.fileSize}")
|
||||||
|
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
|
||||||
|
val process: Process = Runtime.getRuntime().exec(command)
|
||||||
|
val inputPipe = apkFile.getInputStream()
|
||||||
|
try {
|
||||||
|
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||||
|
process.destroyForcibly()
|
||||||
|
else
|
||||||
|
process.destroy()
|
||||||
|
|
||||||
if (sessionId == null) {
|
sendFailure(e.stackTrace.map { it.toString() }.toMutableList(), context)
|
||||||
sendFailure("Session ID is null", context)
|
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
process.waitFor()
|
||||||
apkFiles?.filter { !filenames.contains(it.name) }?.forEach { apkFile ->
|
|
||||||
val apkName = apkFile.name
|
|
||||||
log(INSTALLER_TAG, "installing APK: $apkName")
|
|
||||||
val newPath = "/data/local/tmp/$apkName"
|
|
||||||
//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) {
|
|
||||||
sendFailure(command.out, context)
|
|
||||||
sendCloseDialog(context)
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log(INSTALLER_TAG, "committing...")
|
Log.d(INSTALLER_TAG, "committing...")
|
||||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||||
if (!installResult.isSuccess) {
|
if (installResult.isSuccess) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
sendFailure(installResult.out, context)
|
sendFailure(installResult.out, context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
|
private fun SimpleDateFormat.tryParse(str: String) = try {
|
||||||
|
parse(str) != null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getFileInfoList(splitApkPath: String): ArrayList<FileInfo> {
|
||||||
|
val parentFile = File(splitApkPath)
|
||||||
|
val result = ArrayList<FileInfo>()
|
||||||
|
|
||||||
|
if (parentFile.exists() && parentFile.canRead()) {
|
||||||
|
val listFiles = parentFile.listFiles() ?: return ArrayList()
|
||||||
|
listFiles.mapTo(result) {
|
||||||
|
FileInfo(it.name, it.length(), it)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
val longLines = Shell.su("ls -l $splitApkPath").exec().out
|
||||||
|
val pattern = Pattern.compile(" +")
|
||||||
|
val formatter = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||||
|
longLinesLoop@ for (line in longLines) {
|
||||||
|
val matcher = pattern.matcher(line)
|
||||||
|
for (i in 0 until 4)
|
||||||
|
if (!matcher.find())
|
||||||
|
continue@longLinesLoop
|
||||||
|
val startSizeStr = matcher.end()
|
||||||
|
matcher.find()
|
||||||
|
val endSizeStr = matcher.start()
|
||||||
|
val fileSizeStr = line.substring(startSizeStr, endSizeStr)
|
||||||
|
while (true) {
|
||||||
|
val testTimeStr: String =
|
||||||
|
line.substring(matcher.end(), line.indexOf(' ', matcher.end()))
|
||||||
|
if (formatter.tryParse(testTimeStr)) {
|
||||||
|
//found time, so apk is next
|
||||||
|
val fileName = line.substring(line.indexOf(' ', matcher.end()) + 1)
|
||||||
|
if (fileName.endsWith("apk"))
|
||||||
|
result.add(FileInfo(fileName, fileSizeStr.toLong(), File(splitApkPath, fileName)))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
matcher.find()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
//overwrite stock Vanced/Music
|
//overwrite stock Vanced/Music
|
||||||
private fun overwriteBase(
|
private fun overwriteBase(
|
||||||
apkFile: File,
|
apkFile: FileInfo,
|
||||||
baseApkFiles: List<File>,
|
baseApkFiles: ArrayList<FileInfo>,
|
||||||
versionCode: Int,
|
versionCode: Int,
|
||||||
pkg: String,
|
pkg: String,
|
||||||
app: String,
|
app: String,
|
||||||
|
@ -361,7 +451,8 @@ object PackageHelper {
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (checkVersion(versionCode, baseApkFiles, pkg, context)) {
|
if (checkVersion(versionCode, baseApkFiles, pkg, context)) {
|
||||||
val path = getPackageDir(context, pkg)
|
val path = getPackageDir(context, pkg)
|
||||||
val apath = apkFile.absolutePath
|
apkFile.file?.let {
|
||||||
|
val apath = it.absolutePath
|
||||||
|
|
||||||
setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}")
|
setupFolder("$apkInstallPath/${app.capitalize(Locale.ROOT)}")
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
|
@ -375,35 +466,27 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupScript(
|
private fun setupScript(apkFPath: String, path: String, app: String, pkg: String, context: Context): Boolean
|
||||||
apkFPath: String,
|
{
|
||||||
path: String,
|
|
||||||
app: String,
|
|
||||||
pkg: String,
|
|
||||||
context: Context
|
|
||||||
): Boolean {
|
|
||||||
try {
|
try {
|
||||||
log(INSTALLER_TAG, "Setting up script")
|
Log.d(INSTALLER_TAG, "Setting up script")
|
||||||
context.writeServiceDScript(apkFPath, path, app)
|
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""")
|
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()
|
||||||
.exec()
|
|
||||||
return Shell.su("chmod 744 /data/adb/service.d/$app.sh").exec().isSuccess
|
return Shell.su("chmod 744 /data/adb/service.d/$app.sh").exec().isSuccess
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
sendFailure(e.stackTraceToString(), context)
|
e.printStackTrace()
|
||||||
sendCloseDialog(context)
|
|
||||||
log(INSTALLER_TAG, e.stackTraceToString())
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun linkApp(apkFPath: String, pkg: String, path: String): Boolean {
|
private fun linkApp(apkFPath: String, pkg: String, path: String): Boolean {
|
||||||
log(INSTALLER_TAG, "Linking app")
|
Log.d(INSTALLER_TAG, "Linking app")
|
||||||
Shell.su("am force-stop $pkg").exec()
|
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 """)
|
Shell.su("""for i in ${'$'}(ls /data/app/ | grep $pkg | tr " "); do umount -l "/data/app/${"$"}i/base.apk"; done """).exec()
|
||||||
.exec()
|
|
||||||
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
||||||
Thread.sleep(500)
|
Thread.sleep(500)
|
||||||
Shell.su("am force-stop $pkg").exec()
|
Shell.su("am force-stop $pkg").exec()
|
||||||
|
@ -415,17 +498,12 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
//check version and perform action based on result
|
//check version and perform action based on result
|
||||||
private fun checkVersion(
|
private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList<FileInfo>, pkg: String, context: Context): Boolean {
|
||||||
versionCode: Int,
|
Log.d(INSTALLER_TAG, "Checking stock version")
|
||||||
baseApkFiles: List<File>,
|
|
||||||
pkg: String,
|
|
||||||
context: Context
|
|
||||||
): Boolean {
|
|
||||||
log(INSTALLER_TAG, "Checking stock version")
|
|
||||||
val path = getPackageDir(context, pkg)
|
val path = getPackageDir(context, pkg)
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
if (path.contains("/data/app/")) {
|
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 fixHigherVer(baseApkFiles, pkg, context)
|
||||||
-1 -> return installStock(baseApkFiles, pkg, context)
|
-1 -> return installStock(baseApkFiles, pkg, context)
|
||||||
}
|
}
|
||||||
|
@ -439,8 +517,8 @@ object PackageHelper {
|
||||||
private fun getPkgInfo(pkg: String, context: Context): PackageInfo? {
|
private fun getPkgInfo(pkg: String, context: Context): PackageInfo? {
|
||||||
return try {
|
return try {
|
||||||
context.packageManager.getPackageInfo(pkg, 0)
|
context.packageManager.getPackageInfo(pkg, 0)
|
||||||
} catch (e: Exception) {
|
} catch (e:Exception) {
|
||||||
log(INSTALLER_TAG, "Unable to get package info")
|
Log.d(INSTALLER_TAG, "Unable to get package info")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,13 +532,10 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
//uninstall current update and install base that works with patch
|
//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: ArrayList<FileInfo>, pkg: String, context: Context) : Boolean {
|
||||||
log(INSTALLER_TAG, "Downgrading stock")
|
Log.d(INSTALLER_TAG, "Downgrading stock")
|
||||||
if (uninstallRootApk(pkg)) {
|
if (uninstallRootApk(pkg)) {
|
||||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
return if (pkg == vancedRootPkg) installSplitApkFiles(apkFiles, context) else installRootMusic(apkFiles, context)
|
||||||
apkFiles,
|
|
||||||
context
|
|
||||||
) else installRootMusic(apkFiles, context)
|
|
||||||
}
|
}
|
||||||
sendFailure(listOf("Failed_Uninstall").toMutableList(), context)
|
sendFailure(listOf("Failed_Uninstall").toMutableList(), context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
|
@ -468,70 +543,78 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
//install stock youtube matching vanced version
|
//install stock youtube matching vanced version
|
||||||
private fun installStock(baseApkFiles: List<File>, pkg: String, context: Context): Boolean {
|
private fun installStock(baseApkFiles: ArrayList<FileInfo>, pkg: String, context: Context): Boolean {
|
||||||
log(INSTALLER_TAG, "Installing stock")
|
Log.d(INSTALLER_TAG, "Installing stock")
|
||||||
return if (pkg == vancedRootPkg) installSplitApkFilesRoot(
|
return if (pkg == vancedRootPkg) installSplitApkFiles(baseApkFiles, context) else installRootMusic(baseApkFiles, context)
|
||||||
baseApkFiles,
|
|
||||||
context
|
|
||||||
) else installRootMusic(baseApkFiles, context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isMagiskInstalled() = Shell.su("magisk -c").exec().isSuccess
|
|
||||||
|
|
||||||
//set chcon to apk_data_file
|
//set chcon to apk_data_file
|
||||||
private fun chConV(apkFPath: String, context: Context): Boolean {
|
private fun chConV(apkFPath: String, context: Context): Boolean {
|
||||||
log(INSTALLER_TAG, "Running chcon")
|
Log.d(INSTALLER_TAG, "Running chcon")
|
||||||
val response = Shell.su("chcon u:object_r:apk_data_file:s0 $apkFPath").exec()
|
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()
|
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
|
||||||
if (!response.isSuccess) {
|
return if (response.isSuccess) {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
sendFailure(response.out, context)
|
sendFailure(response.out, context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
return false
|
false
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//move patch to data/app
|
//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")
|
Log.d(INSTALLER_TAG, "Moving app")
|
||||||
|
val apkinF = SuFile.open(apkFile)
|
||||||
|
val apkoutF = SuFile.open(path)
|
||||||
|
|
||||||
|
if(apkinF.exists()) {
|
||||||
|
try {
|
||||||
Shell.su("am force-stop $pkg").exec()
|
Shell.su("am force-stop $pkg").exec()
|
||||||
|
|
||||||
val mv = Shell.su("cp $apkFile $path").exec()
|
//Shell.su("rm -r SuFile.open(path).parent")
|
||||||
if (!mv.isSuccess) {
|
|
||||||
sendFailure(mv.out.apply { add(0, "MV_Fail") }, context)
|
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)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendFailure(listOf("IFile_Missing").toMutableList(), context)
|
||||||
sendCloseDialog(context)
|
sendCloseDialog(context)
|
||||||
return false
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
val chown = Shell.su("chown system:system $path").exec()
|
@Throws(IOException::class)
|
||||||
if (!chown.isSuccess) {
|
fun copy(src: File, dst: File) {
|
||||||
sendFailure(chown.out.apply { add(0, "Chown_Fail") }, context)
|
val cmd = Shell.su("mv ${src.absolutePath} ${dst.absolutePath}").exec().isSuccess
|
||||||
sendCloseDialog(context)
|
Log.d("ZLog", cmd.toString())
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
private fun getVersionNumber(pkg: String, context: Context): Int? {
|
private fun getVersionNumber(pkg: String, context: Context): Int? {
|
||||||
try {
|
try {
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(
|
context.packageManager.getPackageInfo(vancedRootPkg, 0).longVersionCode.and(0xFFFFFFFF).toInt()
|
||||||
0xFFFFFFFF
|
|
||||||
).toInt()
|
|
||||||
else
|
else
|
||||||
context.packageManager.getPackageInfo(vancedRootPkg, 0).versionCode
|
context.packageManager.getPackageInfo(vancedRootPkg, 0).versionCode
|
||||||
} catch (e: Exception) {
|
}
|
||||||
|
catch (e : Exception) {
|
||||||
val execRes = Shell.su("dumpsys package $pkg | grep versionCode").exec()
|
val execRes = Shell.su("dumpsys package $pkg | grep versionCode").exec()
|
||||||
if (execRes.isSuccess) {
|
if(execRes.isSuccess) {
|
||||||
val result = execRes.out
|
val result = execRes.out
|
||||||
var version = 0
|
var version = 0
|
||||||
result
|
result
|
||||||
|
@ -547,16 +630,22 @@ object PackageHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
//get path of the installed youtube
|
//get path of the installed youtube
|
||||||
fun getPackageDir(context: Context, pkg: String): String? {
|
fun getPackageDir(context: Context, pkg: String): String?
|
||||||
|
{
|
||||||
val p = getPkgInfo(pkg, context)
|
val p = getPkgInfo(pkg, context)
|
||||||
return if (p != null) {
|
return if(p != null)
|
||||||
|
{
|
||||||
p.applicationInfo.sourceDir
|
p.applicationInfo.sourceDir
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
val execRes = Shell.su("dumpsys package $pkg | grep codePath").exec()
|
val execRes = Shell.su("dumpsys package $pkg | grep codePath").exec()
|
||||||
if (execRes.isSuccess) {
|
if(execRes.isSuccess)
|
||||||
|
{
|
||||||
val result = execRes.out
|
val result = execRes.out
|
||||||
for (line in result) {
|
for (line in result)
|
||||||
if (line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
{
|
||||||
|
if(line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
null
|
null
|
||||||
|
@ -566,17 +655,16 @@ object PackageHelper {
|
||||||
private fun setInstallerPackage(context: Context, target: String, installer: String) {
|
private fun setInstallerPackage(context: Context, target: String, installer: String) {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) return
|
||||||
try {
|
try {
|
||||||
log(INSTALLER_TAG, "Setting installer package to $installer for $target")
|
Log.d(INSTALLER_TAG, "Setting installer package to $installer for $target")
|
||||||
val installerUid = context.packageManager.getPackageUid(installer, 0)
|
val installerUid = context.packageManager.getPackageUid(installer, 0)
|
||||||
val res =
|
val res = Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
||||||
Shell.su("""su $installerUid -c 'pm set-installer $target $installer'""").exec()
|
|
||||||
if (res.out.any { line -> line.contains("Success") }) {
|
if (res.out.any { line -> line.contains("Success") }) {
|
||||||
log(INSTALLER_TAG, "Installer package successfully set")
|
Log.d(INSTALLER_TAG, "Installer package successfully set")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log(INSTALLER_TAG, "Failed setting installer package")
|
Log.d(INSTALLER_TAG, "Failed setting installer package")
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
log(INSTALLER_TAG, "Installer package $installer not found. Skipping setting installer")
|
Log.d(INSTALLER_TAG, "Installer package $installer not found. Skipping setting installer")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
package com.vanced.manager.utils
|
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import okhttp3.ResponseBody
|
|
||||||
import retrofit2.Call
|
|
||||||
|
|
||||||
val downloadProgress = MutableLiveData(0)
|
|
||||||
val downloadingFile = MutableLiveData("")
|
|
||||||
val installing = MutableLiveData(false)
|
|
||||||
|
|
||||||
var currentDownload: Call<ResponseBody>? = null
|
|
||||||
|
|
||||||
fun reset() {
|
|
||||||
downloadProgress.value = 0
|
|
||||||
downloadingFile.value = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
fun postReset() {
|
|
||||||
downloadProgress.postValue(0)
|
|
||||||
downloadingFile.postValue("")
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,30 +7,20 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import com.vanced.manager.R
|
import com.vanced.manager.R
|
||||||
|
|
||||||
const val defAccentColor: Int = -13732865
|
const val defAccentColor: Int = -13732865
|
||||||
const val LIGHT = "Light"
|
|
||||||
const val DARK = "Dark"
|
|
||||||
const val SYSTEM_DEFAULT = "System Default"
|
|
||||||
|
|
||||||
val mutableAccentColor = MutableLiveData(defAccentColor)
|
val mutableAccentColor = MutableLiveData<Int>()
|
||||||
val accentColor: LiveData<Int> = mutableAccentColor
|
val accentColor: LiveData<Int> = mutableAccentColor
|
||||||
|
|
||||||
var currentTheme = ""
|
|
||||||
|
|
||||||
fun Activity.setFinalTheme() {
|
fun Activity.setFinalTheme() {
|
||||||
when (defPrefs.managerTheme) {
|
when (defPrefs.managerTheme) {
|
||||||
LIGHT -> setTheme(R.style.LightTheme, LIGHT)
|
"Light" -> setTheme(R.style.LightTheme)
|
||||||
DARK -> setTheme(R.style.DarkTheme, DARK)
|
"Dark" -> setTheme(R.style.DarkTheme)
|
||||||
SYSTEM_DEFAULT -> {
|
"System Default" -> {
|
||||||
when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||||
Configuration.UI_MODE_NIGHT_YES -> setTheme(R.style.DarkTheme, DARK)
|
Configuration.UI_MODE_NIGHT_YES -> setTheme(R.style.DarkTheme)
|
||||||
Configuration.UI_MODE_NIGHT_NO -> setTheme(R.style.LightTheme, LIGHT)
|
Configuration.UI_MODE_NIGHT_NO -> setTheme(R.style.LightTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> setTheme(R.style.LightTheme, LIGHT)
|
else -> setTheme(R.style.LightTheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Activity.setTheme(resId: Int, themeValue: String) {
|
|
||||||
setTheme(resId)
|
|
||||||
currentTheme = themeValue
|
|
||||||
}
|
|
11
app/src/main/res/animator/fragment_enter_right.xml
Normal file
11
app/src/main/res/animator/fragment_enter_right.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<objectAnimator
|
||||||
|
android:duration="@android:integer/config_mediumAnimTime"
|
||||||
|
android:valueType="floatType"
|
||||||
|
android:valueFrom="1.0"
|
||||||
|
android:valueTo="0"
|
||||||
|
android:propertyName="xFraction" />
|
||||||
|
|
||||||
|
</set>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue