I wish to use an boolean array (with specific size) in Room DB with constructor.
How I initialize it and how to use it?
#Entity
data class RoleEntity(
#PrimaryKey(autoGenerate = true) var id: Long? = null,
#ColumnInfo(name = "title") var title: String?,
#ColumnInfo(name = "startTime") var startTime: Long?,
#ColumnInfo(name = "endTime") var endTime: Long?,
#ColumnInfo(name = "recurrence") var recurrence: BooleanArray,
#ColumnInfo(name = "priority") var priority: Int?
): Serializable {
constructor() : this(title = "", startTime = 0, endTime = 0, recurrence = ??, priority = 0)
}
For such attributes, it is necessary to use converters, and in your case, I will give my example, you will have it there by analogy.#Convert(converter = ListJsonConverter::class) var list: List<Map<String, Any>>,
Also the converter itself
#Converter(autoApply = true)
class ListJsonConverter : AttributeConverter<List<Map<String, Any>>, String> {
private val objectMapper = ObjectMapper()
override fun convertToDatabaseColumn(attribute: List<Map<String, Any>>): String {
return objectMapper.writeValueAsString(attribute)
}
override fun convertToEntityAttribute(dbData: String?): List<Map<String, Any>> {
try {
return dbData?.let { objectMapper.readValue<List<Map<String, Any>>>(it) } ?: emptyList()
} catch (e: Exception){
return emptyList()
}
}
}
Here it is well written about them.
Related
i am trying to create a video player app using kotlin , First of all I got the videos files by using MediaStore , than store this in ArrayList so far it's been perfect but When I made a folder list of videos, I tried to find out the size of those folders and how many video files there are in those folders, but I failed. like this (Image)
Check this image for more clear
This is my data class code (VideoItem.Kt)
import android.net.Uri
data class VideoItem(
val id: String,
val title: String,
val duration: Long = 0,
val folderName: String,
val size: String,
val path: String,
val dateAdded: String,
val artUri: Uri
)
data class FolderItem(
val id: String,
val folderName: String,
val folderSize: Long
)
This is my MainActivity Code To get Allvideos Using MediaStore
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
companion object {
lateinit var videoList: ArrayList<VideoItem>
lateinit var folderList: ArrayList<FolderItem>
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
folderList = ArrayList()
videoList = getAllVideos()
setFragment(VideoviewFragment())
}
private fun setFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.FrameLayout, fragment)
transaction.disallowAddToBackStack()
transaction.commit()
}
#SuppressLint("Recycle", "Range")
private fun getAllVideos(): ArrayList<VideoItem> {
val tempList = ArrayList<VideoItem>()
val tempFolderList = ArrayList<String>()
val projection = arrayOf(
MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media._ID,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.BUCKET_ID
)
val cursor = this.contentResolver.query(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
projection,
null,
null,
MediaStore.Video.Media.DATE_ADDED + " DESC"
)
if (cursor != null)
if (cursor.moveToNext())
do {
val titleC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE))
val idC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media._ID))
val folderNameC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME))
val folderIdC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID))
val sizeC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
val pathC = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA))
val dateAddedC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED))
val durationC =
cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))
.toLong()
try {
val file = File(pathC)
val artUriC = Uri.fromFile(file)
val video = VideoItem(
title = titleC,
id = idC,
folderName = folderNameC,
size = sizeC,
path = pathC,
duration = durationC,
dateAdded = dateAddedC,
artUri = artUriC
)
if (file.exists()) tempList.add(video)
//for adding Folders
if (!tempFolderList.contains(folderNameC)) {
tempFolderList.add(folderNameC)
val folderSizeC = getFileLength(pathC)
folderList.add(
FolderItem(
id = folderIdC,
folderName = folderNameC,
folderSize = folderSizeC
)
)
}
} catch (_: Exception) {
}
} while (cursor.moveToNext())
cursor?.close()
return tempList
}
private fun getFileLength(path: String?): Long {
return if (!isExistFile(path)) 0 else File(path.toString()).length()
}
private fun isExistFile(path: String?): Boolean {
val file = File(path.toString())
return file.exists()
}
}
This is my RecyclerviwAdapter Code(FolderAdapter.kt)
class FoldersAdapter(private val context: Context, private var foldersList: ArrayList<FolderItem>) :
RecyclerView.Adapter<FoldersAdapter.MyHolder>() {
class MyHolder(binding: FolderItemBinding) : RecyclerView.ViewHolder(binding.root) {
val folderName = binding.folderName
val noofFiles = binding.nooffiles
val folderSize = binding.foldersize
val root = binding.root
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
return MyHolder(FolderItemBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
holder.folderName.text = foldersList[position].folderName
val size: Long = foldersList[position].folderSize
holder.folderSize.text = android.text.format.Formatter.formatFileSize(context, (size))
holder.root.setOnClickListener {
val intent = Intent(context, FolderVideosActivity::class.java)
intent.putExtra("position", position)
ContextCompat.startActivity(context, intent, null)
}
}
override fun getItemCount(): Int {
return foldersList.size
}
}
This is my all codes now please check out all code and suggest the best.
Thank you
Use this function for size
private fun getFolderSize(f: File): Long {
var size: Long = 0
if (f.isDirectory) {
for (file in f.listFiles()!!) {
size += getFolderSize(file)
}
} else {
size = f.length()
}
return size
}
And Count number of files Use this
val length = File("/path/to/folder").listFiles()?.size
I don't know why get this exception, I generated the data classes from json with a converter and all of the fields are nullable, also added JsonClass annotation and I don't use upper case characters.
java.lang.RuntimeException: Failed to find the generated JsonAdapter class for class com.example.movieapplication.data.network.models.movies.MoviesResponse
Unable to create converter for class com.example.movieapplication.data.network.models.movies.MoviesResponse
for method MoviesApi.getMovies
Api class:
interface MoviesApi {
companion object {
const val BASE_URL = "https://api.themoviedb.org/"
}
#GET("3/movie/popular/")
fun getMovies(#Query("api_key") apyKey: String = Credentials.API_KEY): Call<MoviesResponse>
#GET("3/movie/")
fun getMovie(#Path("id") id: Long?, #Query("api_key") apyKey: String = Credentials.API_KEY): Call<MovieResponse>
}
DataSouce class:
#Singleton
class NetworkMoviesDataSource #Inject constructor() {
private val moviesApi: MoviesApi
init {
val retrofit = Retrofit.Builder()
.baseUrl(MoviesApi.BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.build()
this.moviesApi = retrofit.create(MoviesApi::class.java)
}
fun getMovies(): Result<List<UiMovie>, String> {
return try {
val response = moviesApi.getMovies().execute()
if (response.isSuccessful) {
val movies = response.body()?.results?.map { it.toUiMovie() } ?: listOf()
ResultSuccess(movies)
} else {
ResultFailure(response.errorBody().toString())
}
} catch (e: Exception) {
ResultFailure(e.message.toString())
}
}
private fun NetworkMoviesMovie.toUiMovie(): UiMovie {
val response = moviesApi.getMovie(id).execute()
val movie = if (response.isSuccessful) response.body() else null
return UiMovie(
id = id,
title = title,
budget = movie?.budget
)
}
}
Data Classes:
#JsonClass(generateAdapter = true)
data class MoviesResponse(
val page: Long? = null,
val results: List<NetworkMoviesMovie> = listOf(),
#Json(name = "total_pages") val totalPages: Long? = null,
#Json(name = "total_results") val totalResults: Long? = null
)
#JsonClass(generateAdapter = true)
data class NetworkMoviesMovie(
val adult: Boolean? = null,
#Json(name = "backdrop_path") val backdropPath: String? = null,
#Json(name = "genre_ids") val genreIDS: List<Long>? = null,
val id: Long? = null,
#Json(name = "original_language") val originalLanguage: String? = null,
#Json(name = "original_title") val originalTitle: String? = null,
val overview: String? = null,
val popularity: Double? = null,
#Json(name = "poster_path") val posterPath: String? = null,
#Json(name = "release_date") val releaseDate: String? = null,
val title: String? = null,
val video: Boolean? = null,
#Json(name = "vote_average") val voteAverage: Double? = null,
#Json(name = "vote_count") val voteCount: Long? = null
)
I'm trying to map the video_files inside the videos model to video_files inside the videos network model
The error that i'm receiving is a Type Mismatch
Required:List<com.im.xshots.model.videos.Videos.VideoFile>
Found:List<com.im.xshots.data.remote.videos.VideosNetworkEntity.VideoFile>?
Videos Model
data class Videos(
val avg_color: Any? = null,
val duration: Int? = null,
val full_res: Any? = null,
val height: Int? = null,
val id: Int? = null,
val image: String? = null,
val tags: List<Any>? = null,
val url: String? = null,
val width: Int? = null,
val video_files: List<VideoFile>,
val userId: Int? = null,
val name: String? = null,
val userUrlrl: String? = null,
val videoId: Int? = null,
val nr: Int? = null,
val picture: String? = null,
) : Serializable
Network model
data class VideosNetworkEntity(
#Json(name = "avg_color")
val avg_color: Any? = null,
#Json(name = "duration")
val duration: Int? = null,
#Json(name = "full_res")
val full_res: Any? = null,
#Json(name = "height")
val height: Int? = null,
#Json(name = "id")
val id: Int? = null,
#Json(name = "image")
val image: String? = null,
#Json(name = "tags")
val tags: List<Any>? = null,
#Json(name = "url")
val url: String? = null,
#Json(name = "user")
val user: User? = null,
#Json(name = "video_files")
val video_files: List<VideoFile>? = null,
#Json(name = "video_pictures")
val video_pictures: List<VideoPicture>? = null,
#Json(name = "width")
val width: Int? = null,
)
EntityMapper
class VideosResponseMapper : EntityMapper<VideosNetworkEntity, Videos> {
override fun fromEntity(entity: VideosNetworkEntity): Videos {
return Videos(
avg_color = entity.avg_color,
duration= entity.duration,
full_res= entity.full_res,
height= entity.height,
id= entity.id,
image= entity.image,
tags = entity.tags,
url = entity.url,
width = entity.width,
video_files = entity.video_files //
,
userId= entity.id,
name= entity.url,
userUrlrl= entity.url,
videoId= entity.id,
nr= entity.id,
picture= entity.url,
)
}
override fun toEntity(model: Videos): VideosNetworkEntity {
TODO("Not yet implemented")
}
fun mapfromEntityList(entityList: List<VideosNetworkEntity>): List<Videos>{
return entityList.map { fromEntity(it) }
}
}
I mapped it like I did with VideosNetworkEntity and Videos, if there's a better/efficient way please let me know. Thanks
class VideosResponseMapper : EntityMapper<VideosNetworkEntity, Videos> {
override fun fromEntity(entity: VideosNetworkEntity): Videos {
return Videos(
avg_color = entity.avg_color,
duration = entity.duration,
full_res = entity.full_res,
height = entity.height,
id = entity.id,
image = entity.image,
tags = entity.tags,
url = entity.url,
width = entity.width,
video_files = mapFromVideoFileList(entity.video_files!!),
video_pictures = mapFromVideoPicturesList(entity.video_pictures!!),
userId = entity.user?.userId,
name = entity.user?.name,
userUrlrl = entity.user?.userUrl,
)
}
override fun toEntity(model: Videos): VideosNetworkEntity {
TODO("Not yet implemented")
}
fun mapfromEntityList(entityList: List<VideosNetworkEntity>): List<Videos> {
return entityList.map { fromEntity(it) }
}
fun mapFromVideoFile(video: VideosNetworkEntity.VideoFile): Videos.VideoFile {
return Videos.VideoFile(
file_type = video.file_type,
videoFilEhHight = video.videoFileHeight,
videoFileId = video.videoFileId,
link = video.link,
quality = video.quality,
videoFileWidth = video.videoFileWidth,
)
}
fun mapFromVideoFileList(video: List<VideosNetworkEntity.VideoFile>): List<Videos.VideoFile> {
return video.map { mapFromVideoFile(it) }
}
fun mapFromVideoPictures(video: VideosNetworkEntity.VideoPicture): Videos.VideoPicture {
return Videos.VideoPicture(
videoId = video.videoId,
nr = video.nr,
picture = video.picture,
)
}
fun mapFromVideoPicturesList(video: List<VideosNetworkEntity.VideoPicture>): List<Videos.VideoPicture> {
return video.map { mapFromVideoPictures(it) }
}
}
How can I stop creating a quoteData variable every time in my view model?
This solution is working, but the amount of repetitive code is terrifying
class QuoteDetailsViewModel(application: Application) : AndroidViewModel(application) {
private val quoteRepository: QuoteRepository = QuoteRepository(application)
private val quoteId = MutableLiveData<String>("")
val quoteAuthor = MutableLiveData<String>("")
val quoteContent = MutableLiveData<String>("")
val quoteDescription = MutableLiveData<String>("")
val quoteLastUpdate = MutableLiveData<String>("")
val onSaveClickEvent = MutableLiveData<ViewModelEvent<Unit>>()
val onCancelClickEvent = MutableLiveData<ViewModelEvent<Unit>>()
val onDeleteClickEvent = MutableLiveData<ViewModelEvent<String?>>()
var isNewQuote = false
fun setInitialData(arguments: Bundle?) {
if (arguments != null) {
val quoteData = arguments.getSerializable(KEY_DATA) as Quote
isNewQuote = false
quoteData.let { quote ->
quoteId.postValue(quote.id)
quoteAuthor.postValue(quote.author)
quoteContent.postValue(quote.quote)
quoteDescription.postValue(quote.description)
quoteLastUpdate.postValue(quote.lastUpdated.getFormattedDate())
}
} else {
isNewQuote = true
}
}
fun onViewClick(viewId: Int) {
val currentTime = Calendar.getInstance().time
when (viewId) {
R.id.actionSave -> {
if (!isNewQuote) {
val quoteData = Quote(
id = this.quoteId.value ?: "",
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.update(quoteData)
} else {
val quoteData = Quote(
id = UUID.randomUUID().toString(),
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.insert(quoteData)
}
onSaveClickEvent.postValue(ViewModelEvent(Unit))
}
R.id.actionCancel -> onCancelClickEvent.postValue(ViewModelEvent(Unit))
R.id.actionDelete -> onDeleteClickEvent.postValue(ViewModelEvent(this.quoteId.value))
}
}
fun onDeleteItemResult(guid: String?) {
val currentTime = Calendar.getInstance().time
val quoteData = Quote(
id = guid ?: "",
author = this.quoteAuthor.value ?: "",
quote = this.quoteContent.value ?: "",
description = this.quoteDescription.value ?: "",
lastUpdated = currentTime
)
quoteRepository.delete(quoteData)
}
companion object {
fun newBundle(quote: Quote?): Bundle {
return Bundle().apply {
putSerializable(KEY_DATA, quote)
}
}
}
}
Also here is code of my Quote:
#Entity(tableName = "quotes")
class Quote(
#PrimaryKey
val id: String,
val author: String,
val quote: String,
val description: String,
#ColumnInfo(name = "last_updated")
val lastUpdated: Date?
) : Serializable
And here is the code when I click on a list item
val allQuotes: LiveData<List<Quote>>
init {
allQuotes = quoteRepository.allQuotes
}
fun onListItemClick(itemIndex: Int) {
onListItemClickEvent.postValue(
ViewModelEvent(QuoteDetailsViewModel.newBundle(allQuotes.value?.getOrNull(itemIndex)))
)
}
Then I get to my view model - QuoteDetailsViewModel
I want to update my object in Room by id, but it doesn't work.
Here is my DAO class
#Query("UPDATE greeting SET id =:id")
abstract fun updateGreetingByID(id: String)
Here is my DBHelper
fun updateGreetingByID(id: String) {
Thread { dataBase.greetingDao().updateGreetingByID(id) }.start()
}
My model
#Entity(tableName = "greeting")
class GreetingModel(
id: String? = "",
greetingCategory: String = "",
isFavourite: Boolean = false,
position: Int = 0
) : Parcelable {
#PrimaryKey(autoGenerate = true)
var _id: Int = 0
#ColumnInfo(name = "id")
var id: String? = null
#ColumnInfo(name = "greetingCategory")
var greetingCategory: String? = null
#ColumnInfo(name = "isFavourite")
var isFavourite: Boolean? = null
#ColumnInfo(name = "imageId")
var imageId: ByteArray? = null
#ColumnInfo(name = "position")
var position: Int = 0
#ColumnInfo(name = "saved")
var saved: Int = 0
constructor(parcel: Parcel) : this(
) {
_id = parcel.readInt()
id = parcel.readString()
greetingCategory = parcel.readString()
isFavourite = parcel.readValue(Boolean::class.java.classLoader) as? Boolean
imageId = parcel.createByteArray()
position = parcel.readInt()
saved = parcel.readInt()
}
init {
this.id = id
this.greetingCategory = greetingCategory
if (position != null) {
this.position = position
}
this.isFavourite = isFavourite
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(_id)
parcel.writeString(id)
parcel.writeString(greetingCategory)
parcel.writeValue(isFavourite)
parcel.writeByteArray(imageId)
parcel.writeInt(position)
parcel.writeInt(saved)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GreetingModel> {
override fun createFromParcel(parcel: Parcel): GreetingModel {
return GreetingModel(parcel)
}
override fun newArray(size: Int): Array<GreetingModel?> {
return arrayOfNulls(size)
}
}
}
Where id is val id = UUID.randomUUID().toString()
I want to update object when user click to favourite button
val greetingModel = GreetingModel()
greetingModel.isFavourite = true
greetingModel.greetingCategory = " "
greetingModel.position = postImage
greetingModel.id?.let { helper.updateGreetingByID(greetingModel.id!!) }
My update method doesn't work. Hope, you will help!
Your issue is that you are trying to update according to a newly constructed Greeting that has the id as whatever value the id is set to when the Greeting is constructed using the default (no parameters constructor).
The fix is to retrieve the specific id of the Greeting that was clicked and set the id to that value before doing the update.