Skip to content
This repository has been archived by the owner on Apr 14, 2024. It is now read-only.

Commit

Permalink
Life 360 support
Browse files Browse the repository at this point in the history
  • Loading branch information
yschimke committed Oct 23, 2021
1 parent c0d29ff commit cd9701f
Show file tree
Hide file tree
Showing 16 changed files with 322 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class AuthenticatingInterceptor(
override val services: List<AuthInterceptor<*>> = defaultServices()
) : Interceptor, ServiceLibrary {
override fun intercept(chain: Interceptor.Chain): Response {
return runBlocking {
return runBlocking(Dispatchers.IO) {
val firstInterceptor =
services.find { it.supportsUrl(chain.request().url, credentialsStore) }

Expand All @@ -43,9 +43,7 @@ class AuthenticatingInterceptor(
if (firstInterceptor != null) {
intercept(firstInterceptor, chain)
} else {
withContext(Dispatchers.IO) {
chain.proceed(chain.request())
}
chain.proceed(chain.request())
}
}
}
Expand Down Expand Up @@ -113,6 +111,7 @@ class AuthenticatingInterceptor(
com.baulsupp.okurl.services.httpbin.HttpBinAuthInterceptor(),
com.baulsupp.okurl.services.imgur.ImgurAuthInterceptor(),
com.baulsupp.okurl.services.instagram.InstagramAuthInterceptor(),
com.baulsupp.okurl.services.life360.Life360AuthInterceptor(),
com.baulsupp.okurl.services.linkedin.LinkedinAuthInterceptor(),
com.baulsupp.okurl.services.lyft.LyftAuthInterceptor(),
com.baulsupp.okurl.services.mapbox.MapboxAuthInterceptor(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.baulsupp.okurl.services.life360

import com.baulsupp.oksocial.output.handler.OutputHandler
import com.baulsupp.okurl.authenticator.Oauth2AuthInterceptor
import com.baulsupp.okurl.authenticator.oauth2.Oauth2ServiceDefinition
import com.baulsupp.okurl.authenticator.oauth2.Oauth2Token
import com.baulsupp.okurl.completion.ApiCompleter
import com.baulsupp.okurl.completion.BaseUrlCompleter
import com.baulsupp.okurl.completion.CompletionVariableCache
import com.baulsupp.okurl.completion.UrlList
import com.baulsupp.okurl.credentials.CredentialsStore
import com.baulsupp.okurl.kotlin.edit
import com.baulsupp.okurl.kotlin.query
import com.baulsupp.okurl.kotlin.request
import com.baulsupp.okurl.secrets.Secrets
import com.baulsupp.okurl.services.life360.model.Circles
import com.baulsupp.okurl.services.life360.model.Token
import com.baulsupp.okurl.services.squareup.model.LocationList
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.ResponseBody.Companion.asResponseBody

class Life360AuthInterceptor : Oauth2AuthInterceptor() {
override val serviceDefinition =
Oauth2ServiceDefinition(
"www.life360.com", "Life360 API", "life360",
"https://github.com/kaylathedev/life360-node-api", null
)

override suspend fun intercept(chain: Interceptor.Chain, credentials: Oauth2Token): Response =
chain.proceed(chain.request().edit {
addHeader("Authorization", "Bearer ${credentials.accessToken}")
if (chain.request().header("Accept") == null) {
addHeader("Accept", "application/json")
}
}).let {
val body = it.body
if (body != null && body.contentType()?.subtype == "html" && it.peekBody(1L).string() == "{") {
it.newBuilder().body(body.source().asResponseBody("application/json".toMediaType(), body.contentLength())).build()
} else {
it
}
}

override suspend fun authorize(
client: OkHttpClient,
outputHandler: OutputHandler<Response>,
authArguments: List<String>
): Oauth2Token {
val email = Secrets.prompt("Life360 email", "life360.email", "", false)
val password = Secrets.prompt("Life360 password", "life360.password", "", true)

val token = client.query<Token>(request("https://www.life360.com/v3/oauth2/token") {
// dev account from https://github.com/kaylathedev/life360-node-api
header("Authorization", "Basic U3dlcUFOQWdFVkVoVWt1cGVjcmVrYXN0ZXFhVGVXckFTV2E1dXN3MzpXMnZBV3JlY2hhUHJlZGFoVVJhZ1VYYWZyQW5hbWVqdQ==")
header("accept", "application/json")

post(FormBody.Builder()
.add("username", email)
.add("password", password)
.add("grant_type", "password")
.build())
})

return Oauth2Token(token.access_token)
}

override suspend fun apiCompleter(
prefix: String,
client: OkHttpClient,
credentialsStore: CredentialsStore,
completionVariableCache: CompletionVariableCache,
tokenSet: com.baulsupp.okurl.credentials.Token
): ApiCompleter {
val urlList = UrlList.fromResource(name())

val completer = BaseUrlCompleter(urlList!!, hosts(credentialsStore), completionVariableCache)

completer.withCachedVariable(name(), "circleId", keepTemplate = false) {
credentialsStore.get(serviceDefinition, tokenSet)?.let {
client.query<Circles>(
"https://www.life360.com/v3/circles",
tokenSet
).circles.map { it.id }
}
}

return completer
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Alerts(
val crime: String,
val sound: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Circle(
val color: String,
val createdAt: String,
val features: CircleFeatures,
val id: String,
val memberCount: String,
val members: List<Member>?,
val name: String,
val type: String,
val unreadMessages: String,
val unreadNotifications: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class CircleFeatures(
val locationUpdatesLeft: Int,
val ownerId: String?,
val premium: String,
val priceMonth: String,
val priceYear: String,
val skuId: String?,
val skuTier: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Circles(
val circles: List<Circle>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Communication(
val channel: String,
val type: String?,
val value: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Issues(
val action: Any?,
val dialog: Any?,
val disconnected: String,
val status: Any?,
val title: Any?,
val troubleshooting: String,
val type: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Location(
val accuracy: String,
val address1: String?,
val address2: String,
val battery: String,
val charge: String,
val driveSDKStatus: Any?,
val endTimestamp: String,
val inTransit: String,
val isDriving: String,
val latitude: String,
val longitude: String,
val name: String?,
val placeType: Any?,
val shortAddress: String,
val since: Int,
val source: String?,
val sourceId: String?,
val speed: Int,
val startTimestamp: Int,
val timestamp: String,
val tripId: Any?,
val userActivity: Any?,
val wifiState: String
)
17 changes: 17 additions & 0 deletions src/main/kotlin/com/baulsupp/okurl/services/life360/model/Map.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Map(
val advisor: String,
val crime: String,
val crimeDuration: String,
val family: String,
val fire: String,
val hospital: String,
val memberRadius: String,
val placeRadius: String,
val police: String,
val sexOffenders: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Member(
val activity: Any?,
val avatar: String,
val communications: List<Communication>,
val createdAt: String,
val features: MemberFeatures,
val firstName: String,
val id: String,
val isAdmin: String,
val issues: Issues,
val lastName: String,
val location: Location,
val loginEmail: String,
val loginPhone: String,
val medical: Any?,
val pinNumber: Any?,
val relation: Any?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class MemberFeatures(
val device: String,
val disconnected: String,
val geofencing: String,
val mapDisplay: String,
val nonSmartphoneLocating: String,
val pendingInvite: String,
val shareLocation: String,
val shareOffTimestamp: Any?,
val smartphone: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Settings(
val alerts: Alerts,
val dateFormat: String,
val locale: String,
val map: Map,
val timeZone: String,
val unitOfMeasure: String,
val zendrive: Any?
)
14 changes: 14 additions & 0 deletions src/main/kotlin/com/baulsupp/okurl/services/life360/model/Token.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class Token(
val access_token: String,
val cobranding: List<Any>,
val onboarding: Int,
val promotions: List<Any>,
val state: Any?,
val token_type: String,
val user: User
)
20 changes: 20 additions & 0 deletions src/main/kotlin/com/baulsupp/okurl/services/life360/model/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.baulsupp.okurl.services.life360.model

import com.squareup.moshi.JsonClass

@JsonClass(generateAdapter = true)
data class User(
val avatar: String,
val avatarAuthor: Any?,
val cobranding: List<Any>,
val communications: List<Communication>,
val created: String,
val firstName: String,
val id: String,
val language: String,
val lastName: String,
val locale: String,
val loginEmail: String,
val loginPhone: String,
val settings: Settings
)
23 changes: 23 additions & 0 deletions src/main/resources/urls/life360.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
https://www.life360.com/v3/circles
https://www.life360.com/v3/circles/members/request/{requestId}
https://www.life360.com/v3/circles/{circleId}
https://www.life360.com/v3/circles/{circleId}/allplaces
https://www.life360.com/v3/circles/{circleId}/code
https://www.life360.com/v3/circles/{circleId}/driverbehavior/watchlist
https://www.life360.com/v3/circles/{circleId}/emergencyContacts
https://www.life360.com/v3/circles/{circleId}/member/alerts
https://www.life360.com/v3/circles/{circleId}/member/preferences
https://www.life360.com/v3/circles/{circleId}/members
https://www.life360.com/v3/circles/{circleId}/members/history
https://www.life360.com/v3/circles/{circleId}/members/{memberId}
https://www.life360.com/v3/circles/{circleId}/members/{memberId}/request
https://www.life360.com/v3/circles/{circleId}/messages
https://www.life360.com/v3/circles/{circleId}/nearbyplaces/{lat}/{lon}
https://www.life360.com/v3/circles/{circleId}/places
https://www.life360.com/v3/circles/{circleId}/smartRealTime/start
https://www.life360.com/v3/circles/{circleId}/threads/message
https://www.life360.com/v3/crimes
https://www.life360.com/v3/oauth2/token
https://www.life360.com/v3/safetyPoints
https://www.life360.com/v3/users/me
https://www.life360.com/v4/locations

0 comments on commit cd9701f

Please sign in to comment.