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(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(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(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(R.id.frame_layout).updateLayoutParams { 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(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(R.id.frame_layout).updateLayoutParams { 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(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(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() 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, 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" } }