Уже страшновато.
This commit is contained in:
parent
3860540d9c
commit
a530e3b2fe
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
9
compose/.idea/modules.xml
generated
Normal file
9
compose/.idea/modules.xml
generated
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/pmu-demo.app.androidTest.iml" filepath="$PROJECT_DIR$/.idea/modules/app/pmu-demo.app.androidTest.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/app/pmu-demo.app.main.iml" filepath="$PROJECT_DIR$/.idea/modules/app/pmu-demo.app.main.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
8
compose/.idea/modules/app/pmu-demo.app.androidTest.iml
generated
Normal file
8
compose/.idea/modules/app/pmu-demo.app.androidTest.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$/../../../app/src/androidTest">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../../app/src/androidTest/assets" type="java-test-resource" />
|
||||
</content>
|
||||
</component>
|
||||
</module>
|
8
compose/.idea/modules/app/pmu-demo.app.iml
generated
Normal file
8
compose/.idea/modules/app/pmu-demo.app.iml
generated
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module version="4">
|
||||
<component name="ModuleClassLoaderOverlays">
|
||||
<paths>
|
||||
<option value="C:\Users\egore\AppData\Local\Temp\overlay12619265447938240062" />
|
||||
</paths>
|
||||
</component>
|
||||
</module>
|
@ -17,6 +17,7 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
@ -54,7 +55,6 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import ru.ulstu.`is`.pmu.R
|
||||
import ru.ulstu.`is`.pmu.tank.composeui.StudentView
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomDark
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomOrange
|
||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||
@ -66,6 +66,7 @@ import ru.ulstu.`is`.pmu.tank.composeui.TankList
|
||||
import ru.ulstu.`is`.pmu.tanks.composeui.Account
|
||||
import ru.ulstu.`is`.pmu.tanks.composeui.Constructor
|
||||
import ru.ulstu.`is`.pmu.tanks.composeui.Hangar
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomYellow
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@ -149,12 +150,6 @@ fun Navhost(
|
||||
) { Constructor(navController) }
|
||||
composable(Screen.Hangar.route) { Hangar(navController) }
|
||||
composable(Screen.Account.route) { Account(navController) }
|
||||
composable(
|
||||
Screen.StudentView.route,
|
||||
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
||||
) { backStackEntry ->
|
||||
backStackEntry.arguments?.let { StudentView(it.getInt("id")) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,6 +246,12 @@ fun MainNavbar(navController: NavController) {
|
||||
if(currentScreen?.route == Screen.TankList.route){
|
||||
DropDownList(navController)
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
.height(50.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = CustomOrange,
|
||||
contentColor = CustomDark),
|
||||
onClick = {
|
||||
val route = Screen.Constructor.route.replace("{id}", 0.toString())
|
||||
navController.navigate(route)
|
||||
|
@ -41,7 +41,6 @@ enum class Screen(
|
||||
companion object {
|
||||
val bottomBarItems = listOf(
|
||||
TankList,
|
||||
Constructor,
|
||||
Hangar,
|
||||
Account
|
||||
)
|
||||
|
@ -1,74 +0,0 @@
|
||||
package ru.ulstu.`is`.pmu.tank.composeui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ru.ulstu.`is`.pmu.R
|
||||
import ru.ulstu.`is`.pmu.tank.model.getStudents
|
||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun StudentView(id: Int) {
|
||||
val student = getStudents()[id]
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp)
|
||||
) {
|
||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||
value = student.firstName, onValueChange = {}, readOnly = true,
|
||||
label = {
|
||||
Text(stringResource(id = R.string.student_firstname))
|
||||
}
|
||||
)
|
||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||
value = student.lastName, onValueChange = {}, readOnly = true,
|
||||
label = {
|
||||
Text(stringResource(id = R.string.student_lastname))
|
||||
}
|
||||
)
|
||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||
value = student.group, onValueChange = {}, readOnly = true,
|
||||
label = {
|
||||
Text(stringResource(id = R.string.student_group))
|
||||
}
|
||||
)
|
||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||
value = student.phone, onValueChange = {}, readOnly = true,
|
||||
label = {
|
||||
Text(stringResource(id = R.string.student_phone))
|
||||
}
|
||||
)
|
||||
OutlinedTextField(modifier = Modifier.fillMaxWidth(),
|
||||
value = student.email, onValueChange = {}, readOnly = true,
|
||||
label = {
|
||||
Text(stringResource(id = R.string.student_email))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun StudentViewPreview() {
|
||||
PmudemoTheme {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
StudentView(id = 0)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.ulstu.`is`.pmu.tank.composeui.edit
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -7,6 +8,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.application.ui.getEmptyBitmap
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
@ -44,10 +46,11 @@ class TankEditViewModel(
|
||||
|
||||
suspend fun saveTank() {
|
||||
if (validateInput()) {
|
||||
val image: Bitmap = tankUiState.tankDetails.image!!
|
||||
if (tankUid > 0) {
|
||||
tankRepository.updateTank(tankUiState.tankDetails.toTank(tankUid))
|
||||
tankRepository.updateTank(tankUiState.tankDetails.toTank(tankUid), image = image)
|
||||
} else {
|
||||
tankRepository.insertTank(tankUiState.tankDetails.toTank())
|
||||
tankRepository.insertTank(tankUiState.tankDetails.toTank(), image = image)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -56,9 +59,9 @@ class TankEditViewModel(
|
||||
return with(uiState) {
|
||||
name.isNotBlank()
|
||||
&& price > 0
|
||||
&& image > 0
|
||||
&& levelId!! > 0
|
||||
&& nationId!! > 0
|
||||
&& image != null
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,26 +74,28 @@ data class TankUiState(
|
||||
data class TankDetails(
|
||||
val name: String = "",
|
||||
val price: Int = 0,
|
||||
val image: Int = 0,
|
||||
val levelId: Long? = 0,
|
||||
val nationId: Long? = 0,
|
||||
val miniature: Bitmap = getEmptyBitmap(),
|
||||
val image: Bitmap? = null,
|
||||
val imageId: Long = -1
|
||||
)
|
||||
|
||||
fun TankDetails.toTank(uid: Long = 0): Tank = Tank(
|
||||
tankId = uid,
|
||||
name = name,
|
||||
price = price,
|
||||
image = image,
|
||||
levelId = levelId,
|
||||
nationId = nationId
|
||||
nationId = nationId,
|
||||
imageId = imageId
|
||||
)
|
||||
|
||||
fun Tank.toDetails(): TankDetails = TankDetails(
|
||||
name = name,
|
||||
price = price,
|
||||
image = image,
|
||||
levelId = levelId,
|
||||
nationId = nationId
|
||||
nationId = nationId,
|
||||
imageId = imageId
|
||||
)
|
||||
|
||||
fun Tank.toUiState(isEntryValid: Boolean = false): TankUiState = TankUiState(
|
||||
|
@ -23,8 +23,9 @@ interface TankDao {
|
||||
|
||||
//получаем все танки пользователя по его Id
|
||||
@Query(
|
||||
"SELECT t.tankId, t.name, t.price, t.image, l.level, n.nationName FROM UserTankCrossRef AS ut " +
|
||||
"SELECT t.tankId, t.name, t.price, t.image_id, l.level, n.nationName FROM UserTankCrossRef AS ut " +
|
||||
"LEFT JOIN tanks as t on ut.tankId = t.tankId " +
|
||||
"LEFT JOIN tank_images as ti on t.image_id = ti.image_id " +
|
||||
"LEFT JOIN levels as l on t.levelId = l.uid " +
|
||||
"LEFT JOIN nations as n on t.nationId = n.uid " +
|
||||
"WHERE ut.userId = :uid GROUP BY t.nationId, t.levelId ORDER BY t.nationId"
|
||||
@ -39,4 +40,7 @@ interface TankDao {
|
||||
|
||||
@Delete
|
||||
suspend fun delete(tank: Tank)
|
||||
|
||||
@Query("DELETE FROM tanks WHERE tankId = :id")
|
||||
suspend fun delete(id: Long)
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package ru.ulstu.`is`.pmu.tank.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import ru.ulstu.`is`.pmu.tank.model.TankImage
|
||||
|
||||
@Dao
|
||||
interface TankImageDao {
|
||||
@Insert
|
||||
suspend fun insert(productImage: TankImage): Long
|
||||
|
||||
@Insert
|
||||
suspend fun insertMany(productImages: List<TankImage>)
|
||||
|
||||
@Update
|
||||
suspend fun update(productImage: TankImage)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(productImage: TankImage)
|
||||
|
||||
@Query("DELETE FROM tank_images WHERE image_id = :id")
|
||||
suspend fun delete(id: Long)
|
||||
}
|
@ -25,7 +25,9 @@ class AppDataContainer(private val context: Context) : AppContainer {
|
||||
OfflineNationRepository(AppDatabase.getInstance(context).nationDao())
|
||||
}
|
||||
override val tankRepository: TankRepository by lazy {
|
||||
OfflineTankRepository(AppDatabase.getInstance(context).tankDao())
|
||||
OfflineTankRepository(
|
||||
AppDatabase.getInstance(context).tankDao(),
|
||||
tankImageDao = AppDatabase.getInstance(context).tankImageDao())
|
||||
}
|
||||
override val userRepository: UserRepository by lazy {
|
||||
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
|
||||
|
@ -12,6 +12,7 @@ import ru.ulstu.`is`.pmu.R
|
||||
import ru.ulstu.`is`.pmu.tank.dao.LevelDao
|
||||
import ru.ulstu.`is`.pmu.tank.dao.NationDao
|
||||
import ru.ulstu.`is`.pmu.tank.dao.TankDao
|
||||
import ru.ulstu.`is`.pmu.tank.dao.TankImageDao
|
||||
import ru.ulstu.`is`.pmu.tank.dao.UserDao
|
||||
import ru.ulstu.`is`.pmu.tank.model.Level
|
||||
import ru.ulstu.`is`.pmu.tank.model.Nation
|
||||
@ -27,14 +28,15 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun levelDao(): LevelDao
|
||||
abstract fun tankDao() : TankDao
|
||||
abstract fun userDao() : UserDao
|
||||
abstract fun tankImageDao() : TankImageDao
|
||||
|
||||
companion object {
|
||||
private const val DB_NAME: String = "12-db"
|
||||
private const val DB_NAME: String = "13-db"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: AppDatabase? = null
|
||||
|
||||
private suspend fun populateDatabase() {
|
||||
private suspend fun populateDatabase(context: Context) {
|
||||
INSTANCE?.let { database ->
|
||||
// Nations
|
||||
val nationDao = database.nationDao()
|
||||
@ -84,19 +86,23 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
levelDao.insert(level9)
|
||||
levelDao.insert(level10)
|
||||
|
||||
//images
|
||||
val tankImageDao = database.tankImageDao()
|
||||
tankImageDao.insertMany(PrepopulateStore.getProductImages(context))
|
||||
|
||||
//Tanks
|
||||
val tankDao = database.tankDao()
|
||||
val tank1 = Tank(20L,"МС-1", 1000, R.drawable.t_34_85, level1.uid, nation1.uid)
|
||||
val tank2 = Tank(21L, "Т-34-85", 960000, R.drawable.t_34_85, level6.uid, nation1.uid)
|
||||
val tank10 = Tank(22L, "Pershing", 1260000, R.drawable.sherman, level8.uid, nation3.uid)
|
||||
val tank6 = Tank(23L, "Ferdinand", 2500000, R.drawable.tiger_1, level8.uid, nation2.uid)
|
||||
val tank3 = Tank(24L, "ИС-2", 1230000, R.drawable.t_34_85, level7.uid, nation1.uid)
|
||||
val tank4 = Tank(25L, "ИСУ-152", 2350000, R.drawable.t_34_85, level8.uid, nation1.uid)
|
||||
val tank5 = Tank(26L, "Tiger 1", 1430000, R.drawable.tiger_1, level7.uid, nation2.uid)
|
||||
val tank7 = Tank(27L, "Tiger 2", 2500000, R.drawable.tiger_1, level8.uid, nation2.uid)
|
||||
val tank8 = Tank(28L, "Panther", 1350000, R.drawable.tiger_1, level7.uid, nation2.uid)
|
||||
val tank9 = Tank(29L, "M4A2E3", 990000, R.drawable.sherman, level6.uid, nation3.uid)
|
||||
val tank11 = Tank(30L, "Hellcat", 940000, R.drawable.sherman, level7.uid, nation3.uid)
|
||||
val tank1 = Tank(20L,"МС-1", 1000, 1L, level1.uid, nation1.uid)
|
||||
val tank2 = Tank(21L, "Т-34-85", 960000, 1L, level6.uid, nation1.uid)
|
||||
val tank10 = Tank(22L, "Pershing", 1260000,3L, level8.uid, nation3.uid)
|
||||
val tank6 = Tank(23L, "Ferdinand", 2500000, 2L, level8.uid, nation2.uid)
|
||||
val tank3 = Tank(24L, "ИС-2", 1230000, 1L, level7.uid, nation1.uid)
|
||||
val tank4 = Tank(25L, "ИСУ-152", 2350000, 1L, level8.uid, nation1.uid)
|
||||
val tank5 = Tank(26L, "Tiger 1", 1430000,2L, level7.uid, nation2.uid)
|
||||
val tank7 = Tank(27L, "Tiger 2", 2500000, 2L, level8.uid, nation2.uid)
|
||||
val tank8 = Tank(28L, "Panther", 1350000, 2L, level7.uid, nation2.uid)
|
||||
val tank9 = Tank(29L, "M4A2E3", 990000, 3L, level6.uid, nation3.uid)
|
||||
val tank11 = Tank(30L, "Hellcat", 940000, 3L, level7.uid, nation3.uid)
|
||||
|
||||
tankDao.insert(tank1)
|
||||
tankDao.insert(tank2)
|
||||
@ -135,7 +141,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
override fun onCreate(db: SupportSQLiteDatabase) {
|
||||
super.onCreate(db)
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
populateDatabase()
|
||||
populateDatabase(appContext)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -0,0 +1,44 @@
|
||||
package ru.ulstu.`is`.pmu.tank.database
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.room.TypeConverter
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.Date
|
||||
|
||||
class Converters {
|
||||
|
||||
/*
|
||||
@TypeConverter
|
||||
fun toUserRole(value: Int): UserRole {
|
||||
return enumValues<UserRole>()[value]
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromUserRole(value: UserRole): Int {
|
||||
return value.ordinal
|
||||
}
|
||||
*/
|
||||
|
||||
@TypeConverter
|
||||
fun toDate(value: Long?): Date? {
|
||||
return value?.let { Date(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromDate(date: Date?): Long? {
|
||||
return date?.time
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun fromBitmap(bitmap: Bitmap): ByteArray {
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 1, outputStream)
|
||||
return outputStream.toByteArray()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun toBitmap(byteArray: ByteArray): Bitmap {
|
||||
return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package ru.ulstu.`is`.pmu.tank.database
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import com.application.ui.miniatureBound
|
||||
import com.application.ui.resize
|
||||
import com.application.ui.tankImageBound
|
||||
import ru.ulstu.`is`.pmu.tank.model.TankImage
|
||||
|
||||
class PrepopulateStore {
|
||||
companion object {
|
||||
fun getProductImages(context: Context): List<TankImage> {
|
||||
return listOf(
|
||||
TankImage(id = 1, data = getProductImage(context, 1)),
|
||||
TankImage(id = 2, data = getProductImage(context, 2)),
|
||||
TankImage(id = 3, data = getProductImage(context, 3))
|
||||
)
|
||||
}
|
||||
|
||||
private fun getProductMiniature(context: Context, imageId: Int): Bitmap {
|
||||
val inputStream = context.assets.open("${imageId}.jpg")
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
return bitmap.resize(miniatureBound)
|
||||
}
|
||||
|
||||
private fun getProductImage(context: Context, imageId: Int): Bitmap {
|
||||
val inputStream = context.assets.open("${imageId}.jpg")
|
||||
val bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
return bitmap.resize(tankImageBound)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,22 @@ package ru.ulstu.`is`.pmu.tank.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import ru.ulstu.`is`.pmu.R
|
||||
|
||||
@Entity(
|
||||
tableName = "tanks"
|
||||
tableName = "tanks",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = TankImage::class,
|
||||
parentColumns = ["image_id"],
|
||||
childColumns = ["image_id"],
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
onUpdate = ForeignKey.RESTRICT
|
||||
)
|
||||
]
|
||||
)
|
||||
data class Tank(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ -16,21 +26,21 @@ data class Tank(
|
||||
val name: String,
|
||||
@ColumnInfo(name = "price")
|
||||
val price: Int,
|
||||
@ColumnInfo(name="image")
|
||||
val image: Int,
|
||||
@ColumnInfo(name = "image_id", index = true)
|
||||
val imageId: Long,
|
||||
@ColumnInfo(name = "levelId", index = true)
|
||||
val levelId: Long?,
|
||||
@ColumnInfo(name = "nationId", index = true)
|
||||
val nationId: Long?
|
||||
val nationId: Long?,
|
||||
) {
|
||||
@Ignore
|
||||
constructor(
|
||||
name: String,
|
||||
price: Int,
|
||||
image: Int,
|
||||
imageId: Long,
|
||||
level: Level,
|
||||
nation: Nation
|
||||
) : this(null, name, price, image, level.uid, nation.uid)
|
||||
) : this(null, name, price, imageId, level.uid, nation.uid)
|
||||
|
||||
companion object {
|
||||
fun getTank(index: Long = 0): Tank {
|
||||
@ -38,7 +48,7 @@ data class Tank(
|
||||
index,
|
||||
"Первый танк",
|
||||
100000,
|
||||
R.drawable.t_34_85,
|
||||
1L,
|
||||
1,
|
||||
1
|
||||
)
|
||||
|
@ -0,0 +1,36 @@
|
||||
package ru.ulstu.`is`.pmu.tank.model
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "tank_images")
|
||||
data class TankImage(
|
||||
@ColumnInfo(name = "image_id")
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Long?,
|
||||
val data: Bitmap
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (javaClass != other?.javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as TankImage
|
||||
|
||||
if (id != other.id) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return (id ?: -1) as Int
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.ulstu.`is`.pmu.tank.model
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Entity
|
||||
@ -13,7 +14,7 @@ data class TankWithNationAndLevel (
|
||||
@ColumnInfo(name = "price")
|
||||
val price: Int,
|
||||
@ColumnInfo(name="image")
|
||||
val image: Int,
|
||||
val image: Bitmap,
|
||||
val level: Int,
|
||||
val nationName: String
|
||||
) {
|
||||
|
@ -1,20 +1,49 @@
|
||||
package ru.ulstu.`is`.pmu.tank.repository
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import ru.ulstu.`is`.pmu.tank.dao.TankDao
|
||||
import ru.ulstu.`is`.pmu.tank.dao.TankImageDao
|
||||
import ru.ulstu.`is`.pmu.tank.model.Tank
|
||||
import ru.ulstu.`is`.pmu.tank.model.TankImage
|
||||
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
|
||||
|
||||
class OfflineTankRepository(private val tankDao: TankDao) : TankRepository {
|
||||
class OfflineTankRepository(
|
||||
private val tankDao: TankDao,
|
||||
private val tankImageDao: TankImageDao
|
||||
) : TankRepository {
|
||||
override fun getAll(): Flow<List<Tank>> = tankDao.getAll()
|
||||
|
||||
override fun getTank(uid: Long): Flow<Tank?> = tankDao.getTankUid(uid)
|
||||
|
||||
override fun getUserTanks(uid: Long): Flow<List<TankWithNationAndLevel>> = tankDao.getUserTanks(uid)
|
||||
|
||||
override suspend fun insertTank(tank: Tank) = tankDao.insert(tank)
|
||||
override suspend fun insertTank(tank: Tank, image: Bitmap) {
|
||||
val imageId = tankImageDao.insert(
|
||||
TankImage(
|
||||
id = null,
|
||||
data = image
|
||||
)
|
||||
)
|
||||
|
||||
override suspend fun updateTank(tank: Tank) = tankDao.update(tank)
|
||||
tankDao.insert(tank.copy(imageId = imageId))
|
||||
}
|
||||
|
||||
override suspend fun updateTank(tank: Tank, image: Bitmap) {
|
||||
val imageId = tankImageDao.insert(
|
||||
TankImage(
|
||||
id = null,
|
||||
data = image
|
||||
)
|
||||
)
|
||||
|
||||
tankDao.update(tank.copy(imageId = imageId))
|
||||
tankImageDao.delete(tank.imageId)
|
||||
}
|
||||
|
||||
override suspend fun deleteTank(tank: Tank) = tankDao.delete(tank)
|
||||
|
||||
override suspend fun delete(tankId: Long) {
|
||||
tankDao.delete(tankId)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.ulstu.`is`.pmu.tank.repository
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import ru.ulstu.`is`.pmu.tank.model.Tank
|
||||
import ru.ulstu.`is`.pmu.tank.model.TankWithNationAndLevel
|
||||
@ -8,7 +9,8 @@ interface TankRepository {
|
||||
fun getAll(): Flow<List<Tank>>
|
||||
fun getTank(uid: Long): Flow<Tank?>
|
||||
fun getUserTanks(uid: Long): Flow<List<TankWithNationAndLevel>>
|
||||
suspend fun insertTank(tank: Tank)
|
||||
suspend fun updateTank(tank: Tank)
|
||||
suspend fun insertTank(tank: Tank, image: Bitmap)
|
||||
suspend fun updateTank(tank: Tank, image: Bitmap)
|
||||
suspend fun deleteTank(tank: Tank)
|
||||
suspend fun delete(tankId: Long)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -39,6 +40,9 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.application.ui.miniatureBound
|
||||
import com.application.ui.resize
|
||||
import com.application.ui.tankImageBound
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.ulstu.`is`.pmu.R
|
||||
import ru.ulstu.`is`.pmu.tank.composeui.edit.LevelDropDownViewModel
|
||||
@ -54,6 +58,7 @@ import ru.ulstu.`is`.pmu.tank.composeui.edit.toUiState
|
||||
import ru.ulstu.`is`.pmu.tank.model.Level
|
||||
import ru.ulstu.`is`.pmu.tank.model.Nation
|
||||
import ru.ulstu.`is`.pmu.tank.model.Tank
|
||||
import ru.ulstu.`is`.pmu.tanks.composeui.image.CuteImageUploader
|
||||
import ru.ulstu.`is`.pmu.ui.AppViewModelProvider
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomDark
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomOrange
|
||||
@ -73,6 +78,7 @@ fun Constructor(
|
||||
nationDropDownViewModel.setCurrentNation(tankEditViewModel.tankUiState.tankDetails.nationId ?: 1)
|
||||
|
||||
Constructor(
|
||||
tankViewModel = tankEditViewModel,
|
||||
tankUiState = tankEditViewModel.tankUiState,
|
||||
levelUiState = levelDropDownViewModel.levelUiState,
|
||||
levelsListUiState = levelDropDownViewModel.levelsListUiState,
|
||||
@ -192,6 +198,7 @@ private fun NationDropDown(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun Constructor(
|
||||
tankViewModel: TankEditViewModel,
|
||||
tankUiState: TankUiState,
|
||||
levelUiState: LevelUiState,
|
||||
levelsListUiState: LevelsListUiState,
|
||||
@ -208,6 +215,15 @@ private fun Constructor(
|
||||
//для работы выпадающего списка наций
|
||||
var expandedNation by remember { mutableStateOf(false) }
|
||||
|
||||
fun handleImageUpload(bitmap: Bitmap) {
|
||||
tankViewModel.updateUiState(
|
||||
tankUiState.tankDetails.copy(
|
||||
image = bitmap.resize(tankImageBound),
|
||||
miniature = bitmap.resize(miniatureBound)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(35.dp),
|
||||
modifier = Modifier.padding(0.dp, 15.dp)
|
||||
@ -234,6 +250,8 @@ private fun Constructor(
|
||||
modifier = Modifier.padding(0.dp, 5.dp)
|
||||
) {
|
||||
Text(text="Название:", fontSize = 30.sp, color = Color.Black, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(25.dp))
|
||||
Text(text="Изображение:", fontSize = 30.sp, color = Color.Black, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(30.dp))
|
||||
Text(text="Уровень:", fontSize = 30.sp, color = Color.Black, fontWeight = FontWeight.Bold)
|
||||
Spacer(Modifier.height(30.dp))
|
||||
@ -250,6 +268,11 @@ private fun Constructor(
|
||||
.width(200.dp),
|
||||
)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
CuteImageUploader(
|
||||
bitmap = tankUiState.tankDetails.image,
|
||||
onResult = { bitmap: Bitmap -> handleImageUpload(bitmap) }
|
||||
)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
// Выпадающий список уровней
|
||||
LevelDropDown(
|
||||
levelUiState = levelUiState,
|
||||
@ -421,6 +444,7 @@ fun ConstructorEditPreview() {
|
||||
color = CustomDark
|
||||
) {
|
||||
Constructor(
|
||||
tankViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
tankUiState = Tank.getTank().toUiState(true),
|
||||
levelUiState = Level.DEMO_LEVEL.toUiState(),
|
||||
levelsListUiState = LevelsListUiState(listOf()),
|
||||
|
@ -0,0 +1,25 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui.image
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
|
||||
@Composable
|
||||
fun CuteImage(
|
||||
imageBitmap: ImageBitmap,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Image(
|
||||
bitmap = imageBitmap,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = modifier
|
||||
.aspectRatio(1F)
|
||||
.clip(RoundedCornerShape(Dimensions.cornerRadius))
|
||||
)
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui.image
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun CuteImageButton(
|
||||
imageBitmap: ImageBitmap,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val cornerShape = RoundedCornerShape(Dimensions.cornerRadius)
|
||||
Button(
|
||||
onClick = { onClick() },
|
||||
modifier = modifier,
|
||||
shape = cornerShape,
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color.Transparent
|
||||
)
|
||||
) {
|
||||
CuteImage(imageBitmap)
|
||||
}
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui.image
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ru.ulstu.`is`.pmu.R
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomDark
|
||||
import ru.ulstu.`is`.pmu.ui.theme.CustomOrange
|
||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||
|
||||
@Composable
|
||||
fun CuteImageUploader(
|
||||
bitmap: Bitmap?,
|
||||
onResult: (Bitmap) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val title: String = if (bitmap == null) {
|
||||
stringResource(R.string.not_uploaded)
|
||||
} else {
|
||||
stringResource(R.string.size, bitmap.width, bitmap.height)
|
||||
}
|
||||
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.GetContent(),
|
||||
onResult = { uri: Uri? ->
|
||||
uri?.let {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
val newBitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
|
||||
onResult(newBitmap)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fun handleUploadButtonClick() {
|
||||
launcher.launch("image/*")
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.height(IntrinsicSize.Min)
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(Dimensions.cornerRadius)
|
||||
)
|
||||
.weight(0.25F)
|
||||
.aspectRatio(1F)
|
||||
) {
|
||||
if (bitmap != null) {
|
||||
CuteImage(
|
||||
imageBitmap = bitmap.asImageBitmap(),
|
||||
modifier = Modifier.width(100.dp)
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_camera),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
shape = RoundedCornerShape(Dimensions.cornerRadius)
|
||||
)
|
||||
.fillMaxHeight()
|
||||
.padding(10.dp)
|
||||
.weight(0.75F)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.width(200.dp)
|
||||
.height(50.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = CustomOrange,
|
||||
contentColor = CustomDark
|
||||
),
|
||||
onClick = { handleUploadButtonClick() },
|
||||
){
|
||||
Text(text = "Загрузить изображение")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(name = "Light Mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||
@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun CuteImageLoaderPreview() {
|
||||
PmudemoTheme {
|
||||
CuteImageUploader(null) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui.image
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.gestures.detectTransformGestures
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
|
||||
@Composable
|
||||
fun CuteZoomableImage(
|
||||
imageBitmap: ImageBitmap
|
||||
) {
|
||||
val size = remember { mutableStateOf(IntSize.Zero) }
|
||||
val scale = remember { mutableFloatStateOf(1f) }
|
||||
val offsetX = remember { mutableFloatStateOf(0f) }
|
||||
val offsetY = remember { mutableFloatStateOf(0f) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RectangleShape)
|
||||
.fillMaxSize()
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures { _, pan, zoom, _ ->
|
||||
scale.floatValue = maxOf(1f, minOf(3f, scale.floatValue * zoom))
|
||||
val maxX = (size.value.width * (scale.floatValue - 1)) / 2
|
||||
val minX = -maxX
|
||||
offsetX.floatValue = maxOf(minX, minOf(maxX, offsetX.floatValue + pan.x))
|
||||
val maxY = (size.value.height * (scale.floatValue - 1)) / 2
|
||||
val minY = -maxY
|
||||
offsetY.floatValue = maxOf(minY, minOf(maxY, offsetY.floatValue + pan.y))
|
||||
}
|
||||
}.onSizeChanged {
|
||||
size.value = it
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
bitmap = imageBitmap,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.graphicsLayer(
|
||||
scaleX = scale.floatValue,
|
||||
scaleY = scale.floatValue,
|
||||
translationX = offsetX.floatValue,
|
||||
translationY = offsetY.floatValue
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package ru.ulstu.`is`.pmu.tanks.composeui.image
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
object Dimensions {
|
||||
val cornerRadius = 10.dp
|
||||
}
|
26
compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt
Normal file
26
compose/app/src/main/java/ru/ulstu/is/pmu/ui/ImageTools.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package com.application.ui
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import kotlin.math.sqrt
|
||||
|
||||
const val miniatureBound: Int = 250
|
||||
const val tankImageBound: Int = 800
|
||||
|
||||
fun getEmptyBitmap(): Bitmap {
|
||||
return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
|
||||
fun Bitmap.resize(bound: Int): Bitmap {
|
||||
val width = this.width
|
||||
val height = this.height
|
||||
val size: Int = width * height
|
||||
val newSize: Int = bound * bound
|
||||
val factor: Double = if (size > newSize) {
|
||||
sqrt(size.toDouble() / newSize)
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
val newWidth: Int = (width / factor).toInt()
|
||||
val newHeight: Int = (height / factor).toInt()
|
||||
return Bitmap.createScaledBitmap(this, newWidth, newHeight, false)
|
||||
}
|
21
compose/app/src/main/res/drawable/ic_camera.xml
Normal file
21
compose/app/src/main/res/drawable/ic_camera.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="42dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="42"
|
||||
android:viewportHeight="36">
|
||||
<path
|
||||
android:pathData="M35,34H7c-2.76,0 -5,-2.24 -5,-5V12c0,-2.76 2.24,-5 5,-5h6l1.25,-3.11C14.7,2.75 15.8,2 17.03,2h7.94c1.23,0 2.33,0.75 2.79,1.89L29,7h6c2.76,0 5,2.24 5,5v17C40,31.76 37.76,34 35,34z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:pathData="M21,21m-8,0a8,8 0,1 1,16 0a8,8 0,1 1,-16 0"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2.5"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#FFFFFF"/>
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M33,16L33,16c-1.1,0 -2,-0.9 -2,-2v0c0,-1.1 0.9,-2 2,-2h0c1.1,0 2,0.9 2,2v0C35,15.1 34.1,16 33,16z"/>
|
||||
</vector>
|
Binary file not shown.
Before Width: | Height: | Size: 1.5 MiB |
@ -31,4 +31,10 @@
|
||||
<p>Здесь могла быть Ваша реклама!</p>\n\n
|
||||
<p>Наш сайт <a href="https://ulstu.ru">ulstu.ru</a></p>
|
||||
</string>
|
||||
|
||||
<!-- IMAGE -->
|
||||
<string name="image">Image</string>
|
||||
<string name="not_uploaded">Not uploaded</string>
|
||||
<string name="size">Size: %1$dx%2$d</string>
|
||||
<string name="upload_image">Upload image</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user