0
0
Fork 0
mirror of https://github.com/YTVanced/VancedManager synced 2024-11-27 13:33:00 +00:00

manager language selector and url changer fixes

This commit is contained in:
Xinto 2020-09-29 14:47:38 +04:00
parent 82060465ae
commit 41fe28a684
24 changed files with 204 additions and 207 deletions

View file

@ -21,7 +21,7 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 13 versionCode 13
versionName "1.3.0b (Arcturus)" versionName "1.3.0 (Arcturus)"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
@ -88,11 +88,9 @@ def getLanguageNames() {
for (int i = 0; i < langs.size(); i++) { for (int i = 0; i < langs.size(); i++) {
if (langs[i].length() > 2) { if (langs[i].length() > 2) {
Locale loc = new Locale(langs[i].substring(0, langs[i].length() - 3), langs[i].substring(langs[i].length() - 2)) Locale loc = new Locale(langs[i].substring(0, langs[i].length() - 3), langs[i].substring(langs[i].length() - 2))
project.logger.lifecycle(loc.getDisplayLanguage(loc).capitalize() + " (" + loc.getDisplayCountry(loc).capitalize() + ")")
langnames.add(loc.getDisplayLanguage(loc).capitalize() + " (" + loc.getDisplayCountry(loc).capitalize() + ")") langnames.add(loc.getDisplayLanguage(loc).capitalize() + " (" + loc.getDisplayCountry(loc).capitalize() + ")")
} else { } else {
Locale loc = new Locale(langs[i]) Locale loc = new Locale(langs[i])
project.logger.lifecycle(loc.getDisplayLanguage(loc).capitalize())
langnames.add(loc.getDisplayLanguage(loc).capitalize()) langnames.add(loc.getDisplayLanguage(loc).capitalize())
} }
} }

View file

@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<queries> <queries>
<package android:name="com.vanced.android.youtube" /> <package android:name="com.vanced.android.youtube" />
@ -45,6 +46,7 @@
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/DarkTheme.Blue" android:theme="@style/DarkTheme.Blue"
android:configChanges="layoutDirection|locale"
android:exported="true"/> android:exported="true"/>
<meta-data <meta-data

View file

@ -7,15 +7,12 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import android.widget.LinearLayout
import android.widget.Toast import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.databinding.ViewHomeBinding import com.vanced.manager.databinding.ViewHomeBinding
import com.vanced.manager.ui.viewmodels.HomeViewModel import com.vanced.manager.ui.viewmodels.HomeViewModel
import kotlinx.android.synthetic.main.include_changelogs.view.*
class VariantAdapter(private val viewModel: HomeViewModel, private val context: Context) : RecyclerView.Adapter<VariantAdapter.VariantAdapterHolder>() { class VariantAdapter(private val viewModel: HomeViewModel, private val context: Context) : RecyclerView.Adapter<VariantAdapter.VariantAdapterHolder>() {

View file

@ -2,6 +2,7 @@ package com.vanced.manager.core
import android.app.Application import android.app.Application
import android.content.res.Configuration import android.content.res.Configuration
import androidx.databinding.ObservableField
import androidx.preference.PreferenceManager.getDefaultSharedPreferences import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import com.beust.klaxon.JsonObject import com.beust.klaxon.JsonObject
import com.crowdin.platform.Crowdin import com.crowdin.platform.Crowdin
@ -10,14 +11,14 @@ import com.crowdin.platform.data.remote.NetworkType
import com.downloader.PRDownloader import com.downloader.PRDownloader
import com.vanced.manager.utils.InternetTools.baseUrl import com.vanced.manager.utils.InternetTools.baseUrl
import com.vanced.manager.utils.JsonHelper.getJson import com.vanced.manager.utils.JsonHelper.getJson
import kotlinx.coroutines.* import kotlinx.coroutines.runBlocking
open class App: Application() { open class App: Application() {
var vanced: JsonObject? = null var vanced = ObservableField<JsonObject?>()
var music: JsonObject? = null var music = ObservableField<JsonObject?>()
var microg: JsonObject? = null var microg = ObservableField<JsonObject?>()
var manager: JsonObject? = null var manager = ObservableField<JsonObject?>()
override fun onCreate() { override fun onCreate() {
loadJsonAsync() loadJsonAsync()
@ -33,12 +34,13 @@ open class App: Application() {
} }
fun loadJsonAsync() { open fun loadJsonAsync() {
val latest = runBlocking { getJson("${getDefaultSharedPreferences(this@App).getString("update_url", baseUrl)}/latest.json") } val latest = runBlocking { getJson("${getDefaultSharedPreferences(this@App).getString("install_url", baseUrl)}/latest.json") }
vanced = latest.obj("vanced")
music = latest.obj("music") vanced.set(latest?.obj("vanced"))
microg = latest.obj("microg") music.set(latest?.obj("music"))
manager = latest.obj("manager") microg.set(latest?.obj("microg"))
manager.set(latest?.obj("manager"))
} }
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
@ -46,4 +48,5 @@ open class App: Application() {
Crowdin.onConfigurationChanged() Crowdin.onConfigurationChanged()
} }
} }

View file

@ -6,10 +6,10 @@ import com.downloader.Error
import com.downloader.OnDownloadListener import com.downloader.OnDownloadListener
import com.downloader.PRDownloader import com.downloader.PRDownloader
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.core.App
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.microgProgress
import com.vanced.manager.utils.AppUtils.mutableInstall import com.vanced.manager.utils.AppUtils.mutableInstall
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
import com.vanced.manager.utils.InternetTools.getJsonString
import com.vanced.manager.utils.PackageHelper.install import com.vanced.manager.utils.PackageHelper.install
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -21,14 +21,14 @@ object MicrogDownloader {
fun downloadMicrog(context: Context) { fun downloadMicrog(context: Context) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val url = getJsonString("microg.json", "url", context) val url = (context.applicationContext as App).microg.get()?.string("url")
//downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService) //downloadId = download(url, "apk", "microg.apk", this@MicrogDownloadService)
microgProgress.get()?.currentDownload = PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "microg.apk") microgProgress.get()?.currentDownload = PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "microg.apk")
.build() .build()
.setOnStartOrResumeListener { .setOnStartOrResumeListener {
mutableInstall.value = true mutableInstall.value = true
microgProgress.get()?.downloadingFile?.set(context.getString(R.string.downloading_file, getFileNameFromUrl(url))) microgProgress.get()?.downloadingFile?.set(context.getString(R.string.downloading_file, url?.let { getFileNameFromUrl(it) }))
microgProgress.get()?.showDownloadBar?.set(true) microgProgress.get()?.showDownloadBar?.set(true)
} }
.setOnCancelListener { .setOnCancelListener {

View file

@ -6,10 +6,10 @@ import com.downloader.Error
import com.downloader.OnDownloadListener import com.downloader.OnDownloadListener
import com.downloader.PRDownloader import com.downloader.PRDownloader
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.core.App
import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.musicProgress import com.vanced.manager.ui.viewmodels.HomeViewModel.Companion.musicProgress
import com.vanced.manager.utils.AppUtils.mutableInstall import com.vanced.manager.utils.AppUtils.mutableInstall
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl import com.vanced.manager.utils.InternetTools.getFileNameFromUrl
import com.vanced.manager.utils.InternetTools.getJsonString
import com.vanced.manager.utils.PackageHelper.install import com.vanced.manager.utils.PackageHelper.install
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -21,8 +21,7 @@ object MusicDownloader {
fun downloadMusic(context: Context){ fun downloadMusic(context: Context){
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val version = getJsonString("music.json", "version", context) val url = "https://vanced.app/api/v1/music/v${(context.applicationContext as App).music.get()?.string("version")}.apk"
val url = "https://vanced.app/api/v1/music/v$version.apk"
//downloadId = download(url, "apk", "music.apk", this@MusicDownloadService) //downloadId = download(url, "apk", "music.apk", this@MusicDownloadService)

View file

@ -17,7 +17,6 @@ import com.vanced.manager.utils.AppUtils.mutableInstall
import com.vanced.manager.utils.InternetTools import com.vanced.manager.utils.InternetTools
import com.vanced.manager.utils.InternetTools.baseUrl import com.vanced.manager.utils.InternetTools.baseUrl
import com.vanced.manager.utils.InternetTools.getFileNameFromUrl 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.getPkgVerCode
import com.vanced.manager.utils.PackageHelper.installVanced import com.vanced.manager.utils.PackageHelper.installVanced
import com.vanced.manager.utils.PackageHelper.installVancedRoot import com.vanced.manager.utils.PackageHelper.installVancedRoot
@ -50,10 +49,11 @@ object VancedDownloader {
private const val yPkg = "com.google.android.youtube" private const val yPkg = "com.google.android.youtube"
private var vancedVersionCode = 0 private var vancedVersionCode = 0
private val vancedVersion by lazy { runBlocking { getObjectFromJson("$installUrl/vanced.json", "version") }} private var vancedVersion: String? = null
fun downloadVanced(context: Context){ fun downloadVanced(context: Context){
//registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) //registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))val app = context.applicationContext as App
val app = context.applicationContext as App
File(context.getExternalFilesDir("apks")?.path as String).deleteRecursively() File(context.getExternalFilesDir("apks")?.path as String).deleteRecursively()
defPrefs = getDefaultSharedPreferences(context) defPrefs = getDefaultSharedPreferences(context)
installUrl = defPrefs.getString("install_url", baseUrl) installUrl = defPrefs.getString("install_url", baseUrl)
@ -61,6 +61,7 @@ object VancedDownloader {
variant = defPrefs.getString("vanced_variant", "nonroot") variant = defPrefs.getString("vanced_variant", "nonroot")
lang = prefs.getString("lang", "en")?.split(", ")?.toTypedArray() lang = prefs.getString("lang", "en")?.split(", ")?.toTypedArray()
theme = prefs.getString("theme", "dark") theme = prefs.getString("theme", "dark")
vancedVersion = app.vanced.get()?.string("version")
themePath = "$installUrl/apks/v$vancedVersion/$variant/Theme" themePath = "$installUrl/apks/v$vancedVersion/$variant/Theme"
hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json" hashUrl = "apks/v$vancedVersion/$variant/Theme/hash.json"
//newInstaller = defPrefs.getBoolean("new_installer", false) //newInstaller = defPrefs.getBoolean("new_installer", false)
@ -70,8 +71,8 @@ object VancedDownloader {
Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a" Build.SUPPORTED_ABIS.contains("arm64-v8a") -> "arm64_v8a"
else -> "armeabi_v7a" else -> "armeabi_v7a"
} }
val app = context.applicationContext as App
vancedVersionCode = app.vanced?.int("versionCode") ?: 0 vancedVersionCode = app.vanced.get()?.int("versionCode") ?: 0
downloadSplits(context) downloadSplits(context)
} }

View file

@ -15,19 +15,11 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
open class DataModel( open class DataModel(
private val jsonObject: JsonObject?, private val jsonObject: ObservableField<JsonObject?>,
app: String, private val appPkg: String,
private val context: Context private val context: Context
) { ) {
private val appPkg =
when (app) {
"vanced" -> "com.vanced.android.youtube"
"vancedRoot" -> "com.google.android.youtube"
"microg" -> "com.mgoogle.android.gms"
else -> "com.vanced.android.apps.youtube.music"
}
private val versionCode = ObservableInt() private val versionCode = ObservableInt()
private val installedVersionCode = ObservableInt() private val installedVersionCode = ObservableInt()
@ -40,13 +32,13 @@ open class DataModel(
fun fetch() = CoroutineScope(Dispatchers.IO).launch { fun fetch() = CoroutineScope(Dispatchers.IO).launch {
isAppInstalled.set(isPackageInstalled(appPkg, context.packageManager)) isAppInstalled.set(isPackageInstalled(appPkg, context.packageManager))
versionName.set(jsonObject?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable)) versionName.set(jsonObject.get()?.string("version")?.removeSuffix("-vanced") ?: context.getString(R.string.unavailable))
installedVersionName.set(getPkgVersionName(isAppInstalled.get(), appPkg)) installedVersionName.set(getPkgVersionName(isAppInstalled.get(), appPkg))
versionCode.set(jsonObject?.int("versionCode") ?: 0) versionCode.set(jsonObject.get()?.int("versionCode") ?: 0)
installedVersionCode.set(getPkgVersionCode(isAppInstalled.get(), appPkg)) installedVersionCode.set(getPkgVersionCode(isAppInstalled.get(), appPkg))
buttonTxt.set(compareInt(installedVersionCode.get(), versionCode.get())) buttonTxt.set(compareInt(installedVersionCode.get(), versionCode.get()))
buttonIcon.set(compareIntDrawable(installedVersionCode.get(), versionCode.get())) buttonIcon.set(compareIntDrawable(installedVersionCode.get(), versionCode.get()))
changelog.set(jsonObject?.string("changelog") ?: context.getString(R.string.unavailable)) changelog.set(jsonObject.get()?.string("changelog") ?: context.getString(R.string.unavailable))
} }
init { init {

View file

@ -2,6 +2,7 @@ package com.vanced.manager.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.MenuItem import android.view.MenuItem
@ -117,6 +118,17 @@ class MainActivity : AppCompatActivity() {
super.attachBaseContext(LanguageContextWrapper.wrap(newBase)) super.attachBaseContext(LanguageContextWrapper.wrap(newBase))
} }
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
recreate() //restarting activity to recreate viewmodels, otherwise some text won't update
}
override fun recreate() {
//needed for setting language smh
startActivity(Intent(this, this::class.java))
finish()
}
private fun initDialogs() { private fun initDialogs() {
val prefs = getDefaultSharedPreferences(this) val prefs = getDefaultSharedPreferences(this)
val variant = prefs.getString("vanced_variant", "nonroot") val variant = prefs.getString("vanced_variant", "nonroot")

View file

@ -1,23 +1,15 @@
package com.vanced.manager.ui.fragments package com.vanced.manager.ui.fragments
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.FileProvider
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.downloader.OnDownloadListener
import com.downloader.PRDownloader
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.ui.MainActivity import com.vanced.manager.ui.MainActivity
import kotlinx.coroutines.launch import com.vanced.manager.utils.DownloadHelper.downloadManager
import kotlinx.coroutines.runBlocking
import java.io.File
class DevSettingsFragment: PreferenceFragmentCompat() { class DevSettingsFragment: PreferenceFragmentCompat() {
@ -45,6 +37,12 @@ class DevSettingsFragment: PreferenceFragmentCompat() {
true true
} }
findPreference<Preference>("install_url")?.setOnPreferenceClickListener {
URLChangeFragment().show(childFragmentManager.beginTransaction(), "Install URL")
true
}
val supportedAbis: Array<String> = Build.SUPPORTED_ABIS val supportedAbis: Array<String> = Build.SUPPORTED_ABIS
findPreference<Preference>("device_arch")?.summary = findPreference<Preference>("device_arch")?.summary =
@ -58,38 +56,7 @@ class DevSettingsFragment: PreferenceFragmentCompat() {
val forceUpdate: Preference? = findPreference("force_update") val forceUpdate: Preference? = findPreference("force_update")
forceUpdate?.setOnPreferenceClickListener { forceUpdate?.setOnPreferenceClickListener {
runBlocking { downloadManager(true, requireActivity())
launch {
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
//downloadId = activity?.let { download(url, "apk", "manager.apk", it) }!!
PRDownloader.download(url, activity?.getExternalFilesDir("apk")?.path, "manager.apk")
.build()
.start(object : OnDownloadListener {
override fun onDownloadComplete() {
activity?.let {
val apk = File("${activity?.getExternalFilesDir("apk")?.path}/manager.apk")
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
FileProvider.getUriForFile(activity!!, "${activity?.packageName}.provider", apk)
else
Uri.fromFile(apk)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
}
override fun onError(error: com.downloader.Error?) {
Toast.makeText(activity, error.toString(), Toast.LENGTH_SHORT).show()
Log.e("VMUpgrade", error.toString())
}
})
}
}
true true
} }

View file

@ -22,12 +22,15 @@ import com.vanced.manager.adapter.VariantAdapter
import com.vanced.manager.databinding.FragmentHomeBinding import com.vanced.manager.databinding.FragmentHomeBinding
import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder import com.vanced.manager.ui.dialogs.DialogContainer.installAlertBuilder
import com.vanced.manager.ui.viewmodels.HomeViewModel import com.vanced.manager.ui.viewmodels.HomeViewModel
import com.vanced.manager.ui.viewmodels.HomeViewModelFactory
import com.vanced.manager.utils.AppUtils import com.vanced.manager.utils.AppUtils
open class HomeFragment : Fragment() { open class HomeFragment : Fragment() {
private lateinit var binding: FragmentHomeBinding private lateinit var binding: FragmentHomeBinding
private val viewModel: HomeViewModel by viewModels() private val viewModel: HomeViewModel by viewModels {
HomeViewModelFactory(requireActivity())
}
private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) } private val localBroadcastManager by lazy { LocalBroadcastManager.getInstance(requireActivity()) }
private val tabListener = object : TabLayout.OnTabSelectedListener { private val tabListener = object : TabLayout.OnTabSelectedListener {

View file

@ -115,11 +115,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
true true
} }
findPreference<Preference>("install_url")?.setOnPreferenceClickListener {
URLChangeFragment().show(childFragmentManager.beginTransaction(), "Install URL")
true
}
findPreference<Preference>("clear_files")?.setOnPreferenceClickListener { findPreference<Preference>("clear_files")?.setOnPreferenceClickListener {
with(requireActivity()) { with(requireActivity()) {
listOf("apk", "apks").forEach { dir -> listOf("apk", "apks").forEach { dir ->

View file

@ -1,5 +1,6 @@
package com.vanced.manager.ui.fragments package com.vanced.manager.ui.fragments
import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
@ -9,9 +10,10 @@ import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager.getDefaultSharedPreferences
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.core.App
import com.vanced.manager.utils.InternetTools.baseUrl import com.vanced.manager.utils.InternetTools.baseUrl
class URLChangeFragment : DialogFragment() { class URLChangeFragment : DialogFragment() {
@ -29,8 +31,7 @@ class URLChangeFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val urlField = view.findViewById<EditText>(R.id.url_input) val urlField = view.findViewById<EditText>(R.id.url_input)
val prefs = PreferenceManager.getDefaultSharedPreferences(activity) urlField.setText(getDefaultSharedPreferences(requireActivity()).getString("install_url", baseUrl), TextView.BufferType.EDITABLE)
urlField.setText(prefs.getString("install_url", baseUrl), TextView.BufferType.EDITABLE)
view.findViewById<MaterialButton>(R.id.url_save).setOnClickListener { view.findViewById<MaterialButton>(R.id.url_save).setOnClickListener {
val finalUrl = val finalUrl =
if (urlField.text.startsWith("https://") || urlField.text.startsWith("http://")) if (urlField.text.startsWith("https://") || urlField.text.startsWith("http://"))
@ -38,14 +39,15 @@ class URLChangeFragment : DialogFragment() {
else else
"https://${urlField.text}".removeSuffix("/") "https://${urlField.text}".removeSuffix("/")
saveUrl(finalUrl, requireActivity())
}
view.findViewById<MaterialButton>(R.id.url_reset).setOnClickListener {saveUrl(baseUrl, requireActivity())}
}
prefs.edit().putString("install_url", finalUrl).apply() private fun saveUrl(url: String, context: Context) {
getDefaultSharedPreferences(requireActivity()).edit().putString("install_url", url).apply()
(context.applicationContext as App).loadJsonAsync()
dismiss() dismiss()
} }
view.findViewById<MaterialButton>(R.id.url_reset).setOnClickListener {
prefs.edit().putString("install_url", baseUrl).apply()
dismiss()
}
}
} }

View file

@ -1,29 +1,19 @@
package com.vanced.manager.ui.fragments package com.vanced.manager.ui.fragments
import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.ProgressBar
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import com.downloader.OnDownloadListener
import com.downloader.PRDownloader
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.vanced.manager.R import com.vanced.manager.R
import com.vanced.manager.utils.DownloadHelper.downloadManager
import com.vanced.manager.utils.InternetTools.isUpdateAvailable import com.vanced.manager.utils.InternetTools.isUpdateAvailable
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.io.File
class UpdateCheckFragment : DialogFragment() { class UpdateCheckFragment : DialogFragment() {
@ -65,57 +55,16 @@ class UpdateCheckFragment : DialogFragment() {
view?.findViewById<Button>(R.id.update_center_recheck)?.visibility = View.GONE view?.findViewById<Button>(R.id.update_center_recheck)?.visibility = View.GONE
checkingTxt?.text = getString(R.string.update_found) checkingTxt?.text = getString(R.string.update_found)
updatebtn?.setOnClickListener { upgradeManager() } updatebtn?.setOnClickListener {
downLoadingState(true)
downloadManager(false, requireActivity(), view?.findViewById(R.id.update_center_progressbar))
downLoadingState(false)
}
} else } else
checkingTxt?.text = getString(R.string.update_notfound) checkingTxt?.text = getString(R.string.update_notfound)
} }
} }
private fun upgradeManager() {
runBlocking {
launch {
val loadBar = view?.findViewById<ProgressBar>(R.id.update_center_progressbar)
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
downLoadingState(true)
//downloadId = activity?.let { download(url, "apk", "manager.apk", it) }!!
PRDownloader.download(url, activity?.getExternalFilesDir("apk")?.path, "manager.apk")
.build()
.setOnProgressListener { progress ->
val mProgress = progress.currentBytes * 100 / progress.totalBytes
loadBar?.visibility = View.VISIBLE
loadBar?.progress = mProgress.toInt()
}
.start(object : OnDownloadListener {
override fun onDownloadComplete() {
activity?.let {
val apk = File("${activity?.getExternalFilesDir("apk")?.path}/manager.apk")
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
FileProvider.getUriForFile(activity!!, "${activity?.packageName}.provider", apk)
else
Uri.fromFile(apk)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
}
}
override fun onError(error: com.downloader.Error?) {
Toast.makeText(activity, error.toString(), Toast.LENGTH_SHORT).show()
downLoadingState(false)
Log.e("VMUpgrade", error.toString())
}
})
}
}
}
private fun downLoadingState(isDownloading: Boolean) { private fun downLoadingState(isDownloading: Boolean) {
if (isDownloading) { if (isDownloading) {
dialog?.setCancelable(false) dialog?.setCancelable(false)

View file

@ -37,7 +37,7 @@ class VancedLanguageSelectionFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val app = activity?.application as App val app = activity?.application as App
langs = app.vanced?.array<String>("langs")?.value ?: mutableListOf("null") langs = app.vanced?.get()?.array<String>("langs")?.value ?: mutableListOf("null")
loadBoxes(view.findViewById(R.id.lang_button_ll)) loadBoxes(view.findViewById(R.id.lang_button_ll))
view.findViewById<MaterialButton>(R.id.vanced_install_finish).setOnClickListener { view.findViewById<MaterialButton>(R.id.vanced_install_finish).setOnClickListener {
val chosenLangs = mutableListOf("en") val chosenLangs = mutableListOf("en")

View file

@ -1,6 +1,6 @@
package com.vanced.manager.ui.viewmodels package com.vanced.manager.ui.viewmodels
import android.app.Application import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
@ -9,9 +9,9 @@ import android.widget.Toast
import androidx.core.content.ContextCompat.startActivity import androidx.core.content.ContextCompat.startActivity
import androidx.databinding.ObservableBoolean import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField import androidx.databinding.ObservableField
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.crowdin.platform.Crowdin import com.crowdin.platform.Crowdin
import com.downloader.PRDownloader import com.downloader.PRDownloader
import com.downloader.Status import com.downloader.Status
@ -27,12 +27,11 @@ import com.vanced.manager.utils.AppUtils.installing
import com.vanced.manager.utils.InternetTools import com.vanced.manager.utils.InternetTools
import com.vanced.manager.utils.PackageHelper.uninstallApk import com.vanced.manager.utils.PackageHelper.uninstallApk
open class HomeViewModel(application: Application): AndroidViewModel(application) { open class HomeViewModel(private val activity: Activity): ViewModel() {
val app = application private val manageractivity = activity.application as App
private val managerApp = application as App
//val variant = getDefaultSharedPreferences(application).getString("vanced_variant", "nonroot") //val variant = getDefaultSharedPreferences(activity).getString("vanced_variant", "nonroot")
val vanced = ObservableField<DataModel>() val vanced = ObservableField<DataModel>()
val vancedRoot = ObservableField<DataModel>() val vancedRoot = ObservableField<DataModel>()
@ -47,18 +46,18 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
fun fetchData() { fun fetchData() {
fetching.set(true) fetching.set(true)
managerApp.loadJsonAsync() manageractivity.loadJsonAsync()
vanced.get()?.fetch() vanced.get()?.fetch()
vancedRoot.get()?.fetch() vancedRoot.get()?.fetch()
music.get()?.fetch() music.get()?.fetch()
microg.get()?.fetch() microg.get()?.fetch()
manager.get()?.fetch() manager.get()?.fetch()
Crowdin.forceUpdate(getApplication()) Crowdin.forceUpdate(activity)
fetching.set(false) fetching.set(false)
} }
//private val microgSnackbar = Snackbar.make(, R.string.no_microg, Snackbar.LENGTH_LONG).setAction(R.string.install) { downloadMicrog(getApplication()) } //private val microgSnackbar = Snackbar.make(, R.string.no_microg, Snackbar.LENGTH_LONG).setAction(R.string.install) { downloadMicrog(activity) }
private val microgToast = Toast.makeText(app, R.string.no_microg, Toast.LENGTH_LONG) private val microgToast = Toast.makeText(activity, R.string.no_microg, Toast.LENGTH_LONG)
fun openMicrogSettings() { fun openMicrogSettings() {
try { try {
@ -68,9 +67,9 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
"org.microg.gms.ui.SettingsActivity" "org.microg.gms.ui.SettingsActivity"
) )
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(getApplication(), intent, null) startActivity(activity, intent, null)
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
Toast.makeText(getApplication(), "Error", Toast.LENGTH_SHORT).show() Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
} }
} }
@ -81,12 +80,12 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
"https://t.me/joinchat/AAAAAEHf-pi4jH1SDlAL4w" -> R.color.Telegram "https://t.me/joinchat/AAAAAEHf-pi4jH1SDlAL4w" -> R.color.Telegram
"https://twitter.com/YTVanced" -> R.color.Twitter "https://twitter.com/YTVanced" -> R.color.Twitter
"https://reddit.com/r/vanced" -> R.color.Reddit "https://reddit.com/r/vanced" -> R.color.Reddit
"https://vanced.app" -> R.color.Vanced "https://vanced.activity" -> R.color.Vanced
"https://brave.com/van874" -> R.color.Brave "https://brave.com/van874" -> R.color.Brave
else -> R.color.Vanced else -> R.color.Vanced
} }
InternetTools.openUrl(url, color, getApplication()) InternetTools.openUrl(url, color, activity)
} }
fun installVanced(variant: String) { fun installVanced(variant: String) {
@ -95,15 +94,15 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
if (variant == "nonroot" && !microg.get()?.isAppInstalled?.get()!!) { if (variant == "nonroot" && !microg.get()?.isAppInstalled?.get()!!) {
microgToast.show() microgToast.show()
} else { } else {
if (app.getSharedPreferences("installPrefs", Context.MODE_PRIVATE).getBoolean("valuesModified", false)) { if (activity.getSharedPreferences("installPrefs", Context.MODE_PRIVATE).getBoolean("valuesModified", false)) {
downloadVanced(app) downloadVanced(activity)
} else { } else {
_navigateDestination.value = Event(R.id.toInstallThemeFragment) _navigateDestination.value = Event(R.id.toInstallThemeFragment)
} }
} }
} }
} else } else
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.installation_wait, Toast.LENGTH_SHORT).show()
} }
fun installMusic() { fun installMusic() {
@ -112,23 +111,23 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
if (!microg.get()?.isAppInstalled?.get()!!) { if (!microg.get()?.isAppInstalled?.get()!!) {
microgToast.show() microgToast.show()
} else { } else {
downloadMusic(getApplication()) downloadMusic(activity)
} }
} }
} else } else
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.installation_wait, Toast.LENGTH_SHORT).show()
} }
fun installMicrog() { fun installMicrog() {
if (!installing.value!!) if (!installing.value!!)
downloadMicrog(getApplication()) downloadMicrog(activity)
else else
Toast.makeText(getApplication(), R.string.installation_wait, Toast.LENGTH_SHORT).show() Toast.makeText(activity, R.string.installation_wait, Toast.LENGTH_SHORT).show()
} }
fun uninstallVanced(variant: String) = uninstallApk(if (variant == "root") "com.google.android.youtube" else "com.vanced.android.youtube", app) fun uninstallVanced(variant: String) = uninstallApk(if (variant == "root") "com.google.android.youtube" else "com.vanced.android.youtube", activity)
fun uninstallMusic() = uninstallApk("com.vanced.android.apps.youtube.music", app) fun uninstallMusic() = uninstallApk("com.vanced.android.activitys.youtube.music", activity)
fun uninstallMicrog() = uninstallApk("com.mgoogle.android.gms", app) fun uninstallMicrog() = uninstallApk("com.mgoogle.android.gms", activity)
fun cancelDownload(downloadId: Int) { fun cancelDownload(downloadId: Int) {
PRDownloader.cancel(downloadId) PRDownloader.cancel(downloadId)
@ -149,11 +148,11 @@ open class HomeViewModel(application: Application): AndroidViewModel(application
init { init {
fetching.set(true) fetching.set(true)
vanced.set(DataModel(managerApp.vanced, "vanced", app)) vanced.set(DataModel(manageractivity.vanced, "com.vanced.android.youtube", activity))
vancedRoot.set(DataModel(managerApp.vanced, "vancedRoot", app)) vancedRoot.set(DataModel(manageractivity.vanced, "com.google.android.youtube", activity))
music.set(DataModel(managerApp.music, "music", app)) music.set(DataModel(manageractivity.music, "com.vanced.android.apps.youtube.music", activity))
microg.set(DataModel(managerApp.microg, "microg", app)) microg.set(DataModel(manageractivity.microg, "com.mgoogle.android.gms", activity))
manager.set(DataModel(managerApp.manager, "manager", app)) manager.set(DataModel(manageractivity.manager, "com.vanced.manager", activity))
vancedProgress.set(ProgressModel()) vancedProgress.set(ProgressModel())
musicProgress.set(ProgressModel()) musicProgress.set(ProgressModel())
microgProgress.set(ProgressModel()) microgProgress.set(ProgressModel())

View file

@ -0,0 +1,13 @@
package com.vanced.manager.ui.viewmodels
import android.app.Activity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class HomeViewModelFactory(private val activity: Activity) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return HomeViewModel(activity) as T
}
}

View file

@ -2,8 +2,21 @@ package com.vanced.manager.utils
import android.app.DownloadManager import android.app.DownloadManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import android.util.Log
import android.view.View
import android.widget.ProgressBar
import android.widget.Toast
import androidx.core.content.FileProvider
import com.downloader.OnDownloadListener
import com.downloader.PRDownloader
import com.vanced.manager.R import com.vanced.manager.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
object DownloadHelper { object DownloadHelper {
@ -19,4 +32,48 @@ object DownloadHelper {
return downloadManager.enqueue(request) return downloadManager.enqueue(request)
} }
fun downloadManager(forceUpdate: Boolean, context: Context, loadBar: ProgressBar? = null) {
CoroutineScope(if (forceUpdate) Dispatchers.IO else Dispatchers.Main).launch {
val url = "https://github.com/YTVanced/VancedManager/releases/latest/download/manager.apk"
//downloadId = activity?.let { download(url, "apk", "manager.apk", it) }!!
PRDownloader.download(url, context.getExternalFilesDir("apk")?.path, "manager.apk")
.build().apply{
if (!forceUpdate) setOnProgressListener {progress ->
val mProgress = progress.currentBytes * 100 / progress.totalBytes
loadBar?.visibility = View.VISIBLE
loadBar?.progress = mProgress.toInt()
}
start(object : OnDownloadListener {
override fun onDownloadComplete() {
context.let {
val apk =
File("${context.getExternalFilesDir("apk")?.path}/manager.apk")
val uri =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
FileProvider.getUriForFile(
context,
"${context.packageName}.provider",
apk
)
else
Uri.fromFile(apk)
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri, "application/vnd.android.package-archive")
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivity(intent)
}
}
override fun onError(error: com.downloader.Error?) {
Toast.makeText(context, error.toString(), Toast.LENGTH_SHORT).show()
Log.e("VMUpgrade", error.toString())
}
})
}
}
}
} }

View file

@ -30,7 +30,7 @@ object InternetTools {
suspend fun getObjectFromJson(url: String, obj: String): String { suspend fun getObjectFromJson(url: String, obj: String): String {
return try { return try {
JsonHelper.getJson(url).string(obj) ?: "" JsonHelper.getJson(url)?.string(obj) ?: ""
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error: ", e) Log.e(TAG, "Error: ", e)
"" ""
@ -39,7 +39,7 @@ object InternetTools {
suspend fun getArrayFromJson(url: String, array: String): MutableList<String> { suspend fun getArrayFromJson(url: String, array: String): MutableList<String> {
return try { return try {
JsonHelper.getJson(url).array<String>(array)?.value ?: mutableListOf("null") JsonHelper.getJson(url)?.array<String>(array)?.value ?: mutableListOf("null")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error: ", e) Log.e(TAG, "Error: ", e)
mutableListOf("null") mutableListOf("null")
@ -49,7 +49,7 @@ object InternetTools {
suspend fun getJsonInt(file: String, obj: String, context: Context): Int { suspend fun getJsonInt(file: String, obj: String, context: Context): Int {
val installUrl = getDefaultSharedPreferences(context).getString("install_url", baseUrl) val installUrl = getDefaultSharedPreferences(context).getString("install_url", baseUrl)
return try { return try {
JsonHelper.getJson("$installUrl/$file").int(obj) ?: 0 JsonHelper.getJson("$installUrl/$file")?.int(obj) ?: 0
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error: ", e) Log.e(TAG, "Error: ", e)
0 0
@ -59,7 +59,7 @@ object InternetTools {
suspend fun getJsonString(file: String, obj: String, context: Context): String { suspend fun getJsonString(file: String, obj: String, context: Context): String {
val installUrl = getDefaultSharedPreferences(context).getString("install_url", baseUrl) val installUrl = getDefaultSharedPreferences(context).getString("install_url", baseUrl)
return try { return try {
JsonHelper.getJson("$installUrl/$file").string(obj) ?: context.getString(R.string.unavailable) JsonHelper.getJson("$installUrl/$file")?.string(obj) ?: context.getString(R.string.unavailable)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Error: ", e) Log.e(TAG, "Error: ", e)
context.getString(R.string.unavailable) context.getString(R.string.unavailable)
@ -68,7 +68,7 @@ object InternetTools {
suspend fun isUpdateAvailable(): Boolean { suspend fun isUpdateAvailable(): Boolean {
val result = try { val result = try {
JsonHelper.getJson("https://ytvanced.github.io/VancedBackend/manager.json").int("versionCode") ?: 0 JsonHelper.getJson("https://ytvanced.github.io/VancedBackend/manager.json")?.int("versionCode") ?: 0
} catch (e: Exception) { } catch (e: Exception) {
0 0
} }

View file

@ -11,14 +11,17 @@ object JsonHelper {
var dataMap: HashMap<String, JsonObject> = HashMap() var dataMap: HashMap<String, JsonObject> = HashMap()
suspend fun getJson(url: String): JsonObject suspend fun getJson(url: String): JsonObject? {
{ return try {
return if(dataMap.containsKey(url)) { if(dataMap.containsKey(url)) {
dataMap[url]!! dataMap[url]!!
} else { } else {
dataMap[url] = getSuspendJson(url) dataMap[url] = getSuspendJson(url)
dataMap[url]!! dataMap[url]!!
} }
} catch (e: Exception) {
null
}
} }
private suspend fun getSuspendJson(url: String): JsonObject = private suspend fun getSuspendJson(url: String): JsonObject =

View file

@ -3,6 +3,7 @@ package com.vanced.manager.utils
import android.content.Context import android.content.Context
import android.content.ContextWrapper import android.content.ContextWrapper
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.crowdin.platform.Crowdin import com.crowdin.platform.Crowdin
import java.util.* import java.util.*
@ -13,18 +14,22 @@ class LanguageContextWrapper(base: Context?) : ContextWrapper(base) {
fun wrap(context: Context): ContextWrapper { fun wrap(context: Context): ContextWrapper {
val config: Configuration = context.resources.configuration val config: Configuration = context.resources.configuration
context.createConfigurationContext(setLocale(config, context))
Crowdin.wrapContext(context)
return LanguageContextWrapper(context)
}
private fun setLocale(config: Configuration, context: Context): Configuration {
val pref = PreferenceManager.getDefaultSharedPreferences(context).getString("manager_lang", "System Default") val pref = PreferenceManager.getDefaultSharedPreferences(context).getString("manager_lang", "System Default")
val locale = val locale =
when { when {
pref == "System Default" -> Locale(config.locale.displayLanguage) pref == "System Default" -> Locale(Resources.getSystem().configuration.locale.language)
pref?.length!! > 2 -> Locale(pref.substring(0, pref.length - 3), pref.substring(pref.length - 2)) pref?.length!! > 2 -> Locale(pref.substring(0, pref.length - 3), pref.substring(pref.length - 2))
else -> Locale(pref) else -> Locale(pref)
} }
Locale.setDefault(locale) Locale.setDefault(locale)
config.setLocale(locale) config.setLocale(locale)
context.createConfigurationContext(config) return config
Crowdin.wrapContext(context)
return LanguageContextWrapper(context)
} }
} }

View file

@ -202,7 +202,7 @@ object PackageHelper {
Shell.getShell { Shell.getShell {
val application = context.applicationContext as App val application = context.applicationContext as App
val vancedApplication = application.vanced?.int("versionCode") val vancedApplication = application.vanced.get()?.int("versionCode")
val vancedVersionCode = if (vancedApplication != null) vancedApplication else { application.loadJsonAsync(); vancedApplication } val vancedVersionCode = if (vancedApplication != null) vancedApplication else { application.loadJsonAsync(); vancedApplication }
val apkFilesPath = context.getExternalFilesDir("apks")?.path val apkFilesPath = context.getExternalFilesDir("apks")?.path
val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) } val fileInfoList = apkFilesPath?.let { it1 -> getFileInfoList(it1) }

View file

@ -12,6 +12,10 @@
android:title="Force Manager Update" android:title="Force Manager Update"
android:summary="Update manager without comparing versionCode"/> android:summary="Update manager without comparing versionCode"/>
<Preference
android:title="@string/update_url"
android:key="install_url" />
<PreferenceCategory <PreferenceCategory
android:title="Device info"> android:title="Device info">

View file

@ -76,10 +76,6 @@
android:title="@string/chosen_install_values" android:title="@string/chosen_install_values"
android:key="vanced_chosen_modes" /> android:key="vanced_chosen_modes" />
<Preference
android:title="@string/update_url"
android:key="install_url" />
<Preference <Preference
android:title="@string/clear_files" android:title="@string/clear_files"
android:key="clear_files"/> android:key="clear_files"/>