-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
442 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
63 changes: 63 additions & 0 deletions
63
app/src/main/java/jp/juggler/subwaytooter/ui/ossLicense/ActOSSLicenseViewModel.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package jp.juggler.subwaytooter.ui.ossLicense | ||
|
||
import android.app.Application | ||
import android.content.Context | ||
import androidx.lifecycle.AndroidViewModel | ||
import androidx.lifecycle.viewModelScope | ||
import jp.juggler.subwaytooter.R | ||
import jp.juggler.subwaytooter.util.StColorScheme | ||
import jp.juggler.util.coroutine.AppDispatchers | ||
import jp.juggler.util.data.decodeJsonObject | ||
import kotlinx.coroutines.CancellationException | ||
import kotlinx.coroutines.channels.Channel | ||
import kotlinx.coroutines.flow.MutableStateFlow | ||
import kotlinx.coroutines.flow.asStateFlow | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.withContext | ||
|
||
class ActOSSLicenseViewModel( | ||
application: Application, | ||
) : AndroidViewModel(application) { | ||
val context: Context | ||
get() = getApplication<Application>().applicationContext | ||
|
||
private val loadError = Channel<Throwable>(capacity = Channel.CONFLATED) | ||
|
||
private val _libraries = MutableStateFlow<List<LibText>>(emptyList()) | ||
val libraries = _libraries.asStateFlow() | ||
|
||
private val _isProgressShown = MutableStateFlow(false) | ||
val isProgressShown = _isProgressShown.asStateFlow() | ||
|
||
fun load(stColorScheme: StColorScheme) = viewModelScope.launch { | ||
try { | ||
_isProgressShown.value = true | ||
_libraries.value = withContext(AppDispatchers.IO) { | ||
val root = context.resources.openRawResource(R.raw.dep_list) | ||
.use { it.readBytes() } | ||
.decodeToString() | ||
.decodeJsonObject() | ||
val licenses = root.jsonArray("licenses") | ||
?.objectList() | ||
?.associateBy { it.string("shortName") } | ||
?: emptyMap() | ||
root.jsonArray("libs") | ||
?.objectList() | ||
?.map { | ||
parseLibText( | ||
it, | ||
licenses, | ||
stColorScheme = stColorScheme, | ||
) | ||
} | ||
?.sortedBy { it.nameSort } | ||
?: emptyList() | ||
} | ||
} catch (ex: Throwable) { | ||
if (ex is CancellationException) return@launch | ||
loadError.send(ex) | ||
} finally { | ||
_isProgressShown.value = false | ||
} | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
app/src/main/java/jp/juggler/subwaytooter/ui/ossLicense/LibText.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package jp.juggler.subwaytooter.ui.ossLicense | ||
|
||
import androidx.compose.ui.text.AnnotatedString | ||
import androidx.compose.ui.text.buildAnnotatedString | ||
import jp.juggler.subwaytooter.util.StColorScheme | ||
import jp.juggler.subwaytooter.util.annotateUrl | ||
import jp.juggler.subwaytooter.util.isNotEmpty | ||
import jp.juggler.subwaytooter.util.joinAnnotatedString | ||
import jp.juggler.subwaytooter.util.toAnnotatedString | ||
import jp.juggler.util.data.JsonObject | ||
import jp.juggler.util.data.notEmpty | ||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull | ||
|
||
/** | ||
* 依存ライブラリの装飾付きテキスト | ||
*/ | ||
class LibText( | ||
val nameBig: AnnotatedString, | ||
val nameSmall: AnnotatedString?, | ||
val desc: AnnotatedString, | ||
) { | ||
val nameSort = nameBig.toString().lowercase() | ||
} | ||
|
||
/** | ||
* レシーバの文字列がカラでなければdstに追加する | ||
* @receiver 追加先 | ||
* @param text 装飾付き文字列 | ||
* @param prefix nullや空でなければtextより先に追加される | ||
*/ | ||
private fun AnnotatedString.Builder.appendLine( | ||
text: CharSequence?, | ||
prefix: CharSequence? = null, | ||
) { | ||
if (text.isNullOrBlank()) return | ||
if (isNotEmpty()) append("\n") | ||
if (!prefix.isNullOrBlank()) append(prefix) | ||
append(text) | ||
} | ||
|
||
/** | ||
* ライブラリ情報をLibTextに変換する | ||
*/ | ||
fun parseLibText( | ||
lib: JsonObject, | ||
licenses: Map<String?, JsonObject>, | ||
stColorScheme: StColorScheme, | ||
): LibText { | ||
val colorLink = stColorScheme.colorTextLink | ||
|
||
val webSite = lib.string("website")?.toHttpUrlOrNull()?.toString() | ||
val name = lib.string("name")?.notEmpty() | ||
val id = lib.string("id")?.notEmpty() | ||
|
||
val nameBig: CharSequence | ||
val nameSmall: CharSequence? | ||
if (name.isNullOrBlank()) { | ||
// nameがない場合はnameBigはidを大きく表示する | ||
nameBig = (id ?: "(no name, no id)").annotateUrl(webSite, colorLink) | ||
nameSmall = null | ||
} else { | ||
nameBig = name.annotateUrl(webSite, colorLink) | ||
nameSmall = id | ||
// idがない場合はnameSmallはnullとなる | ||
} | ||
|
||
val licenseText = lib.jsonArray("licenses")?.stringList() | ||
?.asSequence() | ||
?.mapNotNull { licenses[it] } | ||
?.map { | ||
val uri = | ||
it.jsonArray("urls")?.stringList()?.firstOrNull()?.toHttpUrlOrNull()?.toString() | ||
it.string("name")!!.annotateUrl(uri, colorLink) | ||
} | ||
?.toList()?.joinAnnotatedString(AnnotatedString(", ")) | ||
|
||
val devNames = lib.jsonArray("developers")?.objectList() | ||
?.map { it.string("name") } | ||
?.filter { !it.isNullOrBlank() } | ||
?.joinToString(", ") | ||
|
||
val libDesc = buildAnnotatedString { | ||
appendLine(lib.string("description")?.takeIf { it != name }) | ||
appendLine(devNames, "- Developers: ") | ||
appendLine(licenseText, "- License: ") | ||
} | ||
return LibText( | ||
nameBig = nameBig.toAnnotatedString(), | ||
nameSmall = nameSmall?.toAnnotatedString(), | ||
desc = libDesc.toAnnotatedString(), | ||
) | ||
} |
59 changes: 59 additions & 0 deletions
59
app/src/main/java/jp/juggler/subwaytooter/util/AnnotatedStringUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package jp.juggler.subwaytooter.util | ||
|
||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.text.AnnotatedString | ||
import androidx.compose.ui.text.SpanStyle | ||
import androidx.compose.ui.text.style.TextDecoration | ||
import androidx.compose.ui.unit.TextUnit | ||
|
||
fun CharSequence.toAnnotatedString() = when (this) { | ||
is AnnotatedString -> this | ||
else -> AnnotatedString(toString()) | ||
} | ||
|
||
fun AnnotatedString.Builder.isEmpty() = length == 0 | ||
fun AnnotatedString.Builder.isNotEmpty() = length != 0 | ||
|
||
/** | ||
* 装飾付き文字列のリストを連結する | ||
* @receiver 装飾付き文字列のリスト | ||
* @param separator 文字列と文字列の間に挿入される区切り | ||
*/ | ||
fun List<CharSequence>.joinAnnotatedString( | ||
separator: CharSequence, | ||
): AnnotatedString = AnnotatedString.Builder().also { dst -> | ||
for (item in this) { | ||
if (dst.isNotEmpty()) dst.append(separator) | ||
dst.append(item) | ||
} | ||
}.toAnnotatedString() | ||
|
||
/** | ||
* クリック可能な装飾付き文字列を作る | ||
* @receiver 表示文字列 | ||
* @param uri クリックしたら開くUri。nullなら装飾しない | ||
* @return CharSequence, 実際には Stringまたは AnnotatedString | ||
*/ | ||
fun String.annotateUrl( | ||
uri: String?, | ||
colorLink: Color, | ||
fontSize: TextUnit = TextUnit.Unspecified, | ||
): CharSequence = when (uri) { | ||
null -> this | ||
else -> AnnotatedString.Builder().apply { | ||
append(this@annotateUrl) | ||
addStyle( | ||
style = SpanStyle( | ||
color = colorLink, | ||
fontSize = fontSize, | ||
textDecoration = TextDecoration.Underline | ||
), start = 0, end = length | ||
) | ||
addStringAnnotation( | ||
tag = "URL", | ||
annotation = uri.toString(), | ||
start = 0, | ||
end = length, | ||
) | ||
}.toAnnotatedString() | ||
} |
116 changes: 116 additions & 0 deletions
116
app/src/main/java/jp/juggler/subwaytooter/util/ComposeUtils.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package jp.juggler.subwaytooter.util | ||
|
||
import android.app.Activity | ||
import android.content.Context | ||
import androidx.compose.material3.ColorScheme | ||
import androidx.compose.material3.darkColorScheme | ||
import androidx.compose.material3.lightColorScheme | ||
import androidx.compose.ui.graphics.Color | ||
import jp.juggler.subwaytooter.App1 | ||
import jp.juggler.subwaytooter.R | ||
import jp.juggler.subwaytooter.pref.PrefI | ||
import jp.juggler.util.ui.resColor | ||
|
||
class StColorScheme( | ||
val materialColorScheme: ColorScheme, | ||
val colorTextLink: Color, | ||
) | ||
|
||
fun Context.createStColorSchemeLight(): StColorScheme { | ||
val colorTextContent = Color(resColor(R.color.Light_colorTextContent)) | ||
val colorTextError = Color(resColor(R.color.Light_colorRegexFilterError)) | ||
val colorTextLink = Color(resColor(R.color.Light_colorLink)) | ||
return StColorScheme( | ||
materialColorScheme = lightColorScheme( | ||
error = colorTextError, | ||
|
||
background = Color.White, | ||
onBackground = colorTextContent, | ||
|
||
primary = colorTextLink, | ||
onPrimary = Color.White, | ||
|
||
secondary = colorTextLink, | ||
onSecondary = Color.White, | ||
|
||
surface = Color(resColor(R.color.Light_colorColumnSettingBackground)), | ||
onSurface = colorTextContent, | ||
onTertiary = colorTextContent, | ||
|
||
onSurfaceVariant = Color(resColor(R.color.Light_colorTextHint)), | ||
), | ||
colorTextLink = colorTextLink, | ||
) | ||
} | ||
|
||
fun Context.createStColorSchemeDark(): StColorScheme { | ||
val colorBackground = Color(resColor(R.color.Dark_colorBackground)) | ||
val colorTextContent = Color(resColor(R.color.Dark_colorTextContent)) | ||
val colorTextError = Color(resColor(R.color.Dark_colorRegexFilterError)) | ||
val colorTextLink = Color(resColor(R.color.Dark_colorLink)) | ||
return StColorScheme( | ||
materialColorScheme = darkColorScheme( | ||
error = colorTextError, | ||
|
||
background = colorBackground, | ||
onBackground = colorTextContent, | ||
|
||
primary = colorTextLink, | ||
onPrimary = colorTextContent, | ||
|
||
secondary = colorTextLink, | ||
onSecondary = colorTextContent, | ||
|
||
surface = Color(resColor(R.color.Dark_colorColumnSettingBackground)), | ||
onSurface = colorTextContent, | ||
onTertiary = colorTextContent, | ||
|
||
onSurfaceVariant = Color(resColor(R.color.Dark_colorTextHint)), | ||
), | ||
colorTextLink = colorTextLink, | ||
) | ||
} | ||
|
||
fun Context.createStColorSchemeMastodonDark(): StColorScheme { | ||
val colorBackground = Color(resColor(R.color.Mastodon_colorBackground)) | ||
val colorTextContent = Color(resColor(R.color.Mastodon_colorTextContent)) | ||
val colorTextError = Color(resColor(R.color.Mastodon_colorRegexFilterError)) | ||
val colorTextLink = Color(resColor(R.color.Mastodon_colorLink)) | ||
return StColorScheme( | ||
materialColorScheme = darkColorScheme( | ||
error = colorTextError, | ||
|
||
background = colorBackground, | ||
onBackground = colorTextContent, | ||
|
||
primary = Color(resColor(R.color.Mastodon_colorAppCompatAccent)), | ||
onPrimary = colorTextContent, | ||
|
||
secondary = colorTextLink, | ||
onSecondary = colorTextContent, | ||
|
||
surface = Color(resColor(R.color.Mastodon_colorColumnSettingBackground)), | ||
onSurface = colorTextContent, | ||
onTertiary = colorTextContent, | ||
|
||
onSurfaceVariant = Color(resColor(R.color.Mastodon_colorTextHint)), | ||
), | ||
colorTextLink = colorTextLink, | ||
) | ||
} | ||
|
||
fun Activity.getStColorTheme(forceDark: Boolean = false): StColorScheme { | ||
App1.prepare(applicationContext, "getStColorTheme") | ||
var nTheme = PrefI.ipUiTheme.value | ||
if (forceDark && nTheme == 0) nTheme = 1 | ||
return when (nTheme) { | ||
2 -> createStColorSchemeMastodonDark() | ||
1 -> createStColorSchemeDark() | ||
else -> createStColorSchemeLight() | ||
} | ||
} | ||
|
||
fun dummyStColorTheme() = StColorScheme( | ||
materialColorScheme = darkColorScheme(), | ||
colorTextLink = Color.Cyan, | ||
) |
Oops, something went wrong.