13
CHANGELOG.md
|
@ -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)*
|
||||
----------------------------
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)) {
|
||||
|
|
Before Width: | Height: | Size: 520 B |
BIN
app/src/main/res/drawable-hdpi/ic_panorama_outline.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_pause_outline.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_play_outline.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 622 B |
BIN
app/src/main/res/drawable-xhdpi/ic_panorama_outline.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_pause_outline.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_play_outline.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 940 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_panorama_outline.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_pause_outline.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_play_outline.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_panorama_outline.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_pause_outline.png
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_play_outline.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
|
@ -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"/>
|
|
@ -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>
|
||||
|
|
51
app/src/main/res/layout/activity_video_player.xml
Normal 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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
18
app/src/main/res/menu/menu_video_player.xml
Normal 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>
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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, πανοραμικές φωτογραφίες και βίντεο.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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í.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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-зображення,панорамні фото і відео.
|
||||
|
||||
|
|
|
@ -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">
|
||||
一个高度可定制的图库,支持很多的图像和视频类型,包括SVG,RAW,全景照片和视频。
|
||||
|
||||
|
|
|
@ -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、全景相片和影片。
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 171 KiB |