(fix) ability to use system font

This commit is contained in:
2024-11-01 21:54:50 +03:00
parent 1f329e86a7
commit 97d543539d
15 changed files with 502 additions and 52 deletions
@@ -69,10 +69,11 @@ class MainActivity : AppCompatActivity() {
val systemBarStyle = when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> SystemBarStyle.light(
Color.Transparent.toArgb(),
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
0
// MaterialTheme.colorScheme.background.toArgb()
else Color.Transparent.toArgb()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Color.Gray.copy(alpha = 0.85f).toArgb()
} else {
Color.Transparent.toArgb()
}
)
Configuration.UI_MODE_NIGHT_YES -> SystemBarStyle.dark(Color.Transparent.toArgb())
@@ -5,7 +5,7 @@ object VkConstants {
const val GROUP_FIELDS = "description,members_count,counters,status,verified"
const val USER_FIELDS =
"photo_50,photo_100,photo_200,status,screen_name,online,online_mobile,last_seen,verified,sex,online_info,bdate"
"photo_50,photo_100,photo_200,photo_400_orig,status,screen_name,online,online_mobile,last_seen,verified,sex,online_info,bdate"
const val ALL_FIELDS =
"$USER_FIELDS,$GROUP_FIELDS"
@@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "25131db1b99fe8c77b721add776790a3",
"identityHash": "2ab59cf15566f7943cd6d7fdff359382",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `isOnline` INTEGER NOT NULL, `isOnlineMobile` INTEGER NOT NULL, `onlineAppId` INTEGER, `lastSeen` INTEGER, `lastSeenStatus` TEXT, `birthday` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, PRIMARY KEY(`id`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `isOnline` INTEGER NOT NULL, `isOnlineMobile` INTEGER NOT NULL, `onlineAppId` INTEGER, `lastSeen` INTEGER, `lastSeenStatus` TEXT, `birthday` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `photo400Orig` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
@@ -79,6 +79,12 @@
"columnName": "photo200",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo400Orig",
"columnName": "photo400Orig",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
@@ -424,7 +430,7 @@
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '25131db1b99fe8c77b721add776790a3')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2ab59cf15566f7943cd6d7fdff359382')"
]
}
}
@@ -0,0 +1,436 @@
{
"formatVersion": 1,
"database": {
"version": 7,
"identityHash": "2ab59cf15566f7943cd6d7fdff359382",
"entities": [
{
"tableName": "users",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `firstName` TEXT NOT NULL, `lastName` TEXT NOT NULL, `isOnline` INTEGER NOT NULL, `isOnlineMobile` INTEGER NOT NULL, `onlineAppId` INTEGER, `lastSeen` INTEGER, `lastSeenStatus` TEXT, `birthday` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `photo400Orig` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "firstName",
"columnName": "firstName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastName",
"columnName": "lastName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "isOnline",
"columnName": "isOnline",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isOnlineMobile",
"columnName": "isOnlineMobile",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "onlineAppId",
"columnName": "onlineAppId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeen",
"columnName": "lastSeen",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "lastSeenStatus",
"columnName": "lastSeenStatus",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "birthday",
"columnName": "birthday",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo50",
"columnName": "photo50",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo100",
"columnName": "photo100",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo200",
"columnName": "photo200",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo400Orig",
"columnName": "photo400Orig",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `screenName` TEXT NOT NULL, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `membersCount` INTEGER, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "screenName",
"columnName": "screenName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "photo50",
"columnName": "photo50",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo100",
"columnName": "photo100",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo200",
"columnName": "photo200",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "membersCount",
"columnName": "membersCount",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "messages",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `conversationMessageId` INTEGER NOT NULL, `text` TEXT, `isOut` INTEGER NOT NULL, `peerId` INTEGER NOT NULL, `fromId` INTEGER NOT NULL, `date` INTEGER NOT NULL, `randomId` INTEGER NOT NULL, `action` TEXT, `actionMemberId` INTEGER, `actionText` TEXT, `actionConversationMessageId` INTEGER, `actionMessage` TEXT, `updateTime` INTEGER, `important` INTEGER NOT NULL, `forwardIds` TEXT, `attachments` TEXT, `replyMessageId` INTEGER, `geoType` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "conversationMessageId",
"columnName": "conversationMessageId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "text",
"columnName": "text",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isOut",
"columnName": "isOut",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "peerId",
"columnName": "peerId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "fromId",
"columnName": "fromId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "randomId",
"columnName": "randomId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "action",
"columnName": "action",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "actionMemberId",
"columnName": "actionMemberId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "actionText",
"columnName": "actionText",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "actionConversationMessageId",
"columnName": "actionConversationMessageId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "actionMessage",
"columnName": "actionMessage",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "updateTime",
"columnName": "updateTime",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "important",
"columnName": "important",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "forwardIds",
"columnName": "forwardIds",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "attachments",
"columnName": "attachments",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "replyMessageId",
"columnName": "replyMessageId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "geoType",
"columnName": "geoType",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "conversations",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `localId` INTEGER NOT NULL, `ownerId` INTEGER, `title` TEXT, `photo50` TEXT, `photo100` TEXT, `photo200` TEXT, `isPhantom` INTEGER NOT NULL, `lastConversationMessageId` INTEGER NOT NULL, `inReadCmId` INTEGER NOT NULL, `outReadCmId` INTEGER NOT NULL, `inRead` INTEGER NOT NULL, `outRead` INTEGER NOT NULL, `lastMessageId` INTEGER, `unreadCount` INTEGER NOT NULL, `membersCount` INTEGER, `canChangePin` INTEGER NOT NULL, `canChangeInfo` INTEGER NOT NULL, `majorId` INTEGER NOT NULL, `minorId` INTEGER NOT NULL, `pinnedMessageId` INTEGER, `peerType` TEXT NOT NULL, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "localId",
"columnName": "localId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ownerId",
"columnName": "ownerId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "title",
"columnName": "title",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo50",
"columnName": "photo50",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo100",
"columnName": "photo100",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "photo200",
"columnName": "photo200",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "isPhantom",
"columnName": "isPhantom",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastConversationMessageId",
"columnName": "lastConversationMessageId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "inReadCmId",
"columnName": "inReadCmId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "outReadCmId",
"columnName": "outReadCmId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "inRead",
"columnName": "inRead",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "outRead",
"columnName": "outRead",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "lastMessageId",
"columnName": "lastMessageId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "unreadCount",
"columnName": "unreadCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "membersCount",
"columnName": "membersCount",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "canChangePin",
"columnName": "canChangePin",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "canChangeInfo",
"columnName": "canChangeInfo",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "majorId",
"columnName": "majorId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "minorId",
"columnName": "minorId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "pinnedMessageId",
"columnName": "pinnedMessageId",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "peerType",
"columnName": "peerType",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '2ab59cf15566f7943cd6d7fdff359382')"
]
}
}
@@ -21,7 +21,7 @@ import dev.meloda.fast.model.database.VkUserEntity
VkConversationEntity::class
],
version = 6
version = 7
)
@TypeConverters(Converters::class)
abstract class CacheDatabase : RoomDatabase() {
@@ -1,9 +1,9 @@
package dev.meloda.fast.model.api.data
import dev.meloda.fast.model.api.domain.OnlineStatus
import dev.meloda.fast.model.api.domain.VkUser
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import dev.meloda.fast.model.api.domain.OnlineStatus
import dev.meloda.fast.model.api.domain.VkUser
@JsonClass(generateAdapter = true)
data class VkUserData(
@@ -17,6 +17,7 @@ data class VkUserData(
@Json(name = "photo_50") val photo50: String?,
@Json(name = "photo_100") val photo100: String?,
@Json(name = "photo_200") val photo200: String?,
@Json(name = "photo_400_orig") val photo400Orig: String?,
@Json(name = "online") val online: Int?,
@Json(name = "online_info") val onlineInfo: OnlineInfo?,
@Json(name = "screen_name") val screenName: String,
@@ -52,6 +53,7 @@ data class VkUserData(
photo50 = photo50,
photo100 = photo100,
photo200 = photo200,
photo400Orig = photo400Orig,
lastSeen = onlineInfo?.lastSeen,
lastSeenStatus = onlineInfo?.status,
birthday = birthday
@@ -10,6 +10,7 @@ data class VkUser(
val photo50: String?,
val photo100: String?,
val photo200: String?,
val photo400Orig: String?,
val lastSeen: Int?,
val lastSeenStatus: String?,
val birthday: String?,
@@ -40,5 +41,6 @@ fun VkUser.asEntity(): VkUserEntity = VkUserEntity(
birthday = birthday,
photo50 = photo50,
photo100 = photo100,
photo200 = photo200
photo200 = photo200,
photo400Orig = photo400Orig
)
@@ -18,7 +18,8 @@ data class VkUserEntity(
val birthday: String?,
val photo50: String?,
val photo100: String?,
val photo200: String?
val photo200: String?,
val photo400Orig: String?
)
fun VkUserEntity.asExternalModel(): VkUser = VkUser(
@@ -33,6 +34,7 @@ fun VkUserEntity.asExternalModel(): VkUser = VkUser(
photo50 = photo50,
photo100 = photo100,
photo200 = photo200,
photo400Orig = photo400Orig,
lastSeen = lastSeen,
lastSeenStatus = lastSeenStatus,
birthday = birthday
@@ -177,9 +177,15 @@ fun AppTheme(
headlineLarge = MaterialTheme.typography.headlineLarge.copy(fontFamily = googleSansFonts),
headlineMedium = MaterialTheme.typography.headlineMedium.copy(fontFamily = googleSansFonts),
headlineSmall = MaterialTheme.typography.headlineSmall.copy(fontFamily = googleSansFonts),
titleLarge = MaterialTheme.typography.titleLarge.copy(fontFamily = robotoFonts),
titleMedium = MaterialTheme.typography.titleMedium.copy(fontFamily = robotoFonts),
titleSmall = MaterialTheme.typography.titleSmall.copy(fontFamily = robotoFonts),
bodyLarge = MaterialTheme.typography.bodyLarge.copy(fontFamily = robotoFonts),
bodyMedium = MaterialTheme.typography.bodyMedium.copy(fontFamily = robotoFonts),
bodySmall = MaterialTheme.typography.bodySmall.copy(fontFamily = robotoFonts),
labelLarge = MaterialTheme.typography.labelLarge.copy(fontFamily = robotoFonts),
labelMedium = MaterialTheme.typography.labelMedium.copy(fontFamily = robotoFonts),
labelSmall = MaterialTheme.typography.labelSmall.copy(fontFamily = robotoFonts)
)
}
@@ -1,5 +1,6 @@
package dev.meloda.fast.chatmaterials.presentation
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
@@ -27,6 +28,7 @@ fun ChatMaterialItem(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
.clickable(onClick = onClick)
)
}
@@ -7,5 +7,6 @@ data class UiFriend(
val userId: Int,
val avatar: UiImage?,
val title: String,
val onlineStatus: OnlineStatus
val onlineStatus: OnlineStatus,
val photo400Orig: UiImage?
)
@@ -58,7 +58,11 @@ fun FriendItem(
modifier = Modifier
.fillMaxSize()
.clip(CircleShape)
.clickable { onPhotoClicked(friendAvatar) },
.clickable {
friend.photo400Orig
?.extractUrl()
?.let(onPhotoClicked)
},
placeholder = painterResource(id = R.drawable.ic_account_circle_cut)
)
}
@@ -15,5 +15,6 @@ fun VkUser.asPresentation(
} else {
fullName
},
onlineStatus = onlineStatus
onlineStatus = onlineStatus,
photo400Orig = photo400Orig?.let(UiImage::Url)
)
@@ -1,7 +1,5 @@
package dev.meloda.fast.photoviewer.presentation
import android.util.Log
import android.widget.Toast
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.Orientation
@@ -9,7 +7,6 @@ import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
@@ -17,15 +14,11 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@@ -36,15 +29,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil.compose.AsyncImage
import com.conena.nanokt.android.content.pxToDp
@@ -54,10 +44,7 @@ import dev.meloda.fast.photoviewer.PhotoViewViewModelImpl
import dev.meloda.fast.photoviewer.model.PhotoViewScreenState
import dev.meloda.fast.ui.util.getImage
import org.koin.androidx.compose.koinViewModel
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.cos
import kotlin.math.sin
import dev.meloda.fast.ui.R as UiR
@Composable
@@ -136,29 +123,29 @@ fun TopBar(
}
},
actions = {
IconButton(
onClick = { dropdownMenuShown = true }
) {
Icon(
imageVector = Icons.Rounded.MoreVert,
contentDescription = "Options"
)
}
// IconButton(
// onClick = { dropdownMenuShown = true }
// ) {
// Icon(
// imageVector = Icons.Rounded.MoreVert,
// contentDescription = "Options"
// )
// }
DropdownMenu(
modifier = Modifier.defaultMinSize(minWidth = 140.dp),
expanded = dropdownMenuShown,
onDismissRequest = { dropdownMenuShown = false },
offset = DpOffset(x = (10).dp, y = (-60).dp)
) {
DropdownMenuItem(
onClick = {
Toast.makeText(context, "Save clicked", Toast.LENGTH_SHORT).show()
dropdownMenuShown = false
},
text = { Text(text = "Save") },
)
}
// DropdownMenu(
// modifier = Modifier.defaultMinSize(minWidth = 140.dp),
// expanded = dropdownMenuShown,
// onDismissRequest = { dropdownMenuShown = false },
// offset = DpOffset(x = (10).dp, y = (-60).dp)
// ) {
// DropdownMenuItem(
// onClick = {
// Toast.makeText(context, "Save clicked", Toast.LENGTH_SHORT).show()
// dropdownMenuShown = false
// },
// text = { Text(text = "Save") },
// )
// }
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent)
)
@@ -399,7 +399,7 @@ class SettingsViewModelImpl(
key = SettingsKeys.KEY_APPEARANCE_USE_BLUR,
defaultValue = SettingsKeys.DEFAULT_VALUE_KEY_APPEARANCE_USE_BLUR,
title = UiText.Simple("[WIP] Use blur"),
text = UiText.Simple("Adds blur wherever possible\nOn android 11 and older will have transparency instead of blurring"),
text = UiText.Simple("Adds blur wherever possible\nWorks on android 12 and newer"),
)
val debugShowEmojiButton = SettingsItem.Switch(
key = SettingsKeys.KEY_SHOW_EMOJI_BUTTON,