mirror of https://github.com/deniscerri/ytdlnis
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
469 lines
19 KiB
Kotlin
469 lines
19 KiB
Kotlin
package com.deniscerri.ytdlnis
|
|
|
|
import android.Manifest
|
|
import android.app.ActionBar.LayoutParams
|
|
import android.content.Context
|
|
import android.content.DialogInterface
|
|
import android.content.Intent
|
|
import android.content.SharedPreferences
|
|
import android.content.pm.PackageManager
|
|
import android.content.res.Configuration
|
|
import android.graphics.drawable.ColorDrawable
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.os.Bundle
|
|
import android.os.Handler
|
|
import android.provider.Settings
|
|
import android.util.Log
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import android.view.WindowInsets
|
|
import android.widget.CheckBox
|
|
import android.widget.TextView
|
|
import androidx.annotation.RequiresApi
|
|
import androidx.constraintlayout.widget.ConstraintLayout
|
|
import androidx.core.app.ActivityCompat
|
|
import androidx.core.view.ViewCompat
|
|
import androidx.core.view.WindowCompat
|
|
import androidx.core.view.WindowInsetsCompat
|
|
import androidx.core.view.forEach
|
|
import androidx.core.view.isVisible
|
|
import androidx.core.view.updateLayoutParams
|
|
import androidx.core.view.updatePadding
|
|
import androidx.fragment.app.FragmentContainerView
|
|
import androidx.lifecycle.ViewModelProvider
|
|
import androidx.lifecycle.lifecycleScope
|
|
import androidx.navigation.NavController
|
|
import androidx.navigation.NavDirections
|
|
import androidx.navigation.fragment.FragmentNavigatorExtras
|
|
import androidx.navigation.fragment.NavHostFragment
|
|
import androidx.navigation.fragment.findNavController
|
|
import androidx.navigation.navOptions
|
|
import androidx.navigation.ui.setupWithNavController
|
|
import androidx.preference.PreferenceManager
|
|
import com.deniscerri.ytdlnis.database.repository.DownloadRepository
|
|
import com.deniscerri.ytdlnis.database.viewmodel.CookieViewModel
|
|
import com.deniscerri.ytdlnis.database.viewmodel.DownloadViewModel
|
|
import com.deniscerri.ytdlnis.database.viewmodel.ResultViewModel
|
|
import com.deniscerri.ytdlnis.ui.BaseActivity
|
|
import com.deniscerri.ytdlnis.ui.HomeFragment
|
|
import com.deniscerri.ytdlnis.ui.more.settings.SettingsActivity
|
|
import com.deniscerri.ytdlnis.util.ThemeUtil
|
|
import com.deniscerri.ytdlnis.util.UpdateUtil
|
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
import com.google.android.material.elevation.SurfaceColors
|
|
import com.google.android.material.navigation.NavigationBarView
|
|
import com.google.android.material.navigation.NavigationView
|
|
import com.google.android.material.navigationrail.NavigationRailView
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.Job
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.runBlocking
|
|
import kotlinx.coroutines.withContext
|
|
import java.io.BufferedReader
|
|
import java.io.InputStreamReader
|
|
import java.io.Reader
|
|
import java.nio.charset.Charset
|
|
import java.nio.charset.StandardCharsets
|
|
import kotlin.system.exitProcess
|
|
|
|
|
|
class MainActivity : BaseActivity() {
|
|
|
|
lateinit var context: Context
|
|
private lateinit var preferences: SharedPreferences
|
|
private lateinit var resultViewModel: ResultViewModel
|
|
private lateinit var cookieViewModel: CookieViewModel
|
|
private lateinit var downloadViewModel: DownloadViewModel
|
|
private lateinit var navigationView: View
|
|
private lateinit var navHostFragment : NavHostFragment
|
|
private lateinit var navController : NavController
|
|
private lateinit var sharedPreferences: SharedPreferences
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
ThemeUtil.updateTheme(this)
|
|
setContentView(R.layout.activity_main)
|
|
context = baseContext
|
|
resultViewModel = ViewModelProvider(this)[ResultViewModel::class.java]
|
|
cookieViewModel = ViewModelProvider(this)[CookieViewModel::class.java]
|
|
downloadViewModel = ViewModelProvider(this)[DownloadViewModel::class.java]
|
|
preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
|
|
|
if (preferences.getBoolean("incognito", false)){
|
|
resultViewModel.deleteAll()
|
|
}
|
|
|
|
askPermissions()
|
|
checkUpdate()
|
|
|
|
navHostFragment = supportFragmentManager.findFragmentById(R.id.frame_layout) as NavHostFragment
|
|
navController = navHostFragment.findNavController()
|
|
navigationView = try {
|
|
findViewById(R.id.bottomNavigationView)
|
|
}catch (e: Exception){
|
|
findViewById<NavigationView>(R.id.navigationView)
|
|
}
|
|
|
|
if (navigationView is NavigationBarView){
|
|
window.decorView.setOnApplyWindowInsetsListener { view: View, windowInsets: WindowInsets? ->
|
|
val windowInsetsCompat = WindowInsetsCompat.toWindowInsetsCompat(
|
|
windowInsets!!, view
|
|
)
|
|
val isImeVisible = windowInsetsCompat.isVisible(WindowInsetsCompat.Type.ime())
|
|
navigationView.visibility =
|
|
if (isImeVisible) View.GONE else View.VISIBLE
|
|
view.onApplyWindowInsets(windowInsets)
|
|
}
|
|
}
|
|
|
|
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
|
|
|
// val graph = navController.navInflater.inflate(R.navigation.nav_graph)
|
|
// graph.setStartDestination(R.id.homeFragment)
|
|
// when(sharedPreferences.getString("start_destination", "")) {
|
|
// "History" -> graph.setStartDestination(R.id.historyFragment)
|
|
// "More" -> if (navigationView is NavigationBarView) graph.setStartDestination(R.id.moreFragment)
|
|
// }
|
|
//
|
|
// navController.graph = graph
|
|
|
|
if (navigationView is NavigationBarView){
|
|
(navigationView as NavigationBarView).setupWithNavController(navController)
|
|
(navigationView as NavigationBarView).setOnItemReselectedListener {
|
|
when (it.itemId) {
|
|
R.id.homeFragment -> {
|
|
(navHostFragment.childFragmentManager.primaryNavigationFragment!! as HomeFragment).scrollToTop()
|
|
}
|
|
R.id.historyFragment -> {
|
|
navController.navigate(R.id.downloadQueueMainFragment)
|
|
}
|
|
R.id.moreFragment -> {
|
|
val intent = Intent(context, SettingsActivity::class.java)
|
|
startActivity(intent)
|
|
}
|
|
}
|
|
}
|
|
|
|
val activeDownloadsBadge = (navigationView as NavigationBarView).getOrCreateBadge(R.id.historyFragment)
|
|
downloadViewModel.activeDownloadsCount.observe(this){
|
|
if (it == 0) {
|
|
activeDownloadsBadge.isVisible = false
|
|
activeDownloadsBadge.clearNumber()
|
|
}
|
|
else {
|
|
activeDownloadsBadge.isVisible = true
|
|
activeDownloadsBadge.number = it
|
|
}
|
|
}
|
|
window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this)
|
|
}
|
|
if (navigationView is NavigationView){
|
|
(navigationView as NavigationView).setupWithNavController(navController)
|
|
//terminate button
|
|
(navigationView as NavigationView).menu.getItem(7).setOnMenuItemClickListener {
|
|
if (sharedPreferences.getBoolean("ask_terminate_app", true)){
|
|
var doNotShowAgain = false
|
|
val terminateDialog = MaterialAlertDialogBuilder(this)
|
|
terminateDialog.setTitle(getString(R.string.confirm_delete_history))
|
|
val dialogView = layoutInflater.inflate(R.layout.dialog_terminate_app, null)
|
|
val checkbox = dialogView.findViewById<CheckBox>(R.id.doNotShowAgain)
|
|
terminateDialog.setView(dialogView)
|
|
checkbox.setOnCheckedChangeListener { compoundButton, _ ->
|
|
doNotShowAgain = compoundButton.isChecked
|
|
}
|
|
|
|
terminateDialog.setNegativeButton(getString(R.string.cancel)) { dialogInterface: DialogInterface, _: Int -> dialogInterface.cancel() }
|
|
terminateDialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
|
runBlocking {
|
|
val job : Job = lifecycleScope.launch(Dispatchers.IO) {
|
|
val activeDownloads = downloadViewModel.getActiveDownloads().toMutableList()
|
|
activeDownloads.map { it.status = DownloadRepository.Status.Queued.toString() }
|
|
activeDownloads.forEach {
|
|
downloadViewModel.updateDownload(it)
|
|
}
|
|
}
|
|
runBlocking {
|
|
job.join()
|
|
if (doNotShowAgain){
|
|
sharedPreferences.edit().putBoolean("ask_terminate_app", false).apply()
|
|
}
|
|
finishAndRemoveTask()
|
|
finishAffinity()
|
|
exitProcess(0)
|
|
}
|
|
}
|
|
}
|
|
terminateDialog.show()
|
|
}else{
|
|
finishAndRemoveTask()
|
|
exitProcess(0)
|
|
}
|
|
true
|
|
}
|
|
|
|
(navigationView as NavigationView).getHeaderView(0).findViewById<TextView>(R.id.title).text = ThemeUtil.getStyledAppName(this)
|
|
}
|
|
cookieViewModel.updateCookiesFile()
|
|
val intent = intent
|
|
handleIntents(intent)
|
|
|
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
|
if (navigationView is NavigationBarView){
|
|
when(destination.id){
|
|
R.id.homeFragment, R.id.historyFragment, R.id.moreFragment -> showBottomNavigation()
|
|
else -> hideBottomNavigation()
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
navigationView.visibilityChanged {
|
|
if (it.isVisible){
|
|
val curr = navController.currentDestination?.id
|
|
if (curr != R.id.homeFragment && curr != R.id.historyFragment && curr != R.id.moreFragment) hideBottomNavigation()
|
|
}
|
|
}
|
|
|
|
|
|
when(sharedPreferences.getString("start_destination", "")) {
|
|
"Queue" -> navController.navigate(R.id.downloadQueueMainFragment)
|
|
}
|
|
|
|
}
|
|
|
|
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
|
super.onSaveInstanceState(savedInstanceState)
|
|
savedInstanceState.putBundle("nav_state", navController.saveState())
|
|
}
|
|
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
|
super.onRestoreInstanceState(savedInstanceState)
|
|
navController.restoreState(savedInstanceState.getBundle("nav_state"))
|
|
}
|
|
|
|
private fun View.visibilityChanged(action: (View) -> Unit) {
|
|
this.viewTreeObserver.addOnGlobalLayoutListener {
|
|
val newVis: Int = this.visibility
|
|
if (this.tag as Int? != newVis) {
|
|
this.tag = this.visibility
|
|
// visibility has changed
|
|
action(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fun hideBottomNavigation(){
|
|
if (navigationView is BottomNavigationView){
|
|
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams<ConstraintLayout.LayoutParams> {
|
|
bottomToTop = ConstraintLayout.LayoutParams.UNSET
|
|
bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID
|
|
}
|
|
navigationView.animate()?.translationY(navigationView.height.toFloat())?.setDuration(300)?.withEndAction {
|
|
navigationView.visibility = View.GONE
|
|
}?.start()
|
|
}else if (navigationView is NavigationRailView){
|
|
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams {
|
|
this.width = LayoutParams.MATCH_PARENT
|
|
}
|
|
navigationView.animate()?.translationX(-navigationView.width.toFloat())?.setDuration(300)?.withEndAction {
|
|
navigationView.visibility = View.GONE
|
|
}?.start()
|
|
}
|
|
}
|
|
|
|
fun showBottomNavigation(){
|
|
if (navigationView is BottomNavigationView){
|
|
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams<ConstraintLayout.LayoutParams> {
|
|
bottomToTop = R.id.bottomNavigationView
|
|
bottomToBottom = ConstraintLayout.LayoutParams.UNSET
|
|
}
|
|
navigationView.animate()?.translationY(0F)?.setDuration(300)?.withEndAction {
|
|
navigationView.visibility = View.VISIBLE
|
|
}?.start()
|
|
}else if (navigationView is NavigationRailView){
|
|
findViewById<FragmentContainerView>(R.id.frame_layout).updateLayoutParams {
|
|
this.width = 0
|
|
}
|
|
navigationView.animate()?.translationX(0F)?.setDuration(300)?.withEndAction {
|
|
navigationView.visibility = View.VISIBLE
|
|
}?.start()
|
|
}
|
|
}
|
|
|
|
fun disableBottomNavigation(){
|
|
if (navigationView is NavigationBarView){
|
|
(navigationView as NavigationBarView).menu.forEach { it.isEnabled = false }
|
|
}else{
|
|
(navigationView as NavigationView).menu.forEach { it.isEnabled = false }
|
|
}
|
|
}
|
|
|
|
fun enableBottomNavigation(){
|
|
if (navigationView is NavigationBarView){
|
|
(navigationView as NavigationBarView).menu.forEach { it.isEnabled = true }
|
|
}else{
|
|
(navigationView as NavigationView).menu.forEach { it.isEnabled = true }
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
//incognito header
|
|
val incognitoHeader = findViewById<TextView>(R.id.incognito_header)
|
|
if (preferences.getBoolean("incognito", false)){
|
|
incognitoHeader.visibility = View.VISIBLE
|
|
window.statusBarColor = (incognitoHeader.background as ColorDrawable).color
|
|
}else{
|
|
window.statusBarColor = getColor(android.R.color.transparent)
|
|
incognitoHeader.visibility = View.GONE
|
|
}
|
|
//check logs option to show in tablet navbar
|
|
if (navigationView is NavigationView){
|
|
(navigationView as NavigationView).menu.findItem(R.id.downloadLogListFragment).isVisible = preferences.getBoolean("log_downloads", false)
|
|
}
|
|
}
|
|
|
|
override fun onNewIntent(intent: Intent) {
|
|
super.onNewIntent(intent)
|
|
handleIntents(intent)
|
|
}
|
|
|
|
private fun handleIntents(intent: Intent) {
|
|
val action = intent.action
|
|
val type = intent.type
|
|
if (Intent.ACTION_SEND == action && type != null) {
|
|
Log.e(TAG, action)
|
|
try {
|
|
val uri = if (Build.VERSION.SDK_INT >= 33){
|
|
intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
|
|
}else{
|
|
intent.getParcelableExtra(Intent.EXTRA_STREAM)
|
|
}
|
|
val `is` = contentResolver.openInputStream(uri!!)
|
|
val textBuilder = StringBuilder()
|
|
val reader: Reader = BufferedReader(
|
|
InputStreamReader(
|
|
`is`, Charset.forName(
|
|
StandardCharsets.UTF_8.name()
|
|
)
|
|
)
|
|
)
|
|
var c: Int
|
|
while (reader.read().also { c = it } != -1) {
|
|
textBuilder.append(c.toChar())
|
|
}
|
|
val bundle = Bundle()
|
|
bundle.putString("url", textBuilder.toString())
|
|
navController.popBackStack(R.id.homeFragment, true)
|
|
navController.navigate(
|
|
R.id.homeFragment,
|
|
bundle
|
|
)
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private fun checkUpdate() {
|
|
if (preferences.getBoolean("update_app", false)) {
|
|
val updateUtil = UpdateUtil(this)
|
|
lifecycleScope.launch(Dispatchers.IO){
|
|
updateUtil.updateApp{}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun askPermissions() {
|
|
val permissions = arrayListOf<String>()
|
|
if (!checkFilePermission()) {
|
|
if (Build.VERSION.SDK_INT >= 33){
|
|
permissions.add(Manifest.permission.READ_MEDIA_AUDIO)
|
|
permissions.add(Manifest.permission.READ_MEDIA_VIDEO)
|
|
}else{
|
|
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
}
|
|
}
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
if (!checkNotificationPermission()){
|
|
permissions.add(Manifest.permission.POST_NOTIFICATIONS)
|
|
}
|
|
}
|
|
|
|
if (permissions.isNotEmpty()){
|
|
ActivityCompat.requestPermissions(
|
|
this,
|
|
permissions.toTypedArray(),
|
|
1
|
|
)
|
|
}
|
|
}
|
|
|
|
override fun onRequestPermissionsResult(
|
|
requestCode: Int,
|
|
permissions: Array<String>,
|
|
grantResults: IntArray
|
|
) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
for (i in permissions.indices) {
|
|
if (permissions.contains(Manifest.permission.POST_NOTIFICATIONS)) continue
|
|
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
|
createPermissionRequestDialog()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun exit() {
|
|
finishAffinity()
|
|
exitProcess(0)
|
|
}
|
|
|
|
private fun checkFilePermission(): Boolean {
|
|
return if(Build.VERSION.SDK_INT >= 33){
|
|
(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO)
|
|
== PackageManager.PERMISSION_GRANTED) &&
|
|
(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_VIDEO)
|
|
== PackageManager.PERMISSION_GRANTED)
|
|
}else{
|
|
(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
== PackageManager.PERMISSION_GRANTED) &&
|
|
(ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
|
== PackageManager.PERMISSION_GRANTED)
|
|
}
|
|
}
|
|
|
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
|
private fun checkNotificationPermission(): Boolean {
|
|
return (ActivityCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
|
== PackageManager.PERMISSION_GRANTED)
|
|
}
|
|
|
|
private fun createPermissionRequestDialog() {
|
|
val dialog = MaterialAlertDialogBuilder(this)
|
|
dialog.setTitle(getString(R.string.warning))
|
|
dialog.setMessage(getString(R.string.request_permission_desc))
|
|
dialog.setOnCancelListener { exit() }
|
|
dialog.setNegativeButton(getString(R.string.exit_app)) { _: DialogInterface?, _: Int -> exit() }
|
|
dialog.setPositiveButton(getString(R.string.ok)) { _: DialogInterface?, _: Int ->
|
|
val intent = Intent(
|
|
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
|
Uri.fromParts("package", packageName, null)
|
|
)
|
|
startActivity(intent)
|
|
exitProcess(0)
|
|
}
|
|
dialog.show()
|
|
}
|
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
|
startActivity(Intent(this, MainActivity::class.java))
|
|
super.onConfigurationChanged(newConfig)
|
|
}
|
|
|
|
companion object {
|
|
private const val TAG = "MainActivity"
|
|
}
|
|
} |