|
@ -0,0 +1,45 @@
|
|||
name: Signed APK Builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: Grant rights
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Build project with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Build Release APK with Gradle
|
||||
run: ./gradlew assembleRelease
|
||||
|
||||
- name: Sign APK
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
id: sign-app
|
||||
with:
|
||||
releaseDirectory: app/build/outputs/apk/release
|
||||
signingKeyBase64: ${{ secrets.TOKEN }}
|
||||
alias: ${{ secrets.ALIAS }}
|
||||
keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }}
|
||||
|
||||
- name: Upload to GitHub
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: 'Vanced-Manager'
|
||||
path: ${{ steps.sign-app.outputs.signedReleaseFile }}
|
|
@ -0,0 +1,36 @@
|
|||
name: Debug APK Builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: set up JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
|
||||
- name: Grant rights
|
||||
run: chmod +x ./gradlew
|
||||
|
||||
- name: Build project with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Build Release APK with Gradle
|
||||
run: ./gradlew assembleDebug
|
||||
|
||||
- name: Upload to GitHub
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: 'Vanced-Manager'
|
||||
path: app/build/outputs/apk/debug/app-debug.apk
|
|
@ -1,14 +1,7 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
.gradle/
|
||||
.idea/
|
||||
.github/
|
||||
build/
|
||||
out/
|
||||
app/src/main/java/com/vanced/manager/core/base/DummyJava.java
|
||||
local.properties
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
|
@ -1,5 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="PLATFORM" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
23
README.md
|
@ -1,2 +1,21 @@
|
|||
# VancedInstaller
|
||||
Vanced Installer
|
||||
# Vanced Manager
|
||||
Hi, when we released Vanced 15.05.54, people were upset because it used .apks format, which was way harder to install than a traditional .apk file. even tho we wrote clear instructions on how to install new Vanced, 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 5mb. that's how Vanced Manager was born.
|
||||
|
||||
After 3 months of making, we are finally ready to introduce Vanced Manager to you. Vanced manager can easily install and uninstall vanced and microg, has various settings for customisation and better experience. Manager comes with easy-to-use interface, support for background download and installation*
|
||||
##### *Due to changes in Android Oreo and up, also because of aggressive battery optimisations in some ROMs, feature may not be available for all devices.
|
||||
|
||||
## Vanced Developers
|
||||
- xfileFIN
|
||||
- KevinX8
|
||||
- Zanezam
|
||||
- Laura Almeida
|
||||
|
||||
## Vanced Manager Developer
|
||||
- Xinto (X1nto)
|
||||
|
||||
## Credits
|
||||
- topjohnwu for his wonderful [LibSU](https://github.com/topjohnwu/libsu)
|
||||
- Mindorks for their amazing [PRDownloader](https://github.com/MindorksOpenOource/PRDownloader)
|
||||
- aefyr for [SAI](https://github.com/aefyr/SAI), which was inspiration for our Manager
|
||||
- 100rabhkr for [GetJson](https://github.com/100rabhkr/getjson) library
|
|
@ -0,0 +1,42 @@
|
|||
![Signed APK Builder](https://github.com/X1nto/VancedInstaller/workflows/Signed%20APK%20Builder/badge.svg?branch=master)
|
||||
|
||||
# Prelude
|
||||
Hi, my name is Steve Cock, I'm the main developer for the upcoming Vanced Manager. When xfileFIN first published Vanced 15.05.54, people were upset because new Vanced used split apk files. The reason for that was pretty simple:
|
||||
1) YouTube itself does that
|
||||
2) Split apk files reduce the size of the downloaded file itself
|
||||
|
||||
No one really thought there would be problems with this format, because installation was pretty simple, at least that's what xfile thought...
|
||||
## Problems with .apks format
|
||||
Main problems with new format were either with device CPU architecture or MemeUI shit with MiUI optimisations. We wrote instructions for VancedHelper but no one used it for troubleshooting. Then some users complained about new format and refused to upgrade to newest version (We don't give a fuck about that) because "I dOn'T WaNT To HaVe OnE MoRE apP To insTalL VanCeD" so we decided to make an installer for Vanced
|
||||
# Vanced Manager
|
||||
Ladies and gentlemen, I'm very proud to introduce the new **Vancad Manger 1.0.0™** (typo is intentional)
|
||||
Vanced Manager is an universal utility for installing/updating Vanced and MicroG. It will push notifications once the update is ready (Now that's what I call pwetty epic).
|
||||
Vanced manager comes with a slick UI ~~that was stolen from the new Magisk Manager (I'm very sorry John but I looked at your code for about 100 times).~~ Actually, while UI may look very similar to new Magisk Manager's UI, It's still very different (that's a blatant lie, I know).
|
||||
|
||||
Main Menu screenshot taken from tablet
|
||||
![screenshot](https://i.imgur.com/r2jiq7J.png)
|
||||
Isn't this lovely and beautiful?
|
||||
|
||||
## Manager (clap) Reviews (clap)
|
||||
|
||||
- 1337Potato: shit
|
||||
- Response: Yes
|
||||
|
||||
- Noobbot: The app is not useful because I have YT Premium. Thank you bye
|
||||
- Response: I hope you get sucked by a di-
|
||||
|
||||
- Vortextriangle: The app is so useful that I uninstalled it after installing Vanced
|
||||
- Response: yo that's finna woke
|
||||
|
||||
## Credits
|
||||
### Vanced Manager developers
|
||||
- X1nto (UI, UX, Downloader, Installer, Signature Checker, PussiSlayer69, Collector of 400 BAT, Professional Liar)
|
||||
### The Vanced Team
|
||||
- xfileFIN
|
||||
![xfileFIN](https://i.imgur.com/hLdzTVq.png)
|
||||
- KevinX8
|
||||
![KevinX8](https://i.imgur.com/cS9C7P8.png)
|
||||
- Zanezam
|
||||
![Zanezam](https://i.imgur.com/QVcXA6q.png)
|
||||
- Laura Almeida
|
||||
![Laura Almeida](https://i.imgur.com/ovVD939.png)
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module external.linked.project.id="Vanced Manager" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="java-gradle" name="Java-Gradle">
|
||||
<configuration>
|
||||
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
|
||||
<option name="BUILDABLE" value="false" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,6 +1,7 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
|
@ -10,25 +11,38 @@ android {
|
|||
applicationId "com.vanced.manager"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 29
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
versionCode 8
|
||||
versionName "1.0.0"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
disable 'MissingTranslation', 'ExtraTranslation'
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
noCompress 'apk', '.apk'
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
variant.resValue "string", "versionName", variant.versionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
dataBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
// To inline the bytecode built with JVM target 1.8 into
|
||||
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
|
||||
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
|
@ -42,16 +56,18 @@ android {
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||
implementation 'androidx.core:core-ktx:1.0.2'
|
||||
implementation 'com.google.android.material:material:1.0.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.3.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
implementation 'androidx.navigation:navigation-fragment:2.0.0'
|
||||
implementation 'androidx.navigation:navigation-ui:2.0.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
|
||||
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
implementation 'com.github.100rabhkr:GetJSON:1.0'
|
||||
implementation 'com.github.topjohnwu.libsu:core:2.5.1'
|
||||
implementation 'com.mindorks.android:prdownloader:0.6.0'
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
package com.vanced.manager
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("com.vanced.manager", appContext.packageName)
|
||||
}
|
||||
}
|
|
@ -1,26 +1,70 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.vanced.manager">
|
||||
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".core.App"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
tools:ignore="UnusedAttribute"
|
||||
tools:replace="android:allowBackup">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name">
|
||||
android:name=".ui.core.SplashScreenActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.MainActivity"
|
||||
android:label="@string/app_name" />
|
||||
|
||||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
android:resource="@array/preloaded_fonts" />
|
||||
|
||||
<provider
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_provider" />
|
||||
|
||||
</provider>
|
||||
|
||||
<service android:name=".core.installer.SplitInstallerService" />
|
||||
<service android:name=".core.installer.RootSplitInstallerService" />
|
||||
<service android:name=".core.installer.SplitInstaller" />
|
||||
<service android:name=".core.installer.AppUninstallerService" />
|
||||
<service android:name=".core.installer.AppInstallerService" />
|
||||
<service android:name=".core.installer.AppInstaller" />
|
||||
<service android:name=".core.downloader.VancedDownloadService" />
|
||||
<service android:name=".core.downloader.MicrogDownloadService" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
After Width: | Height: | Size: 35 KiB |
|
@ -1,26 +0,0 @@
|
|||
package com.vanced.manager
|
||||
|
||||
import android.os.Bundle
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
val navView: BottomNavigationView = findViewById(R.id.navigation_home)
|
||||
|
||||
val navController = findNavController(R.id.mobile_navigation)
|
||||
// Passing each menu ID as a set of Ids because each
|
||||
// menu should be considered as top level destinations.
|
||||
val appBarConfiguration = AppBarConfiguration(setOf(
|
||||
R.id.navigation_home))
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.vanced.manager.ui.fragments.ManagerChangelogFragment
|
||||
import com.vanced.manager.ui.fragments.MicrogChangelogFragment
|
||||
import com.vanced.manager.ui.fragments.VancedChangelogFragment
|
||||
|
||||
class SectionPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
|
||||
private val fragmentItems = 3
|
||||
override fun getItemCount(): Int {
|
||||
return fragmentItems
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
var fragment: Fragment? = null
|
||||
when (position) {
|
||||
0 -> fragment = VancedChangelogFragment()
|
||||
1 -> fragment = MicrogChangelogFragment()
|
||||
2 -> fragment = ManagerChangelogFragment()
|
||||
}
|
||||
return fragment!!
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.vanced.manager.adapter
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.vanced.manager.ui.fragments.ManagerChangelogFragment
|
||||
import com.vanced.manager.ui.fragments.VancedChangelogFragment
|
||||
|
||||
class SectionPageRootAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
||||
|
||||
private val fragmentItems = 2
|
||||
override fun getItemCount(): Int {
|
||||
return fragmentItems
|
||||
}
|
||||
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
var fragment: Fragment? = null
|
||||
when (position) {
|
||||
0 -> fragment = VancedChangelogFragment()
|
||||
1 -> fragment = ManagerChangelogFragment()
|
||||
}
|
||||
return fragment!!
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import android.app.Application
|
||||
import com.downloader.PRDownloader
|
||||
import com.vanced.manager.utils.NotificationHelper.createNotifChannel
|
||||
|
||||
class App: Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
PRDownloader.initialize(applicationContext)
|
||||
createNotifChannel(this)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.showSecurityDialog
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.statementFalse
|
||||
import com.vanced.manager.ui.fragments.UpdateCheckFragment
|
||||
import com.vanced.manager.utils.InternetTools
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.secondMiuiDialog
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.showRootDialog
|
||||
import com.vanced.manager.utils.MiuiHelper.isMiui
|
||||
import com.vanced.manager.utils.MiuiHelper.isMiuiOptimisationsDisabled
|
||||
|
||||
// This activity will NOT be used in manifest
|
||||
// since MainActivity will extend it
|
||||
@SuppressLint("Registered")
|
||||
open class Main: AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
checkUpdates()
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val firstStart = prefs.getBoolean("firstStart", true)
|
||||
val isUpgrading = prefs.getBoolean("isUpgrading", false)
|
||||
val variant = prefs.getString("vanced_variant", "nonroot")
|
||||
val shouldShowRootDialog = prefs.getBoolean("show_root_dialog", true)
|
||||
|
||||
val falseStatement = prefs.getBoolean("statement", true)
|
||||
|
||||
when {
|
||||
firstStart -> showSecurityDialog(this)
|
||||
!falseStatement -> statementFalse(this)
|
||||
isUpgrading -> prefs.edit().putBoolean("isUpgrading", false).apply()
|
||||
variant == "root" && shouldShowRootDialog -> showRootDialog(this)
|
||||
!firstStart && !isMiuiOptimisationsDisabled() -> secondMiuiDialog(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isInstalling", false).apply()
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isVancedDownloading", false).apply()
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isMicrogDownloading", false).apply()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
private fun checkUpdates() {
|
||||
val checkPrefs = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("auto_check_update", false)
|
||||
if (checkPrefs) {
|
||||
if (GetJson().isConnected(this) && InternetTools.isUpdateAvailable()) {
|
||||
UpdateCheckFragment().show(supportFragmentManager, "Update")
|
||||
} else Toast.makeText(this, getString(R.string.update_notfound), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.vanced.manager.core.base
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
open class BaseFragment : Fragment() {
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
import com.downloader.OnStartOrResumeListener
|
||||
import com.downloader.PRDownloader
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.installer.AppInstaller
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.NotificationHelper
|
||||
import com.vanced.manager.utils.NotificationHelper.cancelNotif
|
||||
import com.vanced.manager.utils.NotificationHelper.createBasicNotif
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
||||
class MicrogDownloadService: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
try {
|
||||
downloadMicrog()
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is ExecutionException, is InterruptedException -> Toast.makeText(this, "Unable to download Vanced", Toast.LENGTH_SHORT).show()
|
||||
else -> throw e
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun downloadMicrog() {
|
||||
val prefs = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
|
||||
val apkUrl = GetJson().AsJSONObject("https://x1nto.github.io/VancedFiles/microg.json")
|
||||
val dwnldUrl = apkUrl.get("url").asString
|
||||
val channel = 420
|
||||
PRDownloader.download(dwnldUrl, filesDir.path, "microg.apk")
|
||||
.build()
|
||||
.setOnStartOrResumeListener { OnStartOrResumeListener { prefs?.edit()?.putBoolean("isMicrogDownloading", true)?.apply() } }
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
NotificationHelper.displayDownloadNotif(
|
||||
channel,
|
||||
mProgress.toInt(),
|
||||
getFileNameFromUrl(dwnldUrl),
|
||||
this
|
||||
)
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply()
|
||||
cancelNotif(channel, this@MicrogDownloadService)
|
||||
val intent = Intent(this@MicrogDownloadService, AppInstaller::class.java)
|
||||
intent.putExtra("path", "${filesDir.path}/microg.apk")
|
||||
intent.putExtra("pkg", "com.mgoogle.android.gms")
|
||||
val mIntent = Intent(HomeFragment.MICROG_DOWNLOADED)
|
||||
mIntent.action = HomeFragment.MICROG_DOWNLOADED
|
||||
LocalBroadcastManager.getInstance(this@MicrogDownloadService).sendBroadcast(mIntent)
|
||||
startService(intent)
|
||||
}
|
||||
override fun onError(error: Error) {
|
||||
prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply()
|
||||
createBasicNotif(getString(R.string.error_downloading, "Microg"), channel, this@MicrogDownloadService)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cancelNotif(420, this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
import com.downloader.OnStartOrResumeListener
|
||||
import com.downloader.PRDownloader
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.installer.RootSplitInstallerService
|
||||
import com.vanced.manager.core.installer.SplitInstaller
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.NotificationHelper.cancelNotif
|
||||
import com.vanced.manager.utils.NotificationHelper.createBasicNotif
|
||||
import com.vanced.manager.utils.NotificationHelper.displayDownloadNotif
|
||||
import java.util.concurrent.ExecutionException
|
||||
|
||||
class VancedDownloadService: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
try {
|
||||
downloadSplits()
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is ExecutionException, is InterruptedException -> Toast.makeText(this, "Unable to download Vanced", Toast.LENGTH_SHORT).show()
|
||||
else -> throw e
|
||||
}
|
||||
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun downloadSplits(
|
||||
type: String = "arch"
|
||||
) {
|
||||
val baseUrl = PreferenceManager.getDefaultSharedPreferences(this).getString("install_url", baseUrl)
|
||||
val vancedVer = GetJson().AsJSONObject("https://x1nto.github.io/VancedFiles/vanced.json").get("version").asString
|
||||
val prefs = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val variant = PreferenceManager.getDefaultSharedPreferences(this).getString("vanced_variant", "nonroot")
|
||||
val lang = prefs?.getString("lang", "en")
|
||||
val theme = prefs?.getString("theme", "dark")
|
||||
val arch =
|
||||
when {
|
||||
Build.SUPPORTED_ABIS.contains("x86") -> "x86"
|
||||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||
else -> "armeabi_v7a"
|
||||
}
|
||||
val url =
|
||||
when (type) {
|
||||
"arch" -> "$baseUrl/apks/v$vancedVer/$variant/Config/config.$arch.apk"
|
||||
"theme" -> "$baseUrl/apks/v$vancedVer/$variant/Theme/$theme.apk"
|
||||
"lang" -> "$baseUrl/apks/v$vancedVer/$variant/Language/split_config.$lang.apk"
|
||||
"enlang" -> "$baseUrl/apks/v$vancedVer/$variant/Language/split_config.en.apk"
|
||||
else -> throw NotImplementedError("This type of APK is NOT valid. What the hell did you even do?")
|
||||
}
|
||||
|
||||
val channel = 69
|
||||
PRDownloader
|
||||
.download(url, cacheDir.path, getFileNameFromUrl(url))
|
||||
.build()
|
||||
.setOnStartOrResumeListener { OnStartOrResumeListener { prefs?.edit()?.putBoolean("isVancedDownloading", true)?.apply() } }
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
displayDownloadNotif(channel, mProgress.toInt(), getFileNameFromUrl(url), this)
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
when (type) {
|
||||
"arch" -> downloadSplits("theme")
|
||||
"theme" -> downloadSplits("lang")
|
||||
"lang" -> {
|
||||
if (lang == "en") {
|
||||
prepareInstall(variant!!)
|
||||
cancelNotif(channel, this@VancedDownloadService)
|
||||
} else {
|
||||
downloadSplits("enlang")
|
||||
}
|
||||
}
|
||||
"enlang" -> {
|
||||
prepareInstall(variant!!)
|
||||
cancelNotif(channel, this@VancedDownloadService)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(error: Error) {
|
||||
createBasicNotif(getString(R.string.error_downloading, "Vanced"), channel, this@VancedDownloadService)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun prepareInstall(variant: String) {
|
||||
val intent = Intent(HomeFragment.VANCED_DOWNLOADED)
|
||||
intent.action = HomeFragment.VANCED_DOWNLOADED
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
|
||||
if (variant == "root")
|
||||
startService(Intent(this, RootSplitInstallerService::class.java))
|
||||
else
|
||||
startService(Intent(this, SplitInstaller::class.java))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
cancelNotif(69, this)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.vanced.manager.core.fragments
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.vanced.manager.core.base.BaseFragment
|
||||
|
||||
open class About : BaseFragment() {
|
||||
|
||||
private var count = 0
|
||||
private var startMillSec: Long = 0
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
view.setOnTouchListener { _, event: MotionEvent ->
|
||||
|
||||
val eventAction = event.action
|
||||
if (eventAction == MotionEvent.ACTION_UP) {
|
||||
val time = System.currentTimeMillis()
|
||||
if (startMillSec == 0L || time - startMillSec > 3000) {
|
||||
startMillSec = time
|
||||
count = 1
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
|
||||
if (count == 5) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val devSettings = prefs.getBoolean("devSettings", false)
|
||||
if (!devSettings) {
|
||||
Toast.makeText(requireContext(), "Dev options unlocked!", Toast.LENGTH_SHORT).show()
|
||||
prefs.edit().putBoolean("devSettings", true).apply()
|
||||
} else
|
||||
Toast.makeText(requireContext(), "Dev options already unlocked", Toast.LENGTH_SHORT).show()
|
||||
|
||||
}
|
||||
return@setOnTouchListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package com.vanced.manager.core.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.base.BaseFragment
|
||||
import com.vanced.manager.core.downloader.MicrogDownloadService
|
||||
import com.vanced.manager.core.downloader.VancedDownloadService
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.secondMiuiDialog
|
||||
import com.vanced.manager.utils.MiuiHelper
|
||||
import com.vanced.manager.utils.PackageHelper.uninstallApk
|
||||
|
||||
open class Home : BaseFragment(), View.OnClickListener {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val microginstallbtn = view.findViewById<MaterialButton>(R.id.microg_installbtn)
|
||||
val vancedinstallbtn = view.findViewById<MaterialButton>(R.id.vanced_installbtn)
|
||||
val microguninstallbtn = view.findViewById<ImageView>(R.id.microg_uninstallbtn)
|
||||
val vanceduninstallbtn = view.findViewById<ImageView>(R.id.vanced_uninstallbtn)
|
||||
val rootswitch = view.findViewById<MaterialButton>(R.id.root_switch)
|
||||
val nonrootswitch = view.findViewById<MaterialButton>(R.id.nonroot_switch)
|
||||
|
||||
vancedinstallbtn.setOnClickListener(this)
|
||||
microginstallbtn.setOnClickListener(this)
|
||||
microguninstallbtn.setOnClickListener(this)
|
||||
vanceduninstallbtn.setOnClickListener(this)
|
||||
rootswitch.setOnClickListener(this)
|
||||
nonrootswitch.setOnClickListener(this)
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val prefs = activity?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val isInstalling = prefs?.getBoolean("isInstalling", false)
|
||||
if (isInstalling!!) {
|
||||
activity?.startService(Intent(activity, VancedDownloadService::class.java))
|
||||
prefs.edit().putBoolean("isInstalling", false).apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val prefs = activity?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val defPrefs = getDefaultSharedPreferences(activity)
|
||||
val isVancedDownloading: Boolean? = prefs?.getBoolean("isVancedDownloading", false)
|
||||
val isMicrogDownloading: Boolean? = prefs?.getBoolean("isMicrogDownloading", false)
|
||||
val variant = getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot")
|
||||
val vancedPkgName =
|
||||
if (variant == "root") {
|
||||
"com.google.android.youtube"
|
||||
} else {
|
||||
"com.vanced.android.youtube"
|
||||
}
|
||||
|
||||
when (v?.id) {
|
||||
R.id.vanced_installbtn -> {
|
||||
if (!isVancedDownloading!!) {
|
||||
try {
|
||||
activity?.cacheDir?.deleteRecursively()
|
||||
} catch (e: Exception) {
|
||||
Log.d("VMCache", "Unable to delete cacheDir")
|
||||
}
|
||||
if (prefs.getBoolean("valuesModified", false)) {
|
||||
activity?.startService(
|
||||
Intent(
|
||||
activity,
|
||||
VancedDownloadService::class.java
|
||||
)
|
||||
)
|
||||
} else {
|
||||
view?.findNavController()?.navigate(R.id.toInstallThemeFragment)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
activity,
|
||||
"Please wait until installation finishes",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
R.id.microg_installbtn -> {
|
||||
if (!isMicrogDownloading!!) {
|
||||
try {
|
||||
activity?.startService(Intent(activity, MicrogDownloadService::class.java))
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(activity, "Unable to start installation", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(activity, "Please wait until installation finishes", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
R.id.microg_uninstallbtn -> activity?.let { uninstallApk("com.mgoogle.android.gms", it) }
|
||||
R.id.vanced_uninstallbtn -> activity?.let { uninstallApk(vancedPkgName, it) }
|
||||
R.id.nonroot_switch -> writeToVariantPref("nonroot", R.anim.slide_in_left, R.anim.slide_out_right)
|
||||
R.id.root_switch ->
|
||||
if (Shell.rootAccess())
|
||||
writeToVariantPref("root", R.anim.slide_in_right, R.anim.slide_out_left)
|
||||
else {
|
||||
writeToVariantPref("nonroot", R.anim.slide_in_left, R.anim.slide_out_right)
|
||||
Toast.makeText(activity, "Root access not granted", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeToVariantPref(variant: String, animIn: Int, animOut: Int) {
|
||||
val prefs = getDefaultSharedPreferences(activity)
|
||||
if (prefs.getString("vanced_variant", "nonroot") != variant) {
|
||||
prefs.edit().putString("vanced_variant", variant).apply()
|
||||
startActivity(Intent(activity, MainActivity::class.java))
|
||||
activity?.overridePendingTransition(animIn, animOut)
|
||||
activity?.finish()
|
||||
} else Log.d("VMvariant", "$variant is already selected")
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.vanced.manager.core.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import androidx.navigation.findNavController
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.base.BaseFragment
|
||||
|
||||
open class LanguageInstall : BaseFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val finishButton = view.findViewById<Button>(R.id.vanced_install_finish)
|
||||
val langGroup = view.findViewById<RadioGroup>(R.id.lang_radiogroup)
|
||||
val prefs = activity?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
|
||||
val langPref = prefs?.getString("lang", "en")
|
||||
val button = langGroup.findViewWithTag<RadioButton>(langPref)
|
||||
button.isChecked = true
|
||||
|
||||
finishButton.setOnClickListener {
|
||||
val selectedLangId = langGroup.checkedRadioButtonId
|
||||
val selectedButton = view.findViewById<RadioButton>(selectedLangId)
|
||||
prefs?.edit()?.putString("lang", selectedButton.tag.toString())?.apply()
|
||||
prefs?.edit()?.putBoolean("isInstalling", true)?.apply()
|
||||
prefs?.edit()?.putBoolean("valuesModified", true)?.apply()
|
||||
view.findNavController().navigate(R.id.action_installTo_homeFragment)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.vanced.manager.core.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import androidx.navigation.findNavController
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.base.BaseFragment
|
||||
|
||||
open class ThemeInstall : BaseFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val nextButton = view.findViewById<Button>(R.id.vanced_next_to_variant)
|
||||
val themeGroup = view.findViewById<RadioGroup>(R.id.theme_radiogroup)
|
||||
val prefs = activity?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
|
||||
val themePref = prefs?.getString("theme", "dark")
|
||||
val button = themeGroup.findViewWithTag<RadioButton>(themePref)
|
||||
button.isChecked = true
|
||||
|
||||
nextButton.setOnClickListener {
|
||||
val selectedThemeId = themeGroup.checkedRadioButtonId
|
||||
val selectedButton = view.findViewById<RadioButton>(selectedThemeId)
|
||||
prefs?.edit()?.putString("theme", selectedButton.tag.toString())?.apply()
|
||||
view.findNavController().navigate(R.id.toInstallLanguageFragment)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
class AppInstaller: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val callbackIntent = Intent(applicationContext, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0)
|
||||
val packageInstaller = packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
params.setAppPackageName(intent?.getStringExtra("pkg"))
|
||||
val sessionId = packageInstaller.createSession(params)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
val inputStream: InputStream = FileInputStream(intent?.getStringExtra("path") as String)
|
||||
val outputStream = session.openWrite("install", 0, -1)
|
||||
val buffer = ByteArray(65536)
|
||||
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)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.utils.MiuiHelper
|
||||
import com.vanced.manager.utils.NotificationHelper
|
||||
import com.vanced.manager.utils.NotificationHelper.createBasicNotif
|
||||
|
||||
class AppInstallerService: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
val notifId = 42
|
||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
Toast.makeText(this, "Installing...", Toast.LENGTH_SHORT).show()
|
||||
Log.d(TAG, "Requesting user confirmation for installation")
|
||||
createBasicNotif(getString(R.string.installing_app, "MicroG"), notifId, this)
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
startActivity(confirmationIntent)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Log.d(TAG, "Installation succeed")
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isInstalling", false).apply()
|
||||
val mIntent = Intent(MainActivity.INSTALL_COMPLETED)
|
||||
mIntent.action = MainActivity.INSTALL_COMPLETED
|
||||
mIntent.putExtra("package", "normal")
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
createBasicNotif(getString(
|
||||
R.string.successfully_installed,
|
||||
"Microg"
|
||||
), notifId, this)
|
||||
}
|
||||
else -> {
|
||||
sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999))
|
||||
createBasicNotif(
|
||||
getErrorMessage(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)),
|
||||
notifId,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun sendFailure(status: Int) {
|
||||
val mIntent = Intent(MainActivity.INSTALL_FAILED)
|
||||
mIntent.action = MainActivity.INSTALL_FAILED
|
||||
mIntent.putExtra("errorMsg", getErrorMessage(status))
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
}
|
||||
|
||||
private fun getErrorMessage(status: Int): String {
|
||||
return when (status) {
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> getString(R.string.installation_aborted)
|
||||
PackageInstaller.STATUS_FAILURE_BLOCKED -> getString(R.string.installation_blocked)
|
||||
PackageInstaller.STATUS_FAILURE_STORAGE -> getString(R.string.installation_storage)
|
||||
PackageInstaller.STATUS_FAILURE_INVALID -> getString(R.string.installation_invalid)
|
||||
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> getString(R.string.installation_incompatible)
|
||||
PackageInstaller.STATUS_FAILURE_CONFLICT -> getString(R.string.installation_conflict)
|
||||
else ->
|
||||
if (MiuiHelper.isMiui())
|
||||
getString(R.string.installation_miui)
|
||||
else
|
||||
getString(R.string.installation_failed)
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
companion object{
|
||||
const val TAG = "VMInstall"
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Handler
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
|
||||
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.d(AppInstallerService.TAG, "Requesting user confirmation for uninstallation")
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
startActivity(confirmationIntent)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Handler().postDelayed({
|
||||
val mIntent = Intent()
|
||||
mIntent.action = MainActivity.APP_UNINSTALLED
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
Log.d("VMpm", "Successfully uninstalled $pkgName")
|
||||
}, 500)
|
||||
}
|
||||
PackageInstaller.STATUS_FAILURE -> {
|
||||
Handler().postDelayed({
|
||||
val mIntent = Intent()
|
||||
mIntent.action = MainActivity.APP_NOT_UNINSTALLED
|
||||
mIntent.putExtra("pkgName", pkgName)
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
Log.d("VMpm", "Failed to uninstall $pkgName")
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.utils.FileInfo
|
||||
import com.vanced.manager.utils.NotificationHelper.createBasicNotif
|
||||
import java.io.File
|
||||
import java.nio.charset.Charset
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class RootSplitInstallerService: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Shell.getShell {
|
||||
val isRoot = it.isRoot
|
||||
Log.d("AppLog", "isRoot ?$isRoot ")
|
||||
AsyncTask.execute {
|
||||
val apkFilesPath = cacheDir.path
|
||||
val fileInfoList = getFileInfoList(apkFilesPath)
|
||||
installSplitApkFiles(fileInfoList)
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>) {
|
||||
var sessionId: Int?
|
||||
val notifId = 666
|
||||
Log.d("AppLog", "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)!!)
|
||||
}
|
||||
for (apkFile in apkFiles) {
|
||||
Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ")
|
||||
createBasicNotif(getString(R.string.installing_app, "Vanced"), notifId, this)
|
||||
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: java.lang.Exception) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
process.destroyForcibly()
|
||||
else
|
||||
process.destroy()
|
||||
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
process.waitFor()
|
||||
val inputStr = process.inputStream.readBytes().toString(Charset.defaultCharset())
|
||||
val errStr = process.errorStream.readBytes().toString(Charset.defaultCharset())
|
||||
val isSucceeded = process.exitValue() == 0
|
||||
Log.d("AppLog", "isSucceeded?$isSucceeded inputStr:$inputStr errStr:$errStr")
|
||||
}
|
||||
Log.d("AppLog", "committing...")
|
||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||
Log.d("AppLog", "succeeded installing?${installResult.isSuccess}")
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isInstalling", false).apply()
|
||||
if (installResult.isSuccess) {
|
||||
val mIntent = Intent(MainActivity.INSTALL_COMPLETED)
|
||||
mIntent.action = MainActivity.INSTALL_COMPLETED
|
||||
mIntent.putExtra("package", "split")
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
createBasicNotif(getString(R.string.successfully_installed, "Vanced"), notifId, this)
|
||||
} else {
|
||||
val mIntent = Intent(MainActivity.INSTALL_FAILED)
|
||||
mIntent.action = MainActivity.INSTALL_FAILED
|
||||
mIntent.putExtra("errorMsg", getString(R.string.installation_signature))
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
createBasicNotif(getString(R.string.installation_signature), notifId, this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SimpleDateFormat.tryParse(str: String) = try {
|
||||
parse(str) != null
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
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()
|
||||
for (file in listFiles)
|
||||
result.add(FileInfo(file.name, file.length(), file))
|
||||
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
|
||||
}
|
||||
|
||||
@Nullable
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import java.io.*
|
||||
|
||||
class SplitInstaller: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
installSplitApk(this)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun installSplitApk(context: Context): Int {
|
||||
val apkFolderPath = context.cacheDir.path + "/"
|
||||
val nameSizeMap = HashMap<String, Long>()
|
||||
var totalSize: Long = 0
|
||||
var sessionId = 0
|
||||
val folder = File(apkFolderPath)
|
||||
val listOfFiles = folder.listFiles()
|
||||
try {
|
||||
for (listOfFile in listOfFiles!!) {
|
||||
if (listOfFile.isFile) {
|
||||
Log.d("AppLog", "installApk: " + listOfFile.name)
|
||||
nameSizeMap[listOfFile.name] = listOfFile.length()
|
||||
totalSize += listOfFile.length()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
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("AppLog","Success: created install session [$sessionId]")
|
||||
for ((key, value) in nameSizeMap) {
|
||||
doWriteSession(sessionId, apkFolderPath + key, value, key, context)
|
||||
}
|
||||
doCommitSession(sessionId, context)
|
||||
Log.d("AppLog","Success")
|
||||
} catch (e: IOException) {
|
||||
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("AppLog", "Success: streamed $total bytes")
|
||||
return PackageInstaller.STATUS_SUCCESS
|
||||
} catch (e: IOException) {
|
||||
Log.e("AppLog", "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 {
|
||||
try {
|
||||
session = context.packageManager.packageInstaller.openSession(sessionId)
|
||||
val callbackIntent = Intent(context.applicationContext, SplitInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context.applicationContext, 0, callbackIntent, 0)
|
||||
session.commit(pendingIntent.intentSender)
|
||||
session.close()
|
||||
Log.d("AppLog", "install request sent")
|
||||
Log.d("AppLog", "doCommitSession: " + context.packageManager.packageInstaller.mySessions)
|
||||
Log.d("AppLog", "doCommitSession: after session commit ")
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
} finally {
|
||||
session!!.close()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.utils.MiuiHelper.isMiui
|
||||
import com.vanced.manager.utils.NotificationHelper.createBasicNotif
|
||||
|
||||
class SplitInstallerService: Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
val notifId = 666
|
||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
Toast.makeText(this, "Installing...", Toast.LENGTH_SHORT).show()
|
||||
createBasicNotif(getString(R.string.installing_app, "Vanced"), notifId, this)
|
||||
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) {
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Log.d(TAG, "Installation succeed")
|
||||
getSharedPreferences("installPrefs", Context.MODE_PRIVATE).edit().putBoolean("isInstalling", false).apply()
|
||||
val mIntent = Intent(MainActivity.INSTALL_COMPLETED)
|
||||
mIntent.action = MainActivity.INSTALL_COMPLETED
|
||||
mIntent.putExtra("package", "split")
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
createBasicNotif(
|
||||
getString(R.string.successfully_installed, "Vanced"),
|
||||
notifId,
|
||||
this
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999))
|
||||
createBasicNotif(
|
||||
getErrorMessage(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)),
|
||||
notifId,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun sendFailure(status: Int) {
|
||||
val mIntent = Intent(MainActivity.INSTALL_FAILED)
|
||||
mIntent.action = MainActivity.INSTALL_FAILED
|
||||
mIntent.putExtra("errorMsg", getErrorMessage(status))
|
||||
LocalBroadcastManager.getInstance(this).sendBroadcast(mIntent)
|
||||
}
|
||||
|
||||
private fun getErrorMessage(status: Int): String {
|
||||
return when (status) {
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> getString(R.string.installation_aborted)
|
||||
PackageInstaller.STATUS_FAILURE_BLOCKED -> getString(R.string.installation_blocked)
|
||||
PackageInstaller.STATUS_FAILURE_STORAGE -> getString(R.string.installation_storage)
|
||||
PackageInstaller.STATUS_FAILURE_INVALID -> getString(R.string.installation_invalid)
|
||||
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> getString(R.string.installation_incompatible)
|
||||
PackageInstaller.STATUS_FAILURE_CONFLICT -> getString(R.string.installation_conflict)
|
||||
else ->
|
||||
if (isMiui())
|
||||
getString(R.string.installation_miui)
|
||||
else
|
||||
getString(R.string.installation_failed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startForegroundNotif(text: String) {
|
||||
val notifBuilder =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
Notification.Builder(this, 666.toString()).setChannelId("69420")
|
||||
else
|
||||
Notification.Builder(this).setPriority(Notification.PRIORITY_DEFAULT)
|
||||
|
||||
notifBuilder.apply {
|
||||
setContentTitle(getString(R.string.app_name))
|
||||
setContentText(text)
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
}
|
||||
|
||||
val notif = notifBuilder.build()
|
||||
startForeground(666, notif)
|
||||
}
|
||||
|
||||
@Nullable
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
companion object{
|
||||
const val TAG = "VMInstall"
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package com.vanced.manager.ui
|
||||
|
||||
import android.content.*
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.Main
|
||||
import com.vanced.manager.databinding.ActivityMainBinding
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.launchVanced
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.regularPackageInstalled
|
||||
import com.vanced.manager.utils.ThemeHelper.setFinalTheme
|
||||
|
||||
class MainActivity : Main() {
|
||||
|
||||
private var isParent = true
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setFinalTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
|
||||
binding.lifecycleOwner = this
|
||||
|
||||
val toolbar: MaterialToolbar = findViewById(R.id.home_toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val navHost = findNavController(R.id.bottom_nav_host)
|
||||
val appBarConfiguration = AppBarConfiguration(navHost.graph)
|
||||
toolbar.setupWithNavController(navHost, appBarConfiguration)
|
||||
|
||||
navHost.addOnDestinationChangedListener{_, currFrag: NavDestination, _ ->
|
||||
isParent = when (currFrag.id) {
|
||||
R.id.home_fragment -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
setDisplayHomeAsUpEnabled(!isParent)
|
||||
|
||||
}
|
||||
|
||||
registerReceivers()
|
||||
|
||||
}
|
||||
|
||||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
INSTALL_COMPLETED -> {
|
||||
if (intent.getStringExtra("package") == "split")
|
||||
launchVanced(this@MainActivity)
|
||||
else
|
||||
regularPackageInstalled(getString(R.string.microg_installed), this@MainActivity)
|
||||
}
|
||||
INSTALL_FAILED -> installAlertBuilder(intent.getStringExtra("errorMsg") as String, this@MainActivity)
|
||||
APP_UNINSTALLED -> {
|
||||
restartActivity()
|
||||
Log.d("VMpm", "test")
|
||||
}
|
||||
APP_NOT_UNINSTALLED -> installAlertBuilder(getString(R.string.failed_uninstall) + intent.getStringExtra("pkgName"), this@MainActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
setFinalTheme(this)
|
||||
super.onResume()
|
||||
registerReceivers()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val navHost = findNavController(R.id.bottom_nav_host)
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.toolbar_about -> {
|
||||
navHost.navigate(R.id.toAboutFragment)
|
||||
return true
|
||||
}
|
||||
R.id.toolbar_settings -> {
|
||||
navHost.navigate(R.id.action_settingsFragment)
|
||||
return true
|
||||
}
|
||||
R.id.dev_settings -> {
|
||||
navHost.navigate(R.id.toDevSettingsFragment)
|
||||
return true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setDisplayHomeAsUpEnabled(isNeeded: Boolean) {
|
||||
val toolbar: MaterialToolbar = findViewById(R.id.home_toolbar)
|
||||
when {
|
||||
isNeeded -> toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
else -> toolbar.navigationIcon = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerReceivers() {
|
||||
val intentFilter = IntentFilter()
|
||||
intentFilter.addAction(INSTALL_COMPLETED)
|
||||
intentFilter.addAction(INSTALL_FAILED)
|
||||
intentFilter.addAction(APP_UNINSTALLED)
|
||||
intentFilter.addAction(APP_NOT_UNINSTALLED)
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter)
|
||||
|
||||
}
|
||||
|
||||
fun restartActivity() {
|
||||
startActivity(Intent(this@MainActivity, MainActivity::class.java))
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
finish()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val INSTALL_COMPLETED = "Installation completed"
|
||||
const val INSTALL_FAILED = "it just failed idk"
|
||||
const val APP_UNINSTALLED = "App uninstalled"
|
||||
const val APP_NOT_UNINSTALLED = "App not uninstalled"
|
||||
}
|
||||
}
|
|
@ -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,33 @@
|
|||
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
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.widget.NestedScrollView
|
||||
|
||||
open class SlidingNestedScrollView : NestedScrollView {
|
||||
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,18 @@
|
|||
package com.vanced.manager.ui.core
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
|
||||
class SplashScreenActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(R.style.SplashTheme)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
startActivity(Intent(this@SplashScreenActivity, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.vanced.manager.ui.dashboard
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.vanced.manager.R
|
||||
|
||||
class DashboardFragment : Fragment() {
|
||||
|
||||
private lateinit var dashboardViewModel: DashboardViewModel
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
dashboardViewModel =
|
||||
ViewModelProviders.of(this).get(DashboardViewModel::class.java)
|
||||
val root = inflater.inflate(R.layout.fragment_dashboard, container, false)
|
||||
val textView: TextView = root.findViewById(R.id.text_dashboard)
|
||||
dashboardViewModel.text.observe(viewLifecycleOwner, Observer {
|
||||
textView.text = it
|
||||
})
|
||||
return root
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.ui.dashboard
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class DashboardViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is dashboard Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package com.vanced.manager.ui.dialogs
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.downloader.VancedDownloadService
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.utils.InternetTools.openUrl
|
||||
import com.vanced.manager.utils.MiuiHelper
|
||||
|
||||
object DialogContainer {
|
||||
|
||||
fun showSecurityDialog(context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(context.resources.getString(R.string.welcome))
|
||||
.setMessage(context.resources.getString(R.string.security_context))
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(context.resources.getString(R.string.close)) { dialog, _ ->
|
||||
run {
|
||||
dialog.dismiss()
|
||||
if (MiuiHelper.isMiui()) {
|
||||
showMiuiDialog(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit().putBoolean("firstStart", false).apply()
|
||||
}
|
||||
|
||||
private fun showMiuiDialog(context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.miui_one_title))
|
||||
.setMessage(context.getString(R.string.miui_one))
|
||||
.setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
.setNeutralButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://telegra.ph/How-to-install-v15-on-MIUI-02-11", R.color.Telegram, context)
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
fun showRootDialog(activity: Activity) {
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.hold_on))
|
||||
.setMessage(activity.getString(R.string.disable_signature))
|
||||
.setPositiveButton(activity.getString(R.string.button_dismiss)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
PreferenceManager.getDefaultSharedPreferences(activity).edit().putBoolean("show_root_dialog", false).apply()
|
||||
}
|
||||
.setNeutralButton(activity.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://lmgtfy.com/?q=andnixsh+apk+verification+disable", R.color.Twitter, activity)
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
fun secondMiuiDialog(context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.miui_two_title))
|
||||
.setMessage(context.getString(R.string.miui_two))
|
||||
.setPositiveButton(context.getString(R.string.button_fine)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNeutralButton(context.getString(R.string.guide)) { _, _ ->
|
||||
openUrl("https://telegra.ph/How-to-install-v15-on-MIUI-02-11", R.color.Telegram, context)
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
//Easter Egg
|
||||
fun statementFalse(context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle("Wait what?")
|
||||
.setMessage("So this statement is false huh? I'll go with True!")
|
||||
.setPositiveButton("wut?") { dialog, _ -> dialog.dismiss() }
|
||||
.create()
|
||||
.show()
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
prefs.edit().putBoolean("statement", true).apply()
|
||||
}
|
||||
|
||||
fun installAlertBuilder(msg: String, context: Context) {
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.error))
|
||||
.setMessage(msg)
|
||||
.setPositiveButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
fun regularPackageInstalled(msg: String, activity: MainActivity) {
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.success))
|
||||
.setMessage(msg)
|
||||
.setPositiveButton(activity.getString(R.string.close)) { _, _ -> activity.restartActivity() }
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
fun launchVanced(activity: MainActivity) {
|
||||
val intent = Intent()
|
||||
intent.component =
|
||||
if (PreferenceManager.getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot") == "root")
|
||||
ComponentName("com.google.android.youtube", "com.google.android.youtube.HomeActivity")
|
||||
else
|
||||
ComponentName("com.vanced.android.youtube", "com.google.android.youtube.HomeActivity")
|
||||
AlertDialog.Builder(activity)
|
||||
.setTitle(activity.getString(R.string.success))
|
||||
.setMessage(activity.getString(R.string.vanced_installed))
|
||||
.setPositiveButton(activity.getString(R.string.launch)) { _, _ ->
|
||||
run {
|
||||
startActivity(activity, intent, null)
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
.setNegativeButton(activity.getString(R.string.close)) { dialog, _ ->
|
||||
run {
|
||||
dialog.dismiss()
|
||||
activity.restartActivity()
|
||||
}
|
||||
}
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.fragments.About
|
||||
import com.vanced.manager.databinding.FragmentAboutBinding
|
||||
import com.vanced.manager.ui.viewmodels.AboutViewModel
|
||||
|
||||
class AboutFragment : About() {
|
||||
|
||||
private lateinit var binding: FragmentAboutBinding
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
activity?.title = getString(R.string.title_about)
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_about, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val viewModel: AboutViewModel by viewModels()
|
||||
binding.viewModel = viewModel
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.DialogFragment
|
||||
|
||||
import com.vanced.manager.R
|
||||
|
||||
class ChosenPreferenceDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
if (dialog != null && dialog?.window != null) {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
return inflater.inflate(R.layout.fragment_chosen_preferences, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val closebtn = view.findViewById<Button>(R.id.chosen_prefs_close)
|
||||
val resetbtn = view.findViewById<Button>(R.id.chosen_prefs_reset)
|
||||
val themetxt = view.findViewById<TextView>(R.id.chosen_theme)
|
||||
val langtxt = view.findViewById<TextView>(R.id.chosen_lang)
|
||||
|
||||
val prefs = activity?.getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
|
||||
themetxt.text = activity?.getString(R.string.chosen_theme, prefs?.getString("theme", "dark"))
|
||||
langtxt.text = activity?.getString(R.string.chosen_lang, prefs?.getString("lang", "en"))
|
||||
|
||||
closebtn.setOnClickListener { dismiss() }
|
||||
|
||||
resetbtn.setOnClickListener {
|
||||
prefs?.edit()?.putString("theme", "dark")?.apply()
|
||||
prefs?.edit()?.putString("lang", "en")?.apply()
|
||||
prefs?.edit()?.putBoolean("valuesModified", false)?.apply()
|
||||
dismiss()
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
|
||||
class DevSettingsFragment: PreferenceFragmentCompat() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.dev_settings, rootKey)
|
||||
|
||||
val ftSwitch: Preference? = findPreference("firststart_switch")
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
ftSwitch?.setOnPreferenceClickListener {
|
||||
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setTitle("FirstStart activated")
|
||||
.setMessage("boolean will be activated on next app start")
|
||||
.setPositiveButton("Restart") { _, _ ->
|
||||
run {
|
||||
startActivity(Intent(requireContext(), MainActivity::class.java))
|
||||
activity?.finish()
|
||||
}
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
|
||||
prefs.edit().putBoolean("firstStart", true).apply()
|
||||
true
|
||||
|
||||
}
|
||||
val archPref: Preference? = findPreference("device_arch")
|
||||
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
|
||||
|
||||
if (supportedAbis.contains("arm64-v8a") || supportedAbis.contains("x86_64")) {
|
||||
archPref?.summary = "64bit"
|
||||
} else {
|
||||
archPref?.summary = "32bit"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.RotateAnimation
|
||||
import android.widget.*
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.SectionPageAdapter
|
||||
import com.vanced.manager.adapter.SectionPageRootAdapter
|
||||
import com.vanced.manager.core.fragments.Home
|
||||
import com.vanced.manager.databinding.FragmentHomeBinding
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.PackageHelper.installApp
|
||||
|
||||
class HomeFragment : Home() {
|
||||
|
||||
private lateinit var binding: FragmentHomeBinding
|
||||
private var isExpanded: Boolean = false
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
activity?.title = getString(R.string.title_home)
|
||||
setHasOptionsMenu(true)
|
||||
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val viewModel: HomeViewModel by viewModels()
|
||||
binding.viewModel = viewModel
|
||||
|
||||
val variantPref = getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot")
|
||||
registerReceivers()
|
||||
|
||||
if (variantPref == "root")
|
||||
attachRootChangelog()
|
||||
else {
|
||||
attachNonrootChangelog()
|
||||
if (!viewModel.microgInstalled) {
|
||||
disableVancedButton()
|
||||
}
|
||||
}
|
||||
|
||||
view.findViewById<ImageButton>(R.id.changelog_button).setOnClickListener {
|
||||
cardExpandCollapse()
|
||||
}
|
||||
}
|
||||
|
||||
private fun cardExpandCollapse() {
|
||||
val viewPagerContainer = view?.findViewById<ViewPager2>(R.id.viewpager)
|
||||
val tabLayoutContainer = view?.findViewById<TabLayout>(R.id.tablayout)
|
||||
val arrow = view?.findViewById<ImageButton>(R.id.changelog_button)
|
||||
if (isExpanded) {
|
||||
viewPagerContainer?.visibility = View.GONE
|
||||
tabLayoutContainer?.visibility = View.GONE
|
||||
isExpanded = false
|
||||
arrow?.animate()?.rotation(0F)?.interpolator = AccelerateDecelerateInterpolator()
|
||||
} else {
|
||||
viewPagerContainer?.visibility = View.VISIBLE
|
||||
tabLayoutContainer?.visibility = View.VISIBLE
|
||||
isExpanded = true
|
||||
arrow?.animate()?.rotation(180F)?.interpolator = AccelerateDecelerateInterpolator()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
activity?.let { LocalBroadcastManager.getInstance(it).unregisterReceiver(broadcastReceiver) }
|
||||
}
|
||||
|
||||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
MICROG_DOWNLOADED -> {
|
||||
view?.findViewById<ProgressBar>(R.id.microg_installing)?.visibility = View.VISIBLE
|
||||
activity?.let { installApp(it, it.filesDir.path + "/microg.apk", "com.mgoogle.android.gms") }
|
||||
}
|
||||
VANCED_DOWNLOADED -> {
|
||||
view?.findViewById<ProgressBar>(R.id.vanced_installing)?.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerReceivers() {
|
||||
val intentFilter = IntentFilter()
|
||||
intentFilter.addAction(VANCED_DOWNLOADED)
|
||||
intentFilter.addAction(MICROG_DOWNLOADED)
|
||||
activity?.let {
|
||||
LocalBroadcastManager.getInstance(it).registerReceiver(broadcastReceiver, intentFilter)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun attachNonrootChangelog() {
|
||||
val sectionPageAdapter = SectionPageAdapter(this)
|
||||
val tabLayout = view?.findViewById(R.id.tablayout) as TabLayout
|
||||
val viewPager = view?.findViewById(R.id.viewpager) as ViewPager2
|
||||
viewPager.adapter = sectionPageAdapter
|
||||
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
when (position) {
|
||||
0 -> tab.text = "Vanced"
|
||||
1 -> tab.text = "MicroG"
|
||||
2 -> tab.text = "Manager"
|
||||
}
|
||||
}.attach()
|
||||
}
|
||||
|
||||
private fun attachRootChangelog() {
|
||||
val sectionPageRootAdapter = SectionPageRootAdapter(this)
|
||||
val tabLayout = view?.findViewById(R.id.tablayout) as TabLayout
|
||||
val viewPager = view?.findViewById(R.id.viewpager) as ViewPager2
|
||||
viewPager.adapter = sectionPageRootAdapter
|
||||
|
||||
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
|
||||
when (position) {
|
||||
0 -> tab.text = "Vanced"
|
||||
1 -> tab.text = "Manager"
|
||||
}
|
||||
}.attach()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.toolbar_menu, menu)
|
||||
super .onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
private fun disableVancedButton() {
|
||||
val vancedinstallbtn = view?.findViewById<MaterialButton>(R.id.vanced_installbtn)
|
||||
vancedinstallbtn?.isEnabled = false
|
||||
vancedinstallbtn?.backgroundTintList = ColorStateList.valueOf(Color.DKGRAY)
|
||||
vancedinstallbtn?.setTextColor(ColorStateList.valueOf(Color.GRAY))
|
||||
vancedinstallbtn?.icon = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VANCED_DOWNLOADED = "Vanced downloaded"
|
||||
const val MICROG_DOWNLOADED = "MicroG downloaded"
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.R
|
||||
|
||||
class ManagerChangelogFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_manager_changelog, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val changelogTxt = view.findViewById<TextView>(R.id.manager_changelog)
|
||||
|
||||
if (GetJson().isConnected(activity)) {
|
||||
val checkUrl = GetJson().AsJSONObject("https://vanced.app/api/v1/manager.json")
|
||||
val changelog = checkUrl.get("changelog").asString
|
||||
changelogTxt.text = changelog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.R
|
||||
|
||||
class MicrogChangelogFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_microg_changelog, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val changelogTxt = view.findViewById<TextView>(R.id.microg_changelog)
|
||||
|
||||
if (GetJson().isConnected(activity)) {
|
||||
val checkUrl = GetJson().AsJSONObject("https://x1nto.github.io/VancedFiles/microg.json")
|
||||
val changelog = checkUrl.get("changelog").asString
|
||||
changelogTxt.text = changelog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import androidx.preference.*
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.MiuiHelper.isMiui
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
|
||||
activity?.title = getString(R.string.title_settings)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
val updateCheck: Preference? = findPreference("update_check")
|
||||
updateCheck?.setOnPreferenceClickListener {
|
||||
val fm = childFragmentManager.beginTransaction()
|
||||
val updateDialog = UpdateCheckFragment()
|
||||
updateDialog.show(fm, "Update Center")
|
||||
true
|
||||
}
|
||||
|
||||
val themeSwitch: ListPreference? = findPreference("theme_mode")
|
||||
themeSwitch?.summary =
|
||||
preferenceScreen.sharedPreferences.getString("theme_mode", "Follow System")
|
||||
themeSwitch?.setOnPreferenceChangeListener { _, _ ->
|
||||
activity?.recreate()
|
||||
true
|
||||
}
|
||||
|
||||
val accentSwitch: ListPreference? = findPreference("accent_color")
|
||||
accentSwitch?.summary = preferenceScreen.sharedPreferences.getString("accent_color", "Blue")
|
||||
accentSwitch?.setOnPreferenceChangeListener { _, _ ->
|
||||
activity?.recreate()
|
||||
true
|
||||
}
|
||||
|
||||
val chosenPrefs: Preference? = findPreference("vanced_chosen_modes")
|
||||
chosenPrefs?.setOnPreferenceClickListener {
|
||||
val fm = childFragmentManager.beginTransaction()
|
||||
val chosenPrefsDialog = ChosenPreferenceDialogFragment()
|
||||
chosenPrefsDialog.show(fm, "Chosen Preferences")
|
||||
true
|
||||
}
|
||||
|
||||
val installUrl: Preference? = findPreference("install_url")
|
||||
installUrl?.setOnPreferenceClickListener {
|
||||
val fm = childFragmentManager.beginTransaction()
|
||||
val chosenPrefsDialog = URLChangeFragment()
|
||||
chosenPrefsDialog.show(fm, "Install URL")
|
||||
true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
val devSettings = PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("devSettings", false)
|
||||
if (devSettings) {
|
||||
inflater.inflate(R.menu.dev_settings_menu, menu)
|
||||
}
|
||||
super .onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
|
||||
class URLChangeFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
if (dialog != null && dialog?.window != null) {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
return inflater.inflate(R.layout.fragment_custom_url, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val urlField = view.findViewById<EditText>(R.id.url_input)
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
urlField.hint = prefs.getString("install_url", baseUrl)
|
||||
view.findViewById<MaterialButton>(R.id.url_save).setOnClickListener {
|
||||
prefs.edit().putString("install_url", urlField.text.toString()).apply()
|
||||
dismiss()
|
||||
}
|
||||
view.findViewById<MaterialButton>(R.id.url_reset).setOnClickListener {
|
||||
prefs.edit().putString("install_url", baseUrl).apply()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
import com.downloader.PRDownloader
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.vanced.manager.BuildConfig
|
||||
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.InternetTools.isUpdateAvailable
|
||||
import com.vanced.manager.utils.PackageHelper.installApp
|
||||
|
||||
class UpdateCheckFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
if (dialog != null && dialog?.window != null) {
|
||||
dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
return inflater.inflate(R.layout.fragment_update_check, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
checkUpdates()
|
||||
view.findViewById<Button>(R.id.update_center_dismiss).setOnClickListener { dismiss() }
|
||||
view.findViewById<MaterialButton>(R.id.update_center_recheck).setOnClickListener{ checkUpdates() }
|
||||
}
|
||||
|
||||
private fun checkUpdates() {
|
||||
val updatebtn = view?.findViewById<Button>(R.id.update_center_update)
|
||||
val checkingTxt = view?.findViewById<TextView>(R.id.update_center_checking)
|
||||
if (GetJson().isConnected(requireContext())) {
|
||||
|
||||
if (isUpdateAvailable()) {
|
||||
view?.findViewById<Button>(R.id.update_center_recheck)?.visibility = View.GONE
|
||||
checkingTxt?.text = getString(R.string.update_found)
|
||||
|
||||
updatebtn?.setOnClickListener {
|
||||
upgradeManager()
|
||||
}
|
||||
} else checkingTxt?.text = getString(R.string.update_notfound)
|
||||
|
||||
} else {
|
||||
checkingTxt?.text = getString(R.string.network_error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun upgradeManager() {
|
||||
val dwnldUrl = "https://github.com/VancedManager/releases/latest/download/manager.apk"
|
||||
val loadBar = view?.findViewById<ProgressBar>(R.id.update_center_progressbar)
|
||||
|
||||
PRDownloader.download(dwnldUrl, activity?.filesDir?.path, "manager.apk")
|
||||
.build()
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
loadBar?.visibility = View.VISIBLE
|
||||
loadBar?.progress = mProgress.toInt()
|
||||
|
||||
}
|
||||
.start(object : OnDownloadListener{
|
||||
override fun onDownloadComplete() {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
prefs.getBoolean("isUpgrading", false)
|
||||
prefs.edit().putBoolean("isUpgrading", true).apply()
|
||||
|
||||
activity?.let {
|
||||
installApp(
|
||||
it,
|
||||
it.filesDir.path + "/manager.apk",
|
||||
"com.vanced.manager")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(error: Error?) {
|
||||
Toast.makeText(activity, error.toString(), Toast.LENGTH_SHORT).show()
|
||||
Log.e("VMUpgrade", error.toString())
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.R
|
||||
|
||||
class VancedChangelogFragment : Fragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_vanced_changelog, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val changelogTxt = view.findViewById<TextView>(R.id.vanced_changelog)
|
||||
|
||||
if (GetJson().isConnected(activity)) {
|
||||
val checkUrl = GetJson().AsJSONObject("https://vanced.app/api/v1/changelog/15_05_54.json")
|
||||
val changelog = checkUrl.get("message").asString
|
||||
changelogTxt.text = changelog
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.fragments.LanguageInstall
|
||||
|
||||
class VancedLanguageSelectionFragment : LanguageInstall() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
activity?.title = getString(R.string.title_install)
|
||||
return inflater.inflate(R.layout.fragment_vanced_language_selection, container, false)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.vanced.manager.ui.fragments
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.fragments.ThemeInstall
|
||||
|
||||
class VancedThemeSelectionFragment : ThemeInstall() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
activity?.title = getString(R.string.title_install)
|
||||
return inflater.inflate(R.layout.fragment_vanced_theme_selection, container, false)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.vanced.manager.ui.home
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.vanced.manager.R
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
|
||||
private lateinit var homeViewModel: HomeViewModel
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
homeViewModel =
|
||||
ViewModelProviders.of(this).get(HomeViewModel::class.java)
|
||||
val root = inflater.inflate(R.layout.fragment_home, container, false)
|
||||
val textView: TextView = root.findViewById(R.id.text_home)
|
||||
homeViewModel.text.observe(viewLifecycleOwner, Observer {
|
||||
textView.text = it
|
||||
})
|
||||
return root
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.ui.home
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class HomeViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is home Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.vanced.manager.ui.notifications
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.vanced.manager.R
|
||||
|
||||
class NotificationsFragment : Fragment() {
|
||||
|
||||
private lateinit var notificationsViewModel: NotificationsViewModel
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
notificationsViewModel =
|
||||
ViewModelProviders.of(this).get(NotificationsViewModel::class.java)
|
||||
val root = inflater.inflate(R.layout.fragment_notifications, container, false)
|
||||
val textView: TextView = root.findViewById(R.id.text_notifications)
|
||||
notificationsViewModel.text.observe(viewLifecycleOwner, Observer {
|
||||
textView.text = it
|
||||
})
|
||||
return root
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package com.vanced.manager.ui.notifications
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
class NotificationsViewModel : ViewModel() {
|
||||
|
||||
private val _text = MutableLiveData<String>().apply {
|
||||
value = "This is notifications Fragment"
|
||||
}
|
||||
val text: LiveData<String> = _text
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import com.vanced.manager.R
|
||||
|
||||
open class AboutViewModel(application: Application): AndroidViewModel(application) {
|
||||
|
||||
fun openUrl(Url: String) {
|
||||
val builder = CustomTabsIntent.Builder()
|
||||
builder.setToolbarColor(ContextCompat.getColor(getApplication(), R.color.GitHub))
|
||||
val customTabsIntent = builder.build()
|
||||
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
customTabsIntent.launchUrl(getApplication(), Uri.parse(Url))
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
package com.vanced.manager.ui.viewmodels
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.InternetTools.displayJsonInt
|
||||
import com.vanced.manager.utils.InternetTools.displayJsonString
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
|
||||
class HomeViewModel(application: Application): AndroidViewModel(application) {
|
||||
|
||||
private val variant = getDefaultSharedPreferences(application).getString("vanced_variant", "nonroot")
|
||||
private val connected: Boolean = GetJson().isConnected(application)
|
||||
|
||||
private val vancedPkgName: String =
|
||||
if (variant== "root") {
|
||||
"com.google.android.youtube"
|
||||
} else {
|
||||
"com.vanced.android.youtube"
|
||||
}
|
||||
|
||||
private val pm = application.packageManager
|
||||
|
||||
val vancedInstallButtonTxt: MutableLiveData<String> = MutableLiveData()
|
||||
val vancedInstallButtonIcon: MutableLiveData<Drawable> = MutableLiveData()
|
||||
|
||||
val microgInstalled: Boolean = isPackageInstalled("com.mgoogle.android.gms", application.packageManager)
|
||||
val vancedInstalled: Boolean = isPackageInstalled(vancedPkgName, application.packageManager)
|
||||
|
||||
val vancedInstalledVersion: MutableLiveData<String> = MutableLiveData()
|
||||
val microgInstalledVersion: MutableLiveData<String> = MutableLiveData()
|
||||
|
||||
val vancedVersion: MutableLiveData<String> = MutableLiveData()
|
||||
val microgVersion: MutableLiveData<String> = MutableLiveData()
|
||||
|
||||
private val vancedInstalledVersionCode = getPkgVerCode(vancedInstalled, vancedPkgName)
|
||||
private val microgInstalledVersionCode = getPkgVerCode(microgInstalled, "com.mgoogle.android.gms")
|
||||
|
||||
private val vancedVersionCode = displayJsonInt("vanced.json", "versionCode", application)
|
||||
private val microgVersionCode = displayJsonInt("microg.json", "versionCode", application)
|
||||
|
||||
val microgInstallButtonTxt = compareInt(microgInstalledVersionCode, microgVersionCode, application)
|
||||
|
||||
val microgInstallButtonIcon = compareIntDrawable(microgInstalledVersionCode, microgVersionCode, application)
|
||||
|
||||
val nonrootModeSelected: Boolean = variant == "nonroot"
|
||||
|
||||
fun openMicrogSettings() {
|
||||
try {
|
||||
val intent = Intent()
|
||||
intent.component = ComponentName(
|
||||
"com.mgoogle.android.gms",
|
||||
"org.microg.gms.ui.SettingsActivity"
|
||||
)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(getApplication(), intent, null)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Toast.makeText(getApplication(), "App not installed", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun openUrl(Url: String) {
|
||||
val color: Int =
|
||||
when (Url) {
|
||||
"https://discord.gg/TUVd7rd" -> R.color.Discord
|
||||
"https://t.me/joinchat/AAAAAEHf-pi4jH1SDlAL4w" -> R.color.Telegram
|
||||
"https://twitter.com/YTVanced" -> R.color.Twitter
|
||||
"https://reddit.com/r/vanced" -> R.color.Reddit
|
||||
"https://vanced.app" -> R.color.Vanced
|
||||
"https://brave.com/van874" -> R.color.Brave
|
||||
else -> R.color.Vanced
|
||||
}
|
||||
|
||||
val builder = CustomTabsIntent.Builder()
|
||||
builder.setToolbarColor(ContextCompat.getColor(getApplication(), color))
|
||||
val customTabsIntent = builder.build()
|
||||
customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
customTabsIntent.launchUrl(getApplication(), Uri.parse(Url))
|
||||
}
|
||||
|
||||
private fun getPkgInfo(toCheck: Boolean, pkg: String, application: Application): String {
|
||||
return if (toCheck) {
|
||||
pm.getPackageInfo(pkg, 0).versionName
|
||||
} else {
|
||||
application.getString(R.string.unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPkgVerCode(toCheck: Boolean, pkg: String): Int {
|
||||
return if (toCheck) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
pm.getPackageInfo(pkg, 0).longVersionCode.and(0xFFFFFFFF).toInt()
|
||||
else
|
||||
pm.getPackageInfo(pkg, 0).versionCode
|
||||
} else 0
|
||||
}
|
||||
|
||||
private fun compareInt(int1: Int, int2: Int, application: Application): String {
|
||||
return if (connected)
|
||||
when {
|
||||
int2 == 0 -> application.getString(R.string.install)
|
||||
int1 > int2 -> application.getString(R.string.update)
|
||||
int2 == int1 -> application.getString(R.string.button_reinstall)
|
||||
else -> application.getString(R.string.install)
|
||||
} else application.getString(R.string.install)
|
||||
}
|
||||
|
||||
private fun compareIntDrawable(int1: Int, int2: Int, application: Application): Drawable? {
|
||||
return if (connected)
|
||||
when {
|
||||
int2 == 0 -> application.getDrawable(R.drawable.ic_download)
|
||||
int1 > int2 -> application.getDrawable(R.drawable.ic_update)
|
||||
int2 == int1 -> application.getDrawable(R.drawable.ic_done)
|
||||
else -> application.getDrawable(R.drawable.ic_download)
|
||||
} else application.getDrawable(R.drawable.ic_download)
|
||||
|
||||
}
|
||||
|
||||
init {
|
||||
vancedVersion.value = displayJsonString("vanced.json","version", application)
|
||||
microgVersion.value = displayJsonString("microg.json","version", application)
|
||||
vancedInstalledVersion.value = getPkgInfo(vancedInstalled, vancedPkgName, application)
|
||||
microgInstalledVersion.value = getPkgInfo(microgInstalled, "com.mgoogle.android.gms", application)
|
||||
vancedInstallButtonIcon.value =
|
||||
if (variant == "nonroot") {
|
||||
if (microgInstalled)
|
||||
compareIntDrawable(vancedVersionCode, vancedInstalledVersionCode, application)
|
||||
else
|
||||
null
|
||||
} else
|
||||
compareIntDrawable(vancedVersionCode, vancedInstalledVersionCode, application)
|
||||
|
||||
vancedInstallButtonTxt.value =
|
||||
if (variant == "nonroot") {
|
||||
if (microgInstalled) {
|
||||
compareInt(vancedVersionCode, vancedInstalledVersionCode, application)
|
||||
} else {
|
||||
application.getString(R.string.no_microg)
|
||||
}
|
||||
} else
|
||||
compareInt(vancedVersionCode, vancedInstalledVersionCode, application)
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.dezlum.codelabs.getjson.GetJson
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.R
|
||||
import java.lang.Exception
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
object InternetTools {
|
||||
|
||||
fun openUrl(Url: String, color: Int, context: Context) {
|
||||
val builder = CustomTabsIntent.Builder()
|
||||
builder.setToolbarColor(ContextCompat.getColor(context, color))
|
||||
val customTabsIntent = builder.build()
|
||||
customTabsIntent.launchUrl(context, Uri.parse(Url))
|
||||
}
|
||||
|
||||
fun getFileNameFromUrl(url: String) = url.substring(url.lastIndexOf('/')+1, url.length)
|
||||
|
||||
fun displayJsonString(json: String, obj: String, context: Context): String {
|
||||
val installUrl = PreferenceManager.getDefaultSharedPreferences(context).getString("install_url", baseUrl)
|
||||
return if (GetJson().isConnected(context)) {
|
||||
try {
|
||||
GetJson().AsJSONObject("$installUrl/$json").get(obj).asString
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is InterruptedException, is IllegalStateException -> GetJson().AsJSONObject("https://x1nto.github.io/VancedFiles/$json").get(obj).asString
|
||||
else -> throw e
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
context.getString(R.string.unavailable)
|
||||
}
|
||||
}
|
||||
|
||||
fun displayJsonInt(json: String, obj: String, context: Context): Int {
|
||||
val installUrl = PreferenceManager.getDefaultSharedPreferences(context).getString("install_url", baseUrl)
|
||||
return if (GetJson().isConnected(context)) {
|
||||
try {
|
||||
GetJson().AsJSONObject("$installUrl/$json").get(obj).asInt
|
||||
} catch (e: Exception) {
|
||||
when (e) {
|
||||
is InterruptedException, is IllegalStateException -> GetJson().AsJSONObject("https://x1nto.github.io/VancedFiles/$json").get(obj).asInt
|
||||
else -> throw e
|
||||
}
|
||||
|
||||
}
|
||||
} else 0
|
||||
}
|
||||
|
||||
fun isUpdateAvailable(): Boolean {
|
||||
val checkUrl = GetJson().AsJSONObject("https://vanced.app/api/v1/manager.json")
|
||||
val remoteVersion = checkUrl.get("versionCode").asInt
|
||||
|
||||
return remoteVersion > BuildConfig.VERSION_CODE
|
||||
}
|
||||
|
||||
const val baseUrl = "https://vanced.app/api/v1"
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.text.TextUtils
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
object MiuiHelper {
|
||||
|
||||
fun isMiui(): Boolean {
|
||||
return !TextUtils.isEmpty(getSystemProps("ro.miui.ui.version.name"))
|
||||
}
|
||||
|
||||
fun isMiuiOptimisationsDisabled(): Boolean {
|
||||
return if (isMiui())
|
||||
getSystemProps("persist.sys.miui_optimization") == "0"
|
||||
else
|
||||
true
|
||||
}
|
||||
|
||||
private fun getSystemProps(propname: String): String? {
|
||||
val line: String
|
||||
var input: BufferedReader? = null
|
||||
try {
|
||||
val p = Runtime.getRuntime().exec("getprop $propname")
|
||||
input = BufferedReader(InputStreamReader(p.inputStream), 1024)
|
||||
line = input.readLine()
|
||||
input.close()
|
||||
} catch (e: IOException) {
|
||||
return null
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
return line
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import com.vanced.manager.R
|
||||
|
||||
object NotificationHelper {
|
||||
|
||||
fun createNotifChannel(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notifChannel = NotificationChannel(
|
||||
"69420",
|
||||
context.getString(R.string.notif_channel_name),
|
||||
NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.createNotificationChannel(notifChannel)
|
||||
}
|
||||
}
|
||||
|
||||
fun displayDownloadNotif(channel: Int, progress:Int, filename: String, context: Context) {
|
||||
val notifBuilder =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
Notification.Builder(context, channel.toString()).setChannelId("69420")
|
||||
else
|
||||
Notification.Builder(context).setPriority(Notification.PRIORITY_HIGH)
|
||||
|
||||
notifBuilder.apply {
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setContentText(context.getString(R.string.downloading_file, filename))
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
setOnlyAlertOnce(true)
|
||||
setOngoing(true)
|
||||
}
|
||||
|
||||
val notif = notifBuilder.build()
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.apply {
|
||||
notifBuilder.setProgress(100, progress, false)
|
||||
notify(channel, notif)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun createBasicNotif(text: String, channel: Int, context: Context) {
|
||||
val notifBuilder =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
Notification.Builder(context, channel.toString()).setChannelId("69420")
|
||||
else
|
||||
Notification.Builder(context).setPriority(Notification.PRIORITY_DEFAULT)
|
||||
|
||||
notifBuilder.apply {
|
||||
setContentTitle(context.getString(R.string.app_name))
|
||||
setContentText(text)
|
||||
setSmallIcon(R.drawable.ic_stat_name)
|
||||
}
|
||||
|
||||
val notif = notifBuilder.build()
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(channel, notif)
|
||||
}
|
||||
|
||||
fun cancelNotif(id: Int, context: Context) {
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(id)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller.SessionParams
|
||||
import android.content.pm.PackageManager
|
||||
import com.vanced.manager.core.installer.AppInstallerService
|
||||
import com.vanced.manager.core.installer.AppUninstallerService
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
object PackageHelper {
|
||||
|
||||
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
||||
return try {
|
||||
packageManager.getPackageInfo(packageName, 0)
|
||||
true
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun installApp(
|
||||
context: Context,
|
||||
path: String,
|
||||
pkg: String?
|
||||
) {
|
||||
val callbackIntent = Intent(context.applicationContext, AppInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context.applicationContext, 0, callbackIntent, 0)
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
|
||||
params.setAppPackageName(pkg)
|
||||
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 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)
|
||||
}
|
||||
|
||||
fun uninstallApk(pkg: String, activity: Activity) {
|
||||
val callbackIntent = Intent(activity.applicationContext, AppUninstallerService::class.java)
|
||||
callbackIntent.putExtra("pkg", pkg)
|
||||
val pendingIntent = PendingIntent.getService(activity.applicationContext, 0, callbackIntent, 0)
|
||||
activity.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.vanced.manager.utils
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
|
||||
object ThemeHelper {
|
||||
|
||||
fun setFinalTheme(activity: MainActivity) {
|
||||
val currentAccent = PreferenceManager.getDefaultSharedPreferences(activity).getString("accent_color", "Blue")
|
||||
when (PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
.getString("theme_mode", "Follow System")) {
|
||||
"Light" -> activity.setTheme(getLightAccent(currentAccent))
|
||||
"Dark" -> activity.setTheme(getDarkAccent(currentAccent))
|
||||
"Follow System" -> {
|
||||
when (activity.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
Configuration.UI_MODE_NIGHT_YES -> activity.setTheme(getDarkAccent(currentAccent))
|
||||
Configuration.UI_MODE_NIGHT_NO -> activity.setTheme(getLightAccent(currentAccent))
|
||||
}
|
||||
}
|
||||
else -> getLightAccent("Blue")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDarkAccent(accentColor: String?): Int {
|
||||
return when (accentColor) {
|
||||
"Blue" -> R.style.DarkTheme_Blue
|
||||
"Red" -> R.style.DarkTheme_Red
|
||||
"Green" -> R.style.DarkTheme_Green
|
||||
"Yellow" -> R.style.DarkTheme_Yellow
|
||||
"Purple" -> R.style.DarkTheme_Purple
|
||||
else -> R.style.DarkTheme_Blue
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLightAccent(accentColor: String?): Int {
|
||||
return when (accentColor) {
|
||||
"Blue" -> R.style.LightTheme_Blue
|
||||
"Red" -> R.style.LightTheme_Red
|
||||
"Green" -> R.style.LightTheme_Green
|
||||
"Yellow" -> R.style.LightTheme_Yellow
|
||||
"Purple" -> R.style.LightTheme_Purple
|
||||
else -> R.style.LightTheme_Blue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="-100%"
|
||||
android:toXDelta="0%"
|
||||
android:duration="500"/>
|
||||
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%"
|
||||
android:duration="500"/>
|
||||
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="-100%"
|
||||
android:duration="500"/>
|
||||
|
||||
</set>
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%"
|
||||
android:duration="500"/>
|
||||
|
||||
</set>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together">
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="0.8"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleY"
|
||||
android:valueFrom="0.8"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType" />
|
||||
|
||||
</set>
|
|
@ -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>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together">
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="1.1"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleY"
|
||||
android:valueFrom="1.1"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="1"
|
||||
android:valueType="floatType" />
|
||||
|
||||
</set>
|
|
@ -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>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together">
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="0.8"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleY"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="0.8"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="0"
|
||||
android:valueType="floatType" />
|
||||
|
||||
</set>
|
|
@ -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="0"
|
||||
android:valueTo="-1.0"
|
||||
android:propertyName="xFraction" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:ordering="together">
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="1.1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="scaleX"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="1.1"
|
||||
android:valueType="floatType"/>
|
||||
|
||||
<objectAnimator
|
||||
android:duration="@android:integer/config_shortAnimTime"
|
||||
android:propertyName="alpha"
|
||||
android:valueFrom="1"
|
||||
android:valueTo="0"
|
||||
android:valueType="floatType" />
|
||||
|
||||
</set>
|
|
@ -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="0"
|
||||
android:valueTo="1.0"
|
||||
android:propertyName="xFraction" />
|
||||
|
||||
</set>
|
|
@ -0,0 +1,46 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="2373.913"
|
||||
android:viewportHeight="2373.913"
|
||||
android:tint="#FFFFFF">
|
||||
<group android:translateX="186.95653"
|
||||
android:translateY="186.95653">
|
||||
<path
|
||||
android:pathData="M1004.92,1881.1c-38.2,0.05 -76.47,-1.45 -114.57,0.42c-51.76,2.55 -81.84,-18.16 -90.74,-70.96c-7.49,-44.41 -13.2,-90.61 -30.51,-131.43c-15.34,-36.18 -40.29,-72.3 -70.22,-97.5c-83.85,-70.62 -178.04,-88.12 -279.91,-36c-16.76,8.57 -36.28,11.64 -54.35,17.76c-44.7,15.15 -76.87,1.99 -100.01,-39.68c-37.07,-66.74 -75.6,-132.69 -114.44,-198.43c-26.16,-44.27 -21.81,-81.72 19.36,-114.5c18.97,-15.11 34.6,-34.64 54.31,-48.55c72.54,-51.21 94,-120.05 91.6,-207.5c-2.02,-73.79 -23.93,-128.07 -79.03,-177.07c-131.04,-116.54 -130.5,-96.77 -38.4,-254.62c19.24,-32.97 38.08,-66.16 57.32,-99.13c41.41,-70.96 54.33,-75.72 134.39,-49.89c34.65,11.18 69.02,23.28 103.91,33.66c101.86,30.29 254.34,-57.92 278.42,-161.01c11.05,-47.28 20.7,-94.88 31.43,-142.24c8.42,-37.18 32.04,-56.47 70.31,-56.57c83.34,-0.23 166.68,0.02 250.01,-0.19c40.55,-0.11 65.26,19.16 73.99,58.78c10.44,47.42 14.73,97.39 33.08,141.51c15.63,37.56 42.09,74.68 73.09,100.99c82.04,69.63 174.55,87.18 274.79,35.85c18.26,-9.35 39.69,-12.3 59.29,-19.26c42.92,-15.23 73.52,-2.18 95.76,37.26c39.19,69.52 79.65,138.32 119.78,207.31c22.41,38.53 18.11,72.63 -15.69,102.47c-13.01,11.49 -24.36,25.49 -38.81,34.62c-105.9,66.87 -127.21,167.78 -109.49,281c5,31.96 22.73,65.36 43.68,90.58c29.68,35.74 67.12,65.23 102.43,96.04c33.98,29.66 40.49,63.13 18.36,101.95c-40.4,70.85 -81.55,141.28 -121.74,212.25c-21.43,37.85 -52.64,47.95 -92.63,35.44c-43.04,-13.47 -85.74,-28.06 -128.91,-41.12c-106.14,-32.11 -256.7,54.16 -281.98,161.54c-10.73,45.58 -20.37,91.42 -30.39,137.17c-8.68,39.59 -32.96,59.33 -73.71,59.05C1084.79,1880.83 1044.85,1881.04 1004.92,1881.1z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="76"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeLineCap="round">
|
||||
<aapt:attr name="android:strokeColor">
|
||||
<gradient
|
||||
android:startY="1642.6245"
|
||||
android:startX="322.9971"
|
||||
android:endY="287.2871"
|
||||
android:endX="1678.3345"
|
||||
android:type="linear">
|
||||
<item android:offset="1.065558E-7" android:color="#FF2E73FF"/>
|
||||
<item android:offset="1" android:color="#FFFF0032"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M1270.42,895.24L886.12,647.3c-16.68,-9.72 -38.08,-4.08 -47.8,12.59l-0.75,1.28c-9.72,16.67 -4.09,38.07 12.59,47.79l367.49,221.11c23.91,14.39 23.77,49.1 -0.25,63.29l-369.18,218.11c-16.76,9.58 -22.58,30.93 -13,47.69l0.73,1.29c9.58,16.76 30.93,22.57 47.69,12.99l386.24,-244.83C1318.69,997.67 1318.97,926.57 1270.42,895.24z"
|
||||
android:fillColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M1074.21,926.45L884.98,819.87c-24.49,-13.8 -54.77,3.9 -54.77,32v213.18c0,28.11 30.28,45.8 54.77,32l189.22,-106.59C1099.15,976.41 1099.15,940.5 1074.21,926.45z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startY="1058.4124"
|
||||
android:startX="808.329"
|
||||
android:endY="858.5059"
|
||||
android:endX="1008.2355"
|
||||
android:type="linear">
|
||||
<item android:offset="0" android:color="#FF2E73FF"/>
|
||||
<item android:offset="1" android:color="#FFFF0032"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
</group>
|
||||
</vector>
|
After Width: | Height: | Size: 724 B |
Before Width: | Height: | Size: 398 B |
Before Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 684 B |
Before Width: | Height: | Size: 857 B |
Before Width: | Height: | Size: 407 B |
Before Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 823 B |
After Width: | Height: | Size: 435 B |
Before Width: | Height: | Size: 273 B |
Before Width: | Height: | Size: 340 B |
Before Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 600 B |
Before Width: | Height: | Size: 271 B |
Before Width: | Height: | Size: 326 B |
Before Width: | Height: | Size: 463 B |
Before Width: | Height: | Size: 569 B |
After Width: | Height: | Size: 23 KiB |