プロフィールの画像選択もACTION_OPEN_DOCUMENTからACTION_GET_CONTENTに変更。カラム背景を選んだ際にデータをローカルにコピーする。

This commit is contained in:
tateisu 2018-09-07 14:51:59 +09:00
parent 79bb4c80c3
commit f613f1510a
8 changed files with 206 additions and 134 deletions

View File

@ -29,7 +29,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -202,27 +202,17 @@ class ActAccountSetting
}
REQUEST_CODE_AVATAR_ATTACHMENT, REQUEST_CODE_HEADER_ATTACHMENT -> {
if(resultCode == Activity.RESULT_OK && data != null) {
val uri1 = data.data
if(uri1 != null) {
// 単一選択
val type = data.type
data.handleGetContentResult(contentResolver).firstOrNull()?.let{
addAttachment(
requestCode,
uri1,
if(type?.isNotEmpty() == true) type else contentResolver.getType(uri1)
it.first,
if( it.second?.isNotEmpty() == true)
it.second
else
contentResolver.getType(it.first)
)
} else {
// 複数選択
data.clipData?.let { clipData ->
if(clipData.itemCount > 0) {
clipData.getItemAt(0)?.uri?.let { uri2 ->
val type = contentResolver.getType(uri2)
addAttachment(requestCode, uri2, type)
}
}
}
}
}
}
@ -1312,17 +1302,12 @@ class ActAccountSetting
}
private fun performAttachment(request_code : Int) {
// SAFのIntentで開く
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
intent.putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/*", "video/*"))
val intent = intentGetContent(false,getString(R.string.pick_image),"image/*")
startActivityForResult(intent, request_code)
} catch(ex : Throwable) {
log.trace(ex,"ACTION_OPEN_DOCUMENT failed.")
showToast(this, ex, "ACTION_OPEN_DOCUMENT failed.")
log.trace(ex,"performAttachment failed.")
showToast(this, ex, "performAttachment failed.")
}
}

View File

@ -43,8 +43,7 @@ import java.util.Locale
import jp.juggler.subwaytooter.table.AcctColor
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.util.LogCategory
import jp.juggler.subwaytooter.util.showToast
import jp.juggler.subwaytooter.util.*
class ActAppSetting : AppCompatActivity()
, CompoundButton.OnCheckedChangeListener
@ -688,9 +687,7 @@ class ActAppSetting : AppCompatActivity()
}
R.id.btnTimelineFontEdit -> try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
val intent = intentOpenDocument("*/*")
startActivityForResult(intent, REQUEST_CODE_TIMELINE_FONT)
} catch(ex : Throwable) {
showToast(this, ex, "could not open picker for font")
@ -703,9 +700,7 @@ class ActAppSetting : AppCompatActivity()
}
R.id.btnTimelineFontBoldEdit -> try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
val intent = intentOpenDocument("*/*")
startActivityForResult(intent, REQUEST_CODE_TIMELINE_FONT_BOLD)
} catch(ex : Throwable) {
showToast(this, ex, "could not open picker for font")
@ -732,30 +727,31 @@ class ActAppSetting : AppCompatActivity()
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
if(resultCode == RESULT_OK && data != null && requestCode == REQUEST_CODE_TIMELINE_FONT) {
val file = saveTimelineFont(data.data, "TimelineFont")
if(file != null) {
timeline_font = file.absolutePath
saveUIToData()
showTimelineFont(tvTimelineFontUrl, timeline_font)
data.handleGetContentResult(contentResolver).firstOrNull()?.first?.let{ uri->
val file = saveTimelineFont(uri, "TimelineFont")
if(file != null) {
timeline_font = file.absolutePath
saveUIToData()
showTimelineFont(tvTimelineFontUrl, timeline_font)
}
}
} else if(resultCode == RESULT_OK && data != null && requestCode == REQUEST_CODE_TIMELINE_FONT_BOLD) {
val file = saveTimelineFont(data.data, "TimelineFontBold")
if(file != null) {
timeline_font_bold = file.absolutePath
saveUIToData()
showTimelineFont(tvTimelineFontBoldUrl, timeline_font_bold)
}
} else if(resultCode == RESULT_OK && requestCode == REQUEST_CODE_APP_DATA_IMPORT) {
if(data != null) {
val uri = data.data
if(uri != null) {
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
importAppData(false, uri)
data.handleGetContentResult(contentResolver).firstOrNull()?.first?.let{ uri->
val file = saveTimelineFont(uri, "TimelineFontBold")
if(file != null) {
timeline_font_bold = file.absolutePath
saveUIToData()
showTimelineFont(tvTimelineFontBoldUrl, timeline_font_bold)
}
}
} else if(resultCode == RESULT_OK && data != null && requestCode == REQUEST_CODE_APP_DATA_IMPORT) {
data.handleGetContentResult(contentResolver).firstOrNull()?.first?.let{ uri->
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
importAppData(false, uri)
}
}
super.onActivityResult(requestCode, resultCode, data)
}
@ -1104,9 +1100,7 @@ class ActAppSetting : AppCompatActivity()
private fun importAppData() {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
val intent = intentOpenDocument("*/*")
startActivityForResult(intent, REQUEST_CODE_APP_DATA_IMPORT)
} catch(ex : Throwable) {
showToast(this, ex, "importAppData(1) failed.")

View File

@ -1,5 +1,6 @@
package jp.juggler.subwaytooter
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
@ -17,13 +18,14 @@ import android.widget.TextView
import com.jrummyapps.android.colorpicker.ColorPickerDialog
import com.jrummyapps.android.colorpicker.ColorPickerDialogListener
import jp.juggler.subwaytooter.util.*
import org.apache.commons.io.IOUtils
import java.io.File
import java.io.FileOutputStream
import java.text.NumberFormat
import java.util.Locale
import jp.juggler.subwaytooter.util.LogCategory
import jp.juggler.subwaytooter.util.createResizedBitmap
class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPickerDialogListener {
companion object {
@ -181,9 +183,7 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
}
R.id.btnColumnBackgroundImage -> {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "image/*"
val intent = intentOpenDocument("image/*")
startActivityForResult(intent, REQUEST_CODE_PICK_BACKGROUND)
}
@ -220,16 +220,20 @@ class ActColumnCustomize : AppCompatActivity(), View.OnClickListener, ColorPicke
override fun onDialogDismissed(dialogId : Int) {}
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
if(requestCode == REQUEST_CODE_PICK_BACKGROUND && resultCode == RESULT_OK) {
if(data != null) {
val uri = data.data
if(uri != null) {
contentResolver.takePersistableUriPermission(
uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
column.column_bg_image = uri.toString()
if(requestCode == REQUEST_CODE_PICK_BACKGROUND && data != null && resultCode == RESULT_OK) {
data.handleGetContentResult(contentResolver).firstOrNull()?.let { pair->
try{
val backgroundDir = getDir(Column.DIR_BACKGROUND_IMAGE, Context.MODE_PRIVATE)
val file = File(backgroundDir,column.column_id)
FileOutputStream(file).use{ outStream->
contentResolver.openInputStream(pair.first).use{ inStream->
IOUtils.copy(inStream,outStream)
}
}
column.column_bg_image = Uri.fromFile(file).toString()
show()
}catch(ex:Throwable){
showToast(this@ActColumnCustomize,true,ex.withCaption("can't update background image."))
}
}
}

View File

@ -292,36 +292,17 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
}
}
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
if(requestCode == REQUEST_CODE_ATTACHMENT_OLD && resultCode == Activity.RESULT_OK) {
if(data != null) {
val urlList = ArrayList<Pair<Uri, String?>>()
// 単一選択
data.data?.let { urlList.add(Pair(it, data.type)) }
// 複数選択
val cd = data.clipData
if(cd != null) {
for(i in 0 until cd.itemCount) {
cd.getItemAt(i)?.uri?.let { uri ->
if(null == urlList.find { it.first == uri }) {
urlList.add(Pair(uri, null as String?))
}
}
}
}
urlList.forEach { addAttachment(it.first, it.second) }
data?.handleGetContentResult(contentResolver)?.forEach {
addAttachment(it.first, it.second)
}
} else if(requestCode == REQUEST_CODE_ATTACHMENT && resultCode == Activity.RESULT_OK) {
if(data != null) {
// 単一選択
data.data?.let { addAttachment(it, data.type) }
// 複数選択
val cd = data.clipData
if(cd != null) {
for(i in 0 until cd.itemCount) {
cd.getItemAt(i)?.uri?.let { addAttachment(it) }
}
}
data?.handleGetContentResult(contentResolver)?.forEach {
addAttachment(it.first, it.second)
}
} else if(requestCode == REQUEST_CODE_CAMERA) {
@ -1387,31 +1368,17 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
}
private fun performAttachmentOld() {
// SAFのIntentで開く
try {
val intent =
Intent(Intent.ACTION_GET_CONTENT, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
intent.addCategory(Intent.CATEGORY_OPENABLE)
// EXTRA_ALLOW_MULTIPLE は API 18 (4.3)以降。ACTION_GET_CONTENT でも ACTION_OPEN_DOCUMENT でも指定できる
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
// EXTRA_MIME_TYPES は API 19以降。ACTION_GET_CONTENT でも ACTION_OPEN_DOCUMENT でも指定できる
intent.putExtra("android.intent.extra.MIME_TYPES", arrayOf("image/*", "video/*"))
if(Build.VERSION.SDK_INT >= 23) {
// On Android 6.0 and above using "video/* image/" or "image/ video/*" type doesn't work
// it only recognizes the first filter you specify.
intent.type = "*/*"
} else {
intent.type = "image/* video/*"
}
startActivityForResult(
Intent.createChooser(intent, getString(R.string.pick_images)),
REQUEST_CODE_ATTACHMENT_OLD
val intent = intentGetContent(
true,
getString(R.string.pick_images)
, "image/*"
, "video/*"
)
startActivityForResult(intent, REQUEST_CODE_ATTACHMENT_OLD)
} catch(ex : Throwable) {
log.trace(ex)
showToast(this, ex, "ACTION_GET_CONTENT failed.")
@ -1573,11 +1540,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
try {
val opener = createOpener(uri, mime_type)
val media_size_max = when{
mime_type.startsWith("video") ->{
val media_size_max = when {
mime_type.startsWith("video") -> {
1000000 * Math.max(1, Pref.spMovieSizeMax.toInt(pref))
}
else->{
else -> {
1000000 * Math.max(1, Pref.spMediaSizeMax.toInt(pref))
}
}
@ -1604,7 +1572,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
}
multipart_builder.addFormDataPart(
"file", getDocumentName(contentResolver,uri), object : RequestBody() {
"file", getDocumentName(contentResolver, uri), object : RequestBody() {
override fun contentType() : MediaType? {
return MediaType.parse(opener.mimeType)
}
@ -1649,7 +1617,9 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
val multipart_body = MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart(
"file", getDocumentName(contentResolver,uri), object : RequestBody() {
"file",
getDocumentName(contentResolver, uri),
object : RequestBody() {
override fun contentType() : MediaType? {
return MediaType.parse(opener.mimeType)
}
@ -1821,8 +1791,6 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
}
}
private fun showVisibility() {
btnVisibility.setImageResource(
Styler.getVisibilityIcon(

View File

@ -37,6 +37,7 @@ import jp.juggler.subwaytooter.table.HighlightWord
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.util.*
import java.io.File
class AppState(internal val context : Context, internal val pref : SharedPreferences) {
@ -275,7 +276,18 @@ class AppState(internal val context : Context, internal val pref : SharedPrefere
++ i
}
}
enableSpeech()
// 背景フォルダの掃除
val backgroundDir = context.getDir(Column.DIR_BACKGROUND_IMAGE,Context.MODE_PRIVATE)
backgroundDir.list().forEach {name->
val file = File(backgroundDir,name)
if( file.isFile ){
val column = Column.findColumnById( name )
if( column == null) file.delete()
}
}
}
fun isBusyFav(account : SavedAccount, status : TootStatus) : Boolean {

View File

@ -14,6 +14,7 @@ import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.lang.ref.WeakReference
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ConcurrentLinkedQueue
@ -47,12 +48,15 @@ class Column(
val app_state : AppState,
val context : Context,
val access_info : SavedAccount,
val column_type : Int
val column_type : Int,
val column_id: String
) {
companion object {
private val log = LogCategory("Column")
const val DIR_BACKGROUND_IMAGE = "columnBackground"
private const val READ_LIMIT = 80 // API側の上限が80です。ただし指定しても40しか返ってこないことが多い
private const val LOOP_TIMEOUT = 10000L
private const val LOOP_READ_ENOUGH = 30 // フィルタ後のデータ数がコレ以上ならループを諦めます
@ -126,6 +130,7 @@ class Column(
internal const val KEY_ACCOUNT_ROW_ID = "account_id"
internal const val KEY_TYPE = "type"
internal const val KEY_COLUMN_ID = "column_id"
internal const val KEY_DONT_CLOSE = "dont_close"
private const val KEY_WITH_ATTACHMENT = "with_attachment"
private const val KEY_WITH_HIGHLIGHT = "with_highlight"
@ -366,6 +371,38 @@ class Column(
}
dst
}
private val columnIdMap = HashMap<String,WeakReference<Column>?>()
private fun registerColumnId(id:String,column:Column){
synchronized(columnIdMap){
columnIdMap[id] = WeakReference(column)
}
}
private fun generateColumnId():String{
synchronized(columnIdMap){
val buffer = ByteBuffer.allocate(8)
var id=""
while( id.isEmpty() || columnIdMap.containsKey(id) ){
if(id.isNotEmpty()) Thread.sleep(1L)
buffer.clear()
buffer.putLong(System.currentTimeMillis())
id = buffer.array().encodeBase64Url()
}
columnIdMap[id] = null
return id
}
}
private fun decodeColumnId(src:JSONObject):String{
return src.parseString(KEY_COLUMN_ID) ?: generateColumnId()
}
fun findColumnById(id : String) : Column? {
synchronized(columnIdMap){
return columnIdMap[id]?.get()
}
}
}
private var callback_ref : WeakReference<Callback>? = null
@ -550,6 +587,8 @@ class Column(
private var bPutGap : Boolean = false
@Suppress("unused")
val listTitle : String
get() {
@ -591,7 +630,7 @@ class Column(
type : Int,
vararg params : Any
)
: this(app_state, app_state.context, access_info, type) {
: this(app_state, app_state.context, access_info, type, generateColumnId() ) {
this.callback_ref = WeakReference(callback)
when(type) {
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY -> status_id =
@ -614,7 +653,8 @@ class Column(
app_state,
app_state.context,
loadAccount(app_state.context, src),
src.optInt(KEY_TYPE)
src.optInt(KEY_TYPE),
decodeColumnId(src)
) {
dont_close = src.optBoolean(KEY_DONT_CLOSE)
with_attachment = src.optBoolean(KEY_WITH_ATTACHMENT)
@ -684,6 +724,7 @@ class Column(
fun encodeJSON(dst : JSONObject, old_index : Int) {
dst.put(KEY_ACCOUNT_ROW_ID, access_info.db_id)
dst.put(KEY_TYPE, column_type)
dst.put(KEY_COLUMN_ID, column_id)
dst.put(KEY_DONT_CLOSE, dont_close)
dst.put(KEY_WITH_ATTACHMENT, with_attachment)
dst.put(KEY_WITH_HIGHLIGHT, with_highlight)
@ -3629,7 +3670,7 @@ class Column(
var filterUpdated = false
override fun doInBackground(vararg params : Void) : TootApiResult? {
override fun doInBackground(vararg unused : Void) : TootApiResult? {
ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback {
@ -4527,7 +4568,7 @@ class Column(
return result
}
override fun doInBackground(vararg params : Void) : TootApiResult? {
override fun doInBackground(vararg unused : Void) : TootApiResult? {
ctStarted.set(true)
val client = TootApiClient(context, callback = object : TootApiCallback {
@ -5527,4 +5568,7 @@ class Column(
// return null
// }
init{
registerColumnId(column_id,this)
}
}

View File

@ -4,9 +4,11 @@ import android.app.Activity
import android.content.ContentResolver
import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.res.Resources
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@ -36,10 +38,7 @@ import java.util.Locale
import java.util.regex.Pattern
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.isEmpty
import kotlin.collections.isNotEmpty
import kotlin.collections.set
import kotlin.collections.toString
object Utils {
@ -914,3 +913,69 @@ fun getStreamSize(bClose : Boolean, inStream : InputStream) : Long {
if(bClose) IOUtils.closeQuietly(inStream)
}
}
fun intentOpenDocument(mimeType : String):Intent{
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = mimeType // "image/*"
return intent
}
fun intentGetContent(
allowMultiple : Boolean,
caption : String,
vararg mimeTypes : String
) : Intent {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
if(allowMultiple) {
// EXTRA_ALLOW_MULTIPLE は API 18 (4.3)以降。ACTION_GET_CONTENT でも ACTION_OPEN_DOCUMENT でも指定できる
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
// EXTRA_MIME_TYPES は API 19以降。ACTION_GET_CONTENT でも ACTION_OPEN_DOCUMENT でも指定できる
intent.putExtra("android.intent.extra.MIME_TYPES", mimeTypes)
intent.type =when {
mimeTypes.size == 1 -> mimeTypes[0]
// On Android 6.0 and above using "video/* image/" or "image/ video/*" type doesn't work
// it only recognizes the first filter you specify.
Build.VERSION.SDK_INT >= 23 -> "*/*"
else -> mimeTypes.joinToString(" ")
}
return Intent.createChooser(intent, caption)
}
// returns list of pair of uri and mime-type.
fun Intent.handleGetContentResult(contentResolver : ContentResolver) : ArrayList<Pair<Uri, String?>> {
val urlList = ArrayList<Pair<Uri, String?>>()
// 単一選択
this.data?.let {
urlList.add(Pair(it, this.type))
}
// 複数選択
val cd = this.clipData
if(cd != null) {
for(i in 0 until cd.itemCount) {
cd.getItemAt(i)?.uri?.let { uri ->
if(null == urlList.find { it.first == uri }) {
urlList.add(Pair(uri, null as String?))
}
}
}
}
urlList.forEach {
try{
contentResolver.takePersistableUriPermission(
it.first,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
}catch(_:Throwable){
}
}
return urlList
}