AsyncTaskからkotlin coroutinesへの移行

This commit is contained in:
tateisu 2020-04-07 13:52:54 +09:00
parent 2c77eaafae
commit 3defbe0cf3
18 changed files with 574 additions and 718 deletions

View File

@ -10,7 +10,6 @@ import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -21,19 +20,20 @@ import android.text.TextWatcher
import android.view.View import android.view.View
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import jp.juggler.subwaytooter.Styler.defaultColorIcon import jp.juggler.subwaytooter.Styler.defaultColorIcon
import jp.juggler.subwaytooter.api.* import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.dialog.ActionsDialog import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
import jp.juggler.subwaytooter.table.AcctColor import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.* import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.view.MyNetworkImageView import jp.juggler.subwaytooter.view.MyNetworkImageView
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
@ -44,8 +44,8 @@ import org.jetbrains.anko.textColor
import java.io.* import java.io.*
import kotlin.math.max import kotlin.math.max
class ActAccountSetting class ActAccountSetting : AsyncActivity(), View.OnClickListener,
: AppCompatActivity(), View.OnClickListener, CompoundButton.OnCheckedChangeListener { CompoundButton.OnCheckedChangeListener {
companion object { companion object {
@ -313,14 +313,14 @@ class ActAccountSetting
R.id.etFieldName2, R.id.etFieldName2,
R.id.etFieldName3, R.id.etFieldName3,
R.id.etFieldName4 R.id.etFieldName4
).map { requireNotNull(findViewById<EditText>(it)) } ).map { findViewById<EditText>(it) } // 型指定を除去してはいけない
listEtFieldValue = arrayOf( listEtFieldValue = arrayOf(
R.id.etFieldValue1, R.id.etFieldValue1,
R.id.etFieldValue2, R.id.etFieldValue2,
R.id.etFieldValue3, R.id.etFieldValue3,
R.id.etFieldValue4 R.id.etFieldValue4
).map { requireNotNull(findViewById<EditText>(it)) } ).map { findViewById<EditText>(it) } // 型指定を除去してはいけない
btnFields = findViewById(R.id.btnFields) btnFields = findViewById(R.id.btnFields)
@ -743,63 +743,37 @@ class ActAccountSetting
finish() finish()
val task = @SuppressLint("StaticFieldLeak") GlobalScope.launch(Dispatchers.IO) {
object : AsyncTask<Void, Void, String?>() { try {
val install_id = PrefDevice.prefDevice(this@ActAccountSetting)
fun unregister() { .getString(PrefDevice.KEY_INSTALL_ID, null)
try { if(install_id?.isEmpty() != false)
error("missing install_id")
val install_id = PrefDevice.prefDevice(this@ActAccountSetting)
.getString(PrefDevice.KEY_INSTALL_ID, null)
if(install_id?.isEmpty() != false) {
log.d("performAccountRemove: missing install_id")
return
}
val tag = account.notification_tag
if(tag?.isEmpty() != false) {
log.d("performAccountRemove: missing notification_tag")
return
}
val call = App1.ok_http_client.newCall(
("instance_url=" + "https://${account.host.ascii}".encodePercent()
+ "&app_id=" + packageName.encodePercent()
+ "&tag=" + tag
)
.toFormRequestBody()
.toPost()
.url(PollingWorker.APP_SERVER + "/unregister")
.build()
)
val response = call.execute()
log.e("performAccountRemove: %s", response)
} catch(ex : Throwable) {
log.trace(ex, "performAccountRemove failed.")
}
} val tag = account.notification_tag
if(tag?.isEmpty() != false)
override fun doInBackground(vararg params : Void) : String? { error("missing notification_tag")
unregister()
return null val call = App1.ok_http_client.newCall(
} ("instance_url=" + "https://${account.host.ascii}".encodePercent()
+ "&app_id=" + packageName.encodePercent()
override fun onCancelled(s : String?) { + "&tag=" + tag
onPostExecute(s) )
} .toFormRequestBody()
.toPost()
override fun onPostExecute(s : String?) { .url(PollingWorker.APP_SERVER + "/unregister")
.build()
)
val response = call.execute()
log.e("performAccountRemove: %s", response)
} catch(ex : Throwable) {
log.trace(ex, "performAccountRemove failed.")
} }
} }
task.executeOnExecutor(App1.task_executor)
} }
.show() .show()
} }
/////////////////////////////////////////////////// ///////////////////////////////////////////////////
@ -1579,41 +1553,19 @@ class ActAccountSetting
return return
} }
val progress = ProgressDialogEx(this) runWithProgress(
"preparing image",
val task = @SuppressLint("StaticFieldLeak") { createOpener(uri, mime_type) },
object : AsyncTask<Void, Void, InputStreamOpener?>() { {
updateCredential(
override fun doInBackground(vararg params : Void) : InputStreamOpener? { when(request_code) {
return try { REQUEST_CODE_HEADER_ATTACHMENT, REQUEST_CODE_HEADER_CAMERA -> "header"
createOpener(uri, mime_type) else -> "avatar"
} catch(ex : Throwable) { },
showToast(this@ActAccountSetting, ex, "image converting failed.") it
null )
}
} }
)
override fun onPostExecute(opener : InputStreamOpener?) {
progress.dismissSafe()
if(opener != null) {
updateCredential(
when(request_code) {
REQUEST_CODE_HEADER_ATTACHMENT, REQUEST_CODE_HEADER_CAMERA -> "header"
else -> "avatar"
},
opener
)
}
}
}
progress.isIndeterminateEx = true
progress.setMessageEx("preparing image…")
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
} }
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")

View File

@ -1,12 +1,10 @@
package jp.juggler.subwaytooter package jp.juggler.subwaytooter
import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.text.Editable import android.text.Editable
@ -18,7 +16,6 @@ import android.view.Window
import android.widget.* import android.widget.*
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.jrummyapps.android.colorpicker.ColorPickerDialog import com.jrummyapps.android.colorpicker.ColorPickerDialog
@ -26,7 +23,6 @@ import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
import jp.juggler.subwaytooter.action.CustomShare import jp.juggler.subwaytooter.action.CustomShare
import jp.juggler.subwaytooter.action.CustomShareTarget import jp.juggler.subwaytooter.action.CustomShareTarget
import jp.juggler.subwaytooter.dialog.DlgAppPicker import jp.juggler.subwaytooter.dialog.DlgAppPicker
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
import jp.juggler.subwaytooter.table.AcctColor import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.* import jp.juggler.util.*
@ -44,7 +40,7 @@ import kotlin.Comparator
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.math.abs import kotlin.math.abs
class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnClickListener { class ActAppSetting : AsyncActivity(), ColorPickerDialogListener, View.OnClickListener {
companion object { companion object {
internal val log = LogCategory("ActAppSetting") internal val log = LogCategory("ActAppSetting")
@ -780,93 +776,58 @@ class ActAppSetting : AppCompatActivity(), ColorPickerDialogListener, View.OnCli
fun exportAppData() { fun exportAppData() {
@Suppress("DEPRECATION") runWithProgress(
val progress = ProgressDialogEx(this) "export app data",
{
val task = @SuppressLint("StaticFieldLeak") val cache_dir = cacheDir
object : AsyncTask<Void, String, File?>() { cache_dir.mkdir()
override fun doInBackground(vararg params : Void) : File? {
try { val file = File(
val cache_dir = cacheDir cache_dir,
cache_dir.mkdir() "SubwayTooter.${android.os.Process.myPid()}.${android.os.Process.myTid()}.zip"
)
// ZipOutputStreamオブジェクトの作成
ZipOutputStream(FileOutputStream(file)).use { zipStream ->
val file = File( // アプリデータjson
cache_dir, zipStream.putNextEntry(ZipEntry("AppData.json"))
"SubwayTooter.${android.os.Process.myPid()}.${android.os.Process.myTid()}.zip" try {
) val jw = JsonWriter(OutputStreamWriter(zipStream, "UTF-8"))
AppDataExporter.encodeAppData(this@ActAppSetting, jw)
// ZipOutputStreamオブジェクトの作成 jw.flush()
ZipOutputStream(FileOutputStream(file)).use { zipStream -> } finally {
zipStream.closeEntry()
// アプリデータjson
zipStream.putNextEntry(ZipEntry("AppData.json"))
try {
val jw = JsonWriter(OutputStreamWriter(zipStream, "UTF-8"))
AppDataExporter.encodeAppData(this@ActAppSetting, jw)
jw.flush()
} finally {
zipStream.closeEntry()
}
// カラム背景画像
val appState = App1.getAppState(this@ActAppSetting)
for(column in appState.column_list) {
AppDataExporter.saveBackgroundImage(
this@ActAppSetting,
zipStream,
column
)
}
} }
return file // カラム背景画像
} catch(ex : Throwable) { val appState = App1.getAppState(this@ActAppSetting)
log.trace(ex) for(column in appState.column_list) {
showToast(this@ActAppSetting, ex, "exportAppData failed.") AppDataExporter.saveBackgroundImage(
this@ActAppSetting,
zipStream,
column
)
}
} }
return null file
} },
{
override fun onCancelled(result : File?) { val uri = FileProvider.getUriForFile(
onPostExecute(result) this@ActAppSetting,
} App1.FILE_PROVIDER_AUTHORITY,
it
override fun onPostExecute(result : File?) { )
progress.dismissSafe() val intent = Intent(Intent.ACTION_SEND)
intent.type = contentResolver.getType(uri)
intent.putExtra(Intent.EXTRA_SUBJECT, "SubwayTooter app data")
intent.putExtra(Intent.EXTRA_STREAM, uri)
if(isCancelled || result == null) { intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
// cancelled. startActivityForResult(intent, REQUEST_CODE_OTHER)
return
}
try {
val uri = FileProvider.getUriForFile(
this@ActAppSetting,
App1.FILE_PROVIDER_AUTHORITY,
result
)
val intent = Intent(Intent.ACTION_SEND)
intent.type = contentResolver.getType(uri)
intent.putExtra(Intent.EXTRA_SUBJECT, "SubwayTooter app data")
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, REQUEST_CODE_OTHER)
} catch(ex : Throwable) {
log.trace(ex)
showToast(this@ActAppSetting, ex, "exportAppData failed.")
}
} }
} )
progress.isIndeterminateEx = true
progress.setCancelable(true)
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
} }
// open data picker // open data picker

View File

@ -3,7 +3,6 @@ package jp.juggler.subwaytooter
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle import android.os.PersistableBundle
import android.os.Process import android.os.Process
@ -13,11 +12,9 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import jp.juggler.subwaytooter.api.entity.TootStatus import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.dialog.ActionsDialog import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
import jp.juggler.util.* import jp.juggler.util.*
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.jetbrains.anko.textColor import org.jetbrains.anko.textColor
@ -27,8 +24,7 @@ import java.io.FileOutputStream
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ActLanguageFilter : AppCompatActivity(), View.OnClickListener { class ActLanguageFilter : AsyncActivity(), View.OnClickListener {
private class MyItem( private class MyItem(
val code : String, val code : String,
@ -397,84 +393,42 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
} }
} }
private fun export() { private fun export() = runWithProgress(
"export language filter",
val progress = ProgressDialogEx(this) {
val data = JsonObject().apply {
val data = JsonObject().apply { for(item in languageList) {
for(item in languageList) { put(item.code, item.allow)
put(item.code, item.allow) }
} }
.toString()
.encodeUTF8()
val cache_dir = cacheDir
cache_dir.mkdir()
val file = File(
cache_dir,
"SubwayTooter-language-filter.${Process.myPid()}.${Process.myTid()}.json"
)
FileOutputStream(file).use { it.write(data) }
file
},
{
val uri = FileProvider.getUriForFile(
this@ActLanguageFilter,
App1.FILE_PROVIDER_AUTHORITY,
it
)
val intent = Intent(Intent.ACTION_SEND)
intent.type = contentResolver.getType(uri)
intent.putExtra(Intent.EXTRA_SUBJECT, "SubwayTooter language filter data")
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, REQUEST_CODE_OTHER)
} }
.toString() )
.encodeUTF8()
val task = @SuppressLint("StaticFieldLeak")
object : AsyncTask<Void, String, File?>() {
override fun doInBackground(vararg params : Void) : File? {
try {
val cache_dir = cacheDir
cache_dir.mkdir()
val file = File(
cache_dir,
"SubwayTooter-language-filter.${Process.myPid()}.${Process.myTid()}.json"
)
FileOutputStream(file).use { it.write(data) }
return file
} catch(ex : Throwable) {
log.trace(ex)
showToast(
this@ActLanguageFilter,
ex,
"can't save filter data to temporary file."
)
}
return null
}
override fun onCancelled(result : File?) {
onPostExecute(result)
}
override fun onPostExecute(result : File?) {
progress.dismissSafe()
if(isCancelled || result == null) {
// cancelled.
return
}
try {
val uri = FileProvider.getUriForFile(
this@ActLanguageFilter,
App1.FILE_PROVIDER_AUTHORITY,
result
)
val intent = Intent(Intent.ACTION_SEND)
intent.type = contentResolver.getType(uri)
intent.putExtra(Intent.EXTRA_SUBJECT, "SubwayTooter language filter data")
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, REQUEST_CODE_OTHER)
} catch(ex : Throwable) {
log.trace(ex)
showToast(this@ActLanguageFilter, ex, "export failed.")
}
}
}
progress.isIndeterminateEx = true
progress.setCancelable(true)
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
}
private fun import() { private fun import() {
try { try {
@ -495,53 +449,22 @@ class ActLanguageFilter : AppCompatActivity(), View.OnClickListener {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
} }
private fun import2(uri : Uri) { private fun import2(uri : Uri) = runWithProgress(
"import language filter",
val type = contentResolver.getType(uri) {
log.d("import2 type=%s", type) log.d("import2 type=${contentResolver.getType(uri)}")
val source = contentResolver.openInputStream(uri)
val progress = ProgressDialogEx(this) if(source == null) {
showToast( true, "openInputStream failed.")
val task = @SuppressLint("StaticFieldLeak") null
object : AsyncTask<Void, String, JsonObject?>() { } else {
source.use { inStream ->
override fun doInBackground(vararg params : Void) : JsonObject? { val bao = ByteArrayOutputStream()
try { IOUtils.copy(inStream, bao)
val source = contentResolver.openInputStream(uri) bao.toByteArray().decodeUTF8().decodeJsonObject()
if(source == null) {
showToast(this@ActLanguageFilter, true, "openInputStream failed.")
return null
}
return source.use { inStream ->
val bao = ByteArrayOutputStream()
IOUtils.copy(inStream, bao)
bao.toByteArray().decodeUTF8().decodeJsonObject()
}
} catch(ex : Throwable) {
log.trace(ex)
showToast(this@ActLanguageFilter, ex, "can't load filter data.")
return null
} }
} }
},
override fun onCancelled(result : JsonObject?) { { if(it != null) load(it) }
onPostExecute(result) )
}
override fun onPostExecute(result : JsonObject?) {
progress.dismissSafe()
// cancelled.
if(isCancelled || result == null) return
load(result)
}
}
progress.isIndeterminateEx = true
progress.setCancelable(true)
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
}
} }

View File

@ -1,6 +1,5 @@
package jp.juggler.subwaytooter package jp.juggler.subwaytooter
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Dialog import android.app.Dialog
import android.content.Intent import android.content.Intent
@ -19,8 +18,6 @@ import android.view.*
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@ -31,13 +28,17 @@ import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.api.entity.TootStatus.Companion.findStatusIdFromUrl import jp.juggler.subwaytooter.api.entity.TootStatus.Companion.findStatusIdFromUrl
import jp.juggler.subwaytooter.api.entity.TootTag.Companion.findHashtagFromUrl import jp.juggler.subwaytooter.api.entity.TootTag.Companion.findHashtagFromUrl
import jp.juggler.subwaytooter.dialog.* import jp.juggler.subwaytooter.dialog.AccountPicker
import jp.juggler.subwaytooter.dialog.ActionsDialog
import jp.juggler.subwaytooter.dialog.DlgQuickTootMenu
import jp.juggler.subwaytooter.dialog.DlgTextInput
import jp.juggler.subwaytooter.span.MyClickableSpan import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.table.AcctColor import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.* import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.view.* import jp.juggler.subwaytooter.view.*
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.delay
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.jetbrains.anko.backgroundDrawable import org.jetbrains.anko.backgroundDrawable
import java.io.File import java.io.File
@ -51,7 +52,7 @@ import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
class ActMain : AppCompatActivity() class ActMain : AsyncActivity()
, Column.Callback , Column.Callback
, View.OnClickListener , View.OnClickListener
, ViewPager.OnPageChangeListener , ViewPager.OnPageChangeListener
@ -1320,7 +1321,7 @@ class ActMain : AppCompatActivity()
drawer = findViewById(R.id.drawer_layout) drawer = findViewById(R.id.drawer_layout)
drawer.addDrawerListener(this) drawer.addDrawerListener(this)
drawer.setExclusionSize( stripIconSize) drawer.setExclusionSize(stripIconSize)
@ -1548,7 +1549,7 @@ class ActMain : AppCompatActivity()
} }
) )
internal fun updateColumnStrip() { private fun updateColumnStrip() {
llEmpty.vg(app_state.column_list.isEmpty()) llEmpty.vg(app_state.column_list.isEmpty())
val iconSize = stripIconSize val iconSize = stripIconSize
@ -1599,7 +1600,7 @@ class ActMain : AppCompatActivity()
scrollToColumn(idx) scrollToColumn(idx)
} }
viewRoot.contentDescription = column.getColumnName(true) viewRoot.contentDescription = column.getColumnName(true)
viewRoot.backgroundDrawable = getAdaptiveRippleDrawableRound( viewRoot.backgroundDrawable = getAdaptiveRippleDrawableRound(
this, this,
column.getHeaderBackgroundColor(), column.getHeaderBackgroundColor(),
@ -2523,10 +2524,11 @@ class ActMain : AppCompatActivity()
) )
val colorRipple = val colorRipple =
footer_button_fg_color.notZero() ?: getAttributeColor(this, R.attr.colorRippleEffect) footer_button_fg_color.notZero() ?: getAttributeColor(this, R.attr.colorRippleEffect)
btnMenu.backgroundDrawable = getAdaptiveRippleDrawableRound(this,colorBg, colorRipple) btnMenu.backgroundDrawable = getAdaptiveRippleDrawableRound(this, colorBg, colorRipple)
btnToot.backgroundDrawable = getAdaptiveRippleDrawableRound(this,colorBg, colorRipple) btnToot.backgroundDrawable = getAdaptiveRippleDrawableRound(this, colorBg, colorRipple)
btnQuickToot.backgroundDrawable = getAdaptiveRippleDrawableRound(this,colorBg, colorRipple) btnQuickToot.backgroundDrawable = getAdaptiveRippleDrawableRound(this, colorBg, colorRipple)
btnQuickTootMenu.backgroundDrawable = getAdaptiveRippleDrawableRound(this,colorBg, colorRipple) btnQuickTootMenu.backgroundDrawable =
getAdaptiveRippleDrawableRound(this, colorBg, colorRipple)
val csl = ColorStateList.valueOf( val csl = ColorStateList.valueOf(
footer_button_fg_color.notZero() footer_button_fg_color.notZero()
@ -2738,159 +2740,125 @@ class ActMain : AppCompatActivity()
uri ?: return uri ?: return
// remove all columns // remove all columns
run { phoneOnly { env -> env.pager.adapter = null }
phoneOnly { env -> env.pager.adapter = null }
for(c in app_state.column_list) {
for(c in app_state.column_list) { c.dispose()
c.dispose()
}
app_state.column_list.clear()
phoneTab(
{ env -> env.pager.adapter = env.pager_adapter },
{ env -> resizeColumnWidth(env) }
)
updateColumnStrip()
} }
app_state.column_list.clear()
@Suppress("DEPRECATION") phoneTab(
val progress = ProgressDialogEx(this) { env -> env.pager.adapter = env.pager_adapter },
{ env -> resizeColumnWidth(env) }
)
val task = @SuppressLint("StaticFieldLeak") object : updateColumnStrip()
AsyncTask<Void, String, ArrayList<Column>?>() {
runWithProgress(
"importing app data",
fun setProgressMessage(sv : String) { doInBackground = { progress ->
runOnMainLooper { fun setProgressMessage(sv : String) =
progress.setMessageEx(sv) runOnMainLooper { progress.setMessageEx(sv) }
}
}
override fun doInBackground(vararg params : Void) : ArrayList<Column>? {
var newColumnList : ArrayList<Column>? = null var newColumnList : ArrayList<Column>? = null
setProgressMessage("import data to local storage...")
// アプリ内領域に一時ファイルを作ってコピーする
val cacheDir = cacheDir
cacheDir.mkdir()
val file = File(
cacheDir,
"SubwayTooter.${Process.myPid()}.${Process.myTid()}.tmp"
)
val source = contentResolver.openInputStream(uri)
if(source == null) {
showToast(true, "openInputStream failed.")
return@runWithProgress null
}
source.use { inStream ->
FileOutputStream(file).use { outStream ->
IOUtils.copy(inStream, outStream)
}
}
// 通知サービスを止める
setProgressMessage("syncing notification poller…")
PollingWorker.queueAppDataImportBefore(this@ActMain)
while(PollingWorker.mBusyAppDataImportBefore.get()) {
delay(1000L)
log.d("syncing polling task...")
}
// データを読み込む
setProgressMessage("reading app data...")
var zipEntryCount = 0
try { try {
ZipInputStream(FileInputStream(file)).use { zipStream ->
setProgressMessage("import data to local storage...") while(true) {
val entry = zipStream.nextEntry ?: break
// アプリ内領域に一時ファイルを作ってコピーする ++ zipEntryCount
val cacheDir = cacheDir try {
cacheDir.mkdir() //
val file = File( val entryName = entry.name
cacheDir, if(entryName.endsWith(".json")) {
"SubwayTooter.${Process.myPid()}.${Process.myTid()}.tmp" newColumnList = AppDataExporter.decodeAppData(
) this@ActMain,
val source = contentResolver.openInputStream(uri) JsonReader(InputStreamReader(zipStream, "UTF-8"))
if(source == null) { )
showToast(this@ActMain, true, "openInputStream failed.") continue
return null
}
source.use { inStream ->
FileOutputStream(file).use { outStream ->
IOUtils.copy(inStream, outStream)
}
}
// 通知サービスを止める
setProgressMessage("syncing notification poller…")
PollingWorker.queueAppDataImportBefore(this@ActMain)
while(PollingWorker.mBusyAppDataImportBefore.get()) {
Thread.sleep(1000L)
log.d("syncing polling task...")
}
// データを読み込む
setProgressMessage("reading app data...")
var zipEntryCount = 0
try {
ZipInputStream(FileInputStream(file)).use { zipStream ->
while(true) {
val entry = zipStream.nextEntry ?: break
++ zipEntryCount
try {
//
val entryName = entry.name
if(entryName.endsWith(".json")) {
newColumnList = AppDataExporter.decodeAppData(
this@ActMain,
JsonReader(InputStreamReader(zipStream, "UTF-8"))
)
continue
}
if(AppDataExporter.restoreBackgroundImage(
this@ActMain,
newColumnList,
zipStream,
entryName
)
) {
continue
}
} finally {
zipStream.closeEntry()
} }
if(AppDataExporter.restoreBackgroundImage(
this@ActMain,
newColumnList,
zipStream,
entryName
)
) {
continue
}
} finally {
zipStream.closeEntry()
} }
} }
} catch(ex : Throwable) {
log.trace(ex)
if(zipEntryCount != 0) {
showToast(this@ActMain, ex, "importAppData failed.")
}
} }
// zipではなかった場合、zipEntryがない状態になる。例外はPH-1では出なかったが、出ても問題ないようにする。
if(zipEntryCount == 0) {
InputStreamReader(FileInputStream(file), "UTF-8").use { inStream ->
newColumnList = AppDataExporter.decodeAppData(
this@ActMain,
JsonReader(inStream)
)
}
}
} catch(ex : Throwable) { } catch(ex : Throwable) {
log.trace(ex) log.trace(ex)
showToast(this@ActMain, ex, "importAppData failed.") if(zipEntryCount != 0) {
} showToast(this@ActMain, ex, "importAppData failed.")
return newColumnList
}
override fun onCancelled(result : ArrayList<Column>?) {
onPostExecute(result)
}
override fun onPostExecute(result : ArrayList<Column>?) {
progress.dismissSafe()
try {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} catch(ignored : Throwable) {
}
try {
if(isCancelled || result == null) {
// cancelled.
return
} }
}
run { // zipではなかった場合、zipEntryがない状態になる。例外はPH-1では出なかったが、出ても問題ないようにする。
if(zipEntryCount == 0) {
phoneOnly { env -> env.pager.adapter = null } InputStreamReader(FileInputStream(file), "UTF-8").use { inStream ->
newColumnList = AppDataExporter.decodeAppData(
app_state.column_list.clear() this@ActMain,
app_state.column_list.addAll(result) JsonReader(inStream)
app_state.saveColumnList()
phoneTab(
{ env -> env.pager.adapter = env.pager_adapter },
{ env -> resizeColumnWidth(env) }
) )
updateColumnStrip()
} }
}
newColumnList
},
afterProc = {
// cancelled.
if(it == null) return@runWithProgress
try {
phoneOnly { env -> env.pager.adapter = null }
app_state.column_list.clear()
app_state.column_list.addAll(it)
app_state.saveColumnList()
phoneTab(
{ env -> env.pager.adapter = env.pager_adapter },
{ env -> resizeColumnWidth(env) }
)
updateColumnStrip()
} finally { } finally {
// 通知サービスをリスタート // 通知サービスをリスタート
PollingWorker.queueAppDataImportAfter(this@ActMain) PollingWorker.queueAppDataImportAfter(this@ActMain)
@ -2898,19 +2866,14 @@ class ActMain : AppCompatActivity()
showToast(this@ActMain, true, R.string.import_completed_please_restart_app) showToast(this@ActMain, true, R.string.import_completed_please_restart_app)
finish() finish()
},
preProc = {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
},
postProc = {
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} }
} )
try {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} catch(ignored : Throwable) {
}
progress.isIndeterminateEx = true
progress.setCancelable(false)
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
} }
override fun onDrawerSlide(drawerView : View, slideOffset : Float) { override fun onDrawerSlide(drawerView : View, slideOffset : Float) {

View File

@ -11,21 +11,25 @@ import android.content.SharedPreferences
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.* import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.SystemClock
import android.provider.MediaStore import android.provider.MediaStore
import android.text.* import android.text.Editable
import androidx.core.view.inputmethod.InputConnectionCompat import android.text.InputType
import androidx.core.view.inputmethod.InputContentInfoCompat import android.text.TextWatcher
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.view.inputmethod.InputContentInfoCompat
import jp.juggler.subwaytooter.Styler.defaultColorIcon import jp.juggler.subwaytooter.Styler.defaultColorIcon
import jp.juggler.subwaytooter.api.* import jp.juggler.subwaytooter.api.*
import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.api.entity.*
@ -39,6 +43,7 @@ import jp.juggler.subwaytooter.view.FocusPointView
import jp.juggler.subwaytooter.view.MyEditText import jp.juggler.subwaytooter.view.MyEditText
import jp.juggler.subwaytooter.view.MyNetworkImageView import jp.juggler.subwaytooter.view.MyNetworkImageView
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.isActive
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody import okhttp3.MultipartBody
@ -54,7 +59,7 @@ import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.max import kotlin.math.max
class ActPost : AppCompatActivity(), class ActPost : AsyncActivity(),
View.OnClickListener, View.OnClickListener,
PostAttachment.Callback { PostAttachment.Callback {
@ -397,17 +402,17 @@ class ActPost : AppCompatActivity(),
private lateinit var btnPost : ImageButton private lateinit var btnPost : ImageButton
private lateinit var llAttachment : View private lateinit var llAttachment : View
private lateinit var ivMedia : List<MyNetworkImageView> private lateinit var ivMedia : List<MyNetworkImageView>
internal lateinit var cbNSFW : CheckBox private lateinit var cbNSFW : CheckBox
internal lateinit var cbContentWarning : CheckBox private lateinit var cbContentWarning : CheckBox
internal lateinit var etContentWarning : MyEditText private lateinit var etContentWarning : MyEditText
internal lateinit var etContent : MyEditText private lateinit var etContent : MyEditText
private lateinit var btnFeaturedTag : ImageButton private lateinit var btnFeaturedTag : ImageButton
internal lateinit var cbQuote : CheckBox private lateinit var cbQuote : CheckBox
internal lateinit var spEnquete : Spinner private lateinit var spEnquete : Spinner
private lateinit var llEnquete : View private lateinit var llEnquete : View
internal lateinit var list_etChoice : List<MyEditText> private lateinit var list_etChoice : List<MyEditText>
private lateinit var cbMultipleChoice : CheckBox private lateinit var cbMultipleChoice : CheckBox
private lateinit var cbHideTotals : CheckBox private lateinit var cbHideTotals : CheckBox
@ -433,7 +438,7 @@ class ActPost : AppCompatActivity(),
internal lateinit var pref : SharedPreferences internal lateinit var pref : SharedPreferences
internal lateinit var app_state : AppState internal lateinit var app_state : AppState
private lateinit var post_helper : PostHelper private lateinit var post_helper : PostHelper
internal var attachment_list = ArrayList<PostAttachment>() private var attachment_list = ArrayList<PostAttachment>()
private var isPostComplete : Boolean = false private var isPostComplete : Boolean = false
internal var density : Float = 0f internal var density : Float = 0f
@ -478,9 +483,9 @@ class ActPost : AppCompatActivity(),
///////////////////////////////////////////////// /////////////////////////////////////////////////
internal var in_reply_to_id : EntityId? = null internal var in_reply_to_id : EntityId? = null
internal var in_reply_to_text : String? = null private var in_reply_to_text : String? = null
internal var in_reply_to_image : String? = null private var in_reply_to_image : String? = null
internal var in_reply_to_url : String? = null private var in_reply_to_url : String? = null
private var mushroom_input : Int = 0 private var mushroom_input : Int = 0
private var mushroom_start : Int = 0 private var mushroom_start : Int = 0
private var mushroom_end : Int = 0 private var mushroom_end : Int = 0
@ -812,7 +817,7 @@ class ActPost : AppCompatActivity(),
// 比較する前にデフォルトの公開範囲を計算する // 比較する前にデフォルトの公開範囲を計算する
visibility = visibility visibility = visibility
?: account.visibility ?: account.visibility
?: TootVisibility.Public // ?: TootVisibility.Public
// VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになる // VISIBILITY_WEB_SETTING だと 1.5未満のタンスでトラブルになる
if(TootVisibility.WebSetting == visibility) { if(TootVisibility.WebSetting == visibility) {
@ -1446,7 +1451,7 @@ class ActPost : AppCompatActivity(),
etContentWarning.visibility = if(cbContentWarning.isChecked) View.VISIBLE else View.GONE etContentWarning.visibility = if(cbContentWarning.isChecked) View.VISIBLE else View.GONE
} }
internal fun selectAccount(a : SavedAccount?) { private fun selectAccount(a : SavedAccount?) {
this.account = a this.account = a
if(a == null) { if(a == null) {
post_helper.setInstance(null, false) post_helper.setInstance(null, false)
@ -2661,7 +2666,7 @@ class ActPost : AppCompatActivity(),
cbQuote.visibility = if(in_reply_to_id != null) View.VISIBLE else View.GONE cbQuote.visibility = if(in_reply_to_id != null) View.VISIBLE else View.GONE
} }
internal fun showReplyTo() { private fun showReplyTo() {
if(in_reply_to_id == null) { if(in_reply_to_id == null) {
llReply.visibility = View.GONE llReply.visibility = View.GONE
} else { } else {
@ -2773,16 +2778,13 @@ class ActPost : AppCompatActivity(),
private fun restoreDraft(draft : JsonObject) { private fun restoreDraft(draft : JsonObject) {
@Suppress("DEPRECATION") val list_warning = ArrayList<String>()
val progress = ProgressDialogEx(this) var target_account : SavedAccount? = null
val task = @SuppressLint("StaticFieldLeak") runWithProgress(
object : AsyncTask<Void, String, String?>() { "restore from draft",
{progress->
val list_warning = ArrayList<String>() fun isTaskCancelled() = !this.coroutineContext.isActive
var account : SavedAccount? = null
override fun doInBackground(vararg params : Void) : String? {
var content = draft.string(DRAFT_CONTENT) ?: "" var content = draft.string(DRAFT_CONTENT) ?: ""
val account_db_id = draft.long(DRAFT_ACCOUNT_DB_ID) ?: - 1L val account_db_id = draft.long(DRAFT_ACCOUNT_DB_ID) ?: - 1L
@ -2812,15 +2814,16 @@ class ActPost : AppCompatActivity(),
} catch(ignored : JsonException) { } catch(ignored : JsonException) {
} }
return "OK" return@runWithProgress "OK"
} }
this.account = account
target_account = account
// アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ // アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ
val api_client = TootApiClient(this@ActPost, callback = object : TootApiCallback { val api_client = TootApiClient(this@ActPost, callback = object : TootApiCallback {
override val isApiCancelled : Boolean override val isApiCancelled : Boolean
get() = isCancelled get() = isTaskCancelled()
override fun publishApiProgress(s : String) { override fun publishApiProgress(s : String) {
runOnMainLooper { runOnMainLooper {
@ -2833,7 +2836,7 @@ class ActPost : AppCompatActivity(),
if(in_reply_to_id != null) { if(in_reply_to_id != null) {
val result = api_client.request("/api/v1/statuses/$in_reply_to_id") val result = api_client.request("/api/v1/statuses/$in_reply_to_id")
if(isCancelled) return null if(isTaskCancelled()) return@runWithProgress null
val jsonObject = result?.jsonObject val jsonObject = result?.jsonObject
if(jsonObject == null) { if(jsonObject == null) {
list_warning.add(getString(R.string.reply_to_in_draft_is_lost)) list_warning.add(getString(R.string.reply_to_in_draft_is_lost))
@ -2848,7 +2851,7 @@ class ActPost : AppCompatActivity(),
var isSomeAttachmentRemoved = false var isSomeAttachmentRemoved = false
val it = tmp_attachment_list.iterator() val it = tmp_attachment_list.iterator()
while(it.hasNext()) { while(it.hasNext()) {
if(isCancelled) return null if(isTaskCancelled()) return@runWithProgress null
val ta = TootAttachment.decodeJson(it.next()) val ta = TootAttachment.decodeJson(it.next())
if(check_exist(ta.url)) continue if(check_exist(ta.url)) continue
it.remove() it.remove()
@ -2869,20 +2872,11 @@ class ActPost : AppCompatActivity(),
log.trace(ex) log.trace(ex)
} }
return "OK" "OK"
} },
{result->
override fun onCancelled(result : String?) { // cancelled.
onPostExecute(result) if( result == null) return@runWithProgress
}
override fun onPostExecute(result : String?) {
progress.dismissSafe()
if(isCancelled || result == null) {
// cancelled.
return
}
val content = draft.string(DRAFT_CONTENT) ?: "" val content = draft.string(DRAFT_CONTENT) ?: ""
val content_warning = draft.string(DRAFT_CONTENT_WARNING) ?: "" val content_warning = draft.string(DRAFT_CONTENT_WARNING) ?: ""
@ -2935,7 +2929,7 @@ class ActPost : AppCompatActivity(),
} }
} }
if(account != null) selectAccount(account) if(target_account != null) selectAccount(target_account)
if(tmp_attachment_list?.isNotEmpty() == true) { if(tmp_attachment_list?.isNotEmpty() == true) {
attachment_list.clear() attachment_list.clear()
@ -2973,13 +2967,7 @@ class ActPost : AppCompatActivity(),
.show() .show()
} }
} }
} )
progress.isIndeterminateEx = true
progress.setCancelable(true)
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
} }
private fun prepareMushroomText(et : EditText) : String { private fun prepareMushroomText(et : EditText) : String {

View File

@ -5,7 +5,6 @@ import android.content.*
import android.media.Ringtone import android.media.Ringtone
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
import android.os.AsyncTask
import android.os.Handler import android.os.Handler
import android.os.SystemClock import android.os.SystemClock
import android.speech.tts.TextToSpeech import android.speech.tts.TextToSpeech
@ -20,6 +19,10 @@ import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.NetworkStateTracker import jp.juggler.subwaytooter.util.NetworkStateTracker
import jp.juggler.subwaytooter.util.PostAttachment import jp.juggler.subwaytooter.util.PostAttachment
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@ -352,19 +355,14 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
showToast(context, false, R.string.text_to_speech_initializing) showToast(context, false, R.string.text_to_speech_initializing)
log.d("initializing TextToSpeech…") log.d("initializing TextToSpeech…")
object : AsyncTask<Void, Void, TextToSpeech?>() { GlobalScope.launch(Dispatchers.IO) {
var tmp_tts : TextToSpeech? = null var tmp_tts : TextToSpeech? = null
override fun doInBackground(vararg params : Void) : TextToSpeech {
val tts = TextToSpeech(context, tts_init_listener)
this.tmp_tts = tts
return tts
}
val tts_init_listener : TextToSpeech.OnInitListener = val tts_init_listener : TextToSpeech.OnInitListener =
TextToSpeech.OnInitListener { status -> TextToSpeech.OnInitListener { status ->
val tts = this.tmp_tts val tts = tmp_tts
if(tts == null || TextToSpeech.SUCCESS != status) { if(tts == null || TextToSpeech.SUCCESS != status) {
showToast( showToast(
context, context,
@ -443,8 +441,11 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
} }
} }
}.executeOnExecutor(App1.task_executor) tmp_tts = TextToSpeech(context, tts_init_listener)
}
return
} }
if(! willSpeechEnabled && tts != null) { if(! willSpeechEnabled && tts != null) {
showToast(context, false, R.string.text_to_speech_shutdown) showToast(context, false, R.string.text_to_speech_shutdown)
log.d("shutdown TextToSpeech…") log.d("shutdown TextToSpeech…")
@ -516,18 +517,18 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
if(dedupMode != DedupMode.None) { if(dedupMode != DedupMode.None) {
synchronized(this) { synchronized(this) {
val check = duplication_check.find { it.text.equals(sv,ignoreCase = true) } val check = duplication_check.find { it.text.equals(sv, ignoreCase = true) }
if(check == null) { if(check == null) {
duplication_check.addLast(DedupItem(sv)) duplication_check.addLast(DedupItem(sv))
if(duplication_check.size > 60) duplication_check.removeFirst() if(duplication_check.size > 60) duplication_check.removeFirst()
} else{ } else {
val now = SystemClock.elapsedRealtime() val now = SystemClock.elapsedRealtime()
val delta = now - check.time val delta = now - check.time
// 古い項目が残っていることがあるので、check.timeの更新は必須 // 古い項目が残っていることがあるので、check.timeの更新は必須
check.time = now check.time = now
if(dedupMode == DedupMode.Recent) return if(dedupMode == DedupMode.Recent) return
if(dedupMode == DedupMode.RecentExpire && delta < 5000L ) return if(dedupMode == DedupMode.RecentExpire && delta < 5000L) return
} }
} }
} }
@ -576,7 +577,8 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
return false return false
} }
if(item.sound_type == HighlightWord.SOUND_TYPE_CUSTOM && item.sound_uri.mayUri().tryRingtone()) return if(item.sound_type == HighlightWord.SOUND_TYPE_CUSTOM && item.sound_uri.mayUri()
.tryRingtone()) return
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).tryRingtone() RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).tryRingtone()
} }

View File

@ -0,0 +1,95 @@
package jp.juggler.subwaytooter
import android.os.Bundle
import android.os.PersistableBundle
import androidx.appcompat.app.AppCompatActivity
import jp.juggler.subwaytooter.dialog.ProgressDialogEx
import jp.juggler.util.LogCategory
import jp.juggler.util.dismissSafe
import jp.juggler.util.showToast
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.CoroutineContext
abstract class AsyncActivity : AppCompatActivity(), CoroutineScope {
companion object{
private val log =LogCategory("AsyncActivity")
}
private lateinit var job : Job
override val coroutineContext : CoroutineContext
get() = job + Dispatchers.Main
override fun onCreate(savedInstanceState : Bundle?, persistentState : PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
job = Job()
}
override fun onDestroy() {
super.onDestroy()
(job + Dispatchers.Default).cancel()
}
fun showToast(bLong : Boolean, fmt : String?, vararg args : Any) =
showToast(this, bLong, fmt, *args)
fun showToast(ex : Throwable, fmt : String?, vararg args : Any) =
showToast(this, ex, fmt, *args)
fun showToast(bLong : Boolean, string_id : Int, vararg args : Any) =
showToast(this, bLong, string_id, *args)
fun showToast(ex : Throwable, string_id : Int, vararg args : Any) =
showToast(this, ex, string_id, *args)
fun <T : Any?> runWithProgress(
caption : String,
doInBackground : suspend CoroutineScope.(ProgressDialogEx) -> T,
afterProc : suspend CoroutineScope.(result : T) -> Unit = {},
progressInitializer : suspend CoroutineScope.(ProgressDialogEx) -> Unit = {},
preProc : suspend CoroutineScope.() -> Unit ={},
postProc : suspend CoroutineScope.() -> Unit ={}
) {
val progress = ProgressDialogEx(this)
val task = async(Dispatchers.IO) {
doInBackground(progress)
}
launch {
try{
preProc()
}catch(ex:Throwable){
log.trace(ex)
}
progress.setCancelable(true)
progress.setOnCancelListener {task.cancel()}
progress.isIndeterminateEx = true
progress.setMessageEx("${caption}")
progressInitializer(progress)
progress.show()
try {
val result = try {
task.await()
}catch(ex:CancellationException){
null
}
// TODO キャンセル時に呼ぶ必要がある利用例があるかどうか調べる
if(result!=null) afterProc(result)
} catch(ex : Throwable) {
showToast(this@AsyncActivity, ex, "$caption failed.")
} finally {
progress.dismissSafe()
try{
postProc()
}catch(ex:Throwable){
log.trace(ex)
}
}
}
}
}

View File

@ -1450,7 +1450,7 @@ class Column(
private fun cancelLastTask() { private fun cancelLastTask() {
if(lastTask != null) { if(lastTask != null) {
lastTask?.cancel(true) lastTask?.cancel()
lastTask = null lastTask = null
// //
bInitialLoading = false bInitialLoading = false
@ -1929,7 +1929,7 @@ class Column(
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
val task = ColumnTask_Loading(this) val task = ColumnTask_Loading(this)
this.lastTask = task this.lastTask = task
task.executeOnExecutor(App1.task_executor) task.start()
} }
private var bMinIdMatched : Boolean = false private var bMinIdMatched : Boolean = false
@ -2141,7 +2141,7 @@ class Column(
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
val task = ColumnTask_Refresh(this, bSilent, bBottom, posted_status_id, refresh_after_toot) val task = ColumnTask_Refresh(this, bSilent, bBottom, posted_status_id, refresh_after_toot)
this.lastTask = task this.lastTask = task
task.executeOnExecutor(App1.task_executor) task.start()
fireShowColumnStatus() fireShowColumnStatus()
} }
@ -2165,9 +2165,8 @@ class Column(
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
val task = ColumnTask_Gap(this, gap) val task = ColumnTask_Gap(this, gap)
this.lastTask = task this.lastTask = task
task.executeOnExecutor(App1.task_executor) task.start()
fireShowColumnStatus() fireShowColumnStatus()
} }

View File

@ -2,7 +2,6 @@ package jp.juggler.subwaytooter
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.AsyncTask
import android.os.SystemClock import android.os.SystemClock
import jp.juggler.subwaytooter.api.TootApiClient import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.TootApiResult import jp.juggler.subwaytooter.api.TootApiResult
@ -12,6 +11,8 @@ import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.JsonObject import jp.juggler.util.JsonObject
import jp.juggler.util.WordTrieTree import jp.juggler.util.WordTrieTree
import jp.juggler.util.notEmpty import jp.juggler.util.notEmpty
import jp.juggler.util.withCaption
import kotlinx.coroutines.*
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
enum class ColumnTaskType { enum class ColumnTaskType {
@ -24,15 +25,17 @@ enum class ColumnTaskType {
abstract class ColumnTask( abstract class ColumnTask(
val column : Column, val column : Column,
val ctType : ColumnTaskType val ctType : ColumnTaskType
) : AsyncTask<Void, Void, TootApiResult?>() { ) {
override fun onCancelled(result : TootApiResult?) {
onPostExecute(null)
}
val ctStarted = AtomicBoolean(false) val ctStarted = AtomicBoolean(false)
val ctClosed = AtomicBoolean(false) val ctClosed = AtomicBoolean(false)
var job : Job? = null
val isCancelled :Boolean
get()= job?.isCancelled ?: false
var parser = TootParser(context, access_info, highlightTrie = highlight_trie) var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
var list_tmp : ArrayList<TimelineItem>? = null var list_tmp : ArrayList<TimelineItem>? = null
@ -170,4 +173,28 @@ abstract class ColumnTask(
} }
return TootApiResult() return TootApiResult()
} }
fun cancel() {
job?.cancel()
}
abstract fun doInBackground() : TootApiResult?
abstract fun onPostExecute(result : TootApiResult?)
fun start() {
job = GlobalScope.launch(Dispatchers.Main){
val result = try {
withContext(Dispatchers.IO){
doInBackground()
}
}catch(ex:CancellationException){
null // キャンセルされたらresult==nullとする
}catch(ex:Throwable){
// その他のエラー
TootApiResult(ex.withCaption("error"))
}
onPostExecute(result)
}
}
} }

View File

@ -17,7 +17,7 @@ class ColumnTask_Gap(
private var max_id : EntityId? = (gap as? TootGap)?.max_id private var max_id : EntityId? = (gap as? TootGap)?.max_id
private var since_id : EntityId? = (gap as? TootGap)?.since_id private var since_id : EntityId? = (gap as? TootGap)?.since_id
override fun doInBackground(vararg unused : Void) : TootApiResult? { override fun doInBackground() : TootApiResult? {
ctStarted.set(true) ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback { val client = TootApiClient(context, callback = object : TootApiCallback {

View File

@ -18,7 +18,7 @@ class ColumnTask_Loading(
internal var list_pinned : ArrayList<TimelineItem>? = null internal var list_pinned : ArrayList<TimelineItem>? = null
override fun doInBackground(vararg unused : Void) : TootApiResult? { override fun doInBackground() : TootApiResult? {
ctStarted.set(true) ctStarted.set(true)
if(Pref.bpInstanceTicker(pref)) { if(Pref.bpInstanceTicker(pref)) {

View File

@ -26,7 +26,7 @@ class ColumnTask_Refresh(
private var filterUpdated = false private var filterUpdated = false
override fun doInBackground(vararg unused : Void) : TootApiResult? { override fun doInBackground() : TootApiResult? {
ctStarted.set(true) ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback { val client = TootApiClient(context, callback = object : TootApiCallback {

View File

@ -7,7 +7,6 @@ import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.Path import android.graphics.Path
import android.os.AsyncTask
import android.os.SystemClock import android.os.SystemClock
import android.text.InputType import android.text.InputType
import android.text.Spannable import android.text.Spannable
@ -38,11 +37,16 @@ import jp.juggler.subwaytooter.dialog.EmojiPicker
import jp.juggler.subwaytooter.span.NetworkEmojiSpan import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.table.AcctColor import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.util.* import jp.juggler.subwaytooter.util.*
import jp.juggler.subwaytooter.view.* import jp.juggler.subwaytooter.view.ListDivider
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
import jp.juggler.subwaytooter.view.MyTextView
import jp.juggler.subwaytooter.view.OutsideDrawerLayout
import jp.juggler.util.* import jp.juggler.util.*
import kotlinx.coroutines.*
import org.jetbrains.anko.* import org.jetbrains.anko.*
import org.jetbrains.anko.custom.customView import org.jetbrains.anko.custom.customView
import java.io.Closeable import java.io.Closeable
import java.lang.Runnable
import java.lang.reflect.Field import java.lang.reflect.Field
import java.util.regex.Pattern import java.util.regex.Pattern
@ -178,7 +182,7 @@ class ColumnViewHolder(
private var last_image_uri : String? = null private var last_image_uri : String? = null
private var last_image_bitmap : Bitmap? = null private var last_image_bitmap : Bitmap? = null
private var last_image_task : AsyncTask<Void, Void, Bitmap?>? = null private var last_image_task : Job? = null
private fun checkRegexFilterError(src : String) : String? { private fun checkRegexFilterError(src : String) : String? {
try { try {
@ -808,7 +812,7 @@ class ColumnViewHolder(
last_image_bitmap?.recycle() last_image_bitmap?.recycle()
last_image_bitmap = null last_image_bitmap = null
last_image_task?.cancel(true) last_image_task?.cancel()
last_image_task = null last_image_task = null
last_image_uri = null last_image_uri = null
@ -842,41 +846,35 @@ class ColumnViewHolder(
val screen_h = iv.resources.displayMetrics.heightPixels val screen_h = iv.resources.displayMetrics.heightPixels
// 非同期処理を開始 // 非同期処理を開始
val task = object : AsyncTask<Void, Void, Bitmap?>() { last_image_task = GlobalScope.launch(Dispatchers.Main){
override fun doInBackground(vararg params : Void) : Bitmap? { val bitmap = try{
return try { withContext(Dispatchers.IO){
createResizedBitmap( try {
activity, url.toUri(), createResizedBitmap(
if(screen_w > screen_h) activity, url.toUri(),
screen_w if(screen_w > screen_h)
else screen_w
screen_h else
) screen_h
} catch(ex : Throwable) { )
log.trace(ex) } catch(ex : Throwable) {
null log.trace(ex)
} null
}
override fun onCancelled(bitmap : Bitmap?) {
onPostExecute(bitmap)
}
override fun onPostExecute(bitmap : Bitmap?) {
if(bitmap != null) {
if(isCancelled || url != last_image_uri) {
bitmap.recycle()
} else {
last_image_bitmap = bitmap
iv.setImageBitmap(last_image_bitmap)
iv.visibility = View.VISIBLE
} }
} }
}catch(ex:Throwable){
null
}
if(bitmap != null) {
if(!coroutineContext.isActive || url != last_image_uri) {
bitmap.recycle()
} else {
last_image_bitmap = bitmap
iv.setImageBitmap(last_image_bitmap)
iv.visibility = View.VISIBLE
}
} }
} }
last_image_task = task
task.executeOnExecutor(App1.task_executor)
} catch(ex : Throwable) { } catch(ex : Throwable) {
log.trace(ex) log.trace(ex)
} }

View File

@ -2,22 +2,21 @@ package jp.juggler.subwaytooter.api
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.os.AsyncTask
import android.os.Handler import android.os.Handler
import android.os.SystemClock import android.os.SystemClock
import java.lang.ref.WeakReference
import java.text.NumberFormat
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.api.entity.Host import jp.juggler.subwaytooter.api.entity.Host
import jp.juggler.subwaytooter.dialog.ProgressDialogEx import jp.juggler.subwaytooter.dialog.ProgressDialogEx
import jp.juggler.subwaytooter.table.SavedAccount import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.dismissSafe import jp.juggler.util.dismissSafe
import jp.juggler.util.withCaption
import kotlinx.coroutines.*
import java.lang.Runnable
import java.lang.ref.WeakReference
import java.text.NumberFormat
import java.util.concurrent.atomic.AtomicBoolean
/* /*
非同期タスク(TootTask)を実行します APIクライアントを必要とする非同期タスク(TootTask)を実行します
- 内部でAsyncTaskを使いますAndroid Lintの警告を抑制します
- ProgressDialogを表示します抑制することも可能です - ProgressDialogを表示します抑制することも可能です
- TootApiClientの初期化を行います - TootApiClientの初期化を行います
- TootApiClientからの進捗イベントをProgressDialogに伝達します - TootApiClientからの進捗イベントをProgressDialogに伝達します
@ -52,43 +51,15 @@ class TootTaskRunner(
internal var max = 1 internal var max = 1
} }
// has AsyncTask
private class MyTask(
private val runner : TootTaskRunner
) : AsyncTask<Void, Void, TootApiResult?>() {
var isActive : Boolean = true
override fun doInBackground(vararg voids : Void) : TootApiResult? {
val callback = runner.callback
return if(callback == null) {
TootApiResult("callback is null")
} else {
callback.background(runner.client)
}
}
override fun onCancelled(result : TootApiResult?) {
onPostExecute(result)
}
override fun onPostExecute(result : TootApiResult?) {
isActive = false
runner.dismissProgress()
runner.callback?.handleResult(result)
}
}
private val handler : Handler private val handler : Handler
private val client : TootApiClient private val client : TootApiClient
private val task : MyTask
private val info = ProgressInfo() private val info = ProgressInfo()
private var progress : ProgressDialogEx? = null private var progress : ProgressDialogEx? = null
private var progress_prefix : String? = null private var progress_prefix : String? = null
private var task : Deferred<TootApiResult?>? = null
private val refContext : WeakReference<Context> private val refContext : WeakReference<Context>
private var callback : TootTask? = null
private var last_message_shown : Long = 0 private var last_message_shown : Long = 0
private val proc_progress_message = object : Runnable { private val proc_progress_message = object : Runnable {
@ -105,19 +76,29 @@ class TootTaskRunner(
this.refContext = WeakReference(context) this.refContext = WeakReference(context)
this.handler = Handler(context.mainLooper) this.handler = Handler(context.mainLooper)
this.client = TootApiClient(context, callback = this) this.client = TootApiClient(context, callback = this)
this.task = MyTask(this)
} }
private val _isActive = AtomicBoolean(true)
val isActive : Boolean val isActive : Boolean
get() = task.isActive get() = _isActive.get()
fun run(callback : TootTask) : TootTaskRunner { fun run(callback : TootTask) : TootTaskRunner {
openProgress() GlobalScope.launch(Dispatchers.Main) {
openProgress()
this.callback = callback val result = try {
withContext(Dispatchers.IO) {
task.executeOnExecutor(App1.task_executor) callback.background(client)
}
}catch(ex:CancellationException){
null
} catch(ex : Throwable) {
TootApiResult(ex.withCaption("error"))
}
_isActive.set(false)
dismissProgress()
callback.handleResult(result)
}
return this return this
} }
@ -141,7 +122,7 @@ class TootTaskRunner(
// implements TootApiClient.Callback // implements TootApiClient.Callback
override val isApiCancelled : Boolean override val isApiCancelled : Boolean
get() = task.isCancelled get() = task?.isActive == false
override fun publishApiProgress(s : String) { override fun publishApiProgress(s : String) {
synchronized(this) { synchronized(this) {
@ -171,7 +152,7 @@ class TootTaskRunner(
val progress = ProgressDialogEx(context) val progress = ProgressDialogEx(context)
this.progress = progress this.progress = progress
progress.setCancelable(true) progress.setCancelable(true)
progress.setOnCancelListener { task.cancel(true) } progress.setOnCancelListener { task?.cancel() }
progress.setProgressStyle(progress_style) progress.setProgressStyle(progress_style)
progressSetupCallback(progress) progressSetupCallback(progress)
showProgressMessage() showProgressMessage()

View File

@ -6,7 +6,7 @@ import jp.juggler.util.JsonObject
class MisskeyAntenna(parser:TootParser,src: JsonObject) :TimelineItem(){ class MisskeyAntenna(parser:TootParser,src: JsonObject) :TimelineItem(){
val timeCreatedAt:Long // "2020-02-19T09:08:41.929Z" private val timeCreatedAt:Long // "2020-02-19T09:08:41.929Z"
val id: EntityId val id: EntityId
@ -19,7 +19,7 @@ class MisskeyAntenna(parser:TootParser,src: JsonObject) :TimelineItem(){
// "src":"list", // "src":"list",
// "src":"users", // "src":"users",
val keywords: Array<Array<String>> private val keywords: Array<Array<String>>
// "keywords":[[""]], // "keywords":[[""]],
// "keywords":[[""]], // "keywords":[[""]],
// "keywords":[[""]], // "keywords":[[""]],
@ -28,20 +28,20 @@ class MisskeyAntenna(parser:TootParser,src: JsonObject) :TimelineItem(){
// "keywords":[["test"]], // "keywords":[["test"]],
// src=="group" の場合。他はnull // src=="group" の場合。他はnull
val userGroupId : EntityId? private val userGroupId : EntityId?
// src=="list" の場合。他はnull // src=="list" の場合。他はnull
val userListId : EntityId? private val userListId : EntityId?
val users : Array<String> val users : Array<String>
// "users":[""], // "users":[""],
// "users":["@tateisu","@syuilo"], // "users":["@tateisu","@syuilo"],
val caseSensitive:Boolean private val caseSensitive:Boolean
val hasUnreadNote:Boolean private val hasUnreadNote:Boolean
val notify: Boolean private val notify: Boolean
val withFile : Boolean private val withFile : Boolean
val withReplies : Boolean private val withReplies : Boolean
init{ init{
timeCreatedAt = TootStatus.parseTime(src.string("createdAt")) timeCreatedAt = TootStatus.parseTime(src.string("createdAt"))

View File

@ -3,7 +3,6 @@ package jp.juggler.subwaytooter.dialog
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.database.Cursor import android.database.Cursor
import android.os.AsyncTask
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
@ -12,17 +11,22 @@ import android.widget.ListView
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import jp.juggler.subwaytooter.ActPost import jp.juggler.subwaytooter.ActPost
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.R
import jp.juggler.subwaytooter.api.entity.TootStatus import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.table.PostDraft import jp.juggler.subwaytooter.table.PostDraft
import jp.juggler.util.JsonObject import jp.juggler.util.JsonObject
import jp.juggler.util.LogCategory
import jp.juggler.util.dismissSafe import jp.juggler.util.dismissSafe
import jp.juggler.util.showToast import jp.juggler.util.showToast
import kotlinx.coroutines.*
class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener, class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener,
DialogInterface.OnDismissListener { DialogInterface.OnDismissListener {
companion object{
private val log = LogCategory("DlgDraftPicker")
}
private lateinit var activity : ActPost private lateinit var activity : ActPost
private lateinit var callback : (draft : JsonObject) -> Unit private lateinit var callback : (draft : JsonObject) -> Unit
private lateinit var lvDraft : ListView private lateinit var lvDraft : ListView
@ -32,7 +36,7 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
private var cursor : Cursor? = null private var cursor : Cursor? = null
private var colIdx : PostDraft.ColIdx? = null private var colIdx : PostDraft.ColIdx? = null
private var task : AsyncTask<Void, Void, Cursor?>? = null private var task : Job? = null
override fun onItemClick(parent : AdapterView<*>, view : View, position : Int, id : Long) { override fun onItemClick(parent : AdapterView<*>, view : View, position : Int, id : Long) {
val json = getPostDraft(position)?.json val json = getPostDraft(position)?.json
@ -61,7 +65,7 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
} }
override fun onDismiss(dialog : DialogInterface) { override fun onDismiss(dialog : DialogInterface) {
task?.cancel(true) task?.cancel()
task = null task = null
lvDraft.adapter = null lvDraft.adapter = null
@ -98,37 +102,32 @@ class DlgDraftPicker : AdapterView.OnItemClickListener, AdapterView.OnItemLongCl
private fun reload() { private fun reload() {
// cancel old task // cancel old task
task?.cancel(true) task?.cancel()
val new_task = @SuppressLint("StaticFieldLeak") task = GlobalScope.launch(Dispatchers.Main){
object : AsyncTask<Void, Void, Cursor?>() { val cursor =try {
override fun doInBackground(vararg params : Void) : Cursor? { withContext(Dispatchers.IO) {
return PostDraft.createCursor() PostDraft.createCursor()
} ?: error("cursor is null")
}catch(ex:CancellationException) {
return@launch
}catch(ex:Throwable){
log.trace(ex)
showToast(activity,ex, "failed to loading drafts.")
return@launch
} }
override fun onCancelled(cursor : Cursor?) { if(! dialog.isShowing) {
onPostExecute(cursor) // dialog is already closed.
} cursor.close()
} else {
override fun onPostExecute(cursor : Cursor?) { val old = this@DlgDraftPicker.cursor
if(! dialog.isShowing) { this@DlgDraftPicker.cursor = cursor
// dialog is already closed. colIdx = PostDraft.ColIdx(cursor)
cursor?.close() adapter.notifyDataSetChanged()
return old?.close()
}
if(cursor == null) {
// load failed.
showToast(activity, true, "failed to loading drafts.")
} else {
this@DlgDraftPicker.cursor = cursor
colIdx = PostDraft.ColIdx(cursor)
adapter.notifyDataSetChanged()
}
} }
} }
this.task = new_task
new_task.executeOnExecutor(App1.task_executor)
} }
private fun getPostDraft(position : Int) : PostDraft? { private fun getPostDraft(position : Int) : PostDraft? {

View File

@ -3,23 +3,16 @@ package jp.juggler.subwaytooter.dialog
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.graphics.Bitmap import android.graphics.Bitmap
import android.os.AsyncTask
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import jp.juggler.subwaytooter.ActMain import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.R import jp.juggler.subwaytooter.R
import jp.juggler.util.LogCategory
import jp.juggler.util.dismissSafe
import jp.juggler.util.showToast
import net.glxn.qrgen.android.QRCode import net.glxn.qrgen.android.QRCode
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
object DlgQRCode { object DlgQRCode {
private val log = LogCategory("DlgQRCode")
internal interface QrCodeCallback { internal interface QrCodeCallback {
fun onQrCode(bitmap : Bitmap?) fun onQrCode(bitmap : Bitmap?)
} }
@ -30,39 +23,18 @@ object DlgQRCode {
url : String, url : String,
callback : QrCodeCallback callback : QrCodeCallback
) { ) {
@Suppress("DEPRECATION") activity.runWithProgress(
val progress = ProgressDialogEx(activity) "making QR code",
val task = object : AsyncTask<Void, Void, Bitmap?>() { {
QRCode.from(url).withSize(size, size).bitmap()
override fun doInBackground(vararg params : Void) : Bitmap? { },
return try { {
QRCode.from(url).withSize(size, size).bitmap() if(it != null) callback.onQrCode(it)
} catch(ex : Throwable) { },
log.trace(ex) progressInitializer = {
showToast(activity, ex, "makeQrCode failed.") it.setMessageEx(activity.getString(R.string.generating_qr_code))
null
}
} }
)
override fun onCancelled(result : Bitmap?) {
onPostExecute(result)
}
override fun onPostExecute(result : Bitmap?) {
progress.dismissSafe()
if(result != null) {
callback.onQrCode(result)
}
}
}
progress.isIndeterminateEx = true
progress.setCancelable(true)
progress.setMessageEx(activity.getString(R.string.generating_qr_code))
progress.setOnCancelListener { task.cancel(true) }
progress.show()
task.executeOnExecutor(App1.task_executor)
} }
fun open(activity : ActMain, message : CharSequence, url : String) { fun open(activity : ActMain, message : CharSequence, url : String) {
@ -96,10 +68,7 @@ object DlgQRCode {
viewRoot.findViewById<View>(R.id.btnCancel).setOnClickListener { dialog.cancel() } viewRoot.findViewById<View>(R.id.btnCancel).setOnClickListener { dialog.cancel() }
dialog.show() dialog.show()
} }
}) })
} }
} }

View File

@ -24,4 +24,3 @@ org.gradle.caching=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.debug.obsoleteApi=true android.debug.obsoleteApi=true
android.enableR8=true