I have weather app , which display weather in your own location and also shows date.
To show UI I use Fragment, and FragmentContainer.
Literrally , I don't really understand how to bind rest api request with location permission and show it in View.
All permissions and Application Class are already written in Manifest.
import android.app.Application
import di.AppComponent
import di.DaggerAppComponent
class WeatherApp:Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.context(app = this)
.build()
}
}
@GET("v1/forecast?latitude=52.52&longitude=13.41¤t_weather=true")
suspend fun getCurrentWeather(@Query("latitude")lat:Double,@Query("longitude")lon:Double)
:Response<WeatherCurrentResponse>
@Component(modules = [NetworkModule::class,DomainModule::class])
interface AppComponent{
fun viewmodelfac():WeatherViewmodelFactory
fun viewmodel():WeatherViewModel
@Component.Builder
interface Builder{
fun build():AppComponent
@BindsInstance
fun context(app:Application):Builder
}
}
@Module
interface DomainModule {
@Binds
fun bindrepository(currentRepositoryImpl: CurrentRepositoryImpl):CurrentRepository
@Binds
fun bindLocation(trackLocationImpl: TrackLocationImpl):TrackLocation
}
@Module
class NetworkModule {
@Provides
fun provideapi():ServiceAPI{
return Retrofit.Builder()
.baseUrl("https://api.open-meteo.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ServiceAPI::class.java)
}
@Provides
fun provideflocationprovider(app: Application): FusedLocationProviderClient {
return LocationServices.getFusedLocationProviderClient(app)
}
}
interface CurrentRepository {
suspend fun getCurrentWeather(lat:Double,lon:Double):Response<WeatherCurrentResponse>
}
class CurrentRepositoryImpl @Inject constructor(private val api: ServiceAPI) : CurrentRepository{
override suspend fun getCurrentWeather(
lat: Double,
lon: Double
): Response<WeatherCurrentResponse> =
api.getCurrentWeather(lat, lon)
}
interface TrackLocation {
suspend fun getCurrentlocation():Location?
}
class TrackLocationImpl@Inject constructor(private val application: Application,
private val fusedLocationProviderClient: FusedLocationProviderClient
) : TrackLocation{
override suspend fun getCurrentlocation(): Location? {
val hasPermissionCoarse = ContextCompat.checkSelfPermission(
application,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
val hasPermFine = ContextCompat.checkSelfPermission(
application,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
val locationManager = application.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val isGPSenabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ||
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
if(!hasPermissionCoarse || !hasPermFine || !isGPSenabled){
return null
}
return suspendCancellableCoroutine {
i->
fusedLocationProviderClient.lastLocation.apply {
if(isComplete){
if(isSuccessful){i.resume(result)} else{
i.resume(null)
}
return@suspendCancellableCoroutine
}
addOnSuccessListener {
i.resume(it)
}
addOnFailureListener{
i.resume(null)
}
addOnCanceledListener {
i.cancel()
}
}
}
}
}
class WeatherViewModel @Inject constructor(private val repository: CurrentRepository,
private val trackLocation: TrackLocation):ViewModel() {
private val _weatherstate = MutableLiveData<Response<WeatherCurrentResponse>>()
val weatherstate:LiveData<Response<WeatherCurrentResponse>>
get() = _weatherstate
fun initweatherdata() {
viewModelScope.launch(Dispatchers.IO) {
trackLocation.getCurrentlocation()?.let {
location -> _weatherstate.value =
repository.getCurrentWeather(lat = location.latitude, lon = location.longitude)
}
}
}
}
@Suppress("UNCHECKED_CAST")
class WeatherViewmodelFactory@Inject constructor(private val repository: CurrentRepository,
private val location: TrackLocation
):ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
require(WeatherViewModel::class == modelClass)
return WeatherViewModel(repository = repository, trackLocation = location) as T
}
}
class ListWeatherFragment : Fragment() {
private lateinit var binding:FragmentListWeatherBinding
private val viewModel by viewModels<WeatherViewModel>{
getAppComponent().viewmodelfac() // error
}
private fun Fragment.getAppComponent() : AppComponent = (requireContext() as WeatherApp)
.appComponent // error here
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentListWeatherBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.initweatherdata() // error
viewModel.weatherstate.observe(
viewLifecycleOwner
) {
weatherdisplaying ->
binding.tempcurrent.text =
weatherdisplaying.body()?.weathercurrent?.temp.toString()
binding.timecurrent.text = weatherdisplaying.body()?.weathercurrent?.time?.
format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))
val location = weatherdisplaying.body()?.lat?.compareTo(weatherdisplaying.body()!!.lon)
binding.city.text = location.toString()
// error here
}
}
}
An Activity or a Context is not an Application, so you can't cast it to one. Replace
(requireContext() as WeatherApp)
with
(requireActivity().application as WeatherApp)