環境
IDE : Android Studio 4.0.1
OS : Android10
言語 : kotlin
ライブラリ : okhttp, YOLP(Yahoo API)
ライブラリの登録
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'com.squareup.okhttp3:okhttp:4.4.0'
}
保存したら右クリックでsync nowを選択し、ライブラリをIDEに反映させる。
パーミッションの追加
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.takilab.aroundhokkaido">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
インターネットへのアクセスを有効にする権限
android.permission.INTERNET
位置情報を取得する権限
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_BACKGROUND_LOCATION
大文字小文字に注意すること(ハマったのは内緒)
UI
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/locationText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="[0]"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/distanceText"
android:layout_width="76dp"
android:layout_height="17dp"
android:layout_marginTop="24dp"
android:text="[km]"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/locationText" />
</androidx.constraintlayout.widget.ConstraintLayout>
位置情報と距離情報をとりあえず画面に出す。
コード
package com.takilab.aroundhokkaido
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.location.Location
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import com.google.android.gms.location.*
import kotlinx.android.synthetic.main.activity_main.*
import okhttp3.*
import org.json.JSONObject
import java.io.IOException
class MainActivity : AppCompatActivity() {
companion object {
private const val PERMISSION_REQUEST_CODE = 1234
}
private val appid = "ひみつー"
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationCallback: LocationCallback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestPermission()
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
var updatedCount = 0
var prevLocation : Location? = null
val client : OkHttpClient = OkHttpClient()
val url : String = "https://map.yahooapis.jp/dist/V1/distance?output=json&coordinates=%f,%f %f,%f&appid=%s"
// 位置情報取得時のコールバック定義オブジェクト
locationCallback = object : LocationCallback() {
/**
* 位置情報取得時の処理
*/
@SuppressLint("SetTextI18n")
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations) {
updatedCount++
if (prevLocation == null) {
locationText.text = "[${updatedCount}] ${location.latitude} , ${location.longitude}"
} else {
locationText.text = "[${updatedCount}] ${location.latitude} , ${location.longitude}" +
" - ${prevLocation!!.latitude} , ${prevLocation!!.longitude}"
// HTTPリクエストを作成
val request = Request.Builder()
.url(url.format(location.longitude, location.latitude, prevLocation!!.longitude, prevLocation!!.latitude, appid))
.build()
// HTTPリクエストを送信
client.newCall(request).enqueue(object : Callback {
/**
* HTTPリクエスト失敗時の処理
*/
override fun onFailure(call: Call, e: IOException) {
println("fail : $e")
}
/**
* HTTPリクエスト成功時の処理
*/
override fun onResponse(call: Call, response: Response) {
var str = response!!.body!!.string()
val jsonObject = JSONObject(str)
val jsonArray = jsonObject.getJSONArray("Feature")
for (i in 0 until jsonArray.length()) {
val jsonData = jsonArray.getJSONObject(i)
val geometry = jsonData.getJSONObject("Geometry")
val distance = geometry.getDouble("Distance")
// ハンドラを経由してメインスレッドからUIにアクセスしないとエラーになる。
val mainHandler : Handler = Handler(Looper.getMainLooper())
mainHandler.post(Runnable {
distanceText.text = "$distance"
})
}
}
})
}
prevLocation = location
}
}
}
}
override fun onResume() {
super.onResume()
startLocationUpdates()
}
override fun onPause() {
super.onPause()
stopLocationUpdates()
}
/**
* 権限の問い合わせを行う。
*/
private fun requestPermission() {
// 位置情報にアクセスする権限があるかどうかを確認する。
if (!checkPermission()) {
// 権限が無いため、許可を求める
ActivityCompat.requestPermissions(this,
arrayOf(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_BACKGROUND_LOCATION
),
PERMISSION_REQUEST_CODE)
}
}
/**
* 権限の確認を行う
*/
private fun checkPermission() : Boolean {
return ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
}
/**
* 位置情報の取得開始
*/
private fun startLocationUpdates() {
// 権限の再確認(無いとIDE(Android Studio)に怒られる)
if (!checkPermission()) {
requestPermission()
return
}
// 位置情報の取得開始
val locationRequest = createLocationRequest() ?: return
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
null)
}
/**
* 位置情報の取得を停止する
*/
private fun stopLocationUpdates() {
// 位置情報の取得停止
fusedLocationClient.removeLocationUpdates(locationCallback)
}
/**
* 位置情報を行うインスタンスの作成
*/
private fun createLocationRequest(): LocationRequest? {
return LocationRequest.create()?.apply {
interval = 10000
fastestInterval = 5000
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
}
}
GPSの情報を取得する前に、アプリに権限が付与されているかを確認する。
権限が無ければユーザーに権限付与を求めるダイアログを表示する。
startLocationUpdates()でGPSの情報を取得開始。
GPSのデータを取得すると、onLocationResult()が実行される。
現位置と前位置の情報を元に、YahooのAPIを使用して2点間の距離を問い合わせる。
Request.Builder()でHTTPリクエストを作成し、client.newCall()で送信。
HTTPレスポンスを受信するとonResponse()が実行される。
レスポンスからJsonを取得し、解析して、距離データ(km)を取得する。
UIに反映させるには、メインスレッドでなければエラーになるので、
ハンドラを使用してメインスレッドからUIに反映させる。