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