2018-01-04 19:52:25 +01:00
|
|
|
package jp.juggler.subwaytooter
|
|
|
|
|
|
|
|
import android.Manifest
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
import android.app.DownloadManager
|
|
|
|
import android.content.ClipData
|
|
|
|
import android.content.ClipDescription
|
|
|
|
import android.content.ClipboardManager
|
|
|
|
import android.content.Intent
|
|
|
|
import android.content.pm.PackageManager
|
2018-11-22 17:10:34 +01:00
|
|
|
import android.graphics.*
|
2018-01-04 19:52:25 +01:00
|
|
|
import android.os.Build
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.os.Environment
|
|
|
|
import android.os.SystemClock
|
2019-02-15 02:51:22 +01:00
|
|
|
import androidx.core.app.ActivityCompat
|
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2018-01-04 19:52:25 +01:00
|
|
|
import android.view.View
|
|
|
|
import android.view.Window
|
|
|
|
import android.widget.TextView
|
2018-04-05 22:47:25 +02:00
|
|
|
import com.google.android.exoplayer2.*
|
2018-01-04 19:52:25 +01:00
|
|
|
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
|
|
|
|
import com.google.android.exoplayer2.source.ExtractorMediaSource
|
2018-10-01 11:45:51 +02:00
|
|
|
import com.google.android.exoplayer2.source.MediaSource
|
2018-04-05 22:47:25 +02:00
|
|
|
import com.google.android.exoplayer2.source.MediaSourceEventListener
|
2018-01-04 19:52:25 +01:00
|
|
|
import com.google.android.exoplayer2.source.TrackGroupArray
|
|
|
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
|
|
|
import com.google.android.exoplayer2.trackselection.TrackSelectionArray
|
2018-04-05 22:47:25 +02:00
|
|
|
import com.google.android.exoplayer2.ui.PlayerView
|
2018-01-04 19:52:25 +01:00
|
|
|
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
|
|
|
|
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
|
|
|
|
import com.google.android.exoplayer2.util.Util
|
2018-11-22 17:10:34 +01:00
|
|
|
import it.sephiroth.android.library.exif2.ExifInterface
|
2018-01-04 19:52:25 +01:00
|
|
|
import jp.juggler.subwaytooter.api.TootApiClient
|
|
|
|
import jp.juggler.subwaytooter.api.TootApiResult
|
|
|
|
import jp.juggler.subwaytooter.api.TootTask
|
|
|
|
import jp.juggler.subwaytooter.api.TootTaskRunner
|
2018-01-10 16:47:35 +01:00
|
|
|
import jp.juggler.subwaytooter.api.entity.*
|
2018-01-04 19:52:25 +01:00
|
|
|
import jp.juggler.subwaytooter.dialog.ActionsDialog
|
2018-12-01 00:02:18 +01:00
|
|
|
import jp.juggler.subwaytooter.util.ProgressResponseBody
|
2018-01-04 19:52:25 +01:00
|
|
|
import jp.juggler.subwaytooter.view.PinchBitmapView
|
2018-12-01 00:02:18 +01:00
|
|
|
import jp.juggler.util.*
|
2018-08-31 14:47:40 +02:00
|
|
|
import okhttp3.Request
|
2018-11-30 11:54:32 +01:00
|
|
|
import org.json.JSONObject
|
2018-11-22 17:10:34 +01:00
|
|
|
import java.io.ByteArrayInputStream
|
2018-04-05 22:47:25 +02:00
|
|
|
import java.io.IOException
|
2018-10-01 11:45:51 +02:00
|
|
|
import java.util.*
|
2018-12-06 22:38:49 +01:00
|
|
|
import javax.net.ssl.HttpsURLConnection
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
internal val log = LogCategory("ActMediaViewer")
|
|
|
|
|
|
|
|
internal val download_history_list = LinkedList<DownloadHistory>()
|
|
|
|
internal const val DOWNLOAD_REPEAT_EXPIRE = 3000L
|
|
|
|
internal const val short_limit = 5000L
|
|
|
|
|
|
|
|
private const val PERMISSION_REQUEST_CODE = 1
|
|
|
|
|
|
|
|
internal const val EXTRA_IDX = "idx"
|
|
|
|
internal const val EXTRA_DATA = "data"
|
2018-08-18 12:58:14 +02:00
|
|
|
internal const val EXTRA_SERVICE_TYPE = "serviceType"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
internal fun <T : TootAttachmentLike> encodeMediaList(list : ArrayList<T>?) =
|
|
|
|
list?.encodeJson()?.toString() ?: "[]"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-12-01 00:02:18 +01:00
|
|
|
internal fun decodeMediaList(src : String?) =
|
2018-11-30 11:54:32 +01:00
|
|
|
ArrayList<TootAttachment>().apply {
|
|
|
|
src?.toJsonArray()?.forEach {
|
|
|
|
if(it !is JSONObject) return@forEach
|
|
|
|
add(TootAttachment.decodeJson(it))
|
|
|
|
}
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
fun open(
|
|
|
|
activity : ActMain,
|
|
|
|
serviceType : ServiceType,
|
|
|
|
list : ArrayList<TootAttachmentLike>,
|
|
|
|
idx : Int
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
val intent = Intent(activity, ActMediaViewer::class.java)
|
|
|
|
intent.putExtra(EXTRA_IDX, idx)
|
2018-08-18 12:58:14 +02:00
|
|
|
intent.putExtra(EXTRA_SERVICE_TYPE, serviceType.ordinal)
|
2018-01-04 19:52:25 +01:00
|
|
|
intent.putExtra(EXTRA_DATA, encodeMediaList(list))
|
|
|
|
activity.startActivity(intent)
|
2018-12-06 09:31:50 +01:00
|
|
|
activity.overridePendingTransition(R.anim.slide_from_bottom, android.R.anim.fade_out)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal var idx : Int = 0
|
2018-01-18 19:09:36 +01:00
|
|
|
private lateinit var media_list : ArrayList<TootAttachment>
|
2018-08-18 12:58:14 +02:00
|
|
|
private lateinit var serviceType : ServiceType
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
private lateinit var pbvImage : PinchBitmapView
|
|
|
|
private lateinit var btnPrevious : View
|
|
|
|
private lateinit var btnNext : View
|
|
|
|
private lateinit var tvError : TextView
|
|
|
|
private lateinit var exoPlayer : SimpleExoPlayer
|
2018-04-05 22:47:25 +02:00
|
|
|
private lateinit var exoView : PlayerView
|
2018-01-04 19:52:25 +01:00
|
|
|
private lateinit var svDescription : View
|
|
|
|
private lateinit var tvDescription : TextView
|
|
|
|
private lateinit var tvStatus : TextView
|
|
|
|
|
|
|
|
internal var buffering_last_shown : Long = 0
|
|
|
|
|
|
|
|
private val player_listener = object : Player.EventListener {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
2018-04-05 22:47:25 +02:00
|
|
|
override fun onTimelineChanged(
|
|
|
|
timeline : Timeline?,
|
|
|
|
manifest : Any?,
|
|
|
|
reason : Int
|
|
|
|
) {
|
|
|
|
log.d("exoPlayer onTimelineChanged manifest=$manifest reason=$reason")
|
|
|
|
}
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
override fun onSeekProcessed() {
|
2018-04-05 22:47:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onShuffleModeEnabledChanged(shuffleModeEnabled : Boolean) {
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
override fun onTracksChanged(
|
|
|
|
trackGroups : TrackGroupArray?,
|
|
|
|
trackSelections : TrackSelectionArray?
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
log.d("exoPlayer onTracksChanged")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onLoadingChanged(isLoading : Boolean) {
|
|
|
|
// かなり頻繁に呼ばれる
|
2018-01-28 20:03:04 +01:00
|
|
|
// warning.d( "exoPlayer onLoadingChanged %s" ,isLoading );
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPlayerStateChanged(playWhenReady : Boolean, playbackState : Int) {
|
|
|
|
// かなり頻繁に呼ばれる
|
2018-01-28 20:03:04 +01:00
|
|
|
// warning.d( "exoPlayer onPlayerStateChanged %s %s", playWhenReady, playbackState );
|
2018-01-04 19:52:25 +01:00
|
|
|
if(playWhenReady && playbackState == Player.STATE_BUFFERING) {
|
|
|
|
val now = SystemClock.elapsedRealtime()
|
|
|
|
if(now - buffering_last_shown >= short_limit && exoPlayer.duration >= short_limit) {
|
|
|
|
buffering_last_shown = now
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActMediaViewer, false, R.string.video_buffering)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
exoPlayer.getDuration() may returns negative value (TIME_UNSET ,same as Long.MIN_VALUE + 1).
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRepeatModeChanged(repeatMode : Int) {
|
|
|
|
log.d("exoPlayer onRepeatModeChanged %d", repeatMode)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPlayerError(error : ExoPlaybackException) {
|
|
|
|
log.d("exoPlayer onPlayerError")
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActMediaViewer, error, "player error.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2018-04-05 22:47:25 +02:00
|
|
|
override fun onPositionDiscontinuity(reason : Int) {
|
|
|
|
log.d("exoPlayer onPositionDiscontinuity reason=$reason")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2018-01-11 10:31:25 +01:00
|
|
|
override fun onPlaybackParametersChanged(playbackParameters : PlaybackParameters?) {
|
2018-01-04 19:52:25 +01:00
|
|
|
log.d("exoPlayer onPlaybackParametersChanged")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSaveInstanceState(outState : Bundle?) {
|
|
|
|
super.onSaveInstanceState(outState)
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
outState ?: return
|
2018-01-21 13:46:36 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
outState.putInt(EXTRA_IDX, idx)
|
2018-08-18 12:58:14 +02:00
|
|
|
outState.putInt(EXTRA_SERVICE_TYPE, serviceType.ordinal)
|
2018-01-04 19:52:25 +01:00
|
|
|
outState.putString(EXTRA_DATA, encodeMediaList(media_list))
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState : Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2018-12-06 09:31:50 +01:00
|
|
|
App1.setActivityTheme(this, true, R.style.AppTheme_Dark_NoActionBar)
|
2018-01-04 19:52:25 +01:00
|
|
|
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
|
|
|
|
|
|
|
val intent = intent
|
|
|
|
|
|
|
|
this.idx = savedInstanceState?.getInt(EXTRA_IDX) ?: intent.getIntExtra(EXTRA_IDX, idx)
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
this.serviceType = ServiceType.values()[
|
|
|
|
savedInstanceState?.getInt(EXTRA_SERVICE_TYPE)
|
|
|
|
?: intent.getIntExtra(EXTRA_SERVICE_TYPE, 0)
|
|
|
|
]
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
this.media_list = decodeMediaList(
|
2018-08-18 12:58:14 +02:00
|
|
|
savedInstanceState?.getString(EXTRA_DATA)
|
|
|
|
?: intent.getStringExtra(EXTRA_DATA)
|
2018-01-04 19:52:25 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
if(idx < 0 || idx >= media_list.size) idx = 0
|
|
|
|
|
|
|
|
initUI()
|
|
|
|
|
|
|
|
load()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDestroy() {
|
|
|
|
super.onDestroy()
|
|
|
|
pbvImage.setBitmap(null)
|
|
|
|
exoPlayer.release()
|
|
|
|
exoPlayer
|
|
|
|
}
|
2018-12-06 09:31:50 +01:00
|
|
|
|
|
|
|
override fun finish(){
|
|
|
|
super.finish()
|
|
|
|
overridePendingTransition(R.anim.fade_in,R.anim.slide_to_bottom)
|
|
|
|
}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
internal fun initUI() {
|
|
|
|
setContentView(R.layout.act_media_viewer)
|
|
|
|
pbvImage = findViewById(R.id.pbvImage)
|
|
|
|
btnPrevious = findViewById(R.id.btnPrevious)
|
|
|
|
btnNext = findViewById(R.id.btnNext)
|
|
|
|
exoView = findViewById(R.id.exoView)
|
|
|
|
tvError = findViewById(R.id.tvError)
|
|
|
|
svDescription = findViewById(R.id.svDescription)
|
|
|
|
tvDescription = findViewById(R.id.tvDescription)
|
|
|
|
tvStatus = findViewById(R.id.tvStatus)
|
|
|
|
|
|
|
|
val enablePaging = media_list.size > 1
|
|
|
|
btnPrevious.isEnabled = enablePaging
|
|
|
|
btnNext.isEnabled = enablePaging
|
|
|
|
btnPrevious.alpha = if(enablePaging) 1f else 0.3f
|
|
|
|
btnNext.alpha = if(enablePaging) 1f else 0.3f
|
|
|
|
|
|
|
|
btnPrevious.setOnClickListener(this)
|
|
|
|
btnNext.setOnClickListener(this)
|
|
|
|
findViewById<View>(R.id.btnDownload).setOnClickListener(this)
|
|
|
|
findViewById<View>(R.id.btnMore).setOnClickListener(this)
|
|
|
|
|
|
|
|
pbvImage.setCallback(object : PinchBitmapView.Callback {
|
2018-12-03 21:00:03 +01:00
|
|
|
override fun onSwipe(deltaX : Int,deltaY:Int) {
|
2018-01-04 19:52:25 +01:00
|
|
|
if(isDestroyed) return
|
2018-12-03 21:00:03 +01:00
|
|
|
if( deltaX != 0) {
|
|
|
|
loadDelta(deltaX)
|
|
|
|
}else{
|
|
|
|
log.d("finish by vertical swipe")
|
|
|
|
finish()
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
override fun onMove(
|
|
|
|
bitmap_w : Float,
|
|
|
|
bitmap_h : Float,
|
|
|
|
tx : Float,
|
|
|
|
ty : Float,
|
|
|
|
scale : Float
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
App1.getAppState(this@ActMediaViewer).handler.post(Runnable {
|
|
|
|
if(isDestroyed) return@Runnable
|
|
|
|
if(tvStatus.visibility == View.VISIBLE) {
|
2018-01-21 13:46:36 +01:00
|
|
|
tvStatus.text = getString(
|
|
|
|
R.string.zooming_of,
|
|
|
|
bitmap_w.toInt(),
|
|
|
|
bitmap_h.toInt(),
|
|
|
|
scale
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
exoPlayer = ExoPlayerFactory.newSimpleInstance(this, DefaultTrackSelector())
|
|
|
|
exoPlayer.addListener(player_listener)
|
|
|
|
|
|
|
|
exoView.player = exoPlayer
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun loadDelta(delta : Int) {
|
|
|
|
if(media_list.size < 2) return
|
|
|
|
val size = media_list.size
|
|
|
|
idx = (idx + size + delta) % size
|
|
|
|
load()
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun load() {
|
|
|
|
|
|
|
|
exoPlayer.stop()
|
|
|
|
pbvImage.visibility = View.GONE
|
|
|
|
exoView.visibility = View.GONE
|
|
|
|
tvError.visibility = View.GONE
|
|
|
|
svDescription.visibility = View.GONE
|
|
|
|
tvStatus.visibility = View.GONE
|
|
|
|
|
|
|
|
if(idx < 0 || idx >= media_list.size) {
|
|
|
|
showError(getString(R.string.media_attachment_empty))
|
|
|
|
return
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
val ta = media_list[idx]
|
2018-01-18 19:09:36 +01:00
|
|
|
val description = ta.description
|
2018-01-21 13:46:36 +01:00
|
|
|
if(description?.isNotEmpty() == true) {
|
2018-01-18 19:09:36 +01:00
|
|
|
svDescription.visibility = View.VISIBLE
|
|
|
|
tvDescription.text = description
|
|
|
|
}
|
|
|
|
|
2018-12-17 21:41:17 +01:00
|
|
|
when(ta.type){
|
|
|
|
TootAttachmentLike.TYPE_IMAGE ->loadBitmap(ta)
|
|
|
|
TootAttachmentLike.TYPE_VIDEO,
|
|
|
|
TootAttachmentLike.TYPE_GIFV,
|
|
|
|
TootAttachmentLike.TYPE_AUDIO ->loadVideo(ta)
|
2018-01-18 19:09:36 +01:00
|
|
|
// maybe TYPE_UNKNOWN
|
2018-12-17 21:41:17 +01:00
|
|
|
else-> showError(getString(R.string.media_attachment_type_error, ta.type))
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
2018-12-17 21:41:17 +01:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun showError(message : String) {
|
|
|
|
exoView.visibility = View.GONE
|
|
|
|
pbvImage.visibility = View.GONE
|
|
|
|
tvError.visibility = View.VISIBLE
|
|
|
|
tvError.text = message
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
|
|
|
private fun loadVideo(ta : TootAttachment) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val url = ta.getLargeUrl(App1.pref)
|
|
|
|
if(url == null) {
|
|
|
|
showError("missing media attachment url.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-06 22:38:49 +01:00
|
|
|
// https://github.com/google/ExoPlayer/issues/1819
|
|
|
|
HttpsURLConnection.setDefaultSSLSocketFactory(MySslSocketFactory)
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
exoView.visibility = View.VISIBLE
|
|
|
|
|
|
|
|
val defaultBandwidthMeter = DefaultBandwidthMeter()
|
|
|
|
val extractorsFactory = DefaultExtractorsFactory()
|
|
|
|
|
|
|
|
val dataSourceFactory = DefaultDataSourceFactory(
|
|
|
|
this, Util.getUserAgent(this, getString(R.string.app_name)), defaultBandwidthMeter
|
|
|
|
)
|
|
|
|
|
2018-04-05 22:47:25 +02:00
|
|
|
val mediaSource = ExtractorMediaSource.Factory(dataSourceFactory)
|
|
|
|
.setExtractorsFactory(extractorsFactory)
|
2018-11-30 05:42:20 +01:00
|
|
|
.createMediaSource(url.toUri())
|
2018-11-22 17:10:34 +01:00
|
|
|
|
|
|
|
mediaSource.addEventListener(App1.getAppState(this).handler, mediaSourceEventListener)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
exoPlayer.prepare(mediaSource)
|
|
|
|
exoPlayer.playWhenReady = true
|
2018-12-17 21:41:17 +01:00
|
|
|
exoPlayer.repeatMode = when(ta.type){
|
|
|
|
TootAttachmentLike.TYPE_VIDEO -> Player.REPEAT_MODE_OFF
|
|
|
|
// GIFV or AUDIO
|
|
|
|
else -> Player.REPEAT_MODE_ALL
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-01 04:57:13 +02:00
|
|
|
private val mediaSourceEventListener = object : MediaSourceEventListener {
|
2018-04-05 22:47:25 +02:00
|
|
|
override fun onLoadStarted(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
loadEventInfo : MediaSourceEventListener.LoadEventInfo?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?
|
2018-11-22 17:10:34 +01:00
|
|
|
) {
|
2018-04-05 22:47:25 +02:00
|
|
|
log.d("onLoadStarted")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onDownstreamFormatChanged(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?
|
2018-11-22 17:10:34 +01:00
|
|
|
) {
|
2018-04-05 22:47:25 +02:00
|
|
|
log.d("onDownstreamFormatChanged")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onUpstreamDiscarded(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?
|
2018-11-22 17:10:34 +01:00
|
|
|
) {
|
2018-04-05 22:47:25 +02:00
|
|
|
log.d("onUpstreamDiscarded")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onLoadCompleted(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
loadEventInfo : MediaSourceEventListener.LoadEventInfo?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?
|
2018-04-05 22:47:25 +02:00
|
|
|
) {
|
|
|
|
log.d("onLoadCompleted")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onLoadCanceled(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
loadEventInfo : MediaSourceEventListener.LoadEventInfo?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?
|
2018-04-05 22:47:25 +02:00
|
|
|
) {
|
|
|
|
log.d("onLoadCanceled")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onLoadError(
|
2018-10-01 11:45:51 +02:00
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?,
|
|
|
|
loadEventInfo : MediaSourceEventListener.LoadEventInfo?,
|
|
|
|
mediaLoadData : MediaSourceEventListener.MediaLoadData?,
|
2018-04-05 22:47:25 +02:00
|
|
|
error : IOException?,
|
|
|
|
wasCanceled : Boolean
|
|
|
|
) {
|
2018-08-18 12:58:14 +02:00
|
|
|
if(error != null) {
|
2018-04-05 22:47:25 +02:00
|
|
|
showError(error.withCaption("load error."))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-01 11:45:51 +02:00
|
|
|
override fun onMediaPeriodCreated(
|
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?
|
|
|
|
) {
|
|
|
|
log.d("onMediaPeriodCreated")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onMediaPeriodReleased(
|
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?
|
|
|
|
) {
|
|
|
|
log.d("onMediaPeriodReleased")
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onReadingStarted(
|
|
|
|
windowIndex : Int,
|
|
|
|
mediaPeriodId : MediaSource.MediaPeriodId?
|
|
|
|
) {
|
|
|
|
log.d("onReadingStarted")
|
|
|
|
}
|
|
|
|
|
2018-04-05 22:47:25 +02:00
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
@SuppressLint("StaticFieldLeak")
|
|
|
|
private fun loadBitmap(ta : TootAttachment) {
|
2018-01-14 22:47:42 +01:00
|
|
|
val urlList = ta.getLargeUrlList(App1.pref)
|
2018-01-21 13:46:36 +01:00
|
|
|
if(urlList.isEmpty()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
showError("missing media attachment url.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
tvStatus.visibility = View.VISIBLE
|
|
|
|
tvStatus.text = null
|
|
|
|
|
|
|
|
pbvImage.visibility = View.VISIBLE
|
|
|
|
pbvImage.setBitmap(null)
|
|
|
|
|
|
|
|
TootTaskRunner(this, TootTaskRunner.PROGRESS_HORIZONTAL).run(object : TootTask {
|
|
|
|
|
|
|
|
private val options = BitmapFactory.Options()
|
|
|
|
|
2018-08-31 14:47:40 +02:00
|
|
|
var bitmap : Bitmap? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
private fun decodeBitmap(data : ByteArray, pixel_max : Int) : Bitmap? {
|
|
|
|
|
2018-11-22 17:10:34 +01:00
|
|
|
// EXIF回転情報の取得
|
|
|
|
val orientation : Int? = try {
|
|
|
|
ExifInterface().apply {
|
|
|
|
readExif(
|
|
|
|
ByteArrayInputStream(data),
|
|
|
|
ExifInterface.Options.OPTION_IFD_0
|
|
|
|
or ExifInterface.Options.OPTION_IFD_1
|
|
|
|
or ExifInterface.Options.OPTION_IFD_EXIF
|
|
|
|
)
|
|
|
|
}.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
|
|
|
|
// detects image size
|
2018-01-04 19:52:25 +01:00
|
|
|
options.inJustDecodeBounds = true
|
|
|
|
options.inScaled = false
|
|
|
|
options.outWidth = 0
|
|
|
|
options.outHeight = 0
|
2018-01-10 16:47:35 +01:00
|
|
|
BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
2018-01-04 19:52:25 +01:00
|
|
|
var w = options.outWidth
|
|
|
|
var h = options.outHeight
|
|
|
|
if(w <= 0 || h <= 0) {
|
|
|
|
log.e("can't decode bounds.")
|
|
|
|
return null
|
|
|
|
}
|
2018-11-22 17:10:34 +01:00
|
|
|
|
|
|
|
// calc bits to reduce size
|
2018-01-04 19:52:25 +01:00
|
|
|
var bits = 0
|
|
|
|
while(w > pixel_max || h > pixel_max) {
|
|
|
|
++ bits
|
|
|
|
w = w shr 1
|
|
|
|
h = h shr 1
|
|
|
|
}
|
|
|
|
options.inJustDecodeBounds = false
|
|
|
|
options.inSampleSize = 1 shl bits
|
2018-11-22 17:10:34 +01:00
|
|
|
|
|
|
|
// decode image
|
|
|
|
val bitmap1 = BitmapFactory.decodeByteArray(data, 0, data.size, options)
|
|
|
|
|
|
|
|
// デコード失敗、または回転情報がない
|
|
|
|
if(bitmap1 == null || orientation == null) return bitmap1
|
|
|
|
|
|
|
|
val src_width = bitmap1.width
|
|
|
|
val src_height = bitmap1.height
|
|
|
|
|
|
|
|
// 回転行列を作る
|
|
|
|
val matrix = Matrix()
|
|
|
|
matrix.reset()
|
|
|
|
|
|
|
|
// 画像の中心が原点に来るようにして
|
|
|
|
matrix.postTranslate(src_width * - 0.5f, src_height * - 0.5f)
|
2018-11-30 05:42:20 +01:00
|
|
|
|
2018-11-22 17:10:34 +01:00
|
|
|
// orientationに合わせた回転指定
|
|
|
|
val flipWh = when(orientation) {
|
|
|
|
2 -> {
|
|
|
|
// 上下反転
|
|
|
|
matrix.postScale(1f, - 1f)
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
3 -> {
|
|
|
|
// 180度回転
|
|
|
|
matrix.postRotate(180f)
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
4 -> {
|
|
|
|
// 左右反転
|
|
|
|
matrix.postScale(- 1f, 1f)
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
5 -> {
|
|
|
|
// 上下反転して反時計回りに90度
|
|
|
|
matrix.postScale(1f, - 1f)
|
|
|
|
matrix.postRotate(- 90f)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
6 -> {
|
|
|
|
// 時計回りに90度
|
|
|
|
matrix.postRotate(90f)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
7 -> {
|
|
|
|
// 上下反転して時計回りに90度
|
|
|
|
matrix.postScale(1f, - 1f)
|
|
|
|
matrix.postRotate(90f)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
8 -> {
|
|
|
|
// 上下反転して反時計回りに90度
|
|
|
|
matrix.postRotate(- 90f)
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
// 回転は不要
|
|
|
|
return bitmap
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 05:42:20 +01:00
|
|
|
|
2018-11-22 17:10:34 +01:00
|
|
|
// 出力サイズ
|
|
|
|
val dst_width : Int
|
|
|
|
val dst_height : Int
|
2018-11-30 05:42:20 +01:00
|
|
|
when(flipWh) {
|
2018-11-22 17:10:34 +01:00
|
|
|
true -> {
|
|
|
|
dst_width = src_height
|
|
|
|
dst_height = src_width
|
|
|
|
}
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
dst_width = src_width
|
|
|
|
dst_height = src_height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 表示領域に埋まるように平行移動
|
|
|
|
matrix.postTranslate(dst_width * 0.5f, dst_height * 0.5f)
|
|
|
|
|
|
|
|
// 回転後の画像
|
|
|
|
val bitmap2 = try {
|
|
|
|
Bitmap.createBitmap(dst_width, dst_height, Bitmap.Config.ARGB_8888)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
null
|
|
|
|
} ?: return bitmap1
|
|
|
|
|
|
|
|
try {
|
|
|
|
Canvas(bitmap2).drawBitmap(
|
|
|
|
bitmap1,
|
|
|
|
matrix,
|
|
|
|
Paint().apply { isFilterBitmap = true }
|
|
|
|
)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
bitmap2.recycle()
|
|
|
|
return bitmap1
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
bitmap1.recycle()
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
return bitmap2
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2019-01-16 16:27:37 +01:00
|
|
|
fun getHttpCached(client : TootApiClient, url : String) : Pair<TootApiResult?,ByteArray?> {
|
2018-01-14 22:47:42 +01:00
|
|
|
val result = TootApiResult.makeWithCaption(url)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-08-31 14:47:40 +02:00
|
|
|
val request = Request.Builder()
|
2018-04-30 16:01:00 +02:00
|
|
|
.url(url)
|
2019-01-15 15:47:08 +01:00
|
|
|
.cacheControl(App1.CACHE_CONTROL)
|
2018-08-18 12:58:14 +02:00
|
|
|
.addHeader("Accept", "image/webp,image/*,*/*;q=0.8")
|
2018-04-30 16:01:00 +02:00
|
|
|
.build()
|
|
|
|
|
2018-11-22 17:10:34 +01:00
|
|
|
if(! client.sendRequest(
|
|
|
|
result,
|
|
|
|
tmpOkhttpClient = App1.ok_http_client_media_viewer
|
|
|
|
) {
|
2018-04-30 16:01:00 +02:00
|
|
|
request
|
2019-01-16 16:27:37 +01:00
|
|
|
}
|
|
|
|
) return Pair(result,null)
|
|
|
|
|
|
|
|
if(client.isApiCancelled) return Pair(null,null)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2018-01-14 22:47:42 +01:00
|
|
|
val response = requireNotNull(result.response)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(! response.isSuccessful) {
|
2019-01-16 16:27:37 +01:00
|
|
|
result.setError(TootApiClient.formatResponse(response, result.caption))
|
|
|
|
return Pair(result,null)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
2018-01-14 22:47:42 +01:00
|
|
|
try {
|
2019-01-16 16:27:37 +01:00
|
|
|
val ba = ProgressResponseBody.bytes(response) { bytesRead, bytesTotal ->
|
2018-01-04 19:52:25 +01:00
|
|
|
// 50MB以上のデータはキャンセルする
|
|
|
|
if(Math.max(bytesRead, bytesTotal) >= 50000000) {
|
|
|
|
throw RuntimeException("media attachment is larger than 50000000")
|
|
|
|
}
|
|
|
|
client.publishApiProgressRatio(bytesRead.toInt(), bytesTotal.toInt())
|
|
|
|
}
|
2019-01-16 16:27:37 +01:00
|
|
|
if(client.isApiCancelled) return Pair(null,null)
|
|
|
|
return Pair(result,ba)
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
result.setError(TootApiClient.formatResponse(response, result.caption, "?"))
|
2019-01-16 16:27:37 +01:00
|
|
|
return Pair(result,null)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-14 22:47:42 +01:00
|
|
|
override fun background(client : TootApiClient) : TootApiResult? {
|
2018-01-21 13:46:36 +01:00
|
|
|
if(urlList.isEmpty()) return TootApiResult("missing url")
|
2019-01-16 16:27:37 +01:00
|
|
|
var lastResult : TootApiResult? = null
|
2018-01-14 22:47:42 +01:00
|
|
|
for(url in urlList) {
|
2019-01-16 16:27:37 +01:00
|
|
|
val(result,ba) = getHttpCached(client, url)
|
|
|
|
lastResult = result
|
|
|
|
if( ba != null){
|
2018-01-14 22:47:42 +01:00
|
|
|
client.publishApiProgress("decoding image…")
|
2019-01-16 16:27:37 +01:00
|
|
|
val bitmap = decodeBitmap(ba, 2048)
|
2018-01-21 13:46:36 +01:00
|
|
|
if(bitmap != null) {
|
2018-01-14 22:47:42 +01:00
|
|
|
this.bitmap = bitmap
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-16 16:27:37 +01:00
|
|
|
return lastResult
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun handleResult(result : TootApiResult?) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val bitmap = this.bitmap
|
2018-01-21 13:46:36 +01:00
|
|
|
if(bitmap != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
pbvImage.setBitmap(bitmap)
|
2018-01-21 13:46:36 +01:00
|
|
|
} else if(result != null) {
|
|
|
|
showToast(this@ActMediaViewer, true, result.error)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onClick(v : View) {
|
|
|
|
try {
|
|
|
|
when(v.id) {
|
|
|
|
|
|
|
|
R.id.btnPrevious -> loadDelta(- 1)
|
|
|
|
R.id.btnNext -> loadDelta(+ 1)
|
|
|
|
R.id.btnDownload -> download(media_list[idx])
|
|
|
|
R.id.btnMore -> more(media_list[idx])
|
|
|
|
}
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "action failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class DownloadHistory(val time : Long, val url : String)
|
|
|
|
|
|
|
|
private fun download(ta : TootAttachmentLike) {
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
val permissionCheck = ContextCompat.checkSelfPermission(
|
|
|
|
this,
|
|
|
|
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
if(permissionCheck != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
preparePermission()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
val downLoadManager = getSystemService(DOWNLOAD_SERVICE) as? DownloadManager
|
2018-01-04 19:52:25 +01:00
|
|
|
?: throw NotImplementedError("missing DownloadManager system service")
|
|
|
|
|
|
|
|
val url = if(ta is TootAttachment) {
|
|
|
|
ta.getLargeUrl(App1.pref)
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
} ?: return
|
|
|
|
|
|
|
|
// ボタン連打対策
|
|
|
|
run {
|
|
|
|
val now = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
// 期限切れの履歴を削除
|
|
|
|
val it = download_history_list.iterator()
|
|
|
|
while(it.hasNext()) {
|
|
|
|
val dh = it.next()
|
|
|
|
if(now - dh.time >= DOWNLOAD_REPEAT_EXPIRE) {
|
|
|
|
// この履歴は十分に古いので捨てる
|
|
|
|
it.remove()
|
|
|
|
} else if(url == dh.url) {
|
|
|
|
// 履歴に同じURLがあればエラーとする
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.dont_repeat_download_to_same_url)
|
2018-01-04 19:52:25 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 履歴の末尾に追加(履歴は古い順に並ぶ)
|
|
|
|
download_history_list.addLast(DownloadHistory(now, url))
|
|
|
|
}
|
|
|
|
|
|
|
|
var fileName : String? = null
|
|
|
|
|
|
|
|
try {
|
2018-11-30 05:42:20 +01:00
|
|
|
val pathSegments = url.toUri().pathSegments
|
2018-01-04 19:52:25 +01:00
|
|
|
if(pathSegments != null) {
|
|
|
|
val size = pathSegments.size
|
|
|
|
for(i in size - 1 downTo 0) {
|
|
|
|
val s = pathSegments[i]
|
2018-01-21 13:46:36 +01:00
|
|
|
if(s?.isNotEmpty() == true) {
|
2018-01-04 19:52:25 +01:00
|
|
|
fileName = s
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fileName == null) {
|
|
|
|
fileName = url
|
|
|
|
.replaceFirst("https?://".toRegex(), "")
|
|
|
|
.replace("[^.\\w\\d]+".toRegex(), "-")
|
|
|
|
}
|
|
|
|
if(fileName.length >= 20) fileName = fileName.substring(fileName.length - 20)
|
|
|
|
|
2018-11-30 05:42:20 +01:00
|
|
|
val request = DownloadManager.Request(url.toUri())
|
2018-01-04 19:52:25 +01:00
|
|
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
|
|
|
|
request.setTitle(fileName)
|
|
|
|
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE or DownloadManager.Request.NETWORK_WIFI)
|
|
|
|
//メディアスキャンを許可する
|
|
|
|
request.allowScanningByMediaScanner()
|
|
|
|
|
|
|
|
//ダウンロード中・ダウンロード完了時にも通知を表示する
|
|
|
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
|
|
|
|
|
|
|
downLoadManager.enqueue(request)
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.downloading)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun share(action : String, url : String) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
val intent = Intent(action)
|
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
|
if(action == Intent.ACTION_SEND) {
|
|
|
|
intent.type = "text/plain"
|
|
|
|
intent.putExtra(Intent.EXTRA_TEXT, url)
|
|
|
|
} else {
|
2018-11-30 05:42:20 +01:00
|
|
|
intent.data = url.toUri()
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
startActivity(intent)
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "can't open app.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun copy(url : String) {
|
2018-01-10 16:47:35 +01:00
|
|
|
val cm = getSystemService(CLIPBOARD_SERVICE) as? ClipboardManager
|
2018-01-04 19:52:25 +01:00
|
|
|
?: throw NotImplementedError("missing ClipboardManager system service")
|
|
|
|
|
|
|
|
try {
|
|
|
|
//クリップボードに格納するItemを作成
|
|
|
|
val item = ClipData.Item(url)
|
|
|
|
|
|
|
|
val mimeType = arrayOfNulls<String>(1)
|
|
|
|
mimeType[0] = ClipDescription.MIMETYPE_TEXT_PLAIN
|
|
|
|
|
|
|
|
//クリップボードに格納するClipDataオブジェクトの作成
|
|
|
|
val cd = ClipData(ClipDescription("media URL", mimeType), item)
|
|
|
|
|
|
|
|
//クリップボードにデータを格納
|
|
|
|
cm.primaryClip = cd
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, false, R.string.url_is_copied)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, ex, "clipboard access failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
internal fun more(ta : TootAttachmentLike) {
|
|
|
|
val ad = ActionsDialog()
|
|
|
|
|
|
|
|
if(ta is TootAttachment) {
|
|
|
|
val url = ta.getLargeUrl(App1.pref) ?: return
|
|
|
|
|
|
|
|
ad.addAction(getString(R.string.open_in_browser)) { share(Intent.ACTION_VIEW, url) }
|
|
|
|
ad.addAction(getString(R.string.share_url)) { share(Intent.ACTION_SEND, url) }
|
|
|
|
ad.addAction(getString(R.string.copy_url)) { copy(url) }
|
|
|
|
|
|
|
|
addMoreMenu(ad, "url", ta.url, Intent.ACTION_VIEW)
|
|
|
|
addMoreMenu(ad, "remote_url", ta.remote_url, Intent.ACTION_VIEW)
|
|
|
|
addMoreMenu(ad, "preview_url", ta.preview_url, Intent.ACTION_VIEW)
|
|
|
|
addMoreMenu(ad, "text_url", ta.text_url, Intent.ACTION_VIEW)
|
|
|
|
|
|
|
|
} else if(ta is TootAttachmentMSP) {
|
|
|
|
val url = ta.preview_url
|
|
|
|
ad.addAction(getString(R.string.open_in_browser)) { share(Intent.ACTION_VIEW, url) }
|
|
|
|
ad.addAction(getString(R.string.share_url)) { share(Intent.ACTION_SEND, url) }
|
|
|
|
ad.addAction(getString(R.string.copy_url)) { copy(url) }
|
|
|
|
}
|
|
|
|
|
|
|
|
ad.show(this, null)
|
|
|
|
}
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
private fun addMoreMenu(
|
|
|
|
ad : ActionsDialog,
|
|
|
|
caption_prefix : String,
|
|
|
|
url : String?,
|
|
|
|
action : String
|
|
|
|
) {
|
2018-11-30 05:42:20 +01:00
|
|
|
val uri = url.mayUri() ?: return
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
val caption = getString(R.string.open_browser_of, caption_prefix)
|
|
|
|
|
|
|
|
ad.addAction(caption) {
|
|
|
|
try {
|
2018-11-30 05:42:20 +01:00
|
|
|
val intent = Intent(action, uri)
|
2018-01-04 19:52:25 +01:00
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
|
startActivity(intent)
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this@ActMediaViewer, ex, "can't open app.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun preparePermission() {
|
|
|
|
if(Build.VERSION.SDK_INT >= 23) {
|
2018-01-21 13:46:36 +01:00
|
|
|
ActivityCompat.requestPermissions(
|
|
|
|
this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_CODE
|
2018-01-04 19:52:25 +01:00
|
|
|
)
|
|
|
|
} else {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.missing_permission_to_access_media)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onRequestPermissionsResult(
|
|
|
|
requestCode : Int, permissions : Array<String>, grantResults : IntArray
|
|
|
|
) {
|
|
|
|
when(requestCode) {
|
|
|
|
PERMISSION_REQUEST_CODE -> {
|
|
|
|
var bNotGranted = false
|
|
|
|
var i = 0
|
|
|
|
val ie = permissions.size
|
|
|
|
while(i < ie) {
|
|
|
|
if(grantResults[i] != PackageManager.PERMISSION_GRANTED) {
|
|
|
|
bNotGranted = true
|
|
|
|
}
|
|
|
|
++ i
|
|
|
|
}
|
|
|
|
if(bNotGranted) {
|
2018-01-21 13:46:36 +01:00
|
|
|
showToast(this, true, R.string.missing_permission_to_access_media)
|
2018-01-04 19:52:25 +01:00
|
|
|
} else {
|
|
|
|
download(media_list[idx])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-30 05:42:20 +01:00
|
|
|
|
2018-11-26 21:36:14 +01:00
|
|
|
}
|