2018-01-04 19:52:25 +01:00
|
|
|
package jp.juggler.subwaytooter
|
|
|
|
|
|
|
|
import android.content.Intent
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.Bundle
|
2019-02-15 02:51:22 +01:00
|
|
|
import androidx.appcompat.app.AppCompatActivity
|
2021-05-22 00:03:16 +02:00
|
|
|
import androidx.core.content.FileProvider
|
2022-09-10 23:09:26 +02:00
|
|
|
import jp.juggler.util.*
|
2023-01-13 13:22:25 +01:00
|
|
|
import jp.juggler.util.data.digestSHA256Hex
|
|
|
|
import jp.juggler.util.log.LogCategory
|
|
|
|
import jp.juggler.util.log.showToast
|
2021-06-13 13:48:48 +02:00
|
|
|
import okhttp3.internal.toHexString
|
2018-01-04 19:52:25 +01:00
|
|
|
import java.io.File
|
|
|
|
import java.io.FileOutputStream
|
2018-12-01 00:02:18 +01:00
|
|
|
import java.util.*
|
2018-01-04 19:52:25 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicReference
|
|
|
|
|
|
|
|
class ActCallback : AppCompatActivity() {
|
2021-06-20 15:12:25 +02:00
|
|
|
|
|
|
|
companion object {
|
|
|
|
private val log = LogCategory("ActCallback")
|
|
|
|
|
|
|
|
internal val last_uri = AtomicReference<Uri>(null)
|
|
|
|
internal val sent_intent = AtomicReference<Intent>(null)
|
|
|
|
|
|
|
|
private fun String?.isMediaMimeType() = when {
|
|
|
|
this == null -> false
|
|
|
|
this.startsWith("image/") -> true
|
|
|
|
this.startsWith("video/") -> true
|
|
|
|
this.startsWith("audio/") -> true
|
|
|
|
else -> false
|
|
|
|
}
|
2021-11-06 03:11:28 +01:00
|
|
|
|
|
|
|
@Volatile
|
|
|
|
private var uriFromApp: Uri? = null
|
|
|
|
|
|
|
|
fun setUriFromApp(uri: Uri?) {
|
|
|
|
synchronized(this) {
|
|
|
|
uriFromApp = uri
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun containsUriFromApp(uri: Uri?) =
|
|
|
|
synchronized(this) {
|
|
|
|
uri == uriFromApp
|
|
|
|
}
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
log.d("onCreate flags=0x${intent.flags.toHexString()}")
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
|
|
|
|
var intent: Intent? = intent
|
|
|
|
when {
|
|
|
|
intent == null -> {
|
|
|
|
// 多分起きないと思う
|
|
|
|
}
|
|
|
|
|
|
|
|
(intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0 -> {
|
|
|
|
// 履歴から開いた場合はIntentの中味を読まない
|
|
|
|
}
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
val action = intent.action
|
|
|
|
val type = intent.type
|
|
|
|
// ACTION_SEND か ACTION_SEND_MULTIPLE
|
|
|
|
// ACTION_VIEW かつ type が 画像かビデオか音声
|
|
|
|
if (
|
|
|
|
Intent.ACTION_SEND == action ||
|
|
|
|
Intent.ACTION_SEND_MULTIPLE == action ||
|
|
|
|
(Intent.ACTION_VIEW == action && type.isMediaMimeType())
|
|
|
|
) {
|
|
|
|
|
|
|
|
// Google Photo などから送られるIntentに含まれるuriの有効期間はActivityが閉じられるまで
|
|
|
|
// http://qiita.com/pside/items/a821e2fe9ae6b7c1a98c
|
|
|
|
|
|
|
|
// 有効期間を延長する
|
|
|
|
intent = remake(intent)
|
|
|
|
if (intent != null) {
|
|
|
|
sent_intent.set(intent)
|
|
|
|
}
|
2021-11-06 03:11:28 +01:00
|
|
|
} else if (forbidUriFromApp(intent)) {
|
|
|
|
// last_uriをクリアする
|
|
|
|
last_uri.set(null)
|
|
|
|
// ダイアログを閉じるまで画面遷移しない
|
|
|
|
return
|
2021-06-20 15:12:25 +02:00
|
|
|
} else {
|
|
|
|
val uri = intent.data
|
|
|
|
if (uri != null) {
|
|
|
|
last_uri.set(uri)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// どうであれメイン画面に戻る
|
2021-11-06 03:11:28 +01:00
|
|
|
afterDispatch()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun afterDispatch() {
|
|
|
|
val intent = Intent(this, ActMain::class.java)
|
2021-06-20 15:12:25 +02:00
|
|
|
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY or Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
|
startActivity(intent)
|
|
|
|
finish()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun copyExtraTexts(dst: Intent, src: Intent) {
|
|
|
|
var sv: String?
|
|
|
|
//
|
|
|
|
sv = src.getStringExtra(Intent.EXTRA_TEXT)
|
|
|
|
if (sv != null) dst.putExtra(Intent.EXTRA_TEXT, sv)
|
|
|
|
//
|
|
|
|
sv = src.getStringExtra(Intent.EXTRA_SUBJECT)
|
|
|
|
if (sv != null) dst.putExtra(Intent.EXTRA_SUBJECT, sv)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun remake(src: Intent): Intent? {
|
|
|
|
|
|
|
|
sweepOldCache()
|
|
|
|
|
|
|
|
try {
|
|
|
|
val action = src.action
|
|
|
|
val type = src.type
|
|
|
|
|
|
|
|
if (type.isMediaMimeType()) {
|
|
|
|
if (Intent.ACTION_VIEW == action) {
|
|
|
|
src.data?.let { uriOriginal ->
|
|
|
|
try {
|
|
|
|
val uri = saveToCache(uriOriginal)
|
|
|
|
val dst = Intent(action)
|
|
|
|
dst.setDataAndType(uri, type)
|
|
|
|
copyExtraTexts(dst, src)
|
|
|
|
return dst
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "remake failed. src=$src")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (Intent.ACTION_SEND == action) {
|
2022-09-10 23:09:26 +02:00
|
|
|
var uri = src.getStreamUriExtra()
|
|
|
|
?: return src // text/plainの場合
|
|
|
|
try {
|
|
|
|
uri = saveToCache(uri)
|
|
|
|
|
|
|
|
val dst = Intent(action)
|
|
|
|
dst.type = type
|
|
|
|
dst.putExtra(Intent.EXTRA_STREAM, uri)
|
|
|
|
copyExtraTexts(dst, src)
|
|
|
|
return dst
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "remake failed. src=$src")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
} else if (Intent.ACTION_SEND_MULTIPLE == action) {
|
2022-09-10 23:09:26 +02:00
|
|
|
val listUri = src.getStreamUriListExtra()
|
|
|
|
?: return null
|
2021-06-20 15:12:25 +02:00
|
|
|
val listDst = ArrayList<Uri>()
|
|
|
|
for (uriOriginal in listUri) {
|
|
|
|
if (uriOriginal != null) {
|
|
|
|
try {
|
|
|
|
val uri = saveToCache(uriOriginal)
|
|
|
|
listDst.add(uri)
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "remake failed. src=$src")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (listDst.isEmpty()) return null
|
|
|
|
val dst = Intent(action)
|
|
|
|
dst.type = type
|
|
|
|
dst.putParcelableArrayListExtra(Intent.EXTRA_STREAM, listDst)
|
|
|
|
copyExtraTexts(dst, src)
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
} else if (Intent.ACTION_SEND == action) {
|
|
|
|
|
|
|
|
// Swarmアプリから送られたインテントは getType()==null だが EXTRA_TEXT は含まれている
|
|
|
|
// EXTRA_TEXT の存在を確認してからtypeがnullもしくは text/plain なら受け取る
|
|
|
|
|
|
|
|
val sv = src.getStringExtra(Intent.EXTRA_TEXT)
|
|
|
|
if (sv?.isNotEmpty() == true && (type == null || type.startsWith("text/"))) {
|
|
|
|
val dst = Intent(action)
|
|
|
|
dst.type = "text/plain"
|
|
|
|
copyExtraTexts(dst, src)
|
|
|
|
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "remake failed. src=$src")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
@Throws(Throwable::class)
|
|
|
|
private fun saveToCache(uri: Uri): Uri {
|
|
|
|
|
|
|
|
// prepare cache directory
|
|
|
|
val cacheDir = this.cacheDir
|
|
|
|
|
|
|
|
cacheDir.mkdirs()
|
|
|
|
|
|
|
|
val name =
|
|
|
|
"img." + System.currentTimeMillis().toString() + "." + uri.toString().digestSHA256Hex()
|
|
|
|
|
|
|
|
val dst = File(cacheDir, name)
|
|
|
|
|
|
|
|
FileOutputStream(dst).use { outStream ->
|
|
|
|
val source = contentResolver.openInputStream(uri)
|
|
|
|
?: error("getContentResolver.openInputStream returns null.")
|
|
|
|
source.use { inStream ->
|
2022-03-13 13:05:54 +01:00
|
|
|
inStream.copyTo(outStream)
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return FileProvider.getUriForFile(this, App1.FILE_PROVIDER_AUTHORITY, dst)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun sweepOldCache() {
|
|
|
|
// sweep old cache
|
|
|
|
try {
|
|
|
|
// prepare cache directory
|
|
|
|
val cacheDir = this.cacheDir
|
|
|
|
|
|
|
|
cacheDir.mkdirs()
|
|
|
|
|
|
|
|
val now = System.currentTimeMillis()
|
|
|
|
val files = cacheDir.listFiles()
|
|
|
|
if (files != null) for (f in files) {
|
|
|
|
try {
|
|
|
|
if (f.isFile &&
|
|
|
|
f.name.startsWith("img.") &&
|
|
|
|
now - f.lastModified() >= 86400000L
|
|
|
|
) {
|
|
|
|
f.delete()
|
|
|
|
}
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "sweepOldCache: delete item failed.")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (ex: Throwable) {
|
2022-12-27 03:54:52 +01:00
|
|
|
log.e(ex, "sweepOldCache failed.")
|
2021-06-20 15:12:25 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-06 03:11:28 +01:00
|
|
|
|
|
|
|
// return true if open app chooser dialog
|
|
|
|
private fun forbidUriFromApp(src: Intent): Boolean {
|
|
|
|
if (src.action != Intent.ACTION_VIEW) return false
|
|
|
|
|
|
|
|
val uri = src.data ?: return false
|
|
|
|
if (!containsUriFromApp(uri)) return false
|
|
|
|
setUriFromApp(null)
|
|
|
|
|
|
|
|
try {
|
|
|
|
startActivity(Intent.createChooser(src, uri.toString()))
|
|
|
|
finish()
|
|
|
|
} catch (ex: Throwable) {
|
|
|
|
showToast(ex, "can't open chooser for $uri")
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|