i'm practising a bit with kotlin and was testing Room and livedata, my app gets data from a json and the stores it in room, i want to move this network call to its own file and class, but if i do so the observer i set to get the changes don't trigger anymore, any help would be appreciated
here is a snipped of my mainactivity, if more is needed to know what happens please let me know
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import androidx.room.Room
import com.optiva.videoplayer.data.*
import com.optiva.videoplayer.network.GetData
import com.optiva.videoplayer.network.Networking
import com.optiva.videoplayer.network.RetrofitConnect
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val dbcategories = Room.databaseBuilder(applicationContext, CategoriesDatabase::class.java,"categories.db").build()
val dbvideo = Room.databaseBuilder(applicationContext, VideosDatabase::class.java,"videos.db").build()
val retrofitData = RetrofitConnect.retrofitInst?.create(GetData::class.java)
val categoriesList = retrofitData?.getAll()
categoriesList?.enqueue(object: Callback<DataList> {
override fun onResponse(
call: Call<DataList>,
response: Response<DataList>
) {
val test = response?.body()
val cat = test?.categories
val vid = test?.videos
lifecycleScope.launch(Dispatchers.IO) {
if (cat != null) {
for(c in cat){
dbcategories.categoriesDAO().insertAll(CategoriesEntity(c.id,c.title,c.type))
}
}
if (vid != null) {
for(v in vid){
dbvideo.VideosDAO().insertAll(VideosEntity(v.id,v.thumb,v.videoUrl,v.categoryId,v.name))
}
}
}
}
override fun onFailure(call: Call<DataList>, t: Throwable) {
Toast.makeText(applicationContext,"error", Toast.LENGTH_LONG).show()
}
})
val textView: TextView = findViewById(R.id.test) as TextView
dbcategories.categoriesDAO().getALL().observeForever({categories ->
if(categories.size>0){
textView.text= categories[0].title
}
})
dbcategories.categoriesDAO().getALL().observe(this, {categories ->
if(categories.size>0){
textView.text= categories[0].title
}
}
} ```
Related
I'm trying to create a chart that displays sensor data from a cell phone in real time. I wrote a code for this, but the data was recorded, but the graph was recorded only on the same x-axis. The code is shown below. How should I solve this? If I need to create a separate thread, where should I add it?
package com.example.ex_linear_acc_graph
import android.content.Context
import android.graphics.Color
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.widget.LinearLayout
import android.widget.TextView
import com.github.mikephil.charting.charts.LineChart
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.ValueFormatter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.concurrent.timer
class linearActivity : AppCompatActivity(), SensorEventListener {
private val mSensorManager by lazy {
getSystemService(Context.SENSOR_SERVICE) as SensorManager
}
private lateinit var chart: LineChart
private lateinit var data: LineData
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_linear)
chart = findViewById(R.id.chart_line)
chart.description.isEnabled = false
chart.setTouchEnabled(true)
chart.isDragEnabled = true
chart.setScaleEnabled(true)
chart.setPinchZoom(false)
chart.legend.form = Legend.LegendForm.LINE
chart.xAxis.position = XAxis.XAxisPosition.BOTTOM
chart.xAxis.setDrawLabels(true)
chart.xAxis.axisMaximum = 0f
chart.xAxis.setDrawAxisLine(true)
chart.xAxis.setDrawGridLines(false)
chart.axisRight.isEnabled = false
chart.legend.textColor = Color.WHITE
chart.animateXY(2000, 2000)
chart.invalidate()
data = LineData()
chart.data = data
}
override fun onResume() {
super.onResume()
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)?.also{
linearacceleration -> mSensorManager.registerListener(
this,
linearacceleration,
SensorManager.SENSOR_DELAY_NORMAL,
SensorManager.SENSOR_DELAY_UI
)}
}
override fun onPause() {
super.onPause()
mSensorManager.unregisterListener(this)
}
override fun onSensorChanged(event: SensorEvent) {
var second : Float = 0f
timer(period = 1000, initialDelay = 1000){
second++
}
if(event.sensor.type == Sensor.TYPE_MAGNETIC_FIELD){
val mag_x = event.values[0]
val mag_y = event.values[1]
val mag_z = event.values[2]
val tv_mag : TextView = findViewById(R.id.tv_linear_acc)
tv_mag.text = "mag_x : ${mag_x}\nmag_y : ${mag_y}\nmag_z : ${mag_z}"
val dataSet = LineDataSet(listOf(Entry(second, mag_x), Entry(second, mag_y), Entry(second, mag_z)), "Accelerometer Data")
data.addDataSet(dataSet)
chart.notifyDataSetChanged()
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
// Do nothing
}
}
I'm having an issue where I've imported an import in order for a piece of code to work but no matter how many times I import the import, the code isn't recognizing that it's there. I've already tried invalidating and restarting, multiple times. I've read that another solution to this is to Sync with File System, but I don't appear to have that option under File.
The import in question is import java.text.MessageFormat.format
import android.content.ContentValues.TAG
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.firestore.ktx.firestore
import com.google.firebase.ktx.Firebase
import com.squareup.okhttp.internal.http.HttpDate.format
import java.math.BigDecimal
import java.sql.Time
import java.sql.Timestamp
import java.text.DateFormat
import java.text.MessageFormat.format
import java.util.*
import kotlin.collections.ArrayList
class RemindersActivity : AppCompatActivity() {
lateinit var petID: String
val db = Firebase.firestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reminders)
displayReminders()
petID = intent.getStringExtra("petID").toString()
val fab: View = findViewById(R.id.fab_reminders)
fab.setOnClickListener {
val intent = Intent(this, AddReminderActivity::class.java)
intent.putExtra("petID", petID)
startActivity(intent)
}
}
override fun onStart() {
super.onStart()
displayReminders()
}
private fun displayReminders() {
val recyclerview = findViewById<RecyclerView>(R.id.recyclerview_reminders)
recyclerview.layoutManager = LinearLayoutManager(this)
db.collection("pets").document(petID).collection("reminders").get().addOnSuccessListener { result ->
val data = mutableListOf<RemindersData>()
for (document in result) {
val title = document.data["title"].toString()
val timestamp = document.data["timestamp"] as Long
val cal = Calendar.getInstance()
cal.timeInMillis = timestamp * 1000L
val date = DateFormat.format("dd-MM-yyyy hh:mm:ss aa", cal).toString() //<- code note recognizing import is format
val frequency = document.data["frequency"].toString()
data.add(RemindersData(title, date, frequency))
}
val adapter = RemindersAdapter(data)
recyclerview.adapter = adapter
}.addOnFailureListener { e->
Log.w(TAG, "Error getting documents", e)
}
}
}
It just seems that you have added a few imports from the java packages instead of the android packages. It can happen when you use auto import in the IDE and there are multiple options and you click the wrong one.
When that happens you either have to undo the import or fix it manually.
Remove the import for
import java.text.DateFormat
Add the import for
import android.text.format.DateFormat
Afterwards, if there are any unused imports left, you can remove those as well.
Running into an Issue early on in importing a declared extension function in another Kotlin File (Extensions.kt), call the extension function From another class (ForecastsRepository.kt) it doesn't compile but when i remove it there is no problem with the build. Obviously I need it and wonder why importing it would become an issue .
Here is the class:
import com.benmohammad.climatemvvm.base.Success
import com.benmohammad.climatemvvm.custom.errors.ErrorHandler
import com.benmohammad.climatemvvm.custom.errors.NoDataException
import com.benmohammad.climatemvvm.custom.errors.NoResponseException
import com.benmohammad.climatemvvm.entitymappers.forecasts.ForecastMapper
import com.benmohammad.climatemvvm.features.home.di.HomeScope
import com.benmohammad.climatemvvm.network.api.OpenWeatherApi
import com.benmohammad.climatemvvm.network.response.ErrorResponse
import com.benmohammad.climatemvvm.room.dao.forecasts.ForecastDao
import com.benmohammad.climatemvvm.room.dao.utils.StringKeyValueDao
import com.benmohammad.climatemvvm.room.models.forecasts.DbForecast
import com.benmohammad.climatemvvm.utils.Utils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
import javax.inject.Inject
import com.benmohammad.climatemvvm.extensions.applyCommonSideEffects//import
#HomeScope
class ForecastsRepository #Inject constructor(
private val openWeatherApi: OpenWeatherApi,
private val forecastDao: ForecastDao,
private val stringKeyValueDao: StringKeyValueDao
) {
private val forecastCacheThresholdMillis = 3 * 3600000L //3 hours//
fun getForecasts(cityId: Int) = flow {
stringKeyValueDao.get(Utils.LAST_FORECASTS_API_CALL_TIMESTAMP)
?.takeIf { !Utils.shouldCallApi(it.value, forecastCacheThresholdMillis) }
?.let { emit(getDataOrError(NoDataException())) }
?: emit((getForecastFromAPI(cityId)))
}
//.applyCommonSideEffects()
.catch {
emit(getDataOrError(it))
}
private suspend fun getForecastFromAPI(cityId: Int) = openWeatherApi.getWeatherForecast(cityId)
.run {
if (isSuccessful && body() != null) {
stringKeyValueDao.insert(
Utils.getCurrentTimeKeyValuePair(Utils.LAST_FORECASTS_API_CALL_TIMESTAMP)
)
forecastDao.deleteAllAndInsert(ForecastMapper(body()!!).map())
getDataOrError(NoDataException())
} else {
Error(
NoResponseException(
ErrorHandler.parseError<ErrorResponse>(errorBody())?.message
)
)
}
}
private suspend fun getDataOrError(throwable: Throwable) =
forecastDao.get()
?.let { dbValue -> Success(getForecastList(dbValue)) }
?: Error(throwable)
private suspend fun getForecastList(dbForecast: DbForecast) = withContext(Dispatchers.Default) {
dbForecast.list.map { it.forecast }
}
}
and here is the file for the Extension functions:
package com.benmohammad.climatemvvm.extensions
import com.benmohammad.climatemvvm.base.Progress
import com.benmohammad.climatemvvm.base.Result
import com.benmohammad.climatemvvm.utils.Utils
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.retryWhen
import okhttp3.Call
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.Retrofit
import java.io.IOException
fun String.capitalizeWords(): String = this.split(' ').joinToString(" "){it.capitalize()}
#PublishedApi
internal inline fun Retrofit.Builder.callFactory(crossinline body: (Request) -> Call) =
callFactory(object: Call.Factory {
override fun newCall(request: Request): Call = body(request)
})
#Suppress("NOTHING_TO_INLINE")
inline fun Retrofit.Builder.delegatingCallFactory(delegate: dagger.Lazy<OkHttpClient>): Retrofit.Builder =
callFactory {
delegate.get().newCall(it) }
fun < T: Any> Flow<Result<T>>.applyCommonSideEffects() = //<<-----------T H I S Function!!!!HERE
retryWhen { cause, attempt ->
when {
(cause is IOException && attempt < Utils.MAX_RETRIES) -> {
delay(Utils.getBackOffDelay(attempt))
true
}
else -> {
false
}
}
}
.onStart { emit(Progress(isLoading = true)) }
.onCompletion { emit(Progress(isLoading = false)) }
fun Job?.cancelIfActive() {
if(this?.isActive == true) {
cancel()
}
}
as it doesnt compile it leads me to think the bug is deeper.
the IDE also hunderlines the function call stating it is "Unresolved reference"
GitHub Repo
Thanks any advice appreciated.
I am learning Kotlin and all was going well until it stopped working, the only thing I changed were the references.
I've tried to adjust the "this" parameter and it has not worked,
Here is my code:
package com.example.myweatherapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ArrayAdapter
import android.widget.ListView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class ForecastActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_forecast)
var retriever = WeatherRetriever()
val callback = object : Callback<Weather> {
override fun onFailure(call: Call<Weather>?, t: Throwable) {
println("It failed")
}
override fun onResponse(
call: Call<Weather>?, response: Response<Weather>?) {
println("It wORKED")
println(response?.body()?.main)
title = response?.body()?.name
var forecasts = response?.body()?.main
var castListView = findViewById<ListView>(R.id.forecastListView)
var adapter = ArrayAdapter(this#ForecastActivity,android.R.layout.simple_list_item_1,forecasts)
castListView.adapter=adapter
}
}
retriever.getForecast(callback)
}
}
I am getting the following error: "None of the following functions can be called with the arguments supplied:"
Any help for a newbie?
Thanks a lot!
Edit: Here is the weather class
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
interface weatherAPI{
#GET("weather?id=3621224&units=metric&appid=ff2563aab36fc89bc7a3c4fe58dd7f3e")
fun getForecast() : Call<Weather>
}
class Weather(val main: WeatherForecast, val name: String )
class WeatherForecast (val main: List<main>)
class main (val temp: String, val feels_like: String, val temp_min: String, val temp_max: String)
class WeatherRetriever {
val service : weatherAPI
init {
val retrofit= Retrofit.Builder().baseUrl("https://api.openweathermap.org/data/2.5/").addConverterFactory(GsonConverterFactory.create()).build()
service = retrofit.create(weatherAPI::class.java)
}
fun getForecast(callback : Callback<Weather>){
val call = service.getForecast()
call.enqueue(callback)
}
}
I imported retrofi2.Callback
and I still get this error unresolved reference: enqueue
this is the code of the login class
import android.content.Intent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.provider.ContactsContract
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import com.cbmis.imageapp.Common.Common
import com.cbmis.imageapp.Model.APIResponse
import com.cbmis.imageapp.Remote.IMyAPI
import kotlinx.android.synthetic.main.activity_login.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class LoginActivity : AppCompatActivity() {
internal lateinit var mService:IMyAPI
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
//initservice
mService = Common.api
//Event
txtregister.setOnClickListener { startActivity(Intent(this#LoginActivity,RegisterActivity::class.java))
finish()
}
btn_login.setOnClickListener { authentificateUser(findViewById<TextView>(R.id.email).text.toString(), findViewById<TextView>(R.id.password).text.toString()) }
}
private fun authentificateUser(email: String, password: String) {
mService.loginUser(email, password)
.enqueue(object :Callback<APIResponse> {
override fun onFailure(call: Call<APIResponse>?, t: Throwable?) {
Toast.makeText(this#LoginActivity,t!!.message,Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: Call<APIResponse>?, response: Response<APIResponse>?) {
if (response!!.body()!!.error)
Toast.makeText(this#LoginActivity,response!!.body()!!.errr_msg,Toast.LENGTH_SHORT).show()
else
Toast.makeText(this#LoginActivity, "Login Success!",Toast.LENGTH_SHORT).show()
}
})
}
}
and this is the interface/
interface IMyAPI {
#FormUrlEncoded
#POST("signup.php")
fun registerUser(#Field("email") email:String,#Field("name")name:String,#Field("password") password:String,#Field("dateofbirth") dateofbirth:String,#Field("genderM") genderM:String,#Field("genderF") genderF:String):Class<APIResponse>
#FormUrlEncoded
#POST("login.php")
fun loginUser(#Field("email") email:String,#Field("password") password:String):Class<APIResponse>
}
Any solutions can be proposed to solve this problem
You should be returning a Retrofit Call from your API's functions:
interface IMyAPI {
#FormUrlEncoded
#POST("signup.php")
fun registerUser(/* params */) : Call<APIResponse>
#FormUrlEncoded
#POST("login.php")
fun loginUser(/* params */): Call<APIResponse>
}