mirror of
https://github.com/YTVanced/VancedManager
synced 2024-11-25 12:45:12 +00:00
started code cleanup
This commit is contained in:
parent
5681b6fb4f
commit
160a5684f7
30 changed files with 809 additions and 1024 deletions
|
@ -71,14 +71,8 @@
|
|||
android:resource="@drawable/ic_stat_name" />
|
||||
|
||||
<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" />
|
||||
<service android:name=".core.downloader.MusicDownloadService" />
|
||||
|
||||
</application>
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package com.vanced.manager.core
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import android.os.Build
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.CrowdinConfig
|
||||
import com.crowdin.platform.data.remote.NetworkType
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
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.AppUtils.installing
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.InternetTools.getJsonString
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MicrogDownloadService: Service() {
|
||||
|
||||
//private var downloadId: Long = 0
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
//registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
downloadMicrog()
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun downloadMicrog() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val url = getJsonString("microg.json", "url", this@MicrogDownloadService)
|
||||
|
||||
//downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService)
|
||||
|
||||
PRDownloader.download(url, getExternalFilesDir("apk")?.path, "microg.apk")
|
||||
.build()
|
||||
.setOnStartOrResumeListener { installing = true }
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.MICROG_DOWNLOADING).putExtra("progress", mProgress.toInt()).putExtra("file", getFileNameFromUrl(url)))
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
val intent = Intent(this@MicrogDownloadService, AppInstaller::class.java)
|
||||
intent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk")
|
||||
intent.putExtra("pkg", "com.mgoogle.android.gms")
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.MICROG_INSTALLING))
|
||||
startService(intent)
|
||||
}
|
||||
|
||||
override fun onError(error: Error?) {
|
||||
installing = false
|
||||
Toast.makeText(this@MicrogDownloadService, getString(R.string.error_downloading, "microG"), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
private val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) {
|
||||
//prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply()
|
||||
//cancelNotif(channel, this@MicrogDownloadService)
|
||||
val bIntent = Intent(this@MicrogDownloadService, AppInstaller::class.java)
|
||||
bIntent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk")
|
||||
bIntent.putExtra("pkg", "com.mgoogle.android.gms")
|
||||
startService(bIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
import com.downloader.PRDownloader
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.core.installer.AppInstaller
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress
|
||||
import com.vanced.manager.utils.AppUtils.installing
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.InternetTools.getJsonString
|
||||
import com.vanced.manager.utils.PackageHelper.install
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object MicrogDownloader {
|
||||
|
||||
//private var downloadId: Long = 0
|
||||
|
||||
fun downloadMicrog(context: Context) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val url = getJsonString("microg.json", "url", context)
|
||||
|
||||
//downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService)
|
||||
|
||||
PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "microg.apk")
|
||||
.build()
|
||||
.setOnStartOrResumeListener {
|
||||
installing = true
|
||||
microgProgress.get()?.setDownloadingFile(getFileNameFromUrl(url))
|
||||
microgProgress.get()?.showDownloadBar = true
|
||||
}
|
||||
.setOnProgressListener { progress ->
|
||||
microgProgress.get()?.setDownloadProgress(progress.currentBytes * 100 / progress.totalBytes)
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
install("microg", "${context.getExternalFilesDir("apk")}/microg.apk", context)
|
||||
microgProgress.showDownloadBar = false
|
||||
microgProgress.showInstallCircle = true
|
||||
}
|
||||
|
||||
override fun onError(error: Error?) {
|
||||
installing = false
|
||||
Toast.makeText(context, getString(R.string.error_downloading, "microG"), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
private val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) {
|
||||
//prefs?.edit()?.putBoolean("isMicrogDownloading", false)?.apply()
|
||||
//cancelNotif(channel, this@MicrogDownloadService)
|
||||
val bIntent = Intent(this@MicrogDownloadService, AppInstaller::class.java)
|
||||
bIntent.putExtra("path", "${getExternalFilesDir("apk")}/microg.apk")
|
||||
bIntent.putExtra("pkg", "com.mgoogle.android.gms")
|
||||
startService(bIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
|
@ -1,63 +1,53 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
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.ui.viewmodels.HomeViewModel.Companion.musicProgress
|
||||
import com.vanced.manager.utils.AppUtils.installing
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.InternetTools.getJsonString
|
||||
import com.vanced.manager.utils.PackageHelper.install
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MusicDownloadService: Service() {
|
||||
object MusicDownloader {
|
||||
|
||||
//private var downloadId: Long = 0
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
//registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
downloadMusic()
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun downloadMusic() {
|
||||
fun downloadMusic(context: Context) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val version = getJsonString("music.json", "version", this@MusicDownloadService)
|
||||
val version = getJsonString("music.json", "version", context)
|
||||
val url = "https://vanced.app/api/v1/music/v$version.apk"
|
||||
|
||||
//downloadId = download(url, "apk", "music.apk", this@MusicDownloadService)
|
||||
|
||||
PRDownloader.download(url, getExternalFilesDir("apk")?.path, "music.apk")
|
||||
.build()
|
||||
.setOnStartOrResumeListener { installing = true }
|
||||
.setOnStartOrResumeListener {
|
||||
installing = true
|
||||
musicProgress.get()?.setDownloadingFile(getFileNameFromUrl(url))
|
||||
musicProgress.get()?.showDownloadBar = true
|
||||
}
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.MUSIC_DOWNLOADING).putExtra("progress", mProgress.toInt()).putExtra("file", getFileNameFromUrl(url)))
|
||||
musicProgress.get()?.setDownloadProgress(progress.currentBytes * 100 / progress.totalBytes)
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
val intent = Intent(this@MusicDownloadService, AppInstaller::class.java)
|
||||
intent.putExtra("path", "${getExternalFilesDir("apk")}/music.apk")
|
||||
intent.putExtra("pkg", "com.vanced.android.apps.youtube.music")
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.MUSIC_INSTALLING))
|
||||
startService(intent)
|
||||
install("music", "${context.getExternalFilesDir("apk")}/music.apk", context)
|
||||
musicProgress.get().showDownloadBar = false
|
||||
musicProgress.get().showInstallCircle = true
|
||||
}
|
||||
|
||||
override fun onError(error: Error?) {
|
||||
installing = false
|
||||
Toast.makeText(this@MusicDownloadService, getString(R.string.error_downloading, "music"), Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, getString(R.string.error_downloading, "Music"), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
})
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
package com.vanced.manager.core.downloader
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.widget.Toast
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.downloader.Error
|
||||
import com.downloader.OnDownloadListener
|
||||
|
@ -15,13 +13,15 @@ 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.ui.viewmodels.HomeViewModel.Companion.vancedProgress
|
||||
import com.vanced.manager.utils.AppUtils.installing
|
||||
import com.vanced.manager.utils.InternetTools
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
|
||||
import com.vanced.manager.utils.InternetTools.getObjectFromJson
|
||||
import com.vanced.manager.utils.PackageHelper.getPkgVerCode
|
||||
import com.vanced.manager.utils.PackageHelper.installVanced
|
||||
import com.vanced.manager.utils.PackageHelper.installvancedRoot
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -30,7 +30,7 @@ import java.io.File
|
|||
import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
|
||||
class VancedDownloadService: Service() {
|
||||
object VancedDownloader {
|
||||
|
||||
private var sha256Val: String? = null
|
||||
|
||||
|
@ -48,16 +48,15 @@ class VancedDownloadService: Service() {
|
|||
//private var downloadId: Long = 0
|
||||
//private var apkType: String = "arch"
|
||||
private var count: Int = 0
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
private var hashUrl = ""
|
||||
|
||||
private val yPkg = "com.google.android.youtube"
|
||||
private val vancedVersionCode by lazy {runBlocking { InternetTools.getJsonInt("vanced.json", "versionCode", applicationContext) }}
|
||||
private val vancedVersion by lazy { runBlocking { getObjectFromJson("$installUrl/vanced.json", "version") }}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
fun downloadVanced(context: Context) {
|
||||
//registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
File(getExternalFilesDir("apks")?.path as String).deleteRecursively()
|
||||
File(context.getExternalFilesDir("apks")?.path as String).deleteRecursively()
|
||||
defPrefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
installUrl = defPrefs.getString("install_url", baseUrl)
|
||||
prefs = getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
|
@ -73,12 +72,11 @@ class VancedDownloadService: Service() {
|
|||
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
|
||||
else -> "armeabi_v7a"
|
||||
}
|
||||
downloadSplits()
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
downloadSplits(context)
|
||||
}
|
||||
|
||||
private fun downloadSplits(
|
||||
private fun downloadSplits(context,
|
||||
context: Context,
|
||||
type: String = "theme"
|
||||
) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
|
@ -95,54 +93,57 @@ class VancedDownloadService: Service() {
|
|||
//apkType = type
|
||||
//downloadId = download(url, "apks", getFileNameFromUrl(url), this@VancedDownloadService)
|
||||
|
||||
PRDownloader
|
||||
.download(url, getExternalFilesDir("apks")?.path, getFileNameFromUrl(url))
|
||||
.build()
|
||||
.setOnStartOrResumeListener { installing = true }
|
||||
.setOnProgressListener { progress ->
|
||||
val mProgress = progress.currentBytes * 100 / progress.totalBytes
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_DOWNLOADING).putExtra("progress", mProgress.toInt()).putExtra("file", getFileNameFromUrl(url)))
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root" && newInstaller == true) {
|
||||
if (ValidateTheme()) {
|
||||
if(downloadStockCheck())
|
||||
downloadSplits("arch")
|
||||
else
|
||||
prepareInstall(variant!!)
|
||||
PRDownloader
|
||||
.download(url, getExternalFilesDir("apks")?.path, getFileNameFromUrl(url))
|
||||
.build()
|
||||
.setOnStartOrResumeListener {
|
||||
installing = true
|
||||
vancedProgress.get()?.setDownloadingFile(getFileNameFromUrl(url))
|
||||
vancedProgress.get()?.showDownloadBar = true
|
||||
}
|
||||
.setOnProgressListener { progress ->
|
||||
vancedProgress.get()?.setDownloadProgress(progress.currentBytes * 100 / progress.totalBytes)
|
||||
}
|
||||
.start(object : OnDownloadListener {
|
||||
override fun onDownloadComplete() {
|
||||
when (type) {
|
||||
"theme" ->
|
||||
if (variant == "root" && newInstaller == true) {
|
||||
if (ValidateTheme()) {
|
||||
if(downloadStockCheck())
|
||||
downloadSplits(context, "arch")
|
||||
else
|
||||
prepareInstall(variant!!)
|
||||
} else
|
||||
downloadSplits(context, "theme")
|
||||
} else
|
||||
downloadSplits("theme")
|
||||
} else
|
||||
downloadSplits("arch")
|
||||
"arch" -> if (variant == "root" && newInstaller == true) downloadSplits("stock") else downloadSplits("lang")
|
||||
"stock" -> downloadSplits("dpi")
|
||||
"dpi" -> downloadSplits("lang")
|
||||
"lang" -> {
|
||||
downloadSplits(context, "arch")
|
||||
"arch" -> if (variant == "root" && newInstaller == true) downloadSplits(context, "stock") else downloadSplits(context, "lang")
|
||||
"stock" -> downloadSplits(context, "dpi")
|
||||
"dpi" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
count++
|
||||
if (count < lang?.count()!!)
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
prepareInstall(variant!!)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
override fun onError(error: Error?) {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
if (count < lang?.count()!!)
|
||||
downloadSplits("lang")
|
||||
downloadSplits(context, "lang")
|
||||
else
|
||||
prepareInstall(variant!!)
|
||||
} else {
|
||||
installing = false
|
||||
Toast.makeText(context, getString(R.string.error_downloading, "Vanced"), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
override fun onError(error: Error?) {
|
||||
if (type == "lang") {
|
||||
count++
|
||||
if (count < lang?.count()!!)
|
||||
downloadSplits("lang")
|
||||
else
|
||||
prepareInstall(variant!!)
|
||||
} else {
|
||||
installing = false
|
||||
Toast.makeText(this@VancedDownloadService, getString(R.string.error_downloading, "Vanced"), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,14 +177,14 @@ class VancedDownloadService: Service() {
|
|||
val lang = prefs?.getString("lang", "en")
|
||||
if (intent?.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) == downloadId) {
|
||||
when (apkType) {
|
||||
"arch" -> downloadSplits("theme")
|
||||
"theme" -> downloadSplits("lang")
|
||||
"arch" -> downloadSplits(context, "theme")
|
||||
"theme" -> downloadSplits(context, "lang")
|
||||
"lang" -> {
|
||||
if (lang == "en") {
|
||||
prepareInstall(variant!!)
|
||||
//cancelNotif(channel, this@VancedDownloadService)
|
||||
} else {
|
||||
downloadSplits("enlang")
|
||||
downloadSplits(context, "enlang")
|
||||
}
|
||||
}
|
||||
"enlang" -> {
|
||||
|
@ -196,12 +197,13 @@ class VancedDownloadService: Service() {
|
|||
}
|
||||
*/
|
||||
|
||||
private fun prepareInstall(variant: String) {
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_INSTALLING))
|
||||
private fun prepareInstall(variant: String, context: Context) {
|
||||
vancedProgress.get()?.showDownloadBar = false
|
||||
vancedProgress.get()?.showInstallCircle = true
|
||||
if (variant == "root")
|
||||
startService(Intent(this, RootSplitInstallerService::class.java))
|
||||
installVancedRoot(context)
|
||||
else
|
||||
startService(Intent(this, SplitInstaller::class.java))
|
||||
installvanced(context)
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
|
@ -1,40 +0,0 @@
|
|||
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 app = if (intent?.getStringExtra("pkg")?.contains("mgoogle") == true) "microg" else "music"
|
||||
val callbackIntent = Intent(applicationContext, AppInstallerService::class.java).putExtra("app", app)
|
||||
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)
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -5,9 +5,8 @@ import android.content.Intent
|
|||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.musicProgress
|
||||
|
||||
class AppInstallerService: Service() {
|
||||
|
||||
|
@ -17,7 +16,6 @@ class AppInstallerService: Service() {
|
|||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
Log.d(TAG, "Requesting user confirmation for installation")
|
||||
localBroadcastManager.sendBroadcast(Intent(if (intent.getStringExtra("app") == "microg") HomeFragment.MICROG_INSTALLING else HomeFragment.MUSIC_INSTALLING))
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
|
@ -28,10 +26,7 @@ class AppInstallerService: Service() {
|
|||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Log.d(TAG, "Installation succeed")
|
||||
with(localBroadcastManager) {
|
||||
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
sendBroadcast(Intent(if (intent.getStringExtra("app") == "microg") HomeFragment.MICROG_INSTALLING else HomeFragment.MUSIC_INSTALLING))
|
||||
}
|
||||
if (intent?.getStringExtra("app") == "microg") microgProgress.showInstallCircle = false else musicProgress.showInstallCircle = false
|
||||
}
|
||||
else -> sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this)
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ import android.content.Intent
|
|||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import kotlinx.coroutines.*
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.fetchData
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AppUninstallerService: Service() {
|
||||
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
val pkgName = intent?.getStringExtra("pkg")
|
||||
when (intent?.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
|
@ -25,18 +25,18 @@ class AppUninstallerService: Service() {
|
|||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
//Delay broadcast until activity (and fragment) show up on screen
|
||||
//Delay broadcast until activity (and fragment) show up on the screen
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
delay(500)
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
fetchData()
|
||||
Log.d("VMpm", "Successfully uninstalled $pkgName")
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_FAILURE -> {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
delay(500)
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
fetchData()
|
||||
Log.d("VMpm", "Failed to uninstall $pkgName")
|
||||
}
|
||||
}
|
||||
|
@ -49,4 +49,4 @@ class AppUninstallerService: Service() {
|
|||
return null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,402 +0,0 @@
|
|||
package com.vanced.manager.core.installer
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
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 androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.topjohnwu.superuser.io.SuFile
|
||||
import com.vanced.manager.BuildConfig
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||
import com.vanced.manager.utils.FileInfo
|
||||
import com.vanced.manager.utils.InternetTools.getJsonInt
|
||||
import com.vanced.manager.utils.PackageHelper
|
||||
import com.vanced.manager.utils.PackageHelper.getPkgVerCode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class RootSplitInstallerService: Service() {
|
||||
|
||||
private val vancedVersionCode by lazy{ runBlocking { getJsonInt("vanced.json","versionCode", applicationContext) }}
|
||||
private val yPkg = "com.google.android.youtube"
|
||||
private val apkInstallPath = "/data/adb/Vanced/"
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
.setTimeout(10)
|
||||
)
|
||||
|
||||
Shell.getShell {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val apkFilesPath = getExternalFilesDir("apks")?.path
|
||||
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }
|
||||
if (fileInfoList != null) {
|
||||
var modApk: FileInfo? = null
|
||||
for (file in fileInfoList) {
|
||||
if (file.name == "dark.apk" || file.name == "black.apk") {
|
||||
modApk = file
|
||||
}
|
||||
}
|
||||
if (modApk != null) {
|
||||
if (getDefaultSharedPreferences(this@RootSplitInstallerService).getBoolean("new_installer", false)) {
|
||||
if (overwriteBase(modApk, fileInfoList, vancedVersionCode)) {
|
||||
with(localBroadcastManager) {
|
||||
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED))
|
||||
}
|
||||
}
|
||||
} else
|
||||
installSplitApkFiles(fileInfoList)
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sendFailure(listOf("ModApk_Missing").toMutableList(), applicationContext)
|
||||
}
|
||||
//installSplitApkFiles(fileInfoList)
|
||||
}
|
||||
else
|
||||
{
|
||||
sendFailure(listOf("Files_Missing_VA").toMutableList(), applicationContext)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>) : Boolean {
|
||||
var sessionId: Int?
|
||||
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)!!)
|
||||
}
|
||||
apkFiles.forEach { apkFile ->
|
||||
if(apkFile.name != "black.apk" && apkFile.name != "dark.apk" && apkFile.name != "hash.json")
|
||||
{
|
||||
Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ")
|
||||
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
|
||||
val process: Process = Runtime.getRuntime().exec(command)
|
||||
val inputPipe = apkFile.getInputStream()
|
||||
try {
|
||||
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
|
||||
} catch (e: Exception) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
process.destroyForcibly()
|
||||
else
|
||||
process.destroy()
|
||||
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
process.waitFor()
|
||||
}
|
||||
}
|
||||
Log.d("AppLog", "committing...")
|
||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||
if (installResult.isSuccess) {
|
||||
return true
|
||||
} else
|
||||
sendFailure(installResult.out, this)
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
//install Vanced
|
||||
private fun overwriteBase(apkFile: FileInfo,baseApkFiles: ArrayList<FileInfo>, versionCode: Int): Boolean {
|
||||
if (checkVersion(versionCode,baseApkFiles)) {
|
||||
val path = getPackageDir()
|
||||
apkFile.file?.let {
|
||||
val apath = it.absolutePath
|
||||
|
||||
setupFolder(apkInstallPath)
|
||||
if(path != null)
|
||||
{
|
||||
val apkFPath = apkInstallPath + "base.apk"
|
||||
if(moveAPK(apath, apkFPath))
|
||||
{
|
||||
if(chConV(apkFPath))
|
||||
{
|
||||
if(setupScript(apkFPath,path))
|
||||
{
|
||||
return linkVanced(apkFPath,path)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setupScript(apkFPath: String, path: String): Boolean
|
||||
{
|
||||
if(Shell.su("""echo "#!/system/bin/sh\nsleep 1m\nmount -o bind $apkFPath $path" > /data/adb/service.d/vanced.sh""").exec().isSuccess)
|
||||
{
|
||||
return Shell.su("chmod 744 /data/adb/service.d/vanced.sh").exec().isSuccess
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun linkVanced(apkFPath: String, path: String): Boolean
|
||||
{
|
||||
Shell.su("am force-stop $yPkg").exec()
|
||||
Thread.sleep(500)
|
||||
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
||||
|
||||
return response.isSuccess
|
||||
}
|
||||
|
||||
private fun setupFolder(apkInstallPath: String): Boolean {
|
||||
return Shell.su("mkdir -p $apkInstallPath").exec().isSuccess
|
||||
}
|
||||
|
||||
//check version and perform action based on result
|
||||
private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList<FileInfo>): Boolean {
|
||||
val path = getPackageDir()
|
||||
if (path != null) {
|
||||
if(path.contains("/data/app/"))
|
||||
{
|
||||
when(getVersionNumber()?.let { compareVersion(it,versionCode) })
|
||||
{
|
||||
1 -> {return fixHigherVer(baseApkFiles) }
|
||||
-1 -> {return fixLowerVer(baseApkFiles) }
|
||||
}
|
||||
return true
|
||||
}
|
||||
else
|
||||
{
|
||||
return fixNoInstall(baseApkFiles)
|
||||
}
|
||||
}
|
||||
return fixNoInstall(baseApkFiles)
|
||||
}
|
||||
|
||||
private fun getPkgInfo(pkg: String): PackageInfo? {
|
||||
return try {
|
||||
packageManager.getPackageInfo(pkg, 0)
|
||||
} catch (e:Exception) {
|
||||
Log.d("VMpm", "Unable to get package info")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareVersion(pkgVerCode: Int, versionCode: Int): Int {
|
||||
return when {
|
||||
pkgVerCode > versionCode -> 1
|
||||
pkgVerCode < versionCode -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
//uninstall current update and install base that works with patch
|
||||
private fun fixHigherVer(apkFiles: ArrayList<FileInfo>) : Boolean {
|
||||
if(PackageHelper.uninstallApk(yPkg, applicationContext)) {
|
||||
return installSplitApkFiles(apkFiles)
|
||||
}
|
||||
sendFailure(listOf("Failed_Uninstall").toMutableList(), this)
|
||||
return false
|
||||
}
|
||||
|
||||
//install newer stock youtube
|
||||
private fun fixLowerVer(apkFiles: ArrayList<FileInfo>): Boolean {
|
||||
return installSplitApkFiles(apkFiles)
|
||||
}
|
||||
|
||||
//install stock youtube since no install was found
|
||||
private fun fixNoInstall(baseApkFiles: ArrayList<FileInfo>): Boolean {
|
||||
return installSplitApkFiles(baseApkFiles)
|
||||
}
|
||||
|
||||
//set chcon to apk_data_file
|
||||
private fun chConV(path: String): Boolean {
|
||||
val response = Shell.su("chcon u:object_r:apk_data_file:s0 $path").exec()
|
||||
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
|
||||
return if (response.isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(response.out, applicationContext)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
//move patch to data/app
|
||||
private fun moveAPK(apkFile: String, path: String) : Boolean {
|
||||
|
||||
val apkinF = SuFile.open(apkFile)
|
||||
val apkoutF = SuFile.open(path)
|
||||
|
||||
if(apkinF.exists()) {
|
||||
try {
|
||||
Shell.su("am force-stop $yPkg").exec()
|
||||
|
||||
//Shell.su("rm -r SuFile.open(path).parent")
|
||||
|
||||
copy(apkinF,apkoutF)
|
||||
Shell.su("chmod 644 $path").exec().isSuccess
|
||||
return if(Shell.su("chown system:system $path").exec().isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(listOf("Chown_Fail").toMutableList(), applicationContext)
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
catch (e: IOException)
|
||||
{
|
||||
sendFailure(listOf("${e.message}").toMutableList(), applicationContext)
|
||||
return false
|
||||
}
|
||||
}
|
||||
else {
|
||||
sendFailure(listOf("IFile_Missing").toMutableList(), applicationContext)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun copy(src: File?, dst: File?) {
|
||||
val cmd = Shell.su("mv ${src!!.absolutePath} ${dst!!.absolutePath}").exec().isSuccess
|
||||
Log.d("ZLog", cmd.toString())
|
||||
}
|
||||
|
||||
//get path of the installed youtube
|
||||
private fun getVPath(): String? {
|
||||
return try {
|
||||
val p = getPkgInfo(yPkg)
|
||||
p?.applicationInfo?.sourceDir
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getVersionNumber(): Int?
|
||||
{
|
||||
try {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
packageManager.getPackageInfo(yPkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
||||
else
|
||||
packageManager.getPackageInfo(yPkg, 0)?.versionCode
|
||||
}
|
||||
catch (e : Exception)
|
||||
{
|
||||
val execRes = Shell.su("dumpsys package com.google.android.youtube | grep versionCode").exec()
|
||||
if(execRes.isSuccess)
|
||||
{
|
||||
val result = execRes.out
|
||||
var version: Int = 0
|
||||
for(line in result)
|
||||
{
|
||||
val versionCode = line.substringAfter("=")
|
||||
val versionCodeFiltered = versionCode.substringBefore(" ")
|
||||
if(version < Integer.valueOf(versionCodeFiltered))
|
||||
{
|
||||
version = Integer.valueOf(versionCodeFiltered)
|
||||
}
|
||||
}
|
||||
return version
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getPackageDir(): String?
|
||||
{
|
||||
return try {
|
||||
val p = getPkgInfo(yPkg)
|
||||
p?.applicationInfo?.sourceDir
|
||||
} catch (e: Exception) {
|
||||
val execRes = Shell.su("dumpsys package com.google.android.youtube | grep codePath").exec()
|
||||
if(execRes.isSuccess)
|
||||
{
|
||||
val result = execRes.out
|
||||
for (line in result)
|
||||
{
|
||||
if(line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
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_NOT_STICKY
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun installSplitApk(context: Context): Int {
|
||||
val apkFolderPath = context.getExternalFilesDir("apks")?.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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,18 +5,15 @@ import android.content.Intent
|
|||
import android.content.pm.PackageInstaller
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.vancedProgress
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.fetchData
|
||||
import com.vanced.manager.utils.AppUtils.sendFailure
|
||||
|
||||
class SplitInstallerService: Service() {
|
||||
|
||||
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(this) }
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
localBroadcastManager.sendBroadcast(Intent(HomeFragment.VANCED_INSTALLING))
|
||||
Log.d(TAG, "Requesting user confirmation for installation")
|
||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
||||
confirmationIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
@ -27,10 +24,8 @@ class SplitInstallerService: Service() {
|
|||
}
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
Log.d(TAG, "Installation succeed")
|
||||
with(localBroadcastManager) {
|
||||
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED))
|
||||
}
|
||||
vancedProgress.showInstallCircle = false
|
||||
fetchData()
|
||||
}
|
||||
else -> sendFailure(intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999), this)
|
||||
|
||||
|
@ -47,4 +42,4 @@ class SplitInstallerService: Service() {
|
|||
const val TAG = "VMInstall"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,20 +3,14 @@ package com.vanced.manager.model
|
|||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getJsonInt
|
||||
import com.vanced.manager.utils.InternetTools.getJsonString
|
||||
import com.vanced.manager.utils.InternetTools.getObjectFromJson
|
||||
import com.vanced.manager.utils.PackageHelper.isPackageInstalled
|
||||
import com.vanced.manager.R
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
open class DataModel(
|
||||
private val jsonName: String,
|
||||
|
|
27
app/src/main/java/com/vanced/manager/model/ProgressModel.kt
Normal file
27
app/src/main/java/com/vanced/manager/model/ProgressModel.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
package com.vanced.manager.model
|
||||
|
||||
open class ProgressModel {
|
||||
|
||||
private var downloadProgres = 0
|
||||
private var downloadingFile = ""
|
||||
|
||||
var showInstallCircle = false
|
||||
var showDownloadBar = false
|
||||
|
||||
open fun getDownloadProgress(): Int {
|
||||
return downloadProgress
|
||||
}
|
||||
|
||||
open fun setDownloadProgress(progresss: Int) {
|
||||
downloadProgress = progress
|
||||
}
|
||||
|
||||
open fun getDownloadingFile(): String {
|
||||
return downloadProgress
|
||||
}
|
||||
|
||||
open fun setDownloadingFile(file: String) {
|
||||
downloadingFile = file
|
||||
}
|
||||
|
||||
}
|
|
@ -4,17 +4,16 @@ import android.content.Context
|
|||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.crowdin.platform.LoadingStateListener
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.vanced.manager.adapter.SectionVariantAdapter
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.SectionVariantAdapter
|
||||
import com.vanced.manager.databinding.ActivityMainBinding
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer
|
||||
import com.vanced.manager.ui.fragments.UpdateCheckFragment
|
||||
|
|
|
@ -121,20 +121,21 @@ object DialogContainer {
|
|||
}
|
||||
}
|
||||
|
||||
fun launchVanced(activity: Activity) {
|
||||
fun launchVanced(context: Context) {
|
||||
val intent = Intent()
|
||||
intent.component =
|
||||
if (PreferenceManager.getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot") == "root")
|
||||
if (PreferenceManager.getDefaultSharedPreferences(context).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")
|
||||
MaterialAlertDialogBuilder(activity).apply {
|
||||
setTitle(activity.getString(R.string.success))
|
||||
setMessage(activity.getString(R.string.vanced_installed))
|
||||
setPositiveButton(activity.getString(R.string.launch)) { _, _ ->
|
||||
startActivity(activity, intent, null)
|
||||
|
||||
MaterialAlertDialogBuilder(context).apply {
|
||||
setTitle(context.getString(R.string.success))
|
||||
setMessage(context.getString(R.string.vanced_installed))
|
||||
setPositiveButton(context.getString(R.string.launch)) { _, _ ->
|
||||
startActivity(context, intent, null)
|
||||
}
|
||||
setNegativeButton(activity.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
setNegativeButton(context.getString(R.string.close)) { dialog, _ -> dialog.dismiss() }
|
||||
create()
|
||||
show()
|
||||
}
|
||||
|
|
|
@ -11,21 +11,14 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.viewModels
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.adapter.*
|
||||
import com.vanced.manager.core.downloader.MicrogDownloadService
|
||||
import com.vanced.manager.core.downloader.MusicDownloadService
|
||||
import com.vanced.manager.core.downloader.VancedDownloadService
|
||||
import com.vanced.manager.adapter.SectionPageAdapter
|
||||
import com.vanced.manager.adapter.SectionPageRootAdapter
|
||||
import com.vanced.manager.databinding.FragmentHomeBinding
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.launchVanced
|
||||
import com.vanced.manager.ui.dialogs.DialogContainer.launchMusic
|
||||
import com.vanced.manager.ui.viewmodels.HomeViewModel
|
||||
import com.vanced.manager.utils.AppUtils.installing
|
||||
import com.vanced.manager.utils.PackageHelper
|
||||
|
@ -55,12 +48,6 @@ open class HomeFragment : Fragment(), View.OnClickListener {
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
with(binding) {
|
||||
includeVancedLayout.vancedInstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeVancedLayout.vancedUninstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeMusicLayout.musicInstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeMusicLayout.musicUninstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeMicrogLayout.microgInstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeMicrogLayout.microgUninstallbtn.setOnClickListener(this@HomeFragment)
|
||||
includeChangelogsLayout.changelogButton.setOnClickListener(this@HomeFragment)
|
||||
|
||||
includeVancedLayout.vancedCard.setOnLongClickListener {
|
||||
|
@ -107,83 +94,7 @@ open class HomeFragment : Fragment(), View.OnClickListener {
|
|||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val prefs = requireActivity().getSharedPreferences("installPrefs", Context.MODE_PRIVATE)
|
||||
val vancedPkgName =
|
||||
if (variant == "root")
|
||||
"com.google.android.youtube"
|
||||
else
|
||||
"com.vanced.android.youtube"
|
||||
|
||||
when (v?.id) {
|
||||
R.id.vanced_installbtn -> {
|
||||
if (!installing) {
|
||||
if (!viewModel.fetching.get()) {
|
||||
if (variant == "nonroot" && !viewModel.microg.get()?.isAppInstalled()!!) {
|
||||
Snackbar.make(
|
||||
binding.homeRefresh,
|
||||
R.string.no_microg,
|
||||
Snackbar.LENGTH_LONG
|
||||
).setAction(R.string.install) {
|
||||
requireActivity().startService(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
MicrogDownloadService::class.java
|
||||
)
|
||||
)
|
||||
}.show()
|
||||
} else {
|
||||
if (prefs?.getBoolean("valuesModified", false)!!) {
|
||||
requireActivity().startService(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
VancedDownloadService::class.java
|
||||
)
|
||||
)
|
||||
} else {
|
||||
view?.findNavController()?.navigate(R.id.toInstallThemeFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
Toast.makeText(requireActivity(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
R.id.music_installbtn -> {
|
||||
if (!installing) {
|
||||
if (!viewModel.fetching.get()) {
|
||||
if (!viewModel.microg.get()?.isAppInstalled()!!) {
|
||||
Snackbar.make(
|
||||
binding.homeRefresh,
|
||||
R.string.no_microg,
|
||||
Snackbar.LENGTH_LONG
|
||||
).setAction(R.string.install) {
|
||||
requireActivity().startService(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
MicrogDownloadService::class.java
|
||||
)
|
||||
)
|
||||
}.show()
|
||||
} else {
|
||||
requireActivity().startService(
|
||||
Intent(
|
||||
requireActivity(),
|
||||
MusicDownloadService::class.java
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else
|
||||
Toast.makeText(requireActivity(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
R.id.microg_installbtn -> {
|
||||
if (!installing)
|
||||
requireActivity().startService(Intent(requireActivity(), MicrogDownloadService::class.java))
|
||||
else
|
||||
Toast.makeText(requireActivity(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
R.id.vanced_uninstallbtn -> PackageHelper.uninstallApk(vancedPkgName, requireActivity())
|
||||
R.id.music_uninstallbtn -> PackageHelper.uninstallApk("com.vanced.android.apps.youtube.music", requireActivity())
|
||||
R.id.microg_uninstallbtn -> PackageHelper.uninstallApk("com.mgoogle.android.gms", requireActivity())
|
||||
R.id.changelog_button -> cardExpandCollapse()
|
||||
}
|
||||
}
|
||||
|
@ -219,65 +130,6 @@ open class HomeFragment : Fragment(), View.OnClickListener {
|
|||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
VANCED_DOWNLOADING -> {
|
||||
with(binding.includeVancedLayout) {
|
||||
vancedDownloading.visibility = View.VISIBLE
|
||||
vancedDownloading.progress = intent.getIntExtra("progress", 0)
|
||||
vancedDownloadingTxt.visibility = View.VISIBLE
|
||||
vancedDownloadingTxt.text = requireActivity().getString(R.string.downloading_file, intent.getStringExtra("file"))
|
||||
}
|
||||
}
|
||||
MUSIC_DOWNLOADING -> {
|
||||
with(binding.includeMusicLayout) {
|
||||
musicDownloading.visibility = View.VISIBLE
|
||||
musicDownloading.progress = intent.getIntExtra("progress", 0)
|
||||
musicDownloadingTxt.visibility = View.VISIBLE
|
||||
musicDownloadingTxt.text = requireActivity().getString(R.string.downloading_file, "music.apk")
|
||||
}
|
||||
}
|
||||
MICROG_DOWNLOADING -> {
|
||||
with(binding.includeMicrogLayout) {
|
||||
microgDownloading.visibility = View.VISIBLE
|
||||
microgDownloading.progress = intent.getIntExtra("progress", 0)
|
||||
microgDownloadingTxt.visibility = View.VISIBLE
|
||||
microgDownloadingTxt.text = requireActivity().getString(R.string.downloading_file, "microg.apk")
|
||||
}
|
||||
}
|
||||
VANCED_INSTALLING -> {
|
||||
with (binding.includeVancedLayout) {
|
||||
vancedDownloading.visibility = View.GONE
|
||||
vancedDownloadingTxt.visibility = View.GONE
|
||||
vancedInstalling.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
MUSIC_INSTALLING -> {
|
||||
with (binding.includeMusicLayout) {
|
||||
musicDownloading.visibility = View.GONE
|
||||
musicDownloadingTxt.visibility = View.GONE
|
||||
musicInstalling.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
MICROG_INSTALLING -> {
|
||||
with (binding.includeMicrogLayout) {
|
||||
microgDownloading.visibility = View.GONE
|
||||
microgDownloadingTxt.visibility = View.GONE
|
||||
microgInstalling.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
VANCED_INSTALLED -> {
|
||||
binding.includeVancedLayout.vancedInstalling.visibility = View.GONE
|
||||
launchVanced(requireActivity())
|
||||
installing = false
|
||||
}
|
||||
MUSIC_INSTALLED -> {
|
||||
binding.includeMusicLayout.musicInstalling.visibility = View.GONE
|
||||
launchMusic(requireActivity())
|
||||
installing = false
|
||||
}
|
||||
MICROG_INSTALLED -> {
|
||||
binding.includeMicrogLayout.microgInstalling.visibility = View.GONE
|
||||
installing = false
|
||||
}
|
||||
INSTALL_FAILED -> {
|
||||
with(binding) {
|
||||
includeMicrogLayout.microgInstalling.visibility = View.GONE
|
||||
|
@ -287,26 +139,12 @@ open class HomeFragment : Fragment(), View.OnClickListener {
|
|||
installAlertBuilder(intent.getStringExtra("errorMsg") as String, requireActivity())
|
||||
installing = false
|
||||
}
|
||||
REFRESH_HOME -> {
|
||||
Log.d("VMRefresh", "Refreshing home page")
|
||||
viewModel.fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerReceivers() {
|
||||
val intentFilter = IntentFilter()
|
||||
intentFilter.addAction(VANCED_DOWNLOADING)
|
||||
intentFilter.addAction(MUSIC_DOWNLOADING)
|
||||
intentFilter.addAction(MICROG_DOWNLOADING)
|
||||
intentFilter.addAction(VANCED_INSTALLING)
|
||||
intentFilter.addAction(MUSIC_INSTALLING)
|
||||
intentFilter.addAction(MICROG_INSTALLING)
|
||||
intentFilter.addAction(VANCED_INSTALLED)
|
||||
intentFilter.addAction(MUSIC_INSTALLED)
|
||||
intentFilter.addAction(MICROG_INSTALLED)
|
||||
intentFilter.addAction(REFRESH_HOME)
|
||||
intentFilter.addAction(INSTALL_FAILED)
|
||||
localBroadcastManager.registerReceiver(broadcastReceiver, intentFilter)
|
||||
}
|
||||
|
@ -316,18 +154,8 @@ open class HomeFragment : Fragment(), View.OnClickListener {
|
|||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VANCED_DOWNLOADING = "vanced_downloading"
|
||||
const val MUSIC_DOWNLOADING = "music_downloading"
|
||||
const val MICROG_DOWNLOADING = "microg_downloading"
|
||||
const val VANCED_INSTALLING = "vanced_installing"
|
||||
const val MUSIC_INSTALLING = "music_installing"
|
||||
const val MICROG_INSTALLING = "microg_installing"
|
||||
const val VANCED_INSTALLED = "vanced_installed"
|
||||
const val MUSIC_INSTALLED = "music_installed"
|
||||
const val MICROG_INSTALLED = "microg_installed"
|
||||
companion object {
|
||||
const val INSTALL_FAILED = "install_failed"
|
||||
const val REFRESH_HOME = "refresh_home"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,19 +6,17 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.LinearLayout
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.vanced.manager.databinding.FragmentMainBinding
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.databinding.FragmentMainBinding
|
||||
|
||||
class MainFragment : Fragment() {
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ import com.vanced.manager.R
|
|||
import com.vanced.manager.core.downloader.VancedDownloadService
|
||||
import com.vanced.manager.utils.InternetTools.baseUrl
|
||||
import com.vanced.manager.utils.InternetTools.getArrayFromJson
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.*
|
||||
|
||||
class VancedLanguageSelectionFragment : Fragment() {
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.findNavController
|
||||
|
|
|
@ -3,48 +3,66 @@ package com.vanced.manager.ui.viewmodels
|
|||
import android.app.Application
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import androidx.databinding.ObservableField
|
||||
import androidx.databinding.ObservableBoolean
|
||||
import androidx.databinding.ObservableField
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.navigation.Navigation.findNavController
|
||||
import androidx.preference.PreferenceManager.getDefaultSharedPreferences
|
||||
import com.crowdin.platform.Crowdin
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.vanced.manager.core.downloader.MicrogDownloader.downloadMicrog
|
||||
import com.vanced.manager.core.downloader.MusicDownloader.downloadMusic
|
||||
import com.vanced.manager.core.downloader.VancedDownloader.downloadVanced
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.model.DataModel
|
||||
import com.vanced.manager.model.ProgressModel
|
||||
import com.vanced.manager.ui.MainActivity
|
||||
import com.vanced.manager.utils.AppUtils.installing
|
||||
import com.vanced.manager.utils.InternetTools
|
||||
import com.vanced.manager.utils.PackageHelper.uninstallApk
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
open class HomeViewModel(application: Application): AndroidViewModel(application) {
|
||||
|
||||
val app = application
|
||||
|
||||
//val variant = getDefaultSharedPreferences(application).getString("vanced_variant", "nonroot")
|
||||
var variant = "nonroot"
|
||||
|
||||
val fetching = ObservableBoolean()
|
||||
|
||||
|
||||
val vanced = ObservableField<DataModel>()
|
||||
val microg = ObservableField<DataModel>()
|
||||
val music = ObservableField<DataModel>()
|
||||
val manager = ObservableField<DataModel>()
|
||||
|
||||
val fetching = ObservableBoolean()
|
||||
|
||||
fun fetchData() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
fetching.set(true)
|
||||
Crowdin.forceUpdate(getApplication())
|
||||
vanced.set(DataModel("vanced", variant, getApplication()))
|
||||
microg.set(DataModel("microg", context = getApplication()))
|
||||
music.set(DataModel("music", context = getApplication()))
|
||||
manager.set(DataModel("manager", context = getApplication()))
|
||||
vanced.set(DataModel("vanced", variant, app))
|
||||
microg.set(DataModel("microg", context = app))
|
||||
music.set(DataModel("music", context = app))
|
||||
manager.set(DataModel("manager", context = app))
|
||||
fetching.set(false)
|
||||
}
|
||||
}
|
||||
|
||||
private val microgSnackbar = Snackbar.make(, R.string.no_microg, Snackbar.LENGTH_LONG).setAction(R.string.install) { downloadMicrog(getApplication()) }
|
||||
|
||||
private val vancedPkgName =
|
||||
if (variant == "root")
|
||||
"com.google.android.youtube"
|
||||
else
|
||||
"com.vanced.android.youtube"
|
||||
|
||||
fun openMicrogSettings() {
|
||||
try {
|
||||
|
@ -60,10 +78,9 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
|
|||
}
|
||||
}
|
||||
|
||||
fun openUrl(Url: String) {
|
||||
val customTabPrefs = getDefaultSharedPreferences(getApplication()).getBoolean("use_customtabs", true)
|
||||
fun openUrl(url: String) {
|
||||
val color: Int =
|
||||
when (Url) {
|
||||
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
|
||||
|
@ -72,18 +89,55 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
|
|||
"https://brave.com/van874" -> R.color.Brave
|
||||
else -> R.color.Vanced
|
||||
}
|
||||
|
||||
if (customTabPrefs) {
|
||||
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))
|
||||
} else {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(Url))
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(getApplication(), intent , null)
|
||||
}
|
||||
|
||||
InternetTools.openUrl(url, color, getApplication())
|
||||
}
|
||||
|
||||
fun installVanced() {
|
||||
if (!installing) {
|
||||
if (!fetching.get()) {
|
||||
if (variant == "nonroot" && !microg.get()?.isAppInstalled()!!) {
|
||||
microgSnackbar.show()
|
||||
} else {
|
||||
if (app.getSharedPreferences("installPrefs", Context.MODE_PRIVATE).getBoolean("valuesModified", false)!!) {
|
||||
downloadVanced()
|
||||
} else {
|
||||
findNavController(MainActivity, R.id.nav_host).navigate(R.id.toInstallThemeFragment)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun installMusic() {
|
||||
if (!installing) {
|
||||
if (!fetching.get()) {
|
||||
if (!microg.get()?.isAppInstalled()!!) {
|
||||
microgSnackbar.show()
|
||||
} else {
|
||||
downloadMusic(getApplication())
|
||||
}
|
||||
}
|
||||
} else
|
||||
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun installMicrog() {
|
||||
if (!installing)
|
||||
downloadMicrog(getApplication())
|
||||
else
|
||||
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
fun uninstallVanced() = uninstallApk(vancedPkgName, app)
|
||||
fun uninstallMusic() = uninstallApk("com.vanced.android.apps.youtube.music", app)
|
||||
fun uninstallMicrog() = uninstallApk("com.mgoogle.android.gms", app)
|
||||
|
||||
companion object {
|
||||
val vancedProgress = ObservableField<ProgressModel>()
|
||||
val musicProgress = ObservableField<ProgressModel>()
|
||||
val microgProgress = ObservableField<ProgressModel>()
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
|
@ -6,7 +6,10 @@ import android.content.pm.PackageInstaller
|
|||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.vanced.manager.R
|
||||
import com.vanced.manager.ui.fragments.HomeFragment
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object AppUtils {
|
||||
|
||||
|
|
|
@ -19,4 +19,4 @@ object DownloadHelper {
|
|||
return downloadManager.enqueue(request)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ import com.beust.klaxon.Klaxon
|
|||
import com.beust.klaxon.Parser
|
||||
import com.github.kittinunf.fuel.coroutines.awaitString
|
||||
import com.github.kittinunf.fuel.httpGet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
object JsonHelper {
|
||||
|
||||
|
|
|
@ -6,10 +6,17 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import com.vanced.manager.core.installer.AppUninstallerService
|
||||
import java.lang.Exception
|
||||
import com.vanced.manager.core.installer.AppInstallerService
|
||||
import com.vanced.manager.core.installer.SplitInstallerService
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
|
||||
object PackageHelper {
|
||||
|
||||
private const val yPkg = "com.google.android.youtube"
|
||||
private const val apkInstallPath = "/data/adb/Vanced/"
|
||||
|
||||
fun isPackageInstalled(packageName: String, packageManager: PackageManager): Boolean {
|
||||
return try {
|
||||
|
@ -37,18 +44,18 @@ object PackageHelper {
|
|||
}
|
||||
|
||||
fun uninstallApk(pkg: String, activity: Activity) {
|
||||
val callbackIntent = Intent(activity.applicationContext, AppUninstallerService::class.java)
|
||||
val callbackIntent = Intent(activity.context, AppUninstallerService::class.java)
|
||||
callbackIntent.putExtra("pkg", pkg)
|
||||
val pendingIntent = PendingIntent.getService(activity.applicationContext, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(activity.context, 0, callbackIntent, 0)
|
||||
activity.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
}
|
||||
|
||||
fun uninstallApk(pkg: String, applicationContext: Context): Boolean {
|
||||
val callbackIntent = Intent(applicationContext, AppUninstallerService::class.java)
|
||||
fun uninstallApk(pkg: String, context: Context): Boolean {
|
||||
val callbackIntent = Intent(context, AppUninstallerService::class.java)
|
||||
callbackIntent.putExtra("pkg", pkg)
|
||||
val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
try {
|
||||
applicationContext.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
context.packageManager.packageInstaller.uninstall(pkg, pendingIntent.intentSender)
|
||||
return true
|
||||
}
|
||||
catch (e: Exception)
|
||||
|
@ -57,4 +64,477 @@ object PackageHelper {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fun install(app: String, path: String, context: Context){
|
||||
val callbackIntent = Intent(context, AppInstallerService::class.java).putExtra("app", app)
|
||||
val pendingIntent = PendingIntent.getService(context, 0, callbackIntent, 0)
|
||||
val packageInstaller = context.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(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 installVanced(context: Context): Int {
|
||||
val apkFolderPath = context.getExternalFilesDir("apks")?.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.context, SplitInstallerService::class.java)
|
||||
val pendingIntent = PendingIntent.getService(context.context, 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()
|
||||
}
|
||||
}
|
||||
|
||||
fun insallVancedRoot(context: Context) {
|
||||
Shell.enableVerboseLogging = BuildConfig.DEBUG
|
||||
Shell.setDefaultBuilder(
|
||||
Shell.Builder.create()
|
||||
.setFlags(Shell.FLAG_REDIRECT_STDERR)
|
||||
.setTimeout(10)
|
||||
)
|
||||
|
||||
Shell.getShell {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val apkFilesPath = getExternalFilesDir("apks")?.path
|
||||
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }
|
||||
if (fileInfoList != null) {
|
||||
var modApk: FileInfo? = null
|
||||
for (file in fileInfoList) {
|
||||
if (file.name == "dark.apk" || file.name == "black.apk") {
|
||||
modApk = file
|
||||
}
|
||||
}
|
||||
if (modApk != null) {
|
||||
if (getDefaultSharedPreferences(this@RootSplitInstallerService).getBoolean("new_installer", false)) {
|
||||
if (overwriteBase(modApk, fileInfoList, vancedVersionCode)) {
|
||||
with(localBroadcastManager) {
|
||||
sendBroadcast(Intent(HomeFragment.REFRESH_HOME))
|
||||
sendBroadcast(Intent(HomeFragment.VANCED_INSTALLED))
|
||||
}
|
||||
}
|
||||
} else
|
||||
installSplitApkFiles(fileInfoList)
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sendFailure(listOf("ModApk_Missing").toMutableList(), context)
|
||||
}
|
||||
//installSplitApkFiles(fileInfoList)
|
||||
}
|
||||
else
|
||||
{
|
||||
sendFailure(listOf("Files_Missing_VA").toMutableList(), context)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun installSplitApkFiles(apkFiles: ArrayList<FileInfo>) : Boolean {
|
||||
var sessionId: Int?
|
||||
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)!!)
|
||||
}
|
||||
apkFiles.forEach { apkFile ->
|
||||
if(apkFile.name != "black.apk" && apkFile.name != "dark.apk" && apkFile.name != "hash.json")
|
||||
{
|
||||
Log.d("AppLog", "installing APK : ${apkFile.name} ${apkFile.fileSize} ")
|
||||
val command = arrayOf("su", "-c", "pm", "install-write", "-S", "${apkFile.fileSize}", "$sessionId", apkFile.name)
|
||||
val process: Process = Runtime.getRuntime().exec(command)
|
||||
val inputPipe = apkFile.getInputStream()
|
||||
try {
|
||||
process.outputStream.use { outputStream -> inputPipe.copyTo(outputStream) }
|
||||
} catch (e: Exception) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
process.destroyForcibly()
|
||||
else
|
||||
process.destroy()
|
||||
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
process.waitFor()
|
||||
}
|
||||
}
|
||||
Log.d("AppLog", "committing...")
|
||||
val installResult = Shell.su("pm install-commit $sessionId").exec()
|
||||
if (installResult.isSuccess) {
|
||||
return true
|
||||
} else
|
||||
sendFailure(installResult.out, this)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun SimpleDateFormat.tryParse(str: String) = try {
|
||||
parse(str) != null
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
private fun getFileInfoList(splitApkPath: String): ArrayList<FileInfo> {
|
||||
val parentFile = File(splitApkPath)
|
||||
val result = ArrayList<FileInfo>()
|
||||
|
||||
if (parentFile.exists() && parentFile.canRead()) {
|
||||
val listFiles = parentFile.listFiles() ?: return ArrayList()
|
||||
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
|
||||
}
|
||||
|
||||
//install Vanced
|
||||
private fun overwriteBase(apkFile: FileInfo,baseApkFiles: ArrayList<FileInfo>, versionCode: Int): Boolean {
|
||||
if (checkVersion(versionCode,baseApkFiles)) {
|
||||
val path = getPackageDir()
|
||||
apkFile.file?.let {
|
||||
val apath = it.absolutePath
|
||||
|
||||
setupFolder(apkInstallPath)
|
||||
if(path != null)
|
||||
{
|
||||
val apkFPath = apkInstallPath + "base.apk"
|
||||
if(moveAPK(apath, apkFPath))
|
||||
{
|
||||
if(chConV(apkFPath))
|
||||
{
|
||||
if(setupScript(apkFPath,path))
|
||||
{
|
||||
return linkVanced(apkFPath,path)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setupScript(apkFPath: String, path: String): Boolean
|
||||
{
|
||||
if(Shell.su("""echo "#!/system/bin/sh\nsleep 1m\nmount -o bind $apkFPath $path" > /data/adb/service.d/vanced.sh""").exec().isSuccess)
|
||||
{
|
||||
return Shell.su("chmod 744 /data/adb/service.d/vanced.sh").exec().isSuccess
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun linkVanced(apkFPath: String, path: String): Boolean
|
||||
{
|
||||
Shell.su("am force-stop $yPkg").exec()
|
||||
Thread.sleep(500)
|
||||
val response = Shell.su("""su -mm -c "mount -o bind $apkFPath $path"""").exec()
|
||||
|
||||
return response.isSuccess
|
||||
}
|
||||
|
||||
private fun setupFolder(apkInstallPath: String): Boolean {
|
||||
return Shell.su("mkdir -p $apkInstallPath").exec().isSuccess
|
||||
}
|
||||
|
||||
//check version and perform action based on result
|
||||
private fun checkVersion(versionCode: Int, baseApkFiles: ArrayList<FileInfo>): Boolean {
|
||||
val path = getPackageDir()
|
||||
if (path != null) {
|
||||
if(path.contains("/data/app/"))
|
||||
{
|
||||
when(getVersionNumber()?.let { compareVersion(it,versionCode) })
|
||||
{
|
||||
1 -> {return fixHigherVer(baseApkFiles) }
|
||||
-1 -> {return fixLowerVer(baseApkFiles) }
|
||||
}
|
||||
return true
|
||||
}
|
||||
else
|
||||
{
|
||||
return fixNoInstall(baseApkFiles)
|
||||
}
|
||||
}
|
||||
return fixNoInstall(baseApkFiles)
|
||||
}
|
||||
|
||||
private fun getPkgInfo(pkg: String): PackageInfo? {
|
||||
return try {
|
||||
packageManager.getPackageInfo(pkg, 0)
|
||||
} catch (e:Exception) {
|
||||
Log.d("VMpm", "Unable to get package info")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun compareVersion(pkgVerCode: Int, versionCode: Int): Int {
|
||||
return when {
|
||||
pkgVerCode > versionCode -> 1
|
||||
pkgVerCode < versionCode -> -1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
//uninstall current update and install base that works with patch
|
||||
private fun fixHigherVer(apkFiles: ArrayList<FileInfo>) : Boolean {
|
||||
if(PackageHelper.uninstallApk(yPkg, context)) {
|
||||
return installSplitApkFiles(apkFiles)
|
||||
}
|
||||
sendFailure(listOf("Failed_Uninstall").toMutableList(), this)
|
||||
return false
|
||||
}
|
||||
|
||||
//install newer stock youtube
|
||||
private fun fixLowerVer(apkFiles: ArrayList<FileInfo>): Boolean {
|
||||
return installSplitApkFiles(apkFiles)
|
||||
}
|
||||
|
||||
//install stock youtube since no install was found
|
||||
private fun fixNoInstall(baseApkFiles: ArrayList<FileInfo>): Boolean {
|
||||
return installSplitApkFiles(baseApkFiles)
|
||||
}
|
||||
|
||||
//set chcon to apk_data_file
|
||||
private fun chConV(path: String): Boolean {
|
||||
val response = Shell.su("chcon u:object_r:apk_data_file:s0 $path").exec()
|
||||
//val response = Shell.su("chcon -R u:object_r:system_file:s0 $path").exec()
|
||||
return if (response.isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(response.out, context)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
//move patch to data/app
|
||||
private fun moveAPK(apkFile: String, path: String) : Boolean {
|
||||
val apkinF = SuFile.open(apkFile)
|
||||
val apkoutF = SuFile.open(path)
|
||||
|
||||
if(apkinF.exists()) {
|
||||
try {
|
||||
Shell.su("am force-stop $yPkg").exec()
|
||||
|
||||
//Shell.su("rm -r SuFile.open(path).parent")
|
||||
|
||||
copy(apkinF,apkoutF)
|
||||
Shell.su("chmod 644 $path").exec().isSuccess
|
||||
return if(Shell.su("chown system:system $path").exec().isSuccess) {
|
||||
true
|
||||
} else {
|
||||
sendFailure(listOf("Chown_Fail").toMutableList(), context)
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
catch (e: IOException)
|
||||
{
|
||||
sendFailure(listOf("${e.message}").toMutableList(), context)
|
||||
return false
|
||||
}
|
||||
}
|
||||
else {
|
||||
sendFailure(listOf("IFile_Missing").toMutableList(), context)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Throws(IOException::class)
|
||||
fun copy(src: File?, dst: File?) {
|
||||
val cmd = Shell.su("mv ${src!!.absolutePath} ${dst!!.absolutePath}").exec().isSuccess
|
||||
Log.d("ZLog", cmd.toString())
|
||||
}
|
||||
|
||||
//get path of the installed youtube
|
||||
private fun getVPath(): String? {
|
||||
return try {
|
||||
val p = getPkgInfo(yPkg)
|
||||
p?.applicationInfo?.sourceDir
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun getVersionNumber(): Int?
|
||||
{
|
||||
try {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
packageManager.getPackageInfo(yPkg, 0)?.longVersionCode?.and(0xFFFFFFFF)?.toInt()
|
||||
else
|
||||
packageManager.getPackageInfo(yPkg, 0)?.versionCode
|
||||
}
|
||||
catch (e : Exception)
|
||||
{
|
||||
val execRes = Shell.su("dumpsys package com.google.android.youtube | grep versionCode").exec()
|
||||
if(execRes.isSuccess)
|
||||
{
|
||||
val result = execRes.out
|
||||
var version: Int = 0
|
||||
for(line in result)
|
||||
{
|
||||
val versionCode = line.substringAfter("=")
|
||||
val versionCodeFiltered = versionCode.substringBefore(" ")
|
||||
if(version < Integer.valueOf(versionCodeFiltered))
|
||||
{
|
||||
version = Integer.valueOf(versionCodeFiltered)
|
||||
}
|
||||
}
|
||||
return version
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun getPackageDir(): String?
|
||||
{
|
||||
return try {
|
||||
val p = getPkgInfo(yPkg)
|
||||
p?.applicationInfo?.sourceDir
|
||||
} catch (e: Exception) {
|
||||
val execRes = Shell.su("dumpsys package com.google.android.youtube | grep codePath").exec()
|
||||
if(execRes.isSuccess)
|
||||
{
|
||||
val result = execRes.out
|
||||
for (line in result)
|
||||
{
|
||||
if(line.contains("data/app")) "${line.substringAfter("=")}/base.apk"
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
bind:refreshing="@{viewModel.fetching}"
|
||||
bind:onRefreshListener="@{()-> viewModel.fetchData()}">
|
||||
bind:onRefreshListener="@{()-> viewModel.Companion.fetchData()}">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.vanced.manager.ui.viewmodels.HomeViewModel" />
|
||||
type="com.microg.manager.ui.viewmodels.HomeViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
|||
android:id="@+id/microg_installbtn"
|
||||
style="@style/ButtonStyle"
|
||||
android:text="@{viewModel.microg.buttonTxt}"
|
||||
android:onClick="@{()-> viewModel.installMicrog()}"
|
||||
app:icon="@{viewModel.microg.buttonIcon}"
|
||||
app:layout_constraintBottom_toTopOf="@id/microg_uninstallbtn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -112,6 +113,7 @@
|
|||
<ImageView
|
||||
android:id="@+id/microg_uninstallbtn"
|
||||
style="@style/ClickableImageWidget.Red"
|
||||
android:onClick="@{()-> viewModel.uninstallMicrog()}"
|
||||
android:visibility="@{viewModel.microg.appInstalled ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -124,7 +126,8 @@
|
|||
android:id="@+id/microg_downloading_txt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.microgProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:text="@{viewModel.Companion.microgProgress.downloadingFile}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
|
@ -132,14 +135,15 @@
|
|||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.microgProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:progress="@{viewModel.Companion.microgProgress.downloadProgress}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/microg_installing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.microgProgress.showInstallCircle ? View.VISIBLE : View.GONE}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.vanced.manager.ui.viewmodels.HomeViewModel" />
|
||||
type="com.music.manager.ui.viewmodels.HomeViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
|
@ -48,6 +48,7 @@
|
|||
android:id="@+id/music_installbtn"
|
||||
style="@style/ButtonStyle"
|
||||
android:text="@{viewModel.music.buttonTxt}"
|
||||
android:onClick="@{()-> viewModel.installMusic()}"
|
||||
app:icon="@{viewModel.music.buttonIcon}"
|
||||
app:layout_constraintBottom_toTopOf="@id/music_uninstallbtn"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -102,6 +103,7 @@
|
|||
android:id="@+id/music_uninstallbtn"
|
||||
style="@style/ClickableImageWidget.Red"
|
||||
android:visibility="@{viewModel.music.appInstalled ? View.VISIBLE : View.GONE}"
|
||||
android:onClick="@{()-> viewModel.uninstallMusic()}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/music_title_buttons_barrier"
|
||||
|
@ -113,7 +115,8 @@
|
|||
android:id="@+id/music_downloading_txt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.musicProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:text="@{viewModel.Companion.musicProgress.downloadingFile}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
|
@ -121,14 +124,15 @@
|
|||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.musicProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:progress="@{viewModel.Companion.musicProgress.downloadProgress}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/music_installing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.musicProgress.showInstallCircle ? View.VISIBLE : View.GONE}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
android:text="@{viewModel.vanced.buttonTxt}"
|
||||
android:textColor="@color/White"
|
||||
android:backgroundTint="?colorPrimary"
|
||||
android:onClick="@{()-> viewModel.installVanced()}"
|
||||
app:icon="@{viewModel.vanced.buttonIcon}"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
@ -65,6 +66,7 @@
|
|||
<ImageView
|
||||
android:id="@+id/vanced_uninstallbtn"
|
||||
style="@style/ClickableImageWidget.Red"
|
||||
android:onClick="@{()-> viewModel.uninstallVanced()}"
|
||||
android:visibility="@{viewModel.vanced.appInstalled ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
|
@ -114,7 +116,8 @@
|
|||
android:id="@+id/vanced_downloading_txt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.vancedProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:text="@{viewModel.Companion.vancedProgress.downloadingFile}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
|
@ -122,14 +125,15 @@
|
|||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.vancedProgress.showDownloadBar ? View.VISIBLE : View.GONE}"
|
||||
android:progress="@{viewModel.Companion.vancedProgress.downloadProgress}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/vanced_installing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:visibility="@{viewModel.Companion.vancedProgress.showInstallCircle ? View.VISIBLE : View.GONE}"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -10,7 +10,7 @@ org.gradle.jvmargs=-Xmx4096m
|
|||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
org.gradle.parallel=true
|
||||
org.gradle.parallel=false
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
|
@ -19,5 +19,3 @@ android.useAndroidX=true
|
|||
android.enableJetifier=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
|
||||
org.gradle.configureondemand=true
|
||||
|
|
Loading…
Reference in a new issue