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