Merge pull request #43 from SimpleMobileTools/master

upd
This commit is contained in:
solokot 2019-01-04 00:08:07 +03:00 committed by GitHub
commit f7c2496fd6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
81 changed files with 1069 additions and 963 deletions

View file

@ -1,6 +1,19 @@
Changelog
==========
Version 6.1.3 *(2018-12-26)*
----------------------------
* Fixed a glitch at zooming fullscreen images with double tap
* Hide favorite items from hidden folders, if showing hidden items is disabled
Version 6.1.2 *(2018-12-24)*
----------------------------
* Done a few performance improvements here and there
* Allow changing view type individually per folder
* Merry Christmas!
Version 6.1.1 *(2018-12-18)*
----------------------------

View file

@ -15,8 +15,8 @@ android {
applicationId "com.simplemobiletools.gallery.pro"
minSdkVersion 21
targetSdkVersion 28
versionCode 214
versionName "6.1.1"
versionCode 216
versionName "6.1.3"
multiDexEnabled true
setProperty("archivesBaseName", "gallery")
}
@ -57,12 +57,12 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.5.18'
implementation 'com.simplemobiletools:commons:5.6.3'
implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'androidx.multidex:multidex:2.0.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'it.sephiroth.android.exif:library:1.0.1'
implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.16'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
implementation 'com.google.android.exoplayer:exoplayer-core:2.9.2'
implementation 'com.google.vr:sdk-panowidget:1.180.0'
implementation 'com.google.vr:sdk-videowidget:1.180.0'
@ -78,7 +78,7 @@ dependencies {
//implementation 'com.davemorrissey.labs:subsampling-scale-image-view:3.10.0'
//implementation 'com.github.tibbi:subsampling-scale-image-view:v3.10.1-fork'
implementation 'com.github.tibbi:subsampling-scale-image-view:fcb724fb0a'
implementation 'com.github.tibbi:subsampling-scale-image-view:4.0.2'
// implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.github.tibbi:PhotoView:2.3.0-fork'

View file

@ -122,6 +122,11 @@
android:name=".activities.PhotoVideoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"/>
<activity
android:name=".activities.VideoPlayerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:parentActivityName=".activities.MediaActivity"/>
<activity
android:name=".activities.PanoramaPhotoActivity"
android:configChanges="orientation|keyboardHidden|screenSize"

View file

@ -459,7 +459,7 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
}
private fun changeViewType() {
ChangeViewTypeDialog(this) {
ChangeViewTypeDialog(this, true) {
invalidateOptionsMenu()
setupLayoutManager()
directories_grid.adapter = null
@ -787,10 +787,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
runOnUiThread {
checkPlaceholderVisibility(dirs)
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFolders == VIEW_TYPE_GRID
directories_vertical_fastscroller.beVisibleIf(directories_grid.isVisible() && !allowHorizontalScroll)
directories_horizontal_fastscroller.beVisibleIf(directories_grid.isVisible() && allowHorizontalScroll)
setupAdapter(dirs)
setupAdapter(dirs.clone() as ArrayList<Directory>)
}
// cached folders have been loaded, recheck folders one by one starting with the first displayed

View file

@ -22,23 +22,18 @@ import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.transition.Transition
import com.simplemobiletools.commons.dialogs.ConfirmationDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.OTG_PATH
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REQUEST_EDIT_IMAGE
import com.simplemobiletools.commons.models.FileDirItem
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.commons.views.MyGridLayoutManager
import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.adapters.MediaAdapter
import com.simplemobiletools.gallery.pro.asynctasks.GetMediaAsynctask
import com.simplemobiletools.gallery.pro.databases.GalleryDatabase
import com.simplemobiletools.gallery.pro.dialogs.ChangeGroupingDialog
import com.simplemobiletools.gallery.pro.dialogs.ChangeSortingDialog
import com.simplemobiletools.gallery.pro.dialogs.ExcludeFolderDialog
import com.simplemobiletools.gallery.pro.dialogs.FilterMediaDialog
import com.simplemobiletools.gallery.pro.dialogs.*
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.interfaces.DirectoryDao
@ -224,10 +219,10 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
findItem(R.id.temporarily_show_hidden).isVisible = !config.shouldShowHidden
findItem(R.id.stop_showing_hidden).isVisible = config.temporarilyShowHidden
findItem(R.id.increase_column_count).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt < MAX_COLUMN_COUNT
findItem(R.id.reduce_column_count).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID && config.mediaColumnCnt > 1
findItem(R.id.toggle_filename).isVisible = config.viewTypeFiles == VIEW_TYPE_GRID
val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
findItem(R.id.increase_column_count).isVisible = viewType == VIEW_TYPE_GRID && config.mediaColumnCnt < MAX_COLUMN_COUNT
findItem(R.id.reduce_column_count).isVisible = viewType == VIEW_TYPE_GRID && config.mediaColumnCnt > 1
findItem(R.id.toggle_filename).isVisible = viewType == VIEW_TYPE_GRID
}
setupSearch(menu)
@ -367,7 +362,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
initZoomListener()
val fastscroller = if (config.scrollHorizontally) media_horizontal_fastscroller else media_vertical_fastscroller
MediaAdapter(this, mMedia.clone() as ArrayList<ThumbnailItem>, this, mIsGetImageIntent || mIsGetVideoIntent || mIsGetAnyIntent,
mAllowPickingMultiple, media_grid, fastscroller) {
mAllowPickingMultiple, mPath, media_grid, fastscroller) {
if (it is Medium) {
itemClicked(it.path)
}
@ -385,7 +380,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun setupScrollDirection() {
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
val allowHorizontalScroll = config.scrollHorizontally && viewType == VIEW_TYPE_GRID
media_vertical_fastscroller.isHorizontal = false
media_vertical_fastscroller.beGoneIf(allowHorizontalScroll)
@ -493,12 +489,7 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun changeViewType() {
val items = arrayListOf(
RadioItem(VIEW_TYPE_GRID, getString(R.string.grid)),
RadioItem(VIEW_TYPE_LIST, getString(R.string.list)))
RadioGroupDialog(this, items, config.viewTypeFiles) {
config.viewTypeFiles = it as Int
ChangeViewTypeDialog(this, false, mPath) {
invalidateOptionsMenu()
setupLayoutManager()
media_grid.adapter = null
@ -646,7 +637,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun setupLayoutManager() {
if (config.viewTypeFiles == VIEW_TYPE_GRID) {
val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
if (viewType == VIEW_TYPE_GRID) {
setupGridLayoutManager()
} else {
setupListLayoutManager()
@ -722,7 +714,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
}
private fun initZoomListener() {
if (config.viewTypeFiles == VIEW_TYPE_GRID) {
val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
if (viewType == VIEW_TYPE_GRID) {
val layoutManager = media_grid.layoutManager as MyGridLayoutManager
mZoomListener = object : MyRecyclerView.MyZoomListener {
override fun zoomIn() {
@ -840,7 +833,8 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
media_empty_text.beVisibleIf(media.isEmpty() && !isFromCache)
media_grid.beVisibleIf(media_empty_text_label.isGone())
val allowHorizontalScroll = config.scrollHorizontally && config.viewTypeFiles == VIEW_TYPE_GRID
val viewType = config.getFolderViewType(if (mShowAll) SHOW_ALL else mPath)
val allowHorizontalScroll = config.scrollHorizontally && viewType == VIEW_TYPE_GRID
media_vertical_fastscroller.beVisibleIf(media_grid.isVisible() && !allowHorizontalScroll)
media_horizontal_fastscroller.beVisibleIf(media_grid.isVisible() && allowHorizontalScroll)
setupAdapter()

View file

@ -8,23 +8,17 @@ import android.os.Handler
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.view.animation.AnimationUtils
import android.widget.RelativeLayout
import android.widget.SeekBar
import com.google.vr.sdk.widgets.video.VrVideoEventListener
import com.google.vr.sdk.widgets.video.VrVideoView
import com.simplemobiletools.commons.extensions.getFormattedDuration
import com.simplemobiletools.commons.extensions.onGlobalLayout
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.isPiePlus
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.HIDE_PLAY_PAUSE_DELAY
import com.simplemobiletools.gallery.pro.helpers.MIN_SKIP_LENGTH
import com.simplemobiletools.gallery.pro.helpers.PATH
import com.simplemobiletools.gallery.pro.helpers.PLAY_PAUSE_VISIBLE_ALPHA
import kotlinx.android.synthetic.main.activity_panorama_video.*
import kotlinx.android.synthetic.main.bottom_video_time_holder.*
import java.io.File
@ -41,7 +35,6 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
private var mDuration = 0
private var mCurrTime = 0
private var mHidePlayPauseHandler = Handler()
private var mTimerHandler = Handler()
public override fun onCreate(savedInstanceState: Bundle?) {
@ -56,18 +49,6 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
setupButtons()
cardboard.setOnClickListener {
vr_video_view.displayMode = CARDBOARD_DISPLAY_MODE
}
explore.setOnClickListener {
mIsExploreEnabled = !mIsExploreEnabled
vr_video_view.setPureTouchTracking(mIsExploreEnabled)
explore.setImageResource(if (mIsExploreEnabled) R.drawable.ic_explore else R.drawable.ic_explore_off)
}
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
checkIntent()
@ -102,7 +83,6 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
}
if (!isChangingConfigurations) {
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
mTimerHandler.removeCallbacksAndMessages(null)
}
}
@ -115,6 +95,7 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
return
}
setupButtons()
intent.removeExtra(PATH)
video_curr_time.setOnClickListener { skip(false) }
@ -152,10 +133,14 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
setupTimer()
}
if (mPlayOnReady) {
if (mPlayOnReady || config.autoplayVideos) {
mPlayOnReady = false
playVideo()
mIsPlaying = true
resumeVideo()
} else {
video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
}
video_toggle_play_pause.beVisible()
}
override fun onCompletion() {
@ -164,7 +149,7 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
})
}
video_play_outline.setOnClickListener {
video_toggle_play_pause.setOnClickListener {
togglePlayPause()
}
} catch (e: Exception) {
@ -205,18 +190,15 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
private fun togglePlayPause() {
mIsPlaying = !mIsPlaying
video_play_outline.alpha = PLAY_PAUSE_VISIBLE_ALPHA
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
if (mIsPlaying) {
playVideo()
resumeVideo()
} else {
pauseVideo()
}
schedulePlayPauseFadeOut()
}
private fun playVideo() {
video_play_outline.setImageResource(R.drawable.ic_pause)
private fun resumeVideo() {
video_toggle_play_pause.setImageResource(R.drawable.ic_pause_outline)
if (mCurrTime == mDuration) {
setVideoProgress(0)
mPlayOnReady = true
@ -229,7 +211,7 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
private fun pauseVideo() {
vr_video_view.pauseVideo()
video_play_outline.setImageResource(R.drawable.ic_play)
video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
@ -246,55 +228,57 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
video_seekbar.progress = video_seekbar.max
video_curr_time.text = mDuration.getFormattedDuration()
pauseVideo()
video_play_outline.alpha = PLAY_PAUSE_VISIBLE_ALPHA
}
private fun schedulePlayPauseFadeOut() {
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
mHidePlayPauseHandler.postDelayed({
video_play_outline.animate().alpha(0f).start()
}, HIDE_PLAY_PAUSE_DELAY)
}
private fun setupButtons() {
val navBarHeight = navigationBarHeight
video_time_holder.apply {
(layoutParams as RelativeLayout.LayoutParams).bottomMargin = navBarHeight
setPadding(paddingLeft, paddingTop, navigationBarWidth, paddingBottom)
var right = 0
var bottom = 0
if (hasNavBar()) {
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
bottom += navigationBarHeight
} else {
right += navigationBarWidth
bottom += navigationBarHeight
}
}
video_time_holder.setPadding(0, 0, right, bottom)
video_time_holder.onGlobalLayout {
(explore.layoutParams as RelativeLayout.LayoutParams).bottomMargin = navBarHeight + video_time_holder.height
val newBottomMargin = video_time_holder.height - resources.getDimension(R.dimen.video_player_play_pause_size).toInt() - resources.getDimension(R.dimen.activity_margin).toInt()
(explore.layoutParams as RelativeLayout.LayoutParams).bottomMargin = newBottomMargin
(cardboard.layoutParams as RelativeLayout.LayoutParams).apply {
bottomMargin = navBarHeight + video_time_holder.height
bottomMargin = newBottomMargin
rightMargin = navigationBarWidth
}
vr_view_gradient_background.layoutParams.height = navBarHeight + video_time_holder.height + explore.height
explore.requestLayout()
}
video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
cardboard.setOnClickListener {
vr_video_view.displayMode = CARDBOARD_DISPLAY_MODE
}
explore.setOnClickListener {
mIsExploreEnabled = !mIsExploreEnabled
vr_video_view.setPureTouchTracking(mIsExploreEnabled)
explore.setImageResource(if (mIsExploreEnabled) R.drawable.ic_explore else R.drawable.ic_explore_off)
}
}
private fun toggleButtonVisibility() {
val newAlpha = if (mIsFullscreen) 0f else 1f
arrayOf(cardboard, explore, vr_view_gradient_background).forEach {
arrayOf(cardboard, explore).forEach {
it.animate().alpha(newAlpha)
}
arrayOf(cardboard, explore, video_toggle_play_pause, video_curr_time, video_duration).forEach {
it.isClickable = !mIsFullscreen
}
var anim = android.R.anim.fade_in
if (mIsFullscreen) {
anim = android.R.anim.fade_out
video_seekbar.setOnSeekBarChangeListener(null)
} else {
video_seekbar.setOnSeekBarChangeListener(this)
}
AnimationUtils.loadAnimation(this, anim).apply {
duration = 150
fillAfter = true
video_time_holder.startAnimation(this)
}
video_seekbar.setOnSeekBarChangeListener(if (mIsFullscreen) null else this)
video_time_holder.animate().alpha(newAlpha).start()
}
private fun handleClick() {
@ -336,8 +320,7 @@ open class PanoramaVideoActivity : SimpleActivity(), SeekBar.OnSeekBarChangeList
override fun onStopTrackingTouch(seekBar: SeekBar?) {
mIsPlaying = true
playVideo()
resumeVideo()
mIsDragged = false
schedulePlayPauseFadeOut()
}
}

View file

@ -16,6 +16,7 @@ import com.simplemobiletools.commons.helpers.IS_FROM_GALLERY
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.REAL_FILE_PATH
import com.simplemobiletools.commons.helpers.isPiePlus
import com.simplemobiletools.gallery.pro.BuildConfig
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.fragments.PhotoFragment
@ -26,6 +27,7 @@ import com.simplemobiletools.gallery.pro.models.Medium
import kotlinx.android.synthetic.main.bottom_actions.*
import kotlinx.android.synthetic.main.fragment_holder.*
import java.io.File
import java.io.FileInputStream
open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentListener {
@ -63,6 +65,12 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
private fun checkIntent(savedInstanceState: Bundle? = null) {
mUri = intent.data ?: return
var filename = getFilenameFromUri(mUri!!)
mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
if (mIsFromGallery && filename.isVideoFast()) {
launchVideoPlayer()
return
}
if (intent.extras?.containsKey(REAL_FILE_PATH) == true) {
val realPath = intent.extras.getString(REAL_FILE_PATH)
if (realPath != null) {
@ -76,7 +84,6 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
}
}
mIsFromGallery = intent.getBooleanExtra(IS_FROM_GALLERY, false)
if (mUri!!.scheme == "file") {
if (filename.contains('.')) {
scanPathRecursively(mUri!!.path)
@ -134,6 +141,41 @@ open class PhotoVideoActivity : SimpleActivity(), ViewPagerFragment.FragmentList
initBottomActions()
}
private fun launchVideoPlayer() {
val newUri = getFinalUriFromPath(mUri.toString(), BuildConfig.APPLICATION_ID)
if (newUri == null) {
toast(R.string.unknown_error_occurred)
return
}
var isPanorama = false
val realPath = intent?.extras?.getString(REAL_FILE_PATH) ?: ""
try {
if (realPath.isNotEmpty()) {
val fis = FileInputStream(File(realPath))
parseFileChannel(realPath, fis.channel, 0, 0, 0) {
isPanorama = true
}
}
} catch (ignored: Exception) {
} catch (ignored: OutOfMemoryError) {
}
if (isPanorama) {
Intent(applicationContext, PanoramaVideoActivity::class.java).apply {
putExtra(PATH, realPath)
startActivity(this)
}
} else {
val mimeType = getUriMimeType(mUri.toString(), newUri)
Intent(applicationContext, VideoPlayerActivity::class.java).apply {
setDataAndType(newUri, mimeType)
startActivity(this)
}
}
finish()
}
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
initBottomActionsLayout()

View file

@ -0,0 +1,522 @@
package com.simplemobiletools.gallery.pro.activities
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Color
import android.graphics.Point
import android.graphics.SurfaceTexture
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.util.DisplayMetrics
import android.view.*
import android.widget.SeekBar
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.TrackGroupArray
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
import com.google.android.exoplayer2.upstream.ContentDataSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.video.VideoListener
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.PERMISSION_WRITE_STORAGE
import com.simplemobiletools.commons.helpers.isPiePlus
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.MIN_SKIP_LENGTH
import kotlinx.android.synthetic.main.activity_video_player.*
import kotlinx.android.synthetic.main.bottom_video_time_holder.*
open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListener, TextureView.SurfaceTextureListener {
private var mIsFullscreen = false
private var mIsPlaying = false
private var mWasVideoStarted = false
private var mIsDragged = false
private var mCurrTime = 0
private var mDuration = 0
private var mVideoSize = Point(0, 0)
private var mUri: Uri? = null
private var mExoPlayer: SimpleExoPlayer? = null
private var mTimerHandler = Handler()
private var mTouchDownX = 0f
private var mTouchDownY = 0f
private var mCloseDownThreshold = 100f
private var mIgnoreCloseDown = false
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_player)
handlePermission(PERMISSION_WRITE_STORAGE) {
if (it) {
initPlayer()
} else {
toast(R.string.no_storage_permissions)
finish()
}
}
}
override fun onResume() {
super.onResume()
top_shadow.layoutParams.height = statusBarHeight + actionBarHeight
supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
window.statusBarColor = Color.TRANSPARENT
window.navigationBarColor = Color.TRANSPARENT
if (config.blackBackground) {
video_player_holder.background = ColorDrawable(Color.BLACK)
}
if (config.maxBrightness) {
val attributes = window.attributes
attributes.screenBrightness = 1f
window.attributes = attributes
}
updateTextColors(video_player_holder)
}
override fun onPause() {
super.onPause()
pauseVideo()
if (config.rememberLastVideoPosition && mWasVideoStarted) {
saveVideoProgress()
}
}
override fun onDestroy() {
super.onDestroy()
if (!isChangingConfigurations) {
cleanup()
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_video_player, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_change_orientation -> changeOrientation()
else -> return super.onOptionsItemSelected(item)
}
return true
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
setVideoSize()
initTimeHolder()
}
private fun initPlayer() {
mUri = intent.data ?: return
supportActionBar?.title = getFilenameFromUri(mUri!!)
initTimeHolder()
if (isPiePlus()) {
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
showSystemUI(true)
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
val isFullscreen = visibility and View.SYSTEM_UI_FLAG_FULLSCREEN != 0
fullscreenToggled(isFullscreen)
}
// adding an empty click listener just to avoid ripple animation at toggling fullscreen
video_seekbar.setOnClickListener { }
video_curr_time.setOnClickListener { skip(false) }
video_duration.setOnClickListener { skip(true) }
video_toggle_play_pause.setOnClickListener { togglePlayPause() }
video_player_holder.setOnClickListener {
fullscreenToggled(!mIsFullscreen)
}
if (config.allowDownGesture) {
video_player_holder.setOnTouchListener { v, event ->
handleEvent(event)
false
}
video_surface.setOnTouchListener { v, event ->
handleEvent(event)
false
}
}
initExoPlayer()
video_surface.surfaceTextureListener = this
video_surface.setOnClickListener {
fullscreenToggled(!mIsFullscreen)
}
if (config.allowVideoGestures) {
video_brightness_controller.initialize(this, slide_info, true, video_player_holder) { x, y ->
video_player_holder.performClick()
}
video_volume_controller.initialize(this, slide_info, false, video_player_holder) { x, y ->
video_player_holder.performClick()
}
} else {
video_brightness_controller.beGone()
video_volume_controller.beGone()
}
if (config.hideSystemUI) {
Handler().postDelayed({
fullscreenToggled(true)
}, 500)
}
}
private fun initExoPlayer() {
val dataSpec = DataSpec(mUri)
val fileDataSource = ContentDataSource(applicationContext)
try {
fileDataSource.open(dataSpec)
} catch (e: Exception) {
showErrorToast(e)
}
val factory = DataSource.Factory { fileDataSource }
val audioSource = ExtractorMediaSource(fileDataSource.uri, factory, DefaultExtractorsFactory(), null, null)
mExoPlayer = ExoPlayerFactory.newSimpleInstance(applicationContext).apply {
seekParameters = SeekParameters.CLOSEST_SYNC
audioStreamType = C.STREAM_TYPE_MUSIC
prepare(audioSource)
}
initExoPlayerListeners()
}
private fun initExoPlayerListeners() {
mExoPlayer!!.addListener(object : Player.EventListener {
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
override fun onSeekProcessed() {}
override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
override fun onPlayerError(error: ExoPlaybackException?) {}
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onRepeatModeChanged(repeatMode: Int) {}
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
when (playbackState) {
Player.STATE_READY -> videoPrepared()
Player.STATE_ENDED -> videoCompleted()
}
}
})
mExoPlayer!!.addVideoListener(object : VideoListener {
override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
mVideoSize.x = width
mVideoSize.y = height
setVideoSize()
}
override fun onRenderedFirstFrame() {}
})
}
private fun videoPrepared() {
if (!mWasVideoStarted) {
video_toggle_play_pause.beVisible()
mDuration = (mExoPlayer!!.duration / 1000).toInt()
video_seekbar.max = mDuration
video_duration.text = mDuration.getFormattedDuration()
setPosition(mCurrTime)
if (config.rememberLastVideoPosition) {
setLastVideoSavedPosition()
}
if (config.autoplayVideos) {
resumeVideo()
} else {
video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
}
}
}
private fun resumeVideo() {
video_toggle_play_pause.setImageResource(R.drawable.ic_pause_outline)
if (mExoPlayer == null) {
return
}
val wasEnded = didVideoEnd()
if (wasEnded) {
setPosition(0)
}
mWasVideoStarted = true
mIsPlaying = true
mExoPlayer?.playWhenReady = true
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun pauseVideo() {
video_toggle_play_pause.setImageResource(R.drawable.ic_play_outline)
if (mExoPlayer == null) {
return
}
mIsPlaying = false
if (!didVideoEnd()) {
mExoPlayer?.playWhenReady = false
}
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun togglePlayPause() {
mIsPlaying = !mIsPlaying
if (mIsPlaying) {
resumeVideo()
} else {
pauseVideo()
}
}
private fun setPosition(seconds: Int) {
mExoPlayer?.seekTo(seconds * 1000L)
video_seekbar.progress = seconds
video_curr_time.text = seconds.getFormattedDuration()
}
private fun setLastVideoSavedPosition() {
if (config.lastVideoPath == mUri.toString() && config.lastVideoPosition > 0) {
setPosition(config.lastVideoPosition)
}
}
private fun videoCompleted() {
if (mExoPlayer == null) {
return
}
clearLastVideoSavedProgress()
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
if (config.loopVideos) {
resumeVideo()
} else {
video_seekbar.progress = video_seekbar.max
video_curr_time.text = mDuration.getFormattedDuration()
pauseVideo()
}
}
private fun didVideoEnd(): Boolean {
val currentPos = mExoPlayer?.currentPosition ?: 0
val duration = mExoPlayer?.duration ?: 0
return currentPos != 0L && currentPos >= duration
}
private fun saveVideoProgress() {
if (!didVideoEnd()) {
config.apply {
lastVideoPosition = mExoPlayer!!.currentPosition.toInt() / 1000
lastVideoPath = mUri.toString()
}
}
}
private fun clearLastVideoSavedProgress() {
config.apply {
lastVideoPosition = 0
lastVideoPath = ""
}
}
private fun setVideoSize() {
val videoProportion = mVideoSize.x.toFloat() / mVideoSize.y.toFloat()
val display = windowManager.defaultDisplay
val screenWidth: Int
val screenHeight: Int
val realMetrics = DisplayMetrics()
display.getRealMetrics(realMetrics)
screenWidth = realMetrics.widthPixels
screenHeight = realMetrics.heightPixels
val screenProportion = screenWidth.toFloat() / screenHeight.toFloat()
video_surface.layoutParams.apply {
if (videoProportion > screenProportion) {
width = screenWidth
height = (screenWidth.toFloat() / videoProportion).toInt()
} else {
width = (videoProportion * screenHeight.toFloat()).toInt()
height = screenHeight
}
video_surface.layoutParams = this
}
}
private fun changeOrientation() {
requestedOrientation = if (resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
} else {
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
private fun fullscreenToggled(isFullScreen: Boolean) {
mIsFullscreen = isFullScreen
if (isFullScreen) {
hideSystemUI(true)
} else {
showSystemUI(true)
}
val newAlpha = if (isFullScreen) 0f else 1f
top_shadow.animate().alpha(newAlpha).start()
video_time_holder.animate().alpha(newAlpha).start()
video_seekbar.setOnSeekBarChangeListener(if (mIsFullscreen) null else this)
arrayOf(video_toggle_play_pause, video_curr_time, video_duration).forEach {
it.isClickable = !mIsFullscreen
}
}
private fun initTimeHolder() {
var right = 0
var bottom = 0
if (hasNavBar()) {
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
bottom += navigationBarHeight
} else {
right += navigationBarWidth
bottom += navigationBarHeight
}
}
video_time_holder.setPadding(0, 0, right, bottom)
video_seekbar.setOnSeekBarChangeListener(this)
video_seekbar!!.max = mDuration
video_duration.text = mDuration.getFormattedDuration()
video_curr_time.text = mCurrTime.getFormattedDuration()
setupTimer()
}
private fun setupTimer() {
runOnUiThread(object : Runnable {
override fun run() {
if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
video_seekbar.progress = mCurrTime
video_curr_time.text = mCurrTime.getFormattedDuration()
}
mTimerHandler.postDelayed(this, 1000)
}
})
}
private fun skip(forward: Boolean) {
if (mExoPlayer == null) {
return
}
val curr = mExoPlayer!!.currentPosition
val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
val newProgress = if (forward) curr + twoPercents else curr - twoPercents
val roundProgress = Math.round(newProgress / 1000f)
val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
setPosition(limitedProgress)
if (!mIsPlaying) {
togglePlayPause()
}
}
private fun handleEvent(event: MotionEvent) {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
mTouchDownX = event.x
mTouchDownY = event.y
}
MotionEvent.ACTION_POINTER_DOWN -> mIgnoreCloseDown = true
MotionEvent.ACTION_UP -> {
val diffX = mTouchDownX - event.x
val diffY = mTouchDownY - event.y
if (!mIgnoreCloseDown && Math.abs(diffY) > Math.abs(diffX) && diffY < -mCloseDownThreshold) {
supportFinishAfterTransition()
}
mIgnoreCloseDown = false
}
}
}
private fun cleanup() {
pauseVideo()
video_curr_time.text = 0.getFormattedDuration()
releaseExoPlayer()
video_seekbar.progress = 0
mTimerHandler.removeCallbacksAndMessages(null)
}
private fun releaseExoPlayer() {
mExoPlayer?.stop()
Thread {
mExoPlayer?.release()
mExoPlayer = null
}.start()
}
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
if (mExoPlayer != null && fromUser) {
setPosition(progress)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
mIsDragged = true
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
if (mExoPlayer == null)
return
if (mIsPlaying) {
mExoPlayer!!.playWhenReady = true
} else {
togglePlayPause()
}
mIsDragged = false
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?) = false
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
Thread {
mExoPlayer?.setVideoSurface(Surface(video_surface!!.surfaceTexture))
}.start()
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
}
}

View file

@ -41,7 +41,6 @@ import com.simplemobiletools.gallery.pro.dialogs.SaveAsDialog
import com.simplemobiletools.gallery.pro.dialogs.SlideshowDialog
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.fragments.PhotoFragment
import com.simplemobiletools.gallery.pro.fragments.VideoFragment
import com.simplemobiletools.gallery.pro.fragments.ViewPagerFragment
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.models.Medium
@ -129,10 +128,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
setupRotation()
invalidateOptionsMenu()
if (config.blackBackground) {
updateStatusbarColor(Color.BLACK)
}
supportActionBar?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
window.statusBarColor = Color.TRANSPARENT
}
@ -500,8 +495,6 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
swipeToNextMedium()
}
}, mSlideshowInterval * 1000L)
} else {
(getCurrentFragment() as? VideoFragment)!!.playVideo()
}
}
}
@ -511,18 +504,9 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
}
private fun getMediaForSlideshow(): Boolean {
mSlideshowMedia = mMediaFiles.toMutableList()
if (!config.slideshowIncludePhotos) {
mSlideshowMedia = mSlideshowMedia.filter { !it.isImage() } as MutableList
}
if (!config.slideshowIncludeVideos) {
mSlideshowMedia = mSlideshowMedia.filter { it.isImage() || it.isGIF() } as MutableList
}
if (!config.slideshowIncludeGIFs) {
mSlideshowMedia = mSlideshowMedia.filter { !it.isGIF() } as MutableList
}
mSlideshowMedia = mMediaFiles.filter {
it.isImage() || (config.slideshowIncludeGIFs && it.isGIF())
}.toMutableList()
if (config.slideshowRandomOrder) {
mSlideshowMedia.shuffle()

View file

@ -24,6 +24,7 @@ import com.simplemobiletools.commons.views.MyRecyclerView
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.dialogs.DeleteWithRememberDialog
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_LIST
import com.simplemobiletools.gallery.pro.interfaces.MediaOperationsListener
import com.simplemobiletools.gallery.pro.models.Medium
@ -35,7 +36,7 @@ import java.text.SimpleDateFormat
import java.util.*
class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<ThumbnailItem>, val listener: MediaOperationsListener?, val isAGetIntent: Boolean,
val allowMultiplePicks: Boolean, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
val allowMultiplePicks: Boolean, val path: String, recyclerView: MyRecyclerView, fastScroller: FastScroller? = null, itemClick: (Any) -> Unit) :
MyRecyclerViewAdapter(activity, recyclerView, fastScroller, itemClick) {
private val INSTANT_LOAD_DURATION = 2000L
@ -45,7 +46,8 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Thumbnai
private val ITEM_MEDIUM = 1
private val config = activity.config
private val isListViewType = config.viewTypeFiles == VIEW_TYPE_LIST
private val viewType = config.getFolderViewType(if (config.showAll) SHOW_ALL else path)
private val isListViewType = viewType == VIEW_TYPE_LIST
private var visibleItemPaths = ArrayList<String>()
private var loadImageInstantly = false
private var delayHandler = Handler(Looper.getMainLooper())
@ -347,7 +349,12 @@ class MediaAdapter(activity: BaseSimpleActivity, var media: MutableList<Thumbnai
didUpdateFile = true
}
activity.contentResolver.applyBatch(MediaStore.AUTHORITY, operations)
val resultSize = activity.contentResolver.applyBatch(MediaStore.AUTHORITY, operations).size
if (resultSize == 0) {
didUpdateFile = false
activity.rescanPaths(paths)
}
activity.toast(if (didUpdateFile) R.string.dates_fixed_successfully else R.string.unknown_error_occurred)
activity.runOnUiThread {
listener?.refreshItems()

View file

@ -27,7 +27,7 @@ class GetMediaAsynctask(val context: Context, val mPath: String, val isPickImage
val foldersToScan = mediaFetcher.getFoldersToScan().filter { it != RECYCLE_BIN && it != FAVORITES }
val media = ArrayList<Medium>()
foldersToScan.forEach {
val newMedia = mediaFetcher.getFilesFrom(it, isPickImage, isPickVideo, getProperDateTaken, favoritePaths, getVideoDurations)
val newMedia = mediaFetcher.getFilesFrom(it, isPickImage, isPickVideo, getProperDateTaken, favoritePaths, getVideoDurations, false)
media.addAll(newMedia)
}

View file

@ -3,22 +3,47 @@ package com.simplemobiletools.gallery.pro.dialogs
import android.view.View
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.beVisibleIf
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.extensions.config
import com.simplemobiletools.gallery.pro.helpers.SHOW_ALL
import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_GRID
import com.simplemobiletools.gallery.pro.helpers.VIEW_TYPE_LIST
import kotlinx.android.synthetic.main.dialog_change_view_type.view.*
class ChangeViewTypeDialog(val activity: BaseSimpleActivity, val callback: () -> Unit) {
class ChangeViewTypeDialog(val activity: BaseSimpleActivity, val fromFoldersView: Boolean, val path: String = "", val callback: () -> Unit) {
private var view: View
private var config = activity.config
private var pathToUse = if (path.isEmpty()) SHOW_ALL else path
init {
view = activity.layoutInflater.inflate(R.layout.dialog_change_view_type, null).apply {
val viewToCheck = if (config.viewTypeFolders == VIEW_TYPE_GRID) change_view_type_dialog_radio_grid.id else change_view_type_dialog_radio_list.id
val viewToCheck = if (fromFoldersView) {
if (config.viewTypeFolders == VIEW_TYPE_GRID) {
change_view_type_dialog_radio_grid.id
} else {
change_view_type_dialog_radio_list.id
}
} else {
val currViewType = config.getFolderViewType(pathToUse)
if (currViewType == VIEW_TYPE_GRID) {
change_view_type_dialog_radio_grid.id
} else {
change_view_type_dialog_radio_list.id
}
}
change_view_type_dialog_radio.check(viewToCheck)
change_view_type_dialog_group_direct_subfolders.isChecked = config.groupDirectSubfolders
change_view_type_dialog_group_direct_subfolders.apply {
beVisibleIf(fromFoldersView)
isChecked = config.groupDirectSubfolders
}
change_view_type_dialog_use_for_this_folder.apply {
beVisibleIf(!fromFoldersView)
isChecked = config.hasCustomViewType(pathToUse)
}
}
AlertDialog.Builder(activity)
@ -31,8 +56,18 @@ class ChangeViewTypeDialog(val activity: BaseSimpleActivity, val callback: () ->
private fun dialogConfirmed() {
val viewType = if (view.change_view_type_dialog_radio.checkedRadioButtonId == view.change_view_type_dialog_radio_grid.id) VIEW_TYPE_GRID else VIEW_TYPE_LIST
config.viewTypeFolders = viewType
config.groupDirectSubfolders = view.change_view_type_dialog_group_direct_subfolders.isChecked
if (fromFoldersView) {
config.viewTypeFolders = viewType
config.groupDirectSubfolders = view.change_view_type_dialog_group_direct_subfolders.isChecked
} else {
if (view.change_view_type_dialog_use_for_this_folder.isChecked) {
config.saveFolderViewType(pathToUse, viewType)
} else {
config.removeFolderViewType(pathToUse)
config.viewTypeFiles = viewType
}
}
callback()
}
}

View file

@ -22,7 +22,8 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
var dialog: AlertDialog
var shownMedia = ArrayList<ThumbnailItem>()
val view = activity.layoutInflater.inflate(R.layout.dialog_medium_picker, null)
var isGridViewType = activity.config.viewTypeFiles == VIEW_TYPE_GRID
val viewType = activity.config.getFolderViewType(if (activity.config.showAll) SHOW_ALL else path)
var isGridViewType = viewType == VIEW_TYPE_GRID
init {
(view.media_grid.layoutManager as MyGridLayoutManager).apply {
@ -64,7 +65,7 @@ class PickMediumDialog(val activity: BaseSimpleActivity, val path: String, val c
return
shownMedia = media
val adapter = MediaAdapter(activity, shownMedia.clone() as ArrayList<ThumbnailItem>, null, true, false, view.media_grid, null) {
val adapter = MediaAdapter(activity, shownMedia.clone() as ArrayList<ThumbnailItem>, null, true, false, path, view.media_grid, null) {
if (it is Medium) {
callback(it.path)
dialog.dismiss()

View file

@ -5,7 +5,6 @@ import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.hideKeyboard
import com.simplemobiletools.commons.extensions.setupDialogStuff
import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.extensions.config
import com.simplemobiletools.gallery.pro.helpers.SLIDESHOW_DEFAULT_INTERVAL
@ -29,16 +28,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.hideKeyboard(v)
}
include_photos_holder.setOnClickListener {
interval_value.clearFocus()
include_photos.toggle()
}
include_videos_holder.setOnClickListener {
interval_value.clearFocus()
include_videos.toggle()
}
include_gifs_holder.setOnClickListener {
interval_value.clearFocus()
include_gifs.toggle()
@ -73,11 +62,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.setupDialogStuff(view, this) {
hideKeyboard()
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
if (!view.include_photos.isChecked && !view.include_videos.isChecked && !view.include_gifs.isChecked) {
activity.toast(R.string.no_media_for_slideshow)
return@setOnClickListener
}
storeValues()
callback()
dismiss()
@ -90,8 +74,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
val config = activity.config
view.apply {
interval_value.setText(config.slideshowInterval.toString())
include_photos.isChecked = config.slideshowIncludePhotos
include_videos.isChecked = config.slideshowIncludeVideos
include_gifs.isChecked = config.slideshowIncludeGIFs
random_order.isChecked = config.slideshowRandomOrder
use_fade.isChecked = config.slideshowUseFade
@ -107,8 +89,6 @@ class SlideshowDialog(val activity: BaseSimpleActivity, val callback: () -> Unit
activity.config.apply {
slideshowInterval = interval.toInt()
slideshowIncludePhotos = view.include_photos.isChecked
slideshowIncludeVideos = view.include_videos.isChecked
slideshowIncludeGIFs = view.include_gifs.isChecked
slideshowRandomOrder = view.random_order.isChecked
slideshowUseFade = view.use_fade.isChecked

View file

@ -3,6 +3,7 @@ package com.simplemobiletools.gallery.pro.extensions
import android.app.Activity
import android.content.Intent
import android.provider.MediaStore
import android.util.DisplayMetrics
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.simplemobiletools.commons.activities.BaseSimpleActivity
@ -79,7 +80,8 @@ fun SimpleActivity.launchAbout() {
FAQItem(R.string.faq_12_title, R.string.faq_12_text),
FAQItem(R.string.faq_13_title, R.string.faq_13_text),
FAQItem(R.string.faq_14_title, R.string.faq_14_text),
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons))
FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons),
FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons))
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
}
@ -291,3 +293,15 @@ fun BaseSimpleActivity.updateFavoritePaths(fileDirItems: ArrayList<FileDirItem>,
}
}.start()
}
fun Activity.hasNavBar(): Boolean {
val display = windowManager.defaultDisplay
val realDisplayMetrics = DisplayMetrics()
display.getRealMetrics(realDisplayMetrics)
val displayMetrics = DisplayMetrics()
display.getMetrics(displayMetrics)
return (realDisplayMetrics.widthPixels - displayMetrics.widthPixels > 0) || (realDisplayMetrics.heightPixels - displayMetrics.heightPixels > 0)
}

View file

@ -35,6 +35,9 @@ import com.simplemobiletools.gallery.pro.svg.SvgSoftwareLayerSetter
import com.simplemobiletools.gallery.pro.views.MySquareImageView
import pl.droidsonroids.gif.GifDrawable
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
import java.util.HashSet
import java.util.LinkedHashSet
import kotlin.Comparator
@ -170,10 +173,6 @@ fun Context.getSortedDirectories(source: ArrayList<Directory>): ArrayList<Direct
else -> o1.taken.compareTo(o2.taken)
}
if (result == 0) {
result = AlphanumericComparator().compare(o1.path.toLowerCase(), o2.path.toLowerCase())
}
if (sorting and SORT_DESCENDING != 0) {
result *= -1
}
@ -627,3 +626,60 @@ fun Context.updateWidgets() {
}
}
}
// based on https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/com/google/code/mp4parser/example/PrintStructure.java
fun Context.parseFileChannel(path: String, fc: FileChannel, level: Int, start: Long, end: Long, callback: () -> Unit) {
val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl")
try {
var iteration = 0
var currEnd = end
fc.position(start)
if (currEnd <= 0) {
currEnd = start + fc.size()
}
while (currEnd - fc.position() > 8) {
// just a check to avoid deadloop at some videos
if (iteration++ > 50) {
return
}
val begin = fc.position()
val byteBuffer = ByteBuffer.allocate(8)
fc.read(byteBuffer)
byteBuffer.rewind()
val size = IsoTypeReader.readUInt32(byteBuffer)
val type = IsoTypeReader.read4cc(byteBuffer)
val newEnd = begin + size
if (type == "uuid") {
val fis = FileInputStream(File(path))
fis.skip(begin)
val sb = StringBuilder()
val buffer = ByteArray(1024)
while (true) {
val n = fis.read(buffer)
if (n != -1) {
sb.append(String(buffer, 0, n))
} else {
break
}
}
val xmlString = sb.toString().toLowerCase()
if (xmlString.contains("gspherical:projectiontype>equirectangular") || xmlString.contains("gspherical:projectiontype=\"equirectangular\"")) {
callback.invoke()
}
return
}
if (FILE_CHANNEL_CONTAINERS.contains(type)) {
parseFileChannel(path, fc, level + 1, begin + 8, newEnd, callback)
}
fc.position(newEnd)
}
} catch (ignored: Exception) {
}
}

View file

@ -18,12 +18,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.DecodeFormat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.Target
import com.davemorrissey.labs.subscaleview.ImageSource
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.davemorrissey.labs.subscaleview.decoder.DecoderFactory
@ -55,7 +55,7 @@ import java.util.*
class PhotoFragment : ViewPagerFragment() {
private val DEFAULT_DOUBLE_TAP_ZOOM = 2f
private val ZOOMABLE_VIEW_LOAD_DELAY = 300L
private val ZOOMABLE_VIEW_LOAD_DELAY = 150L
// devices with good displays, but the rest of the hardware not good enough for them
private val WEIRD_DEVICES = arrayListOf(
@ -119,10 +119,7 @@ class PhotoFragment : ViewPagerFragment() {
}
}
if (ViewPagerActivity.screenWidth == 0 || ViewPagerActivity.screenHeight == 0) {
measureScreen()
}
checkScreenDimensions()
storeStateVariables()
if (!isFragmentVisible && activity is PhotoActivity) {
isFragmentVisible = true
@ -227,6 +224,12 @@ class PhotoFragment : ViewPagerFragment() {
}
}
private fun checkScreenDimensions() {
if (ViewPagerActivity.screenWidth == 0 || ViewPagerActivity.screenHeight == 0) {
measureScreen()
}
}
private fun measureScreen() {
val metrics = DisplayMetrics()
activity!!.windowManager.defaultDisplay.getRealMetrics(metrics)
@ -297,6 +300,7 @@ class PhotoFragment : ViewPagerFragment() {
}
private fun loadBitmap(degrees: Int = 0) {
checkScreenDimensions()
var pathToLoad = if (medium.path.startsWith("content://")) medium.path else "file://${medium.path}"
pathToLoad = pathToLoad.replace("%", "%25").replace("#", "%23")
@ -350,7 +354,7 @@ class PhotoFragment : ViewPagerFragment() {
.listener(object : RequestListener<Bitmap> {
override fun onLoadFailed(e: GlideException?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, isFirstResource: Boolean) = false
override fun onResourceReady(resource: Bitmap?, model: Any?, target: com.bumptech.glide.request.target.Target<Bitmap>?, dataSource: DataSource?, isFirstResource: Boolean): Boolean {
override fun onResourceReady(resource: Bitmap?, model: Any?, target: Target<Bitmap>?, dataSource: com.bumptech.glide.load.DataSource?, isFirstResource: Boolean): Boolean {
if (isFragmentVisible) {
scheduleZoomableView()
}

View file

@ -2,75 +2,33 @@ package com.simplemobiletools.gallery.pro.fragments
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Point
import android.graphics.SurfaceTexture
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.util.DisplayMetrics
import android.view.*
import android.view.animation.AnimationUtils
import android.widget.SeekBar
import android.widget.TextView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.bumptech.glide.Glide
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.source.TrackGroupArray
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
import com.google.android.exoplayer2.upstream.ContentDataSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DataSpec
import com.google.android.exoplayer2.upstream.FileDataSource
import com.google.android.exoplayer2.video.VideoListener
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.activities.PanoramaVideoActivity
import com.simplemobiletools.gallery.pro.activities.VideoActivity
import com.simplemobiletools.gallery.pro.extensions.*
import com.simplemobiletools.gallery.pro.helpers.*
import com.simplemobiletools.gallery.pro.helpers.MEDIUM
import com.simplemobiletools.gallery.pro.helpers.PATH
import com.simplemobiletools.gallery.pro.models.Medium
import com.simplemobiletools.gallery.pro.views.MediaSideScroll
import kotlinx.android.synthetic.main.bottom_video_time_holder.view.*
import kotlinx.android.synthetic.main.pager_video_item.view.*
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import java.nio.channels.FileChannel
class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, SeekBar.OnSeekBarChangeListener {
private val PROGRESS = "progress"
private val FILE_CHANNEL_CONTAINERS = arrayListOf("moov", "trak", "mdia", "minf", "udta", "stbl")
private var mTextureView: TextureView? = null
private var mCurrTimeView: TextView? = null
private var mSeekBar: SeekBar? = null
private var mExoPlayer: SimpleExoPlayer? = null
private var mVideoSize = Point(0, 0)
private var mTimerHandler = Handler()
private var mHidePlayPauseHandler = Handler()
private var mIsPlaying = false
private var mIsDragged = false
class VideoFragment : ViewPagerFragment() {
private var mIsFullscreen = false
private var mIsFragmentVisible = false
private var mWasFragmentInit = false
private var mIsExoPlayerInitialized = false
private var mIsPanorama = false
private var mWasVideoStarted = false
private var mCurrTime = 0
private var mDuration = 0
private var mStoredShowExtendedDetails = false
private var mStoredHideExtendedDetails = false
private var mStoredBottomActions = true
private var mStoredExtendedDetails = 0
private var mStoredRememberLastVideoPosition = false
private var mStoredLastVideoPath = ""
private var mStoredLastVideoPosition = 0
private lateinit var mTimeHolder: View
private lateinit var mBrightnessSideScroll: MediaSideScroll
private lateinit var mVolumeSideScroll: MediaSideScroll
@ -81,30 +39,19 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mView = inflater.inflate(R.layout.pager_video_item, container, false).apply {
instant_prev_item.setOnClickListener { listener?.goToPrevItem() }
instant_next_item.setOnClickListener { listener?.goToNextItem() }
video_curr_time.setOnClickListener { skip(false) }
video_duration.setOnClickListener { skip(true) }
video_holder.setOnClickListener { toggleFullscreen() }
video_preview.setOnClickListener { toggleFullscreen() }
panorama_outline.setOnClickListener { openPanorama() }
video_play_outline.setOnClickListener { launchVideoPlayer() }
// adding an empty click listener just to avoid ripple animation at toggling fullscreen
video_seekbar.setOnClickListener { }
mTimeHolder = video_time_holder
mBrightnessSideScroll = video_brightness_controller
mVolumeSideScroll = video_volume_controller
mCurrTimeView = video_curr_time
if (context.config.allowDownGesture) {
video_preview.setOnTouchListener { v, event ->
handleEvent(event)
false
}
video_surface.setOnTouchListener { v, event ->
handleEvent(event)
false
}
}
}
@ -112,46 +59,22 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
medium = arguments!!.getSerializable(MEDIUM) as Medium
Glide.with(context!!).load(medium.path).into(mView.video_preview)
// setMenuVisibility is not called at VideoActivity (third party intent)
if (!mIsFragmentVisible && activity is VideoActivity) {
mIsFragmentVisible = true
}
mIsFullscreen = activity!!.window.decorView.systemUiVisibility and View.SYSTEM_UI_FLAG_FULLSCREEN == View.SYSTEM_UI_FLAG_FULLSCREEN
initTimeHolder()
checkIfPanorama()
medium.path.getVideoResolution()?.apply {
mVideoSize.x = x
mVideoSize.y = y
if (mIsPanorama) {
mView.apply {
panorama_outline.beVisible()
video_play_outline.beGone()
mVolumeSideScroll.beGone()
mBrightnessSideScroll.beGone()
Glide.with(context!!).load(medium.path).into(video_preview)
}
if (mIsPanorama) {
mView.apply {
panorama_outline.beVisible()
video_play_outline.beGone()
mVolumeSideScroll.beGone()
mBrightnessSideScroll.beGone()
Glide.with(context!!).load(medium.path).into(video_preview)
}
}
if (!mIsPanorama) {
setupPlayer()
if (savedInstanceState != null) {
mCurrTime = savedInstanceState.getInt(PROGRESS)
}
checkFullscreen()
mWasFragmentInit = true
mExoPlayer = ExoPlayerFactory.newSimpleInstance(context)
mExoPlayer!!.seekParameters = SeekParameters.CLOSEST_SYNC
initExoPlayerListeners()
if (mVideoSize.x != 0 && mVideoSize.y != 0) {
setVideoSize()
}
mView.apply {
mBrightnessSideScroll.initialize(activity!!, slide_info, true, container) { x, y ->
video_holder.performClick()
@ -160,22 +83,10 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mVolumeSideScroll.initialize(activity!!, slide_info, false, container) { x, y ->
video_holder.performClick()
}
video_surface.onGlobalLayout {
if (mIsFragmentVisible && context?.config?.autoplayVideos == true) {
playVideo()
}
}
}
}
setupVideoDuration()
if (mStoredRememberLastVideoPosition) {
setLastVideoSavedPosition()
}
updateInstantSwitchWidths()
return mView
}
@ -197,47 +108,16 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
checkExtendedDetails()
}
if (config.bottomActions != mStoredBottomActions) {
initTimeHolder()
}
mTimeHolder.setBackgroundResource(if (config.bottomActions) 0 else R.drawable.gradient_background)
storeStateVariables()
}
override fun onPause() {
super.onPause()
pauseVideo()
if (mStoredRememberLastVideoPosition && mIsFragmentVisible && mWasVideoStarted) {
saveVideoProgress()
}
storeStateVariables()
}
override fun onDestroy() {
super.onDestroy()
if (activity?.isChangingConfigurations == false) {
cleanup()
}
}
override fun setMenuVisibility(menuVisible: Boolean) {
super.setMenuVisibility(menuVisible)
if (mIsFragmentVisible && !menuVisible) {
pauseVideo()
}
mIsFragmentVisible = menuVisible
if (mWasFragmentInit && menuVisible && context?.config?.autoplayVideos == true) {
playVideo()
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
setVideoSize()
initTimeHolder()
checkExtendedDetails()
updateInstantSwitchWidths()
}
@ -248,365 +128,17 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
mStoredHideExtendedDetails = hideExtendedDetails
mStoredExtendedDetails = extendedDetails
mStoredBottomActions = bottomActions
mStoredRememberLastVideoPosition = rememberLastVideoPosition
mStoredLastVideoPath = lastVideoPath
mStoredLastVideoPosition = lastVideoPosition
}
}
private fun setupPlayer() {
if (activity == null)
return
mView.video_play_outline.setOnClickListener { togglePlayPause() }
mTextureView = mView.video_surface
mTextureView!!.setOnClickListener { toggleFullscreen() }
mTextureView!!.surfaceTextureListener = this
checkExtendedDetails()
}
private fun saveVideoProgress() {
if (!videoEnded()) {
mStoredLastVideoPosition = mExoPlayer!!.currentPosition.toInt() / 1000
mStoredLastVideoPath = medium.path
}
context!!.config.apply {
lastVideoPosition = mStoredLastVideoPosition
lastVideoPath = mStoredLastVideoPath
}
}
private fun initExoPlayer() {
val isContentUri = medium.path.startsWith("content://")
val uri = if (isContentUri) Uri.parse(medium.path) else Uri.fromFile(File(medium.path))
val dataSpec = DataSpec(uri)
val fileDataSource = if (isContentUri) ContentDataSource(context) else FileDataSource()
try {
fileDataSource.open(dataSpec)
} catch (e: Exception) {
activity?.showErrorToast(e)
}
val factory = DataSource.Factory { fileDataSource }
val audioSource = ExtractorMediaSource(fileDataSource.uri, factory, DefaultExtractorsFactory(), null, null)
mExoPlayer!!.audioStreamType = C.STREAM_TYPE_MUSIC
mExoPlayer!!.prepare(audioSource)
}
private fun initExoPlayerListeners() {
mExoPlayer!!.addListener(object : Player.EventListener {
override fun onPlaybackParametersChanged(playbackParameters: PlaybackParameters?) {}
override fun onSeekProcessed() {}
override fun onTracksChanged(trackGroups: TrackGroupArray?, trackSelections: TrackSelectionArray?) {}
override fun onPlayerError(error: ExoPlaybackException?) {
mIsExoPlayerInitialized = false
}
override fun onLoadingChanged(isLoading: Boolean) {}
override fun onPositionDiscontinuity(reason: Int) {}
override fun onRepeatModeChanged(repeatMode: Int) {}
override fun onShuffleModeEnabledChanged(shuffleModeEnabled: Boolean) {}
override fun onTimelineChanged(timeline: Timeline?, manifest: Any?, reason: Int) {}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
mIsExoPlayerInitialized = playbackState == Player.STATE_READY || playbackState == Player.STATE_ENDED
when (playbackState) {
Player.STATE_READY -> videoPrepared()
Player.STATE_ENDED -> videoCompleted()
}
}
})
mExoPlayer!!.addVideoListener(object : VideoListener {
override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
mVideoSize.x = width
mVideoSize.y = height
setVideoSize()
}
override fun onRenderedFirstFrame() {}
})
private fun launchVideoPlayer() {
activity!!.openPath(medium.path, false)
}
private fun toggleFullscreen() {
listener?.fragmentClicked()
}
private fun setLastVideoSavedPosition() {
if (mStoredLastVideoPath == medium.path && mStoredLastVideoPosition > 0) {
setPosition(mStoredLastVideoPosition)
}
}
private fun initTimeHolder() {
val left = 0
val top = 0
var right = 0
var bottom = 0
if (hasNavBar()) {
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
bottom += context!!.navigationBarHeight
} else {
right += context!!.navigationBarWidth
bottom += context!!.navigationBarHeight
}
}
if (context!!.config.bottomActions) {
bottom += resources.getDimension(R.dimen.bottom_actions_height).toInt()
}
mTimeHolder.setPadding(left, top, right, bottom)
mSeekBar = mView.video_seekbar
mSeekBar!!.setOnSeekBarChangeListener(this)
mTimeHolder.beInvisibleIf(mIsFullscreen)
}
private fun hasNavBar(): Boolean {
val display = context!!.windowManager.defaultDisplay
val realDisplayMetrics = DisplayMetrics()
display.getRealMetrics(realDisplayMetrics)
val displayMetrics = DisplayMetrics()
display.getMetrics(displayMetrics)
return (realDisplayMetrics.widthPixels - displayMetrics.widthPixels > 0) || (realDisplayMetrics.heightPixels - displayMetrics.heightPixels > 0)
}
private fun setupTimeHolder() {
mSeekBar!!.max = mDuration
mView.video_duration.text = mDuration.getFormattedDuration()
setupTimer()
}
private fun setupTimer() {
activity!!.runOnUiThread(object : Runnable {
override fun run() {
if (mExoPlayer != null && !mIsDragged && mIsPlaying) {
mCurrTime = (mExoPlayer!!.currentPosition / 1000).toInt()
mSeekBar!!.progress = mCurrTime
mCurrTimeView!!.text = mCurrTime.getFormattedDuration()
}
mTimerHandler.postDelayed(this, 1000)
}
})
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(PROGRESS, mCurrTime)
}
private fun checkFullscreen() {
if (activity == null) {
return
}
var anim = android.R.anim.fade_in
if (mIsFullscreen) {
anim = android.R.anim.fade_out
mSeekBar!!.setOnSeekBarChangeListener(null)
} else {
mSeekBar!!.setOnSeekBarChangeListener(this)
}
AnimationUtils.loadAnimation(activity, anim).apply {
duration = 150
fillAfter = true
mTimeHolder.startAnimation(this)
}
}
private fun togglePlayPause() {
if (activity == null || !isAdded)
return
mIsPlaying = !mIsPlaying
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
if (mIsPlaying) {
playVideo()
} else {
pauseVideo()
}
}
fun playVideo() {
if (mExoPlayer == null) {
return
}
if (mView.video_preview.isVisible()) {
mView.video_preview.beGone()
initExoPlayer()
}
val wasEnded = videoEnded()
if (wasEnded) {
setPosition(0)
}
if (mStoredRememberLastVideoPosition) {
setLastVideoSavedPosition()
clearLastVideoSavedProgress()
}
if (!wasEnded || context?.config?.loopVideos == false) {
mView.video_play_outline.setImageResource(R.drawable.ic_pause)
mView.video_play_outline.alpha = PLAY_PAUSE_VISIBLE_ALPHA
}
schedulePlayPauseFadeOut()
mWasVideoStarted = true
mIsPlaying = true
mExoPlayer?.playWhenReady = true
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun clearLastVideoSavedProgress() {
mStoredLastVideoPosition = 0
mStoredLastVideoPath = ""
}
private fun pauseVideo() {
if (mExoPlayer == null) {
return
}
mIsPlaying = false
if (!videoEnded()) {
mExoPlayer?.playWhenReady = false
}
mView.video_play_outline?.setImageResource(R.drawable.ic_play)
mView.video_play_outline?.alpha = PLAY_PAUSE_VISIBLE_ALPHA
schedulePlayPauseFadeOut()
activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
private fun schedulePlayPauseFadeOut() {
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
mHidePlayPauseHandler.postDelayed({
mView.video_play_outline.animate().alpha(0f).start()
}, HIDE_PLAY_PAUSE_DELAY)
}
private fun videoEnded(): Boolean {
val currentPos = mExoPlayer?.currentPosition ?: 0
val duration = mExoPlayer?.duration ?: 0
return currentPos != 0L && currentPos >= duration
}
private fun setPosition(seconds: Int) {
mExoPlayer?.seekTo(seconds * 1000L)
mSeekBar!!.progress = seconds
mCurrTimeView!!.text = seconds.getFormattedDuration()
}
private fun setupVideoDuration() {
mDuration = medium.path.getVideoDuration()
setupTimeHolder()
setPosition(0)
}
private fun videoPrepared() {
if (mDuration == 0) {
mDuration = (mExoPlayer!!.duration / 1000).toInt()
setupTimeHolder()
setPosition(mCurrTime)
if (mIsFragmentVisible && (context!!.config.autoplayVideos)) {
playVideo()
}
}
}
private fun videoCompleted() {
if (!isAdded || mExoPlayer == null) {
return
}
mCurrTime = (mExoPlayer!!.duration / 1000).toInt()
if (listener?.videoEnded() == false && context!!.config.loopVideos) {
playVideo()
} else {
mSeekBar!!.progress = mSeekBar!!.max
mCurrTimeView!!.text = mDuration.getFormattedDuration()
pauseVideo()
}
}
private fun cleanup() {
pauseVideo()
mCurrTimeView?.text = 0.getFormattedDuration()
releaseExoPlayer()
mSeekBar?.progress = 0
mTimerHandler.removeCallbacksAndMessages(null)
mHidePlayPauseHandler.removeCallbacksAndMessages(null)
mTextureView = null
}
private fun releaseExoPlayer() {
mExoPlayer?.stop()
Thread {
mExoPlayer?.release()
mExoPlayer = null
}.start()
}
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?) = false
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
Thread {
mExoPlayer?.setVideoSurface(Surface(mTextureView!!.surfaceTexture))
}.start()
}
private fun setVideoSize() {
if (activity == null || mTextureView == null)
return
val videoProportion = mVideoSize.x.toFloat() / mVideoSize.y.toFloat()
val display = activity!!.windowManager.defaultDisplay
val screenWidth: Int
val screenHeight: Int
val realMetrics = DisplayMetrics()
display.getRealMetrics(realMetrics)
screenWidth = realMetrics.widthPixels
screenHeight = realMetrics.heightPixels
val screenProportion = screenWidth.toFloat() / screenHeight.toFloat()
mTextureView!!.layoutParams.apply {
if (videoProportion > screenProportion) {
width = screenWidth
height = (screenWidth.toFloat() / videoProportion).toInt()
} else {
width = (videoProportion * screenHeight.toFloat()).toInt()
height = screenHeight
}
mTextureView!!.layoutParams = this
}
}
private fun checkExtendedDetails() {
if (context!!.config.showExtendedDetails) {
mView.video_details.apply {
@ -628,118 +160,17 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
}
}
private fun skip(forward: Boolean) {
if (mExoPlayer == null || mIsPanorama) {
return
}
val curr = mExoPlayer!!.currentPosition
val twoPercents = Math.max((mExoPlayer!!.duration / 50).toInt(), MIN_SKIP_LENGTH)
val newProgress = if (forward) curr + twoPercents else curr - twoPercents
val roundProgress = Math.round(newProgress / 1000f)
val limitedProgress = Math.max(Math.min(mExoPlayer!!.duration.toInt(), roundProgress), 0)
setPosition(limitedProgress)
if (!mIsPlaying) {
togglePlayPause()
}
}
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (mExoPlayer != null && fromUser) {
setPosition(progress)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
if (mExoPlayer == null)
return
mExoPlayer!!.playWhenReady = false
mIsDragged = true
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
if (mIsPanorama) {
openPanorama()
return
}
if (mExoPlayer == null)
return
if (mIsPlaying) {
mExoPlayer!!.playWhenReady = true
} else {
togglePlayPause()
}
mIsDragged = false
}
private fun checkIfPanorama() {
try {
val fis = FileInputStream(File(medium.path))
parseFileChannel(fis.channel, 0, 0, 0)
context!!.parseFileChannel(medium.path, fis.channel, 0, 0, 0) {
mIsPanorama = true
}
} catch (ignored: Exception) {
} catch (ignored: OutOfMemoryError) {
}
}
// based on https://github.com/sannies/mp4parser/blob/master/examples/src/main/java/com/google/code/mp4parser/example/PrintStructure.java
private fun parseFileChannel(fc: FileChannel, level: Int, start: Long, end: Long) {
try {
var iteration = 0
var currEnd = end
fc.position(start)
if (currEnd <= 0) {
currEnd = start + fc.size()
}
while (currEnd - fc.position() > 8) {
// just a check to avoid deadloop at some videos
if (iteration++ > 50) {
return
}
val begin = fc.position()
val byteBuffer = ByteBuffer.allocate(8)
fc.read(byteBuffer)
byteBuffer.rewind()
val size = IsoTypeReader.readUInt32(byteBuffer)
val type = IsoTypeReader.read4cc(byteBuffer)
val newEnd = begin + size
if (type == "uuid") {
val fis = FileInputStream(File(medium.path))
fis.skip(begin)
val sb = StringBuilder()
val buffer = ByteArray(1024)
while (true) {
val n = fis.read(buffer)
if (n != -1) {
sb.append(String(buffer, 0, n))
} else {
break
}
}
val xmlString = sb.toString().toLowerCase()
mIsPanorama = xmlString.contains("gspherical:projectiontype>equirectangular") ||
xmlString.contains("gspherical:projectiontype=\"equirectangular\"")
return
}
if (FILE_CHANNEL_CONTAINERS.contains(type)) {
parseFileChannel(fc, level + 1, begin + 8, newEnd)
}
fc.position(newEnd)
}
} catch (ignored: Exception) {
}
}
private fun openPanorama() {
Intent(context, PanoramaVideoActivity::class.java).apply {
putExtra(PATH, medium.path)
@ -755,7 +186,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
override fun fullscreenToggled(isFullscreen: Boolean) {
mIsFullscreen = isFullscreen
checkFullscreen()
mView.video_details.apply {
if (mStoredShowExtendedDetails && isVisible()) {
animate().y(getExtendedDetailsY(height))
@ -769,7 +199,6 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S
private fun getExtendedDetailsY(height: Int): Float {
val smallMargin = resources.getDimension(R.dimen.small_margin)
val fullscreenOffset = smallMargin + if (mIsFullscreen) 0 else mTimeHolder.height
return context!!.realScreenSize.y.toFloat() - height - fullscreenOffset
return context!!.realScreenSize.y.toFloat() - height - smallMargin
}
}

View file

@ -59,6 +59,22 @@ class Config(context: Context) : BaseConfig(context) {
fun hasCustomGrouping(path: String) = prefs.contains(GROUP_FOLDER_PREFIX + path.toLowerCase())
fun saveFolderViewType(path: String, value: Int) {
if (path.isEmpty()) {
viewTypeFiles = value
} else {
prefs.edit().putInt(VIEW_TYPE_PREFIX + path.toLowerCase(), value).apply()
}
}
fun getFolderViewType(path: String) = prefs.getInt(VIEW_TYPE_PREFIX + path.toLowerCase(), viewTypeFiles)
fun removeFolderViewType(path: String) {
prefs.edit().remove(VIEW_TYPE_PREFIX + path.toLowerCase()).apply()
}
fun hasCustomViewType(path: String) = prefs.contains(VIEW_TYPE_PREFIX + path.toLowerCase())
var wasHideFolderTooltipShown: Boolean
get() = prefs.getBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, false)
set(wasShown) = prefs.edit().putBoolean(HIDE_FOLDER_TOOLTIP_SHOWN, wasShown).apply()
@ -137,7 +153,7 @@ class Config(context: Context) : BaseConfig(context) {
set(includedFolders) = prefs.edit().remove(INCLUDED_FOLDERS).putStringSet(INCLUDED_FOLDERS, includedFolders).apply()
var autoplayVideos: Boolean
get() = prefs.getBoolean(AUTOPLAY_VIDEOS, false)
get() = prefs.getBoolean(AUTOPLAY_VIDEOS, true)
set(autoplay) = prefs.edit().putBoolean(AUTOPLAY_VIDEOS, autoplay).apply()
var animateGifs: Boolean
@ -265,14 +281,6 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(SLIDESHOW_INTERVAL, SLIDESHOW_DEFAULT_INTERVAL)
set(slideshowInterval) = prefs.edit().putInt(SLIDESHOW_INTERVAL, slideshowInterval).apply()
var slideshowIncludePhotos: Boolean
get() = prefs.getBoolean(SLIDESHOW_INCLUDE_PHOTOS, true)
set(slideshowIncludePhotos) = prefs.edit().putBoolean(SLIDESHOW_INCLUDE_PHOTOS, slideshowIncludePhotos).apply()
var slideshowIncludeVideos: Boolean
get() = prefs.getBoolean(SLIDESHOW_INCLUDE_VIDEOS, false)
set(slideshowIncludeVideos) = prefs.edit().putBoolean(SLIDESHOW_INCLUDE_VIDEOS, slideshowIncludeVideos).apply()
var slideshowIncludeGIFs: Boolean
get() = prefs.getBoolean(SLIDESHOW_INCLUDE_GIFS, false)
set(slideshowIncludeGIFs) = prefs.edit().putBoolean(SLIDESHOW_INCLUDE_GIFS, slideshowIncludeGIFs).apply()
@ -384,8 +392,10 @@ class Config(context: Context) : BaseConfig(context) {
fun getEverShownFolders() = hashSetOf(
internalStoragePath,
Environment.DIRECTORY_DCIM,
Environment.DIRECTORY_PICTURES
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).absolutePath,
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).absolutePath,
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath,
"${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).absolutePath}/Screenshots"
)
var showRecycleBinAtFolders: Boolean

View file

@ -6,6 +6,7 @@ import com.simplemobiletools.commons.helpers.MONTH_SECONDS
const val DIRECTORY_SORT_ORDER = "directory_sort_order"
const val SORT_FOLDER_PREFIX = "sort_folder_"
const val GROUP_FOLDER_PREFIX = "group_folder_"
const val VIEW_TYPE_PREFIX = "view_type_folder_"
const val SHOW_HIDDEN_MEDIA = "show_hidden_media"
const val TEMPORARILY_SHOW_HIDDEN = "temporarily_show_hidden"
const val IS_THIRD_PARTY_INTENT = "is_third_party_intent"
@ -76,8 +77,6 @@ const val SHOW_WIDGET_FOLDER_NAME = "show_widget_folder_name"
// slideshow
const val SLIDESHOW_INTERVAL = "slideshow_interval"
const val SLIDESHOW_INCLUDE_PHOTOS = "slideshow_include_photos"
const val SLIDESHOW_INCLUDE_VIDEOS = "slideshow_include_videos"
const val SLIDESHOW_INCLUDE_GIFS = "slideshow_include_gifs"
const val SLIDESHOW_RANDOM_ORDER = "slideshow_random_order"
const val SLIDESHOW_USE_FADE = "slideshow_use_fade"

View file

@ -19,22 +19,27 @@ class MediaFetcher(val context: Context) {
var shouldStop = false
fun getFilesFrom(curPath: String, isPickImage: Boolean, isPickVideo: Boolean, getProperDateTaken: Boolean, favoritePaths: ArrayList<String>,
getVideoDurations: Boolean): ArrayList<Medium> {
getVideoDurations: Boolean, sortMedia: Boolean = true): ArrayList<Medium> {
val filterMedia = context.config.filterMedia
if (filterMedia == 0) {
return ArrayList()
}
val curMedia = ArrayList<Medium>()
if (curPath.startsWith(OTG_PATH)) {
val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
if (curPath.startsWith(OTG_PATH, true)) {
if (context.hasOTGConnected()) {
val newMedia = getMediaOnOTG(curPath, isPickImage, isPickVideo, filterMedia, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
}
} else {
val newMedia = getMediaInFolder(curPath, isPickImage, isPickVideo, filterMedia, getProperDateTaken, favoritePaths, getVideoDurations)
curMedia.addAll(newMedia)
}
sortMedia(curMedia, context.config.getFileSorting(curPath))
if (sortMedia) {
sortMedia(curMedia, context.config.getFileSorting(curPath))
}
return curMedia
}
@ -177,27 +182,27 @@ class MediaFetcher(val context: Context) {
ArrayList()
}
val doExtraCheck = context.config.doExtraCheck
val showHidden = context.config.shouldShowHidden
val dateTakens = if (getProperDateTaken && folder != FAVORITES && folder != RECYCLE_BIN) getFolderDateTakens(folder) else HashMap()
val files = when (folder) {
FAVORITES -> favoritePaths.map { File(it) }.toTypedArray()
FAVORITES -> favoritePaths.filter { showHidden || !it.contains("/.") }.map { File(it) }.toTypedArray()
RECYCLE_BIN -> deletedMedia.map { File(it.path) }.toTypedArray()
else -> File(folder).listFiles() ?: return media
}
val doExtraCheck = context.config.doExtraCheck
val showHidden = context.config.shouldShowHidden
val dateTakens = if (getProperDateTaken) getFolderDateTakens(folder) else HashMap()
for (file in files) {
if (shouldStop) {
break
}
val filename = file.name
val isImage = filename.isImageFast()
val isVideo = if (isImage) false else filename.isVideoFast()
val isGif = if (isImage || isVideo) false else filename.isGif()
val isRaw = if (isImage || isVideo || isGif) false else filename.isRawFast()
val isSvg = if (isImage || isVideo || isGif || isRaw) false else filename.isSvg()
val path = file.absolutePath
val isImage = path.isImageFast()
val isVideo = if (isImage) false else path.isVideoFast()
val isGif = if (isImage || isVideo) false else path.isGif()
val isRaw = if (isImage || isVideo || isGif) false else path.isRawFast()
val isSvg = if (isImage || isVideo || isGif || isRaw) false else path.isSvg()
if (!isImage && !isVideo && !isGif && !isRaw && !isSvg)
continue
@ -217,6 +222,7 @@ class MediaFetcher(val context: Context) {
if (isSvg && filterMedia and TYPE_SVGS == 0)
continue
val filename = file.name
if (!showHidden && filename.startsWith('.'))
continue
@ -224,7 +230,6 @@ class MediaFetcher(val context: Context) {
if (size <= 0L || (doExtraCheck && !file.exists()))
continue
val path = file.absolutePath
if (folder == RECYCLE_BIN) {
deletedMedia.firstOrNull { it.path == path }?.apply {
media.add(this)
@ -364,8 +369,9 @@ class MediaFetcher(val context: Context) {
else -> o1.taken.compareTo(o2.taken)
}
if (result == 0) {
result = AlphanumericComparator().compare(o1.path.toLowerCase(), o2.path.toLowerCase())
// do just a quick extra sorting if the original sorting is equal, does not need to be accurate in all cases
if (result == 0 && sorting and SORT_BY_NAME == 0 && sorting and SORT_BY_PATH == 0) {
result = o1.name.compareTo(o2.name)
}
if (sorting and SORT_DESCENDING != 0) {

View file

@ -10,8 +10,8 @@ import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.RelativeLayout
import android.widget.TextView
import com.simplemobiletools.commons.extensions.onGlobalLayout
import com.simplemobiletools.gallery.pro.R
import com.simplemobiletools.gallery.pro.activities.ViewPagerActivity
import com.simplemobiletools.gallery.pro.extensions.audioManager
import com.simplemobiletools.gallery.pro.helpers.CLICK_MAX_DURATION
import com.simplemobiletools.gallery.pro.helpers.DRAG_THRESHOLD
@ -25,6 +25,7 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
private var mTouchDownValue = -1
private var mTempBrightness = 0
private var mLastTouchY = 0f
private var mViewHeight = 0
private var mIsBrightnessScroll = false
private var mPassTouches = false
private var dragThreshold = DRAG_THRESHOLD * context.resources.displayMetrics.density
@ -44,6 +45,9 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
mParentView = parentView
mIsBrightnessScroll = isBrightness
mSlideInfoText = activity.getString(if (isBrightness) R.string.brightness else R.string.volume)
onGlobalLayout {
mViewHeight = height
}
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
@ -80,7 +84,7 @@ class MediaSideScroll(context: Context, attrs: AttributeSet) : RelativeLayout(co
val diffY = mTouchDownY - event.y
if (Math.abs(diffY) > dragThreshold && Math.abs(diffY) > Math.abs(diffX)) {
var percent = ((diffY / ViewPagerActivity.screenHeight) * 100).toInt() * 3
var percent = ((diffY / mViewHeight) * 100).toInt() * 3
percent = Math.min(100, Math.max(-100, percent))
if ((percent == 100 && event.y > mLastTouchY) || (percent == -100 && event.y < mLastTouchY)) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/circle_black_background"
android:insetBottom="@dimen/activity_margin"
android:insetLeft="@dimen/activity_margin"
android:insetRight="@dimen/activity_margin"
android:insetTop="@dimen/activity_margin"/>

View file

@ -11,12 +11,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/vr_view_gradient_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@drawable/gradient_background"/>
<include layout="@layout/bottom_video_time_holder"/>
<ImageView
android:id="@+id/explore"
@ -36,15 +31,4 @@
android:padding="@dimen/activity_margin"
android:src="@drawable/ic_cardboard"/>
<ImageView
android:id="@+id/video_play_outline"
android:layout_width="@dimen/play_outline_size_big"
android:layout_height="@dimen/play_outline_size_big"
android:layout_centerInParent="true"
android:background="@drawable/circle_black_background_with_inset"
android:padding="26dp"
android:src="@drawable/ic_play"/>
<include layout="@layout/bottom_video_time_holder"/>
</RelativeLayout>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/video_player_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/top_shadow"
android:layout_width="match_parent"
android:layout_height="@dimen/default_status_action_height"
android:background="@drawable/gradient_background_flipped"/>
<TextureView
android:id="@+id/video_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<com.simplemobiletools.gallery.pro.views.MediaSideScroll
android:id="@+id/video_volume_controller"
android:layout_width="@dimen/media_side_slider_width"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"/>
<com.simplemobiletools.gallery.pro.views.MediaSideScroll
android:id="@+id/video_brightness_controller"
android:layout_width="@dimen/media_side_slider_width"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/slide_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="120dp"
android:alpha="0"
android:background="@drawable/black_rounded_background"
android:gravity="center"
android:paddingLeft="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingBottom="@dimen/medium_margin"
android:textColor="@android:color/white"
android:textSize="@dimen/extra_big_text_size"/>
<include layout="@layout/bottom_video_time_holder"/>
</RelativeLayout>

View file

@ -5,12 +5,25 @@
android:id="@+id/video_time_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
android:layout_alignParentBottom="true"
android:background="@drawable/gradient_background">
<ImageView
android:id="@+id/video_toggle_play_pause"
android:layout_width="@dimen/video_player_play_pause_size"
android:layout_height="@dimen/video_player_play_pause_size"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/activity_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/small_margin"
android:src="@drawable/ic_pause_outline"
android:visibility="invisible"/>
<TextView
android:id="@+id/video_curr_time"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_below="@+id/video_toggle_play_pause"
android:layout_alignTop="@+id/video_seekbar"
android:layout_alignBottom="@+id/video_seekbar"
android:layout_alignParentStart="true"
@ -26,6 +39,7 @@
android:id="@+id/video_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/video_toggle_play_pause"
android:layout_toStartOf="@+id/video_duration"
android:layout_toLeftOf="@+id/video_duration"
android:layout_toEndOf="@+id/video_curr_time"
@ -37,6 +51,7 @@
android:id="@+id/video_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/video_toggle_play_pause"
android:layout_alignTop="@+id/video_seekbar"
android:layout_alignBottom="@+id/video_seekbar"
android:layout_alignParentEnd="true"

View file

@ -50,5 +50,13 @@
android:paddingBottom="@dimen/activity_margin"
android:text="@string/group_direct_subfolders"/>
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/change_view_type_dialog_use_for_this_folder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin"
android:text="@string/use_for_this_folder"/>
</LinearLayout>
</ScrollView>

View file

@ -36,53 +36,11 @@
android:textCursorDrawable="@null"
android:textSize="@dimen/normal_text_size"/>
<RelativeLayout
android:id="@+id/include_photos_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/interval_label"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/medium_margin"
android:paddingLeft="@dimen/medium_margin"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/include_photos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:text="@string/include_photos"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/include_videos_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/include_photos_holder"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/medium_margin"
android:paddingLeft="@dimen/medium_margin"
android:paddingTop="@dimen/activity_margin"
android:paddingBottom="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MySwitchCompat
android:id="@+id/include_videos"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:clickable="false"
android:text="@string/include_videos"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/include_gifs_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/include_videos_holder"
android:layout_below="@+id/interval_label"
android:background="?attr/selectableItemBackground"
android:paddingStart="@dimen/medium_margin"
android:paddingLeft="@dimen/medium_margin"

View file

@ -28,9 +28,8 @@
android:layout_width="@dimen/play_outline_size_big"
android:layout_height="@dimen/play_outline_size_big"
android:layout_centerInParent="true"
android:background="@drawable/circle_black_background_with_inset"
android:padding="28dp"
android:src="@drawable/ic_panorama"
android:padding="20dp"
android:src="@drawable/ic_panorama_outline"
android:visibility="gone"/>
<TextView

View file

@ -11,12 +11,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextureView
android:id="@+id/video_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
<com.simplemobiletools.gallery.pro.views.MediaSideScroll
android:id="@+id/video_volume_controller"
android:layout_width="@dimen/media_side_slider_width"
@ -46,9 +40,8 @@
android:layout_width="@dimen/play_outline_size_big"
android:layout_height="@dimen/play_outline_size_big"
android:layout_centerInParent="true"
android:background="@drawable/circle_black_background_with_inset"
android:padding="26dp"
android:src="@drawable/ic_play"/>
android:padding="20dp"
android:src="@drawable/ic_play_outline"/>
<TextView
android:id="@+id/slide_info"
@ -72,9 +65,8 @@
android:layout_width="@dimen/play_outline_size_big"
android:layout_height="@dimen/play_outline_size_big"
android:layout_centerInParent="true"
android:background="@drawable/circle_black_background_with_inset"
android:padding="28dp"
android:src="@drawable/ic_panorama"
android:padding="20dp"
android:src="@drawable/ic_panorama_outline"
android:visibility="gone"/>
<TextView
@ -91,5 +83,4 @@
android:visibility="gone"
tools:text="My video\nAnother line"/>
<include layout="@layout/bottom_video_time_holder"/>
</RelativeLayout>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_change_orientation"
android:icon="@drawable/ic_orientation_auto"
android:title="@string/change_orientation"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_share"
android:icon="@drawable/ic_share"
android:title="@string/share"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_open_with"
android:title="@string/open_with"
app:showAsAction="never"/>
</menu>

View file

@ -218,7 +218,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">استوديو لعرض الصور والفيديو بدون اعلانات.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -149,7 +149,7 @@
<string name="screen_rotation_system_setting">System setting</string>
<string name="screen_rotation_device_rotation">Device rotation</string>
<string name="screen_rotation_aspect_ratio">Aspect ratio</string>
<string name="black_background_at_fullscreen">Black background and status bar at fullscreen media</string>
<string name="black_background_at_fullscreen">Black background and at fullscreen media</string>
<string name="scroll_thumbnails_horizontally">Scroll thumbnails horizontally</string>
<string name="hide_system_ui_at_fullscreen">Automatically hide system UI at fullscreen media</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string>
@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Şəkil və videolara baxmaq üçün reklamsız qalereya.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Una galeria per veure imatges i vídeos sense publicitat.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Una galeria molt personalitzable capaç de mostrar molts tipus d\'imatge i de vídeo diferents, inclosos SVGs, RAWs, fotos panoràmiques i vídeos.

View file

@ -133,8 +133,8 @@
<string name="by_extension">Přípony</string>
<!-- Widgets -->
<string name="folder_on_widget">Folder shown on the widget:</string>
<string name="show_folder_name">Show folder name</string>
<string name="folder_on_widget">Složka zobrazená na widgetu:</string>
<string name="show_folder_name">Zobrazit název složky</string>
<!-- Settings -->
<string name="autoplay_videos">Automaticky přehrávat videa</string>
@ -144,12 +144,12 @@
<string name="animate_gifs">Animovat náhledy souborů GIF</string>
<string name="max_brightness">Nastavit jas obrazovky na max při zobrazení médií</string>
<string name="crop_thumbnails">Oříznout náhledy na čtverce</string>
<string name="show_thumbnail_video_duration">Show video durations</string>
<string name="show_thumbnail_video_duration">Zobrazit dobu trvání videí</string>
<string name="screen_rotation_by">Otočit média podle</string>
<string name="screen_rotation_system_setting">Systémového nastavení</string>
<string name="screen_rotation_device_rotation">Otočení zařízení</string>
<string name="screen_rotation_aspect_ratio">Poměru stran</string>
<string name="black_background_at_fullscreen">Černé pozadí a stavová lišta při médiích na celou obrazovku</string>
<string name="black_background_at_fullscreen">Černé pozadí při médiích na celou obrazovku</string>
<string name="scroll_thumbnails_horizontally">Prohlížet miniatury vodorovně</string>
<string name="hide_system_ui_at_fullscreen">Automaticky skrývat systémové lišty při celoobrazovkových médiích</string>
<string name="delete_empty_folders">Odstranit prázdné složky po smazání jejich obsahu</string>
@ -175,7 +175,7 @@
<string name="thumbnails">Náhledy</string>
<string name="fullscreen_media">Celoobrazovkový režim</string>
<string name="extended_details">Rozšířené vlastnosti</string>
<string name="bottom_actions">Spodní skční tlačítka</string>
<string name="bottom_actions">Spodní akční tlačítka</string>
<!-- Bottom actions -->
<string name="manage_bottom_actions">Upravit viditelné spodní akční tlačítka</string>
@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galerie na prohlížení obrázků a videí bez reklam.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Přizpůsobitelná galerie na zobrazování množství rozličných druhů obrázků a videí, včetně SVG, RAW souborů, panoramatických fotek a videí.
@ -227,7 +227,7 @@
2. Prezentace
3. Podpora pro výřez v displeji
4. Připínání složek na vrch
5. Filtování médií podle typu
5. Filtrování médií podle typu
6. Odpadkový koš pro snadnou obnovu souborů
7. Uzamykání orientace celoobrazovkového režimu
8. Označování oblíbených položek pro snadný přístup

View file

@ -30,15 +30,15 @@
<string name="use_default_orientation">Brug standard orientering</string>
<string name="fix_date_taken">Fiks Dato Taget værdi</string>
<string name="fixing">Fikser…</string>
<string name="dates_fixed_successfully">Dates fixed successfully</string>
<string name="dates_fixed_successfully">Datoer fikset med succes</string>
<!-- Filter -->
<string name="filter_media">Filtrer medier</string>
<string name="filter_media">Filtrér medier</string>
<string name="images">Billeder</string>
<string name="videos">Videoer</string>
<string name="gifs">GIF\'er</string>
<string name="raw_images">RAW images</string>
<string name="svgs">SVGs</string>
<string name="raw_images">RAW billeder</string>
<string name="svgs">SVG\'er</string>
<string name="no_media_with_filters">Der blev ikke fundet nogen filer med det valgte filter.</string>
<string name="change_filters_underlined"><u>Skift filter</u></string>
@ -67,7 +67,7 @@
<string name="resize_and_save">Skalér valgte elementer og gem</string>
<string name="width">Bredde</string>
<string name="height">Højde</string>
<string name="keep_aspect_ratio">Bevar billedeforhold</string>
<string name="keep_aspect_ratio">Bevar billedforhold</string>
<string name="invalid_values">Indtast en gyldig opløsning</string>
<!-- Editor -->
@ -88,8 +88,8 @@
<string name="flip_horizontally">Spejlvend vandret</string>
<string name="flip_vertically">Spejlvend lodret</string>
<string name="edit_with">Rediger med</string>
<string name="free_aspect_ratio">Free</string> <!-- available as an option: 1:1, 4:3, 16:9, free -->
<string name="other_aspect_ratio">Other</string> <!-- available as an option: 1:1, 4:3, 16:9, free, other -->
<string name="free_aspect_ratio">Fri</string> <!-- available as an option: 1:1, 4:3, 16:9, free -->
<string name="other_aspect_ratio">Andet</string> <!-- available as an option: 1:1, 4:3, 16:9, free, other -->
<!-- Set wallpaper -->
<string name="simple_wallpaper">Simple Wallpaper</string>
@ -111,7 +111,7 @@
<string name="include_videos">Inkluder videoer</string>
<string name="include_gifs">Inkluder GIF\'er</string>
<string name="random_order">Tilfældig rækkefølge</string>
<string name="use_fade">Use fade animations</string>
<string name="use_fade">Brug udtonende animationer</string>
<string name="move_backwards">Kør baglæns</string>
<string name="loop_slideshow">Endeløs kørsel</string>
<string name="slideshow_ended">Slideshowet endte</string>
@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Et galleri til visning af billeder og videoer uden reklamer.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -215,7 +215,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Eine schlichte Galerie zum Betrachten von Bildern und Videos, ganz ohne Werbung.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Eine stark anpassbare Galerie fähig zur Anzeige von diversen Bild- und Videoarten u. a. SVG, RAW, Panoramafotos und -videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Μία Gallery για την προβολή φωτογραφιών και βίντεο χωρίς διαφημίσεις.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Μια εξαιρετικά προσαρμόσιμη Gallery ικανή να εμφανίζει πολλούς διαφορετικούς τύπους εικόνας και βίντεο, όπως SVGs, RAWs, πανοραμικές φωτογραφίες και βίντεο.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Una galería para ver fotos y vídeos sin publicidad.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Una galería altamente personalizable capaz de mostrar diferentes tipos de imágenes y videos, incluyendo SVG, RAW, fotos panorámicas y videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galleria kuvien ja videoiden katsomiseen ilman mainoksia.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -144,7 +144,7 @@
<string name="animate_gifs">GIFs animés sur les miniatures</string>
<string name="max_brightness">Luminosité maximale</string>
<string name="crop_thumbnails">Recadrer les miniatures en carrés</string>
<string name="show_thumbnail_video_duration">Show video durations</string>
<string name="show_thumbnail_video_duration">Montrer la durée de la vidéo</string>
<string name="screen_rotation_by">Pivoter l\'affichage selon</string>
<string name="screen_rotation_system_setting">Paramètres système</string>
<string name="screen_rotation_device_rotation">Rotation de l\'appareil</string>
@ -214,7 +214,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Une galerie pour visionner photos et vidéos sans publicité.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Unha galería para ver fotos e videos, pero non publicidade.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galerija za gledanje fotografija i videozapisa bez oglasa.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -150,7 +150,7 @@
<string name="screen_rotation_system_setting">Rendszer beállítások</string>
<string name="screen_rotation_device_rotation">Eszköz elforgatás</string>
<string name="screen_rotation_aspect_ratio">Képarány</string>
<string name="black_background_at_fullscreen">Fekete háttérszín és állapotsáv teljes képernyős médiánál</string>
<string name="black_background_at_fullscreen">Fekete háttérszín teljes képernyős médiánál</string>
<string name="scroll_thumbnails_horizontally">Miniatűrök görgetése vízszintesen</string>
<string name="hide_system_ui_at_fullscreen">Automatikusan elrejti a rendszer UI-t teljes képernyőn</string>
<string name="delete_empty_folders">Az üres mappák törlése a tartalom törlése után</string>
@ -217,7 +217,7 @@ Ezzel csak a kiválasztott mappák láthatók, mivel a kizárás és a befoglal
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galéria a fotók és videók hirdetések nélküli megtekintéséhez.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Nagyon testreszabható galéria, amely alkalmas számos különböző kép- és videotípus megjelenítésére, beleértve az SVG-ket, RAW-t, panorámaképeket és videókat.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Aplikasi galeri untuk melihat foto dan video tanpa iklan.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Aplikasi galeri dengan banyak kustomisasi dan mampu menampilkan banyak jenis gambar dan video termasuk SVG, RAW, panorama foto dan video.

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Simple Gallery</string>
<string name="app_name">Semplice Galleria</string>
<string name="app_launcher_name">Galleria</string>
<string name="edit">Modifica</string>
<string name="open_camera">Apri fotocamera</string>
@ -29,38 +29,38 @@
<string name="force_landscape">Forza orizzontale</string>
<string name="use_default_orientation">Usa l\'orientamento predefinito</string>
<string name="fix_date_taken">Correggi valore Data acquisizione</string>
<string name="fixing">Correzione…</string>
<string name="fixing">Correzione in corso</string>
<string name="dates_fixed_successfully">Date aggiornate correttamente</string>
<!-- Filter -->
<string name="filter_media">Filtra i media</string>
<string name="filter_media">Filtra i file</string>
<string name="images">Immagini</string>
<string name="videos">Video</string>
<string name="gifs">GIF</string>
<string name="raw_images">Immagini RAW</string>
<string name="svgs">SVG</string>
<string name="no_media_with_filters">Nessun file trovato per il filtro selezionato.</string>
<string name="no_media_with_filters">Nessun file trovato con il filtro selezionato.</string>
<string name="change_filters_underlined"><u>Cambia filtro</u></string>
<!-- Hide / Exclude -->
<string name="hide_folder_description">Questa funzione nasconde la cartella aggiungendo un file \'.nomedia\' all\'interno, nasconderà anche tutte le sottocartelle. Puoi vederle attivando l\'opzione \'Mostra cartelle nascoste\' nelle impostazioni. Continuare?</string>
<string name="hide_folder_description">Questa funzione nasconde la cartella aggiungendo un file \'.nomedia\' all\'interno, nasconderà anche tutte le sottocartelle. Si possono vedere attivando l\'opzione \'Mostra cartelle nascoste\' nelle impostazioni. Continuare?</string>
<string name="exclude">Escludi</string>
<string name="excluded_folders">Cartelle escluse</string>
<string name="manage_excluded_folders">Gestisci le cartelle escluse</string>
<string name="exclude_folder_description">Questo escluderà la selezione e le relative sottocartelle solo da Simple Gallery. Puoi gestire le cartelle escluse nelle impostazioni.</string>
<string name="exclude_folder_description">Questo escluderà la selezione e le relative sottocartelle solo da Semplice Galleria. Si possono gestire le cartelle escluse nelle impostazioni.</string>
<string name="exclude_folder_parent">Vuoi invece escluderne una superiore?</string>
<string name="excluded_activity_placeholder">L\'esclusione delle cartelle e delle sottocartelle le renderà nascoste solo in Simple Gallery, saranno ancora visibili in altre applicazioni.\\n\\nSe desideri nasconderle anche nelle altre app, usa la funzione Nascondi.</string>
<string name="excluded_activity_placeholder">L\'esclusione delle cartelle e delle sottocartelle le renderà nascoste solo in Semplice Galleria, saranno ancora visibili in altre applicazioni.\\n\\nSe si desidera nasconderle anche nelle altre app, usa la funzione Nascondi.</string>
<string name="remove_all">Rimuovi tutte</string>
<string name="remove_all_description">Rimuovere tutte le cartelle dalla lista delle esclusioni? Ciò non eliminerà le cartelle.</string>
<string name="hidden_folders">Cartelle nascoste</string>
<string name="manage_hidden_folders">Gestisci le cartelle nascoste</string>
<string name="hidden_folders_placeholder">Pare che tu non abbia alcuna cartella nascosta con un file \".nomedia\".</string>
<string name="hidden_folders_placeholder">Non si ha alcuna cartella nascosta con un file \".nomedia\".</string>
<!-- Include folders -->
<string name="include_folders">Includi cartelle</string>
<string name="manage_included_folders">Gestisci le cartelle incluse</string>
<string name="add_folder">Aggiungi cartella</string>
<string name="included_activity_placeholder">Se hai alcune cartelle che contengono media, ma non sono state riconosciute dall\'app, puoi aggiungerle manualmente qui.</string>
<string name="included_activity_placeholder">Se si hanno alcune cartelle che contengono media, ma non sono state riconosciute dall\'app, si possono aggiungerle manualmente qui.</string>
<!-- Resizing -->
<string name="resize">Ridimensiona</string>
@ -75,11 +75,11 @@
<string name="save">Salva</string>
<string name="rotate">Ruota</string>
<string name="path">Percorso</string>
<string name="invalid_image_path">Percorso immagine non valido</string>
<string name="image_editing_failed">Modifica immagine fallita</string>
<string name="invalid_image_path">Percorso dell\'immagine non valido</string>
<string name="image_editing_failed">Modifica dell\'immagine fallita</string>
<string name="edit_image_with">Modifica immagine con:</string>
<string name="no_editor_found">Editor immagini non trovato</string>
<string name="unknown_file_location">Posizione file sconosciuta</string>
<string name="no_editor_found">Editor delle immagini non trovato</string>
<string name="unknown_file_location">Posizione del file sconosciuta</string>
<string name="error_saving_file">Impossibile sovrascrivere il file originale</string>
<string name="rotate_left">Ruota a sinistra</string>
<string name="rotate_right">Ruota a destra</string>
@ -137,7 +137,7 @@
<string name="show_folder_name">Mostra il nome della cartella</string>
<!-- Settings -->
<string name="autoplay_videos">Riproduci i video automaticamente</string>
<string name="autoplay_videos">Riproduci automaticamente i video</string>
<string name="remember_last_video_position">Ricorda l\'ultimo stato di riproduzione dei video</string>
<string name="toggle_filename">Visibilità nome del file</string>
<string name="loop_videos">Ripeti i video</string>
@ -149,9 +149,9 @@
<string name="screen_rotation_system_setting">Impostazione di sistema</string>
<string name="screen_rotation_device_rotation">Rotazione dispositivo</string>
<string name="screen_rotation_aspect_ratio">Proporzioni</string>
<string name="black_background_at_fullscreen">Sfondo e barra di stato neri con media a schermo intero</string>
<string name="black_background_at_fullscreen">Sfondo e barra di stato neri con i file a schermo intero</string>
<string name="scroll_thumbnails_horizontally">Scorri le miniature orizzontalmente</string>
<string name="hide_system_ui_at_fullscreen">Nascondi UI di sistema con media a schermo intero</string>
<string name="hide_system_ui_at_fullscreen">Nascondi l\'interfaccia utente di sistema con i file a schermo intero</string>
<string name="delete_empty_folders">Elimina cartelle vuote dopo averne eliminato il contenuto</string>
<string name="allow_photo_gestures">Controlla la luminosità delle foto con gesti verticali</string>
<string name="allow_video_gestures">Gestisci il volume e la luminosità dei video con gesti verticali</string>
@ -172,7 +172,7 @@
<string name="allow_down_gesture">Chiudi la visuale a schermo intero con un gesto verso il basso</string>
<!-- Setting sections -->
<string name="thumbnails">Miniature</string>
<string name="thumbnails">Anteprime</string>
<string name="fullscreen_media">Media a schermo intero</string>
<string name="extended_details">Dettagli estesi</string>
<string name="bottom_actions">Azioni inferiori</string>
@ -183,68 +183,68 @@
<string name="toggle_file_visibility">Attiva / disattiva la visibilità dei file</string>
<!-- FAQ -->
<string name="faq_1_title">Come posso impostare Simple Gallery come galleria predefinita del dispositivo?</string>
<string name="faq_1_text">Prima devi trovare l\'attuale galleria predefinita nella sezione App delle impostazioni di sistema, cerca un pulsante che dice qualcosa come \"App predefinite\", cliccalo, poi seleziona \"Ripristina predefiniti\".
La prossima volta che proverai ad aprire un\'immagine o video dovresti vedere un selettore di app, dove puoi scegliere Simple Gallery e renderla l\'app predefinita.</string>
<string name="faq_1_title">Come posso impostare Semplice Galleria come la galleria predefinita del dispositivo?</string>
<string name="faq_1_text">Si deve prima trovare l\'attuale galleria predefinita nella sezione App delle impostazioni di sistema, cercare un pulsante che dice qualcosa come \"App predefinite\", cliccarlo e poi selezionare \"Ripristina predefiniti\".
La prossima volta che proverai ad aprire un\'immagine o video si dovrebbe vedere un selettore di app, dove si può scegliere Semplice Galleria e renderla l\'app predefinita.</string>
<string name="faq_2_title">Ho bloccato l\'app con una password, ma l\'ho dimenticata. Cosa posso fare?</string>
<string name="faq_2_text">Puoi risolvere in 2 modi. Puoi reinstallare l\'app, oppure trova l\'app nelle impostazioni del dispositivo e seleziona \"Cancella dati\". Tutte le impostazioni verranno ripristinate, nessun file multimediale verrà rimosso.</string>
<string name="faq_2_text">Si può risolvere in 2 modi: reinstallare l\'app, oppure trovare l\'app nelle impostazioni del dispositivo e seleziona \"Cancella dati\". Tutte le impostazioni verranno ripristinate, nessun file verrà rimosso.</string>
<string name="faq_3_title">Come posso fare apparire un album sempre in cima?</string>
<string name="faq_3_text">Puoi toccare a lungo l\'album desiderato e selezionare l\'icona puntina nel menù azioni, ciò lo fisserà in cima. Puoi anche fissare varie cartelle, gli elementi fissati verranno ordinati dal metodo di ordinamento predefinito.</string>
<string name="faq_3_text">Si può toccare a lungo l\'album desiderato e selezionare l\'icona puntina nel menù azioni, ciò lo fisserà in cima. Si possono anche fissare varie cartelle, gli elementi fissati saranno ordinati dal metodo di ordinamento predefinito.</string>
<string name="faq_4_title">Come avanzo velocemente nei video?</string>
<string name="faq_4_text">Puoi cliccare sui testi di durata attuale o massima vicino alla barra di avanzamento, ciò avanzerà o riavvolgerà il video.</string>
<string name="faq_4_text">Si può cliccare sui testi di durata attuale o massima vicino alla barra di avanzamento, ciò avanzerà o riavvolgerà il video.</string>
<string name="faq_5_title">Che differenza c\'è tra nascondere ed escludere una cartella?</string>
<string name="faq_5_text">Escludere impedisce la visualizzazione della cartella solo in Simple Gallery, mentre nascondere ha effetto in tutto il sistema e nasconde la cartella anche alle altre gallerie. Funziona creando un file vuoto \".nomedia\" nella cartella in questione, che puoi anche rimuovere successivamente con qualsiasi gestore di file.</string>
<string name="faq_5_text">Escludere impedisce la visualizzazione della cartella solo in Semplice Galleria, mentre nascondere ha effetto in tutto il sistema e nasconde la cartella anche alle altre gallerie. Funziona creando un file vuoto \".nomedia\" nella cartella in questione, si possono anche rimuovere successivamente con qualsiasi gestore dei file.</string>
<string name="faq_6_title">Perchè vengono mostrate cartelle con copertine o adesivi di musica?</string>
<string name="faq_6_text">Può succedere che tu veda apparire alcuni album insoliti. Puoi escluderli facilmente toccandoli a lungo e selezionando Escludi. Nella finestra successiva puoi quindi selezionare la cartella superiore, con la possibilità di impedire la visualizzazione anche di altri album correlati.</string>
<string name="faq_6_text">Può succedere che si vedano apparire alcuni album insoliti. Si possono escluderli facilmente toccandoli a lungo e selezionando Escludi. Nella finestra successiva si possono quindi selezionare la cartella superiore, con la possibilità di impedire la visualizzazione anche di altri album correlati.</string>
<string name="faq_7_title">Una cartella con immagini non viene mostrata, cosa posso fare?</string>
<string name="faq_7_text">Può succedere per vari motivi, ma la soluzione è semplice. Vai in Impostazioni -> Gestisci le cartelle incluse, tocca il tasto Più e naviga verso la cartella desiderata.</string>
<string name="faq_8_title">Che fare se voglio rendere visibili solo poche particolari cartelle?</string>
<string name="faq_8_text">Aggiungere una cartella nelle Cartelle Incluse non esclude automaticamente nulla. Quello che puoi fare è andare in Impostazioni -> Gestisci le cartelle escluse, escludi la cartella root \"/\", poi aggiungi le cartelle desiderate in Impostazioni -> Gestisci le cartelle incluse.
<string name="faq_8_text">Aggiungere una cartella nelle Cartelle Incluse non esclude automaticamente nulla. Quello che puoi fare è andare in Impostazioni Gestisci le cartelle escluse, escludi la cartella root \"/\", poi aggiungi le cartelle desiderate in Impostazioni Gestisci le cartelle incluse.
Ciò renderà visibili solo le cartelle selezionate, dato che sia l\'esclusione che l\'inclusione sono ricorsive e se una cartella è sia esclusa che inclusa, verrà mostrata.</string>
<string name="faq_9_title">Le immagini a schermo intero hanno strani artefatti, posso migliorarne la qualità in qualche modo?</string>
<string name="faq_9_text">Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\", puoi usare quella. Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando proverai a ingrandirle troppo.</string>
<string name="faq_9_text">Sì, c\'è un\'opzione nelle impostazioni che dice \"Sostituisci le immagini ingrandibili a fondo con altre di migliore qualità\". Ciò migliorerà la qualità delle immagini, ma saranno sfuocate quando si proverà a ingrandirle troppo.</string>
<string name="faq_10_title">Posso ritagliare le immagini con questa app?</string>
<string name="faq_10_text">Sì, puoi ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Puoi usare l\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, o selezionando Modifica mentre la vedi a schermo intero.</string>
<string name="faq_10_text">Sì, si possono ritagliare le immagini nell\'editor, trascinando gli angoli dell\'immagine. Si può accedere all\'editor sia premendo a lungo la miniatura di un\'immagine e selezionando Modifica, oppure selezionando Modifica mentre si vede una foto a schermo intero.</string>
<string name="faq_11_title">Posso raggruppare in qualche modo le miniature dei file?</string>
<string name="faq_11_text">Certo, usa il menu \"Raggruppa per\" mentre visualizzi le miniature. Puoi raggruppare i file con diversi criteri, incluso la data di creazione. Se utilizzi la funzione \"Mostra tutti i contenuti\" puoi anche raggrupparli per cartelle.</string>
<string name="faq_11_text">Certo, si può usare il menu \"Raggruppa per\" mentre si visualizzano le miniature. Il raggruppamento può avvenire per diversi criteri, incluso la data di creazione. Se si utilizza la funzione \"Mostra tutti i contenuti\" si può anche raggrupparli per cartelle.</string>
<string name="faq_12_title">L\'ordinamento per data acquisizione sembra non funzionare bene, come posso risolvere?</string>
<string name="faq_12_text">Probabilmente è causato dai file copiati da altre parti. Puoi risolvere selezionando le miniature dei file e scegliendo \"Correggi valore Data acquisizione\".</string>
<string name="faq_12_text">Probabilmente è causato dai file copiati da altre parti. Si può risolvere selezionando le miniature dei file e scegliendo \"Correggi valore Data acquisizione\".</string>
<string name="faq_13_title">Vedo curvature di colore nelle immagini. Come posso migliorarne la qualità?</string>
<string name="faq_13_text">L\'attuale soluzione per visualizzare immagini funziona bene nella maggior parte dei casi, ma se vuoi una qualità ancora maggiore, puoi attivare \"Mostra le immagini alla massima qualità possibile\" nelle impostazioni dell\'app, nella sezione \"Immagini ingrandibili a fondo\".</string>
<string name="faq_13_text">L\'attuale soluzione per visualizzare immagini funziona bene nella maggior parte dei casi, ma se si vuole una qualità ancora maggiore, si può attivare \"Mostra le immagini alla massima qualità possibile\" nelle impostazioni dell\'app, nella sezione \"Immagini ingrandibili a fondo\".</string>
<string name="faq_14_title">Ho nascosto un file/una cartella. Come posso mostrarlo/a di nuovo?</string>
<string name="faq_14_text">Puoi premere \"Mostra temporaneamente nascosti\" nel menu della schermata principale, oppure attivare \"Mostra gli elementi nascosti\" nelle impostazioni dell\'app per vedere l\'elemento nascosto. Se vuoi che resti visibile, premilo a lungo e seleziona \"Non nascondere\". Le cartelle vengono nascoste aggiungendo un file nascosto \".nomedia\" all\'interno di esse, puoi anche eliminare il file con qualsiasi gestore di file.</string>
<string name="faq_14_text">Si può premere \"Mostra temporaneamente nascosti\" nel menu della schermata principale, oppure attivare \"Mostra gli elementi nascosti\" nelle impostazioni dell\'app per vedere l\'elemento nascosto. Per farla rimanere visibile, premere a lungo e selezionare \"Non nascondere\". Le cartelle vengono nascoste aggiungendo un file nascosto \".nomedia\" all\'interno di esse, si può eliminare il file con qualsiasi gestore di file.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Una galleria per visualizzare foto e video senza pubblicità.</string>
<string name="app_short_description">Una galleria per gestire i propri file senza pubblicità che rispetta la privacy.</string>
<string name="app_long_description">
Una galleria altamente personalizzabile e capace di visualizzare tipi di file immagini e video differenti, fra cui SVG, RAW, foto panoramiche e video.
È open source, non contiene pubblicità e autorizzazioni superflue.
L\'applicazione non contiene pubblicità o permessi non necessari; è completamente opensource e la si può personalizzare con i propri colori preferiti.
Alcune funzionalità che vale la pena accennare:
1. Ricerca
2. Presentazione
3. Supporto al notch
4. Fissare le cartelle in alto
5. Filtro dei file per tipo
5. Filtro dei file per tipologia
6. Cestino per un recupero facile dei file
7. Blocco dell\'orientamento nella vista a schermo intero
8. Selezionare file preferiti per un accesso facile
9. Chisura rapida della vista schermo intero con un movimento verso il basso
10. Un editor per modificare le immagini ed applicare filtri
8. Selezione dei file preferiti per un accesso immediato
9. Chisura rapida della vista a schermo intero con un movimento verso il basso
10. Un editor per modificare le immagini e applicare filtri
11. Protezione con password per proteggere elementi nascosti o l\'intera applicazione
12. Cambio delle colonne delle anteprime con un movimento o tramite dei pulsanti nel menu
13. Pulsanti rapidi per azioni personalizzabili nella vista schermo intero
13. Pulsanti rapidi per azioni personalizzabili nella vista a schermo intero
14. Visualizzazione di determinati dettagli aggiuntivi nella vista a schermo intero
15. Molti modi per ordinare o raggruppare gli elementi, sia in ordine crescente che decrescente
16. Cartelle nascoste (anche per altre applicazioni), cartelle escluse (solo per Simple Gallery)
16. Cartelle nascoste (anche per altre applicazioni), cartelle escluse (solo per Semplice Galleria)
L\'autorizzazione per leggere le impronte digitali è necessaria per il blocco della visibilità degli elementi, dell\'intera applicazione o per proteggere alcuni file dalla loro eliminazione.
Questa applicazione è solamente una di una serie più grande. Si possono trovare le altre su https://www.simplemobiletools.com
L\'autorizzazione per leggere le impronte digitali è necessaria per il blocco della visibilità, dell\'intera applicazione o per proteggere alcuni file dalla loro eliminazione.
Questa è solamente una delle tante applicazioni della serie Simple Mobile Tools. Si possono trovare le altre su https://www.simplemobiletools.com
</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">写真やビデオを見るためのギャラリー。広告はありません。</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">광고없이 사진과 동영상을 볼 수 있는 갤러리.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galerija, skirta peržiūrėti nuotraukas ir vaizdo įrašus be reklamų.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">A gallery for viewing photos and videos without ads.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Een galerij voor afbeeldingen en video\'s, zonder advertenties.</string>
<string name="app_short_description">Een privacyvriendelijke advertentievrije galerij voor afbeeldingen en video\'s.</string>
<string name="app_long_description">
Een zeer goed aan te passen galerij voor afbeeldingen en video\'s in vele bestandsformaten, waaronder SVG, RAW, panoramafoto\'s en -video\'s.

View file

@ -89,7 +89,7 @@
<string name="flip_vertically">Przewróć w pionie</string>
<string name="edit_with">Edytuj w:</string>
<string name="free_aspect_ratio">Wolne</string> <!-- available as an option: 1:1, 4:3, 16:9, free -->
<string name="other_aspect_ratio">Other</string> <!-- available as an option: 1:1, 4:3, 16:9, free, other -->
<string name="other_aspect_ratio">Inny</string> <!-- available as an option: 1:1, 4:3, 16:9, free, other -->
<!-- Set wallpaper -->
<string name="simple_wallpaper">Tapeta</string>
@ -133,8 +133,8 @@
<string name="by_extension">Rozszerzenia</string>
<!-- Widgets -->
<string name="folder_on_widget">Folder shown on the widget:</string>
<string name="show_folder_name">Show folder name</string>
<string name="folder_on_widget">Folder wyświetlany na widżecie:</string>
<string name="show_folder_name">Pokaż nazwę folderu</string>
<!-- Settings -->
<string name="autoplay_videos">Odtwarzaj filmy automatycznie</string>
@ -214,7 +214,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Prosta galeria bez reklam do przeglądania obrazów i filmów.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Wysoce konfigurowalna galeria obsługująca wiele formatów obrazów i filmów, w tym SVG, RAW oraz multimedia panoramiczne.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Um aplicativo para visualizar fotos e vídeos.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Uma aplicação para ver fotografias e vídeos.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Um aplicação capaz de mostrar diversos tipos de imagens e vídeos incluíndo SVG, RAW, fotos panorâmicas e vídeos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Галерея для просмотра изображений и видео. Без рекламы.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -149,7 +149,7 @@
<string name="screen_rotation_system_setting">Systémového nastavenia</string>
<string name="screen_rotation_device_rotation">Otočenia zariadenia</string>
<string name="screen_rotation_aspect_ratio">Pomeru strán</string>
<string name="black_background_at_fullscreen">Čierne pozadie a stavová lišta pri médiách na celú obrazovku</string>
<string name="black_background_at_fullscreen">Čierne pozadie pri médiách na celú obrazovku</string>
<string name="scroll_thumbnails_horizontally">Prehliadať miniatúry vodorovne</string>
<string name="hide_system_ui_at_fullscreen">Automaticky skrývať systémové lišty pri celoobrazovkových médiách</string>
<string name="delete_empty_folders">Odstrániť prázdne priečinky po vymazaní ich obsahu</string>
@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galéria na prezeranie obrázkov a videí bez reklám.</string>
<string name="app_short_description">Offline galéria na správu vašich súborov, rešpektujúca vaše súkromie.</string>
<string name="app_long_description">
Nastaviteľná galéria na zobrazovanie množstva rozličných druhov obrázkov a videí, vrátane SVG, RAW súborov, panoramatických fotiek a videí.

View file

@ -215,7 +215,7 @@
<string name="faq_14_text">Lahko uporabite funkcijo \"Začasno prikaži skrite elemente\", ki se nahaja v meniju na glavnem zaslonu ali preklopite \"Prikaži skrite elemente\" v Nastavitvah aplikacije. Če želite element označiti kot viden, z dolgim pritiskom nanj prikličite meni in izberite \"Prikaži\". Skrivanje map deluje tako, da se kreira prazno \".nomedia\" datoteko v izbrani mapi, ki jo lahko odstranite tudi s katerimkoli urejevalnikom datotek.</string>
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Galerija za ogled fotografij in videoposnetkov brez reklam.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
Visoko prilagodljiva galerija, zmožna prikazovanja različnih tipov fotografij in videoposnetkov, vključno s SVGji, RAWi, panoramskimi fotografijami in videoposnetki.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Ett galleri för att visa foton och videor utan reklam.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Reklamsız fotoğrafları ve videoları görüntülemek için bir galeri.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
SVG\'ler, RAW\'lar, panoramik fotoğraflar ve videolar dahil olmak üzere birçok farklı resim ve video türünü gösterebilen son derece özelleştirilebilir bir galeri.

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">Галерея для перегляду фото та відео без реклами.</string>
<string name="app_short_description">Офлайн-галерея для керування файлами: без реклами та з повагою до приватності.</string>
<string name="app_long_description">
Тонко налаштовувана галерея, здатна відображати зображення та відео різноманітних типів, включаючи SVG-зображення, RAW-зображення,панорамні фото і відео.

View file

@ -133,8 +133,8 @@
<string name="by_extension">扩展</string>
<!-- Widgets -->
<string name="folder_on_widget">Folder shown on the widget:</string>
<string name="show_folder_name">Show folder name</string>
<string name="folder_on_widget">要在小部件上显示的文件夹:</string>
<string name="show_folder_name">显示文件夹名称</string>
<!-- Settings -->
<string name="autoplay_videos">自动播放</string>
@ -214,7 +214,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">一个没有广告,用来观看照片及视频的相册。</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
一个高度可定制的图库支持很多的图像和视频类型包括SVGRAW全景照片和视频。

View file

@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">一個用來瀏覽相片和影片,且沒有廣告的相簿。</string>
<string name="app_short_description">一個沒有廣告的離線相簿,用來管理你的檔案,並且尊重您的隱私</string>
<string name="app_long_description">
一個高自訂性的相簿能夠顯示許多不同的圖片和影片類型包含SVGs、RAWs、全景相片和影片。

View file

@ -8,6 +8,7 @@
<dimen name="play_outline_size_big">96dp</dimen>
<dimen name="tmb_shadow_height">60dp</dimen>
<dimen name="media_side_slider_width">60dp</dimen>
<dimen name="video_player_play_pause_size">60dp</dimen>
<dimen name="instant_change_bar_width">50dp</dimen>
<dimen name="list_view_folder_thumbnail_size">72dp</dimen>
<dimen name="bottom_actions_height">64dp</dimen>

View file

@ -149,7 +149,7 @@
<string name="screen_rotation_system_setting">System setting</string>
<string name="screen_rotation_device_rotation">Device rotation</string>
<string name="screen_rotation_aspect_ratio">Aspect ratio</string>
<string name="black_background_at_fullscreen">Black background and status bar at fullscreen media</string>
<string name="black_background_at_fullscreen">Black background and at fullscreen media</string>
<string name="scroll_thumbnails_horizontally">Scroll thumbnails horizontally</string>
<string name="hide_system_ui_at_fullscreen">Automatically hide system UI at fullscreen media</string>
<string name="delete_empty_folders">Delete empty folders after deleting their content</string>
@ -216,7 +216,7 @@
<!-- Strings displayed only on Google Playstore. Optional, but good to have -->
<!-- Short description has to have less than 80 chars -->
<string name="app_short_description">A gallery for viewing photos and videos without ads.</string>
<string name="app_short_description">An offline gallery for managing your files without ads, respecting your privacy.</string>
<string name="app_long_description">
A highly customizable gallery capable of displaying many different image and video types including SVGs, RAWs, panoramic photos and videos.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 171 KiB