Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/activity/MediaViewerActivity.kt

652 lines
26 KiB
Kotlin

/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mariotaku.twidere.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.Parcelable
import androidx.annotation.RequiresApi
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.viewpager.widget.ViewPager
import androidx.core.view.WindowInsetsCompat
import androidx.customview.widget.ViewDragHelper
import androidx.appcompat.app.WindowDecorActionBar
import androidx.appcompat.app.decorToolbar
import android.view.*
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_media_viewer.*
import nl.komponents.kovenant.combine.and
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.chameleon.Chameleon
import org.mariotaku.ktextension.*
import org.mariotaku.mediaviewer.library.*
import org.mariotaku.mediaviewer.library.subsampleimageview.SubsampleImageViewerFragment.EXTRA_MEDIA_URI
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.iface.IControlBarActivity.ControlBarShowHideHelper
import org.mariotaku.twidere.annotation.CacheFileType
import org.mariotaku.twidere.extension.addSystemUiVisibility
import org.mariotaku.twidere.extension.dismissProgressDialog
import org.mariotaku.twidere.extension.removeSystemUiVisibility
import org.mariotaku.twidere.extension.showProgressDialog
import org.mariotaku.twidere.fragment.PermissionRequestDialog
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.fragment.iface.IBaseFragment
import org.mariotaku.twidere.fragment.media.*
import org.mariotaku.twidere.model.ParcelableMedia
import org.mariotaku.twidere.model.ParcelableStatus
import org.mariotaku.twidere.provider.CacheProvider
import org.mariotaku.twidere.provider.ShareProvider
import org.mariotaku.twidere.task.SaveFileTask
import org.mariotaku.twidere.task.SaveMediaToGalleryTask
import org.mariotaku.twidere.util.IntentUtils
import org.mariotaku.twidere.util.PermissionUtils
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.dagger.GeneralComponent
import org.mariotaku.twidere.util.support.WindowSupport
import org.mariotaku.twidere.view.viewer.MediaSwipeCloseContainer
import java.io.File
import javax.inject.Inject
import kotlin.concurrent.thread
import kotlin.math.abs
import kotlin.math.roundToInt
import android.Manifest.permission as AndroidPermissions
class MediaViewerActivity : BaseActivity(), IMediaViewerActivity, MediaSwipeCloseContainer.Listener,
PermissionRequestDialog.PermissionRequestCancelCallback {
@Inject
internal lateinit var mediaFileCache: FileCache
@Inject
internal lateinit var mediaDownloader: MediaDownloader
private var saveToStoragePosition = -1
private var shareMediaPosition = -1
private var wasBarShowing = 0
private var hideOffsetNotSupported = false
private lateinit var mediaViewerHelper: IMediaViewerActivity.Helper
private lateinit var controlBarShowHideHelper: ControlBarShowHideHelper
private val status: ParcelableStatus?
get() = intent.getParcelableExtra<ParcelableStatus>(EXTRA_STATUS)
private val initialMedia: ParcelableMedia?
get() = intent.getParcelableExtra<ParcelableMedia>(EXTRA_CURRENT_MEDIA)
private val media: Array<ParcelableMedia> by lazy {
return@lazy intent.getNullableTypedArrayExtra<ParcelableMedia>(EXTRA_MEDIA) ?: emptyArray()
}
private val currentFragment: MediaViewerFragment?
get() {
val viewPager = findViewPager()
val adapter = viewPager.adapter ?: return null
val currentItem = viewPager.currentItem
if (currentItem < 0 || currentItem >= adapter.count) return null
return adapter.instantiateItem(viewPager, currentItem) as? MediaViewerFragment
}
private fun getCurrentCacheFileInfo(position: Int): SaveFileTask.FileInfo? {
if (position == -1) return null
val viewPager = findViewPager()
val adapter = viewPager.adapter ?: return null
val f = adapter.instantiateItem(viewPager, position) as? MediaViewerFragment ?:
return null
return f.cacheFileInfo()
}
override val shouldApplyWindowBackground: Boolean = false
override val controlBarHeight: Int
get() {
return supportActionBar?.height ?: 0
}
override var controlBarOffset: Float
get() {
val actionBar = supportActionBar
if (actionBar != null) {
return 1 - actionBar.hideOffset / controlBarHeight.toFloat()
}
return 0f
}
@SuppressLint("RestrictedApi")
set(offset) {
val actionBar = supportActionBar
if (actionBar != null && !hideOffsetNotSupported) {
if (actionBar is WindowDecorActionBar) {
val toolbar = actionBar.decorToolbar.viewGroup
toolbar.alpha = offset
activityLayout.statusBarAlpha = offset
}
try {
actionBar.hideOffset = (controlBarHeight * (1f - offset)).roundToInt()
} catch (e: UnsupportedOperationException) {
// Some device will throw this exception
hideOffsetNotSupported = true
}
}
notifyControlBarOffsetChanged()
}
override fun onCreate(savedInstanceState: Bundle?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
}
super.onCreate(savedInstanceState)
GeneralComponent.get(this).inject(this)
mediaViewerHelper = IMediaViewerActivity.Helper(this)
controlBarShowHideHelper = ControlBarShowHideHelper(this)
mediaViewerHelper.onCreate(savedInstanceState)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.elevation = 0f
swipeContainer.listener = this
swipeContainer.backgroundAlpha = 1f
WindowSupport.setStatusBarColor(window, Color.TRANSPARENT)
activityLayout.statusBarColor = overrideTheme.colorToolbar
ViewCompat.setOnApplyWindowInsetsListener(activityLayout) { view, insets ->
val statusBarHeight = insets.systemWindowInsetTop - ThemeUtils.getActionBarHeight(this)
activityLayout.statusBarHeight = statusBarHeight
onApplyWindowInsets(view, insets)
}
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
activityLayout.statusBarAlpha = if (View.SYSTEM_UI_FLAG_FULLSCREEN in visibility) 0f else 1f
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_SHARE_MEDIA -> {
ShareProvider.clearTempFiles(this)
}
REQUEST_SELECT_SAVE_MEDIA -> {
if (resultCode != Activity.RESULT_OK || data == null) return
val uri = data.data ?: return
saveMediaToContentUri(uri)
}
}
}
override fun onContentChanged() {
super.onContentChanged()
mediaViewerHelper.onContentChanged()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.menu_media_viewer, menu)
return true
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
super.onPrepareOptionsMenu(menu)
val obj = currentFragment ?: return false
if (obj.isDetached || obj.host == null) return false
val running = obj.isMediaLoading
val downloaded = obj.isMediaLoaded
val supportedSaveTo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
menu.setItemAvailability(R.id.refresh, !running && !downloaded)
menu.setItemAvailability(R.id.share, !running && downloaded)
menu.setItemAvailability(R.id.save, !running && downloaded)
menu.setItemAvailability(R.id.save_to, supportedSaveTo && !running && downloaded)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val viewPager = findViewPager()
val adapter = viewPager.adapter ?: return false
val currentItem = viewPager.currentItem
if (currentItem < 0 || currentItem >= adapter.count) return false
val obj = adapter.instantiateItem(viewPager, currentItem) as? MediaViewerFragment ?: return false
when (item.itemId) {
R.id.refresh -> {
if (obj is CacheDownloadMediaViewerFragment) {
obj.startLoading(true)
obj.showProgress(true, 0f)
obj.setMediaViewVisible(false)
}
return true
}
R.id.share -> {
val fileInfo = obj.cacheFileInfo()
if (fileInfo != null) {
requestAndShareMedia(currentItem)
} else {
val media = media[currentItem]
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TEXT, media.url)
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
return true
}
R.id.save -> {
requestAndSaveToStorage(currentItem)
return true
}
R.id.save_to -> {
openSaveToDocumentChooser()
return true
}
R.id.open_in_browser -> {
val media = media[currentItem]
try {
val uri = Uri.parse(media.url)
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.addCategory(Intent.CATEGORY_BROWSABLE)
intent.`package` = IntentUtils.getDefaultBrowserPackage(this, uri, true)
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// TODO show error, or improve app url
}
return true
}
android.R.id.home -> {
finish()
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onRequestPermissionCancelled(requestCode: Int) {
when (requestCode) {
REQUEST_PERMISSION_SHARE_MEDIA -> {
shareMedia()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
REQUEST_PERMISSION_SAVE_MEDIA -> {
if (PermissionUtils.hasPermission(permissions, grantResults, AndroidPermissions.WRITE_EXTERNAL_STORAGE)) {
saveToStorage()
} else {
Toast.makeText(this, R.string.message_toast_save_media_no_storage_permission, Toast.LENGTH_LONG).show()
}
return
}
REQUEST_PERMISSION_SHARE_MEDIA -> {
if (!PermissionUtils.hasPermission(permissions, grantResults, AndroidPermissions.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(this, R.string.message_toast_share_media_no_storage_permission, Toast.LENGTH_LONG).show()
}
shareMedia()
return
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
currentFragment
return super.onKeyUp(keyCode, event)
}
override fun toggleBar() {
setBarVisibility(!isBarShowing)
}
override fun getInitialPosition(): Int {
return media.indexOf(initialMedia)
}
override fun getLayoutRes(): Int {
return R.layout.activity_media_viewer
}
override fun findViewPager(): ViewPager {
return viewPager
}
override fun isBarShowing(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return FLAG_SYSTEM_UI_HIDE_BARS !in window.decorView.systemUiVisibility
}
return controlBarOffset >= 1
}
override fun setBarVisibility(visible: Boolean) {
if (isBarShowing == visible) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (visible) {
window.decorView.removeSystemUiVisibility(FLAG_SYSTEM_UI_HIDE_BARS)
} else {
window.decorView.addSystemUiVisibility(FLAG_SYSTEM_UI_HIDE_BARS)
}
} else {
setControlBarVisibleAnimate(visible)
}
}
override fun getDownloader(): MediaDownloader {
return mediaDownloader
}
override fun getFileCache(): FileCache {
return mediaFileCache
}
@SuppressLint("SwitchIntDef")
override fun instantiateMediaFragment(position: Int): MediaViewerFragment {
val media = media[position]
val args = Bundle()
val intent = intent
args.putParcelable(EXTRA_ACCOUNT_KEY, intent.getParcelableExtra<Parcelable>(EXTRA_ACCOUNT_KEY))
args.putParcelable(EXTRA_MEDIA, media)
args.putParcelable(EXTRA_STATUS, intent.getParcelableExtra<Parcelable>(EXTRA_STATUS))
when (media.type) {
ParcelableMedia.Type.IMAGE -> {
val mediaUrl = media.media_url ?: return Fragment.instantiate(this, ExternalBrowserPageFragment::class.java.name, args) as MediaViewerFragment
args.putParcelable(EXTRA_MEDIA_URI, Uri.parse(mediaUrl))
return if (mediaUrl.endsWith(".gif")) {
Fragment.instantiate(this, GifPageFragment::class.java.name, args) as MediaViewerFragment
} else {
Fragment.instantiate(this, ImagePageFragment::class.java.name, args) as MediaViewerFragment
}
}
ParcelableMedia.Type.ANIMATED_GIF, ParcelableMedia.Type.CARD_ANIMATED_GIF -> {
args.putBoolean(VideoPageFragment.EXTRA_LOOP, true)
args.putBoolean(VideoPageFragment.EXTRA_DISABLE_CONTROL, true)
args.putBoolean(VideoPageFragment.EXTRA_DEFAULT_MUTE, true)
return instantiateMediaViewerFragment(args)
}
ParcelableMedia.Type.VIDEO -> {
return instantiateMediaViewerFragment(args)
}
ParcelableMedia.Type.EXTERNAL_PLAYER -> {
return Fragment.instantiate(this, ExternalBrowserPageFragment::class.java.name, args) as MediaViewerFragment
}
else -> {
return Fragment.instantiate(this, ExternalBrowserPageFragment::class.java.name, args) as MediaViewerFragment
}
}
}
override fun getMediaCount(): Int {
return media.size
}
override fun getOverrideTheme(): Chameleon.Theme {
val theme = super.getOverrideTheme()
theme.colorToolbar = ContextCompat.getColor(this, R.color.ab_bg_color_media_viewer)
theme.isToolbarColored = false
return theme
}
override fun onSwipeCloseFinished() {
finish()
overridePendingTransition(0, 0)
}
override fun onSwipeOffsetChanged(offset: Int) {
val offsetFactor = 1 - (abs(offset).toFloat() / swipeContainer.height)
swipeContainer.backgroundAlpha = offsetFactor
val colorToolbar = overrideTheme.colorToolbar
val alpha = (Color.alpha(colorToolbar) * offsetFactor).roundToInt().coerceIn(0..255)
activityLayout.statusBarAlpha = alpha / 255f
}
override fun onSwipeStateChanged(state: Int) {
supportActionBar?.let {
val barShowing = controlBarOffset >= 1
if (state == ViewDragHelper.STATE_IDLE) {
if (wasBarShowing == 1 && !barShowing) {
setControlBarVisibleAnimate(true)
}
wasBarShowing = 0
} else {
if (wasBarShowing == 0) {
wasBarShowing = if (barShowing) 1 else -1
}
if (barShowing) {
setControlBarVisibleAnimate(false)
}
}
}
}
override fun setControlBarVisibleAnimate(visible: Boolean, listener: ControlBarShowHideHelper.ControlBarAnimationListener?) {
controlBarShowHideHelper.setControlBarVisibleAnimate(visible, listener)
}
override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat {
val result = super.onApplyWindowInsets(v, insets)
val adapter = viewPager.adapter ?: return insets
if (adapter.count == 0) return insets
val fragment = adapter.instantiateItem(viewPager, viewPager.currentItem)
if (fragment is IBaseFragment<*>) {
fragment.requestApplyInsets()
}
return result
}
private fun instantiateMediaViewerFragment(args: Bundle): MediaViewerFragment {
return Fragment.instantiate(this, ExoPlayerPageFragment::class.java.name, args) as MediaViewerFragment
}
private fun processShareIntent(intent: Intent) {
val status = status ?: return
intent.putExtra(Intent.EXTRA_SUBJECT, IntentUtils.getStatusShareSubject(this, status))
intent.putExtra(Intent.EXTRA_TEXT, IntentUtils.getStatusShareText(this, status))
}
private fun requestAndSaveToStorage(position: Int) {
saveToStoragePosition = position
if (checkAllSelfPermissionsGranted(AndroidPermissions.WRITE_EXTERNAL_STORAGE)) {
saveToStorage()
} else {
val permissions: Array<String> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
arrayOf(AndroidPermissions.WRITE_EXTERNAL_STORAGE, AndroidPermissions.READ_EXTERNAL_STORAGE)
} else {
arrayOf(AndroidPermissions.WRITE_EXTERNAL_STORAGE)
}
PermissionRequestDialog.show(supportFragmentManager, getString(R.string.message_permission_request_save_media),
permissions, REQUEST_PERMISSION_SAVE_MEDIA)
}
}
private fun requestAndShareMedia(position: Int) {
shareMediaPosition = position
if (checkAllSelfPermissionsGranted(AndroidPermissions.WRITE_EXTERNAL_STORAGE)) {
shareMedia()
} else {
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
arrayOf(AndroidPermissions.WRITE_EXTERNAL_STORAGE, AndroidPermissions.READ_EXTERNAL_STORAGE)
} else {
arrayOf(AndroidPermissions.WRITE_EXTERNAL_STORAGE)
}
PermissionRequestDialog.show(supportFragmentManager, getString(R.string.message_permission_request_share_media),
permissions, REQUEST_PERMISSION_SHARE_MEDIA)
}
}
private fun shareMedia() {
val fileInfo = getCurrentCacheFileInfo(shareMediaPosition) ?: return
val destination = ShareProvider.getFilesDir(this) ?: return
val task = SaveMediaTask(this, destination, fileInfo)
task.execute()
}
private fun saveToStorage() {
val fileInfo = getCurrentCacheFileInfo(saveToStoragePosition) ?: return
val pubDir = when ((fileInfo as? CacheProvider.CacheFileTypeSupport)?.cacheFileType) {
CacheFileType.VIDEO -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
} else {
getExternalFilesDir(Environment.DIRECTORY_MOVIES)
}
}
CacheFileType.IMAGE -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
} else {
getExternalFilesDir(Environment.DIRECTORY_PICTURES)
}
}
else -> {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
} else {
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
}
}
}
val saveDir = File(pubDir, "Twidere")
val task = SaveMediaToGalleryTask(this, fileInfo, saveDir)
task.execute()
}
private fun openSaveToDocumentChooser() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return
val fileInfo = getCurrentCacheFileInfo(viewPager.currentItem) ?: return
thread {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.type = fileInfo.mimeType ?: "*/*"
intent.addCategory(Intent.CATEGORY_OPENABLE)
val extension = fileInfo.fileExtension
val saveFileName = if (extension != null) {
"${fileInfo.fileName?.removeSuffix("_$extension")}.$extension"
} else {
fileInfo.fileName
}
intent.putExtra(Intent.EXTRA_TITLE, saveFileName)
startActivityForResult(intent, REQUEST_SELECT_SAVE_MEDIA)
}
}
private fun saveMediaToContentUri(data: Uri) {
val fileInfo = getCurrentCacheFileInfo(viewPager.currentItem) ?: return
val weakThis = weak()
(showProgressDialog("save_media_to_progress") and task {
val a = weakThis.get() ?: throw InterruptedException()
fileInfo.inputStream().use { st ->
a.contentResolver.openOutputStream(data)?.use {
st.copyTo(it)
}
}
}).successUi {
val a = weakThis.get() ?: return@successUi
Toast.makeText(a, R.string.message_toast_media_saved, Toast.LENGTH_SHORT).show()
}.alwaysUi {
val a = weakThis.get() ?: return@alwaysUi
a.dismissProgressDialog("save_media_to_progress")
}
}
private fun MediaViewerFragment.cacheFileInfo(): SaveFileTask.FileInfo? {
when (this) {
is CacheDownloadMediaViewerFragment -> {
val cacheUri = downloadResult?.cacheUri ?: return null
val type = when (this) {
is ImagePageFragment -> CacheFileType.IMAGE
is VideoPageFragment -> CacheFileType.VIDEO
is GifPageFragment -> CacheFileType.IMAGE
else -> return null
}
return activity?.let {
CacheProvider.ContentUriFileInfo(it, cacheUri, type)
}
}
is ExoPlayerPageFragment -> {
return getRequestFileInfo()
}
else -> return null
}
}
class SaveMediaTask(activity: MediaViewerActivity, destination: File, fileInfo: FileInfo) :
SaveFileTask(activity, destination, fileInfo) {
private val PROGRESS_FRAGMENT_TAG = "progress"
override fun dismissProgress() {
val activity = context as? MediaViewerActivity ?: return
activity.executeAfterFragmentResumed {
val fm = it.supportFragmentManager
val fragment = fm.findFragmentByTag(PROGRESS_FRAGMENT_TAG) as? DialogFragment
fragment?.dismiss()
}
}
override fun showProgress() {
val activity = context as? MediaViewerActivity ?: return
activity.executeAfterFragmentResumed {
val fragment = ProgressDialogFragment()
fragment.isCancelable = false
fragment.show(it.supportFragmentManager, PROGRESS_FRAGMENT_TAG)
}
}
override fun onFileSaved(savedFile: File, mimeType: String?) {
val activity = context as? MediaViewerActivity ?: return
val fileUri = ShareProvider.getUriForFile(activity, AUTHORITY_TWIDERE_SHARE,
savedFile)
val intent = Intent(Intent.ACTION_SEND)
intent.setDataAndType(fileUri, mimeType)
intent.putExtra(Intent.EXTRA_STREAM, fileUri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
}
activity.processShareIntent(intent)
activity.startActivityForResult(Intent.createChooser(intent, activity.getString(R.string.action_share)),
REQUEST_SHARE_MEDIA)
}
override fun onFileSaveFailed() {
val activity = context as? MediaViewerActivity ?: return
Toast.makeText(activity, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
}
}
companion object {
private const val REQUEST_SHARE_MEDIA = 201
private const val REQUEST_PERMISSION_SAVE_MEDIA = 202
private const val REQUEST_PERMISSION_SHARE_MEDIA = 203
private const val REQUEST_SELECT_SAVE_MEDIA = 204
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
const val FLAG_SYSTEM_UI_HIDE_BARS = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN
}
interface Media
}