Я скачал и опробовал несколько онлайн-примеров. Большинство не скомпилировалось. Наконец я набрал код, следуя видео на YouTube. Это было очень близко, и я извлек встроенную графику, исправил ее, чтобы она компилировалась, запускалась и работала с ограничениями. Я попробую прикрепить полученный код.
Оставшаяся проблема заключается в том, что код определения местоположения и графика находятся внутри MainActivity, и я недостаточно знаю о Kotlin. а также правила области видимости и ссылки, чтобы понять, как их разделить. Если я помещу графический код в другое место, я не смогу вызвать getCurrentLocationMine(). Если я попытаюсь вытащить код получения местоположения из MainActivity и поместить его в ViewModel, где я хотел бы, чтобы он находился, я не смогу понять, как предоставить необходимый аргумент Activity функциям, которые в настоящее время обрабатываются с помощью «this " изнутри MainActivity.
Итак, любой из следующих ответов будет полезен:
- Как правильно вызывать функцию, определенную в MainActivity, из другого места
- Как передать аргумент Activity таким функциям, как ActivityCompat.checkSelfPermission изнутри ViewModel
- Другой способ узнать местоположение телефона
Ниже показано, как я хотел бы, чтобы код был устроен, но он не компилируется, потому что, я думаю, нет допустимого параметра Activity или Context. По крайней мере, я не знаю, где их взять.
package com.example.currentlocationtutorial
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.os.Bundle
import android.provider.Settings
//import android.widget.TextView
//import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.lifecycle.ViewModel
import com.example.currentlocationtutorial.ui.theme.CurrentLocationTutorialTheme
import com.example.yo.Greeting
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
fun locationPrint(lat:Double, lon:Double):String {
return "%.6f".format(lat)+" "+"%.6f".format(lon)
}
//
// In this code example I have put the phone location access code in this ViewModel.
// I like segregating the data access code from the graphics like this and, big plus, I know
// how to get the data from the ViewModel anywhere else I need it for computation or display.
// Unfortunately this code does not compile, because the Activity or Context parameter
// needed for many of the functions is no longer available.
// "this" of course references the ViewModel and not the original MainActivity.
// Also the functions startActivity() and
// getSystemService() are no longer defined which I don't understand. Android Studio suggests
// importing ContextCompat.startActivity() and ContextCompat.getSystemService().
//
class DataModel: ViewModel() {
var stext by mutableStateOf("just started")
var ltext by mutableStateOf("Unknown")
var lstatus=false
var lcount=0
var ntry=0
var nrequest=0
//
// in the following line and several others "this" is the wrong type of object
// it needs to be an Activity not a ViewModel
//
var fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
fun getCurrentLocationMine() {
ntry++
if (checkPermissions()) {
if (isLocationEnabled()) {
//final latitude and longitude here
//
// I think this check is superfluous, already done in checkPermissions(),
// but Android Studio really wanted to put it here, so I let it.
//
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
fusedLocationProviderClient.lastLocation.addOnCompleteListener(this) { task ->
val location: Location? = task.result
if (location == null) {
lstatus = false
stext = "null returned"
} else {
lstatus = true
stext = "got location"
lcount++
ltext = locationPrint(location.latitude, location.longitude)
}
}
} else {
// setting open here
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
}
} else {
// request permission here
stext = "requesting permission"
requestPermission()
stext = "requested permission"
}
}
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager =
getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
private fun requestPermission() {
nrequest++
ActivityCompat.requestPermissions(
this,
arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION
), PERMISSION_REQUEST_ACCESS_LOCATION
)
}
//
// I don't know what this is. It came with the original example.
//
companion object {
private const val PERMISSION_REQUEST_ACCESS_LOCATION = 100
}
private fun checkPermissions(): Boolean {
if (ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_COARSE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED
) {
stext = "permission granted"
return true
} else {
stext = "permission denied"
return false
}
}
}
class MainActivity : ComponentActivity() {
val dmodel: DataModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
//
// get location once here to get things started
//
dmodel.getCurrentLocationMine()
CurrentLocationTutorialTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(dmodel)
}
}
}
}
}
//
// I'd like to pull the graphics out like this, because I really want to merge the location on
// a screen that includes data drawn from several other ViewModel. Will also be doing
// computations with the phone location and several other variables to help the user orient
// themselves and find things. Don't need help with these things, I know how to do this.
// I would call getCurrentLocationMine() before presenting this screen, not from a button
// push as it is done in this code. I know how to do this if the location access code is moved into
// the ViewModel as I'd like to do above.
//
@Composable
fun Greeting(dmodel: DataModel) {
val modifier: Modifier = Modifier
Surface(color = Color.Blue,
modifier = Modifier.fillMaxWidth().padding(4.dp)) {
Column() {
Row() {
Text(
text = "Does this get current location??????",
modifier = modifier
)
}
Row() {
val stext2 = dmodel.stext
val showstext = "Status: $stext2"
Text(
text = showstext,
modifier = modifier
)
}
Row() {
val stext2 =
dmodel.lcount.toString() + " " + dmodel.ntry.toString() + " " + dmodel.nrequest.toString()
val showstext = "Count: $stext2"
Text(
text = showstext,
modifier = modifier
)
}
Row() {
val ltext2 = dmodel.ltext
val showltext = "Location: $ltext2"
Text(
text = showltext,
modifier = modifier
)
}
Row() {
OutlinedButton(
onClick = { dmodel.getCurrentLocationMine() },
)
{ Text(text = "Compute") }
}
}
}
}
Этот код компилируется и работает, но не дает мне доступа к функции getCurrentLocationMine() там, где она мне нужна в другом месте проекта.
package com.example.currentlocationtutorial
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import android.os.Bundle
import android.provider.Settings
//import android.widget.TextView
//import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.core.app.ActivityCompat
import androidx.lifecycle.ViewModel
import com.example.currentlocationtutorial.ui.theme.CurrentLocationTutorialTheme
import com.example.yo.Greeting
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
fun locationPrint(lat:Double, lon:Double):String {
return "%.6f".format(lat)+" "+"%.6f".format(lon)
}
class DataModel: ViewModel() {
var stext by mutableStateOf("just started")
var ltext by mutableStateOf("Unknown")
var lstatus=false
var lcount=0
var ntry=0
var nrequest=0
fun update() {
}
fun get(){
// MainActivity.getCurrentLocation();
}
}
class MainActivity : ComponentActivity() {
private lateinit var fusedLocationProviderClient: FusedLocationProviderClient
val dmodel: DataModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
dmodel.update(); // this is only executed once, i think
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
CurrentLocationTutorialTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting(dmodel)
getCurrentLocationMine()
}
}
}
}
fun getCurrentLocationMine() {
dmodel.ntry++
if (checkPermissions()) {
if (isLocationEnabled()) {
//final latitude and longitude here
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
fusedLocationProviderClient.lastLocation.addOnCompleteListener(this) { task ->
val location: Location? = task.result
if (location == null) {
dmodel.lstatus = false
dmodel.stext = "null returned"
} else {
dmodel.lstatus = true
dmodel.stext = "got location"
dmodel.lcount++
dmodel.ltext = locationPrint(location.latitude, location.longitude)
}
}
} else {
// setting open here
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
startActivity(intent)
}
} else {
// request permission here
dmodel.stext = "requesting permission"
requestPermission()
dmodel.stext = "requested permission"
}
}
private fun isLocationEnabled(): Boolean {
val locationManager: LocationManager =
getSystemService(Context.LOCATION_SERVICE) as LocationManager
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
private fun requestPermission() {
dmodel.nrequest++
ActivityCompat.requestPermissions(
this,
arrayOf(
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION
), PERMISSION_REQUEST_ACCESS_LOCATION
)
}
companion object {
private const val PERMISSION_REQUEST_ACCESS_LOCATION = 100
}
private fun checkPermissions(): Boolean {
if (ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_COARSE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(
this,
android.Manifest.permission.ACCESS_FINE_LOCATION
) ==
PackageManager.PERMISSION_GRANTED
) {
dmodel.stext = "permission granted"
return true
} else {
dmodel.stext = "permission denied"
return false
}
}
@Composable
fun Greeting(dmodel: DataModel) {
val modifier: Modifier = Modifier
Surface(color = Color.Blue,
modifier = Modifier.fillMaxWidth().padding(4.dp)) {
Column() {
Row() {
Text(
text = "Does this get current location??????",
modifier = modifier
)
}
Row() {
val stext2 = dmodel.stext
val showstext = "Status: $stext2"
Text(
text = showstext,
modifier = modifier
)
}
Row() {
val stext2 =
dmodel.lcount.toString() + " " + dmodel.ntry.toString() + " " + dmodel.nrequest.toString()
val showstext = "Count: $stext2"
Text(
text = showstext,
modifier = modifier
)
}
Row() {
val ltext2 = dmodel.ltext
val showltext = "Location: $ltext2"
Text(
text = showltext,
modifier = modifier
)
}
Row() {
OutlinedButton(
onClick = { getCurrentLocationMine() },
)
{ Text(text = "Compute") }
}
}
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/790 ... ing-kotlin