Hide navigation bar in full screen mode

This commit is contained in:
Matéo Duparc 2023-03-13 17:02:38 +01:00
parent 22b1522192
commit 821c853a22
Signed by untrusted user: hardcoresushi
GPG Key ID: AFE384344A45E13A
14 changed files with 99 additions and 75 deletions

View File

@ -50,7 +50,7 @@
<activity android:name=".explorers.ExplorerActivity"/>
<activity android:name=".explorers.ExplorerActivityPick"/>
<activity android:name=".explorers.ExplorerActivityDrop"/>
<activity android:name=".file_viewers.ImageViewer" android:configChanges="screenSize|orientation" /> <!-- don't reload content on configuration change -->
<activity android:name=".file_viewers.ImageViewer"/>
<activity android:name=".file_viewers.VideoPlayer" android:configChanges="screenSize|orientation" />
<activity android:name=".file_viewers.PdfViewer" android:configChanges="screenSize|orientation" android:theme="@style/AppTheme" />
<activity android:name=".file_viewers.AudioPlayer" android:configChanges="screenSize|orientation" />

View File

@ -125,7 +125,7 @@ class ExplorerElementAdapter(
volume.loadWholeFile(fullPath, maxSize = adapter.thumbnailMaxSize).first?.let {
if (isActive) {
withContext(Dispatchers.Main) {
if (isActive && !adapter.activity.isFinishing) {
if (isActive && !adapter.activity.isFinishing && !adapter.activity.isDestroyed) {
target = Glide.with(adapter.activity).load(it).skipMemoryCache(true).into(object : DrawableImageViewTarget(icon) {
override fun onResourceReady(
resource: Drawable,

View File

@ -1,10 +1,15 @@
package sushi.hardcore.droidfs.file_viewers
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateMargins
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
@ -29,10 +34,7 @@ abstract class FileViewerActivity: BaseActivity() {
private var wasMapped = false
protected val mappedPlaylist = mutableListOf<ExplorerElement>()
protected var currentPlaylistIndex = -1
protected open var fullscreenMode = true
private val legacyMod by lazy {
sharedPrefs.getBoolean("legacyMod", false)
}
private val isLegacyFullscreen = Build.VERSION.SDK_INT <= Build.VERSION_CODES.R
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -44,38 +46,61 @@ abstract class FileViewerActivity: BaseActivity() {
windowInsetsController.addOnControllableInsetsChangedListener { _, typeMask ->
windowTypeMask = typeMask
}
if (fullscreenMode) {
fixNavBarColor()
hideSystemUi()
}
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE
viewFile()
}
private fun fixNavBarColor() {
window.navigationBarColor = ContextCompat.getColor(this, R.color.fullScreenBackgroundColor)
open fun showPartialSystemUi() {
if (isLegacyFullscreen) {
@Suppress("Deprecation")
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
} else {
windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
windowInsetsController.show(WindowInsetsCompat.Type.navigationBars())
}
}
private fun hideSystemUi() {
if (legacyMod) {
open fun hideSystemUi() {
if (isLegacyFullscreen) {
@Suppress("Deprecation")
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LOW_PROFILE or
View.SYSTEM_UI_FLAG_FULLSCREEN
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
} else {
windowInsetsController.hide(WindowInsetsCompat.Type.statusBars())
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false)
}
}
}
protected fun applyNavigationBarMargin(root: View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ViewCompat.setOnApplyWindowInsetsListener(root) { _, insets ->
root.updateLayoutParams<FrameLayout.LayoutParams> {
val newInsets = insets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars())
this.updateMargins(
left = newInsets.left,
top = newInsets.top,
right = newInsets.right,
bottom = newInsets.bottom
)
}
insets
}
} else {
root.fitsSystemWindows = true
}
}
abstract fun getFileType(): String
abstract fun viewFile()
override fun onUserInteraction() {
super.onUserInteraction()
if (fullscreenMode && windowTypeMask and WindowInsetsCompat.Type.statusBars() == 0) {
hideSystemUi()
}
}
protected fun loadWholeFile(path: String, fileSize: Long? = null, callback: (ByteArray) -> Unit) {
lifecycleScope.launch(Dispatchers.IO) {
val result = encryptedVolume.loadWholeFile(path, size = fileSize)

View File

@ -10,6 +10,8 @@ import android.view.View
import android.view.WindowManager
import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.viewModels
import androidx.lifecycle.ViewModel
import com.bumptech.glide.Glide
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
@ -31,18 +33,23 @@ class ImageViewer: FileViewerActivity() {
private const val MIN_SWIPE_DISTANCE = 150
}
class ImageViewModel : ViewModel() {
var imageBytes: ByteArray? = null
var rotationAngle: Float = 0f
}
private lateinit var fileName: String
private lateinit var handler: Handler
private val imageViewModel: ImageViewModel by viewModels()
private var requestBuilder: RequestBuilder<Drawable>? = null
private var x1 = 0F
private var x2 = 0F
private var slideshowActive = false
private var originalOrientation: Float = 0f
private var rotationAngle: Float = 0F
private var orientationTransformation: OrientationTransformation? = null
private val hideUI = Runnable {
binding.actionButtons.visibility = View.GONE
binding.topBar.visibility = View.GONE
hideSystemUi()
}
private val slideshowNext = Runnable {
if (slideshowActive){
@ -60,6 +67,8 @@ class ImageViewer: FileViewerActivity() {
binding = ActivityImageViewerBinding.inflate(layoutInflater)
setContentView(binding.root)
supportActionBar?.hide()
showPartialSystemUi()
applyNavigationBarMargin(binding.root)
handler = Handler(mainLooper)
binding.imageViewer.setOnInteractionListener(object : ZoomableImageView.OnInteractionListener {
override fun onSingleTap(event: MotionEvent?) {
@ -67,6 +76,7 @@ class ImageViewer: FileViewerActivity() {
if (binding.actionButtons.visibility == View.GONE) {
binding.actionButtons.visibility = View.VISIBLE
binding.topBar.visibility = View.VISIBLE
showPartialSystemUi()
handler.postDelayed(hideUI, hideDelay)
} else {
hideUI.run()
@ -102,7 +112,7 @@ class ImageViewer: FileViewerActivity() {
if (mappedPlaylist.size == 0) { //deleted all images of the playlist
goBackToExplorer()
} else {
loadImage()
loadImage(true)
}
} else {
CustomAlertDialogBuilder(this, theme)
@ -140,14 +150,8 @@ class ImageViewer: FileViewerActivity() {
swipeImage(-1F)
}
}
binding.imageRotateRight.setOnClickListener {
rotationAngle += 90
rotateImage()
}
binding.imageRotateLeft.setOnClickListener {
rotationAngle -= 90
rotateImage()
}
binding.imageRotateRight.setOnClickListener { onClickRotate(90f) }
binding.imageRotateLeft.setOnClickListener { onClickRotate(-90f) }
onBackPressedDispatcher.addCallback(this) {
if (slideshowActive) {
stopSlideshow()
@ -158,19 +162,27 @@ class ImageViewer: FileViewerActivity() {
}
}
}
loadImage()
loadImage(false)
handler.postDelayed(hideUI, hideDelay)
}
private fun loadImage(){
private fun loadImage(newImage: Boolean) {
fileName = File(filePath).name
binding.textFilename.text = fileName
requestBuilder = null
if (newImage || imageViewModel.imageBytes == null) {
loadWholeFile(filePath) {
originalOrientation = 0f
imageViewModel.imageBytes = it
requestBuilder = Glide.with(this).load(it)
requestBuilder?.into(binding.imageViewer)
rotationAngle = originalOrientation
imageViewModel.rotationAngle = 0f
}
} else {
requestBuilder = Glide.with(this).load(imageViewModel.imageBytes)
if (imageViewModel.rotationAngle.mod(360f) != 0f) {
rotateImage()
} else {
requestBuilder?.into(binding.imageViewer)
}
}
}
@ -180,10 +192,16 @@ class ImageViewer: FileViewerActivity() {
handler.postDelayed(hideUI, hideDelay)
}
private fun onClickRotate(angle: Float) {
imageViewModel.rotationAngle += angle
binding.imageViewer.restoreZoomNormal()
rotateImage()
}
private fun swipeImage(deltaX: Float, slideshowSwipe: Boolean = false){
playlistNext(deltaX < 0)
loadImage()
if (slideshowActive){
loadImage(true)
if (slideshowActive) {
if (!slideshowSwipe) { //reset slideshow delay if user swipes
handler.removeCallbacks(slideshowNext)
}
@ -214,14 +232,13 @@ class ImageViewer: FileViewerActivity() {
}
}
private fun rotateImage(){
binding.imageViewer.restoreZoomNormal()
orientationTransformation = OrientationTransformation(rotationAngle)
private fun rotateImage() {
orientationTransformation = OrientationTransformation(imageViewModel.rotationAngle)
requestBuilder?.transform(orientationTransformation)?.into(binding.imageViewer)
}
private fun askSaveRotation(callback: () -> Unit){
if (rotationAngle.mod(360f) != originalOrientation && !slideshowActive) {
if (imageViewModel.rotationAngle.mod(360f) != 0f && !slideshowActive) {
CustomAlertDialogBuilder(this, theme)
.keepFullScreen()
.setTitle(R.string.warning)
@ -235,7 +252,7 @@ class ImageViewer: FileViewerActivity() {
Bitmap.CompressFormat.PNG
} else {
Bitmap.CompressFormat.JPEG
}, 100, outputStream) == true
}, 90, outputStream) == true
){
if (encryptedVolume.importFile(ByteArrayInputStream(outputStream.toByteArray()), filePath)) {
Toast.makeText(this, R.string.image_saved_successfully, Toast.LENGTH_SHORT).show()

View File

@ -10,7 +10,6 @@ class PdfViewer: FileViewerActivity() {
init {
applyCustomTheme = false
}
override var fullscreenMode = false
private lateinit var pdfViewer: PdfViewer
override fun getFileType(): String {

View File

@ -13,7 +13,6 @@ import sushi.hardcore.droidfs.widgets.CustomAlertDialogBuilder
import java.io.File
class TextEditor: FileViewerActivity() {
override var fullscreenMode = false
private lateinit var fileName: String
private lateinit var editor: EditText
private var changedSinceLastSave = false

View File

@ -2,6 +2,7 @@ package sushi.hardcore.droidfs.file_viewers
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.view.View
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.ui.StyledPlayerView
import sushi.hardcore.droidfs.databinding.ActivityVideoPlayerBinding
@ -16,9 +17,15 @@ class VideoPlayer: MediaPlayer() {
override fun viewFile() {
binding = ActivityVideoPlayerBinding.inflate(layoutInflater)
setContentView(binding.root)
applyNavigationBarMargin(binding.root)
binding.videoPlayer.doubleTapOverlay = binding.doubleTapOverlay
binding.videoPlayer.setControllerVisibilityListener(StyledPlayerView.ControllerVisibilityListener { visibility ->
binding.topBar.visibility = visibility
if (visibility == View.VISIBLE) {
showPartialSystemUi()
} else {
hideSystemUi()
}
})
binding.rotateButton.setOnClickListener {
requestedOrientation =

View File

@ -1,6 +0,0 @@
<vector android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?attr/colorAccent"
android:pathData="M5,16c0,3.87 3.13,7 7,7s7,-3.13 7,-7v-4L5,12v4zM16.12,4.37l2.1,-2.1 -0.82,-0.83 -2.3,2.31C14.16,3.28 13.12,3 12,3s-2.16,0.28 -3.09,0.75L6.6,1.44l-0.82,0.83 2.1,2.1C6.14,5.64 5,7.68 5,10v1h14v-1c0,-2.32 -1.14,-4.36 -2.88,-5.63zM9,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM15,9c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
</vector>

View File

@ -159,8 +159,6 @@
<string name="folders_first_summary">إظهار المجلدات في بداية القائمة</string>
<string name="auto_fit_title">التدوير التلقائي لشاشة مشغل الفيديو</string>
<string name="auto_fit_summary">تدوير الشاشة تلقائيًا لتناسب أبعاد الفيديو</string>
<string name="legacy_mod_title">وضع ملء الشاشة القديم</string>
<string name="legacy_mod_summary">فعل هذا الخيار كان windowInsetsController لا يعمل بشكل صحيح على جهازك (عادة لا تكون هناك حاجة)</string>
<string name="open_tree_failed">لم يتم العثور على مستكشف للملفات. رجاء ثبت واحد ثم أعد المحاولة.</string>
<string name="close_volume">إغلاق المجلد المشفر</string>
<string name="sort_by">تصنيف حسب</string>

View File

@ -159,8 +159,6 @@
<string name="folders_first_summary">Mostrar las carpetas al principio de la lista</string>
<string name="auto_fit_title">Rotación automática de pantalla en el reproductor de vídeo</string>
<string name="auto_fit_summary">Rotación automática de la pantalla para adaptarla a las dimensiones del vídeo</string>
<string name="legacy_mod_title">Modo de pantalla completa heredado</string>
<string name="legacy_mod_summary">Prueba esto si windowInsetsController no funciona correctamente en tu dispositivo (normalmente no es necesario)</string>
<string name="open_tree_failed">No se ha encontrado ningún explorador de archivos. Por favor, instala uno y vuelve a intentarlo.</string>
<string name="close_volume">Cerrar volumen</string>
<string name="sort_by">Ordenar por</string>

View File

@ -155,8 +155,6 @@
<string name="folders_first_summary">Mostrar pastas no início da lista</string>
<string name="auto_fit_title">Girar automaticamente a tela ao reproduzir vídeo</string>
<string name="auto_fit_summary">Girar automaticamente a tela para encaixar em dimensões de vídeo</string>
<string name="legacy_mod_title">Modo legado de tela completa</string>
<string name="legacy_mod_summary">Tente isto se windowInsetsController não funciona corretamente no seu dispositivo (geralmente não é necessário)</string>
<string name="open_tree_failed">Não foi possível encontrar gerenciador de arquivos. Instale o app e tente novamente.</string>
<string name="close_volume">Fechar o volume</string>
<string name="sort_by">Ordenar por</string>

View File

@ -153,8 +153,6 @@
<string name="folders_first_summary">Показывать папки в начале списка</string>
<string name="auto_fit_title">Автоповорот видео</string>
<string name="auto_fit_summary">Автоматический поворот экрана в соответствии с размерами видео</string>
<string name="legacy_mod_title">Устаревший полноэкранный режим</string>
<string name="legacy_mod_summary">Использовать данный режим, если windowInsetsController не работает должным образом на вашем устройстве (обычно не требуется)</string>
<string name="open_tree_failed">Проводник файлов не найден. Установите его и повторите попытку.</string>
<string name="close_volume">Закрыть том</string>
<string name="sort_by">Сортировка</string>

View File

@ -160,8 +160,6 @@
<string name="folders_first_summary">Show folders at the beginning of the list</string>
<string name="auto_fit_title">Video player screen auto-rotation</string>
<string name="auto_fit_summary">Auto-rotate the screen to fit video dimensions</string>
<string name="legacy_mod_title">Legacy fullscreen mode</string>
<string name="legacy_mod_summary">Try this if windowInsetsController doesn\'t work properly on your device (usually not needed)</string>
<string name="open_tree_failed">No file explorer found. Please install one and retry.</string>
<string name="close_volume">Close volume</string>
<string name="sort_by">Sort By</string>

View File

@ -67,13 +67,6 @@
android:defaultValue="false"
android:icon="@drawable/icon_screen_rotation"/>
<SwitchPreferenceCompat
android:title="@string/legacy_mod_title"
android:summary="@string/legacy_mod_summary"
android:key="legacyMod"
android:defaultValue="false"
android:icon="@drawable/icon_mod"/>
<SwitchPreferenceCompat
android:title="@string/password_fallback"
android:summary="@string/password_fallback_summary"