Use CarAppService for startActivity instead of CarContext

fixes #375 for startActivity and openUrl

https://issuetracker.google.com/issues/372055514
Warning: You must update to androidx.car.app:1.7.0-alpha01 or later for the permissions dialog to show up on the phone screen when your app is used on a device running Android 14 or higher.
This commit is contained in:
johan12345
2025-05-07 22:57:16 +02:00
parent 45fe297616
commit 01cb551cbc
6 changed files with 86 additions and 30 deletions

View File

@@ -162,7 +162,7 @@ class EVMapSession(val cas: CarAppService) : Session(), DefaultLifecycleObserver
}
if (!prefs.privacyAccepted) {
screens.add(
AcceptPrivacyScreen(carContext)
AcceptPrivacyScreen(carContext, this)
)
}
handleACRAIntent(intent)?.let {

View File

@@ -3,10 +3,18 @@ package net.vonforst.evmap.auto
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.hardware.CarHardwareManager
import androidx.car.app.hardware.info.Model
import androidx.car.app.model.*
import androidx.car.app.model.Action
import androidx.car.app.model.ActionStrip
import androidx.car.app.model.CarIcon
import androidx.car.app.model.ItemList
import androidx.car.app.model.ListTemplate
import androidx.car.app.model.Row
import androidx.car.app.model.SectionedItemList
import androidx.car.app.model.Template
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.lifecycleScope
@@ -18,7 +26,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.vonforst.evmap.R
import net.vonforst.evmap.api.chargeprice.*
import net.vonforst.evmap.api.chargeprice.ChargePrice
import net.vonforst.evmap.api.chargeprice.ChargepriceApi
import net.vonforst.evmap.api.chargeprice.ChargepriceCar
import net.vonforst.evmap.api.chargeprice.ChargepriceChargepointMeta
import net.vonforst.evmap.api.chargeprice.ChargepriceInclude
import net.vonforst.evmap.api.chargeprice.ChargepriceMeta
import net.vonforst.evmap.api.chargeprice.ChargepriceOptions
import net.vonforst.evmap.api.chargeprice.ChargepriceRequest
import net.vonforst.evmap.api.chargeprice.ChargepriceRequestTariffMeta
import net.vonforst.evmap.api.chargeprice.ChargepriceStation
import net.vonforst.evmap.api.equivalentPlugTypes
import net.vonforst.evmap.api.nameForPlugType
import net.vonforst.evmap.api.stringProvider
@@ -32,7 +49,9 @@ import retrofit2.HttpException
import java.io.IOException
import kotlin.math.roundToInt
class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(ctx) {
@ExperimentalCarApi
class ChargepriceScreen(ctx: CarContext, val session: EVMapSession, val charger: ChargeLocation) :
Screen(ctx) {
private val prefs = PreferenceDataSource(ctx)
private val db = AppDatabase.getInstance(carContext)
private val api by lazy {
@@ -130,7 +149,7 @@ class ChargepriceScreen(ctx: CarContext, val charger: ChargeLocation) : Screen(c
)
).build()
).setOnClickListener {
openUrl(carContext, ChargepriceApi.getPoiUrl(charger))
openUrl(carContext, session.cas, ChargepriceApi.getPoiUrl(charger))
}.build()
).build()
)

View File

@@ -16,6 +16,7 @@ import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.HostException
import androidx.car.app.Screen
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.model.Action
import androidx.car.app.model.ActionStrip
@@ -76,7 +77,12 @@ import kotlin.math.roundToInt
private const val TAG = "ChargerDetailScreen"
class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) : Screen(ctx) {
@ExperimentalCarApi
class ChargerDetailScreen(
ctx: CarContext,
val chargerSparse: ChargeLocation,
val session: EVMapSession
) : Screen(ctx) {
var charger: ChargeLocation? = null
var photo: Bitmap? = null
private var availability: ChargeLocationStatus? = null
@@ -153,14 +159,20 @@ class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) :
.setTitle(carContext.getString(R.string.auto_prices))
.setOnClickListener {
if (prefs.chargepriceNativeIntegration) {
screenManager.push(ChargepriceScreen(carContext, charger))
screenManager.push(
ChargepriceScreen(
carContext,
session,
charger
)
)
} else {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse(ChargepriceApi.getPoiUrl(charger))
)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
carContext.startActivity(intent)
session.cas.startActivity(intent)
}
}
.build())
@@ -179,12 +191,12 @@ class ChargerDetailScreen(ctx: CarContext, val chargerSparse: ChargeLocation) :
Action.Builder()
.setTitle(carContext.getString(R.string.open_in_app))
.setOnClickListener(ParkedOnlyOnClickListener.create {
val intent = Intent(carContext, MapsActivity::class.java)
val intent = Intent(session.cas, MapsActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_CHARGER_ID, chargerSparse.id)
.putExtra(EXTRA_LAT, chargerSparse.coordinates.lat)
.putExtra(EXTRA_LON, chargerSparse.coordinates.lng)
carContext.startActivity(intent)
session.cas.startActivity(intent)
CarToast.makeText(
carContext,
R.string.opened_on_phone,

View File

@@ -386,7 +386,7 @@ class MapScreen(ctx: CarContext, val session: EVMapSession) :
)
setOnClickListener {
screenManager.push(ChargerDetailScreen(carContext, charger))
screenManager.push(ChargerDetailScreen(carContext, charger, session))
session.mapScreen = null
}
}.build()

View File

@@ -79,7 +79,7 @@ class SettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
)
setBrowsable(true)
setOnClickListener {
screenManager.push(DataSettingsScreen(carContext))
screenManager.push(DataSettingsScreen(carContext, session))
}
}.build())
addItem(Row.Builder().apply {
@@ -144,7 +144,7 @@ class SettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
)
.setBrowsable(true)
.setOnClickListener {
screenManager.push(AboutScreen(carContext))
screenManager.push(AboutScreen(carContext, session))
}
.build()
)
@@ -153,7 +153,8 @@ class SettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
}
}
class DataSettingsScreen(ctx: CarContext) : Screen(ctx) {
@ExperimentalCarApi
class DataSettingsScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
val prefs = PreferenceDataSource(ctx)
val encryptedPrefs = EncryptedPreferenceDataStore(ctx)
val db = AppDatabase.getInstance(ctx)
@@ -279,7 +280,7 @@ class DataSettingsScreen(ctx: CarContext) : Screen(ctx) {
}
}, IntentFilter(OAuthLoginFragment.ACTION_OAUTH_RESULT))
carContext.startActivity(intent)
session.cas.startActivity(intent)
if (BuildConfig.FLAVOR_automotive != "automotive") {
CarToast.makeText(
@@ -752,7 +753,8 @@ class SelectChargingRangeScreen(ctx: CarContext) : Screen(ctx) {
}
}
class AboutScreen(ctx: CarContext) : Screen(ctx) {
@ExperimentalCarApi
class AboutScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
val prefs = PreferenceDataSource(ctx)
var developerOptionsCounter = 0
private val maxRows = ctx.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST)
@@ -797,7 +799,11 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setTitle(carContext.getString(R.string.faq))
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.faq_link))
openUrl(
carContext,
session.cas,
carContext.getString(R.string.faq_link)
)
}).build()
)
addItem(
@@ -808,12 +814,16 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setOnClickListener(ParkedOnlyOnClickListener.create {
if (BuildConfig.FLAVOR_automotive == "automotive") {
// we can't open the donation page on the phone in this case
openUrl(carContext, carContext.getString(R.string.donate_link))
openUrl(
carContext,
session.cas,
carContext.getString(R.string.donate_link)
)
} else {
val intent = Intent(carContext, MapsActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(EXTRA_DONATE, true)
carContext.startActivity(intent)
session.cas.startActivity(intent)
CarToast.makeText(
carContext,
R.string.opened_on_phone,
@@ -829,7 +839,11 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.addText(carContext.getString(R.string.mastodon_handle))
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.mastodon_url))
openUrl(
carContext,
session.cas,
carContext.getString(R.string.mastodon_url)
)
}).build()
)
if (maxRows > 8) {
@@ -839,7 +853,11 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.addText(carContext.getString(R.string.twitter_handle))
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.twitter_url))
openUrl(
carContext,
session.cas,
carContext.getString(R.string.twitter_url)
)
}).build()
)
}
@@ -849,7 +867,7 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(
carContext,
carContext, session.cas,
carContext.getString(R.string.goingelectric_forum_url)
)
}).build()
@@ -862,7 +880,7 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(
carContext,
carContext, session.cas,
carContext.getString(R.string.tff_forum_url)
)
}).build()
@@ -874,14 +892,18 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
.setTitle(carContext.getString(R.string.github_link_title))
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.github_link))
openUrl(carContext, session.cas, carContext.getString(R.string.github_link))
}).build()
)
addItem(Row.Builder()
.setTitle(carContext.getString(R.string.privacy))
.setBrowsable(true)
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.privacy_link))
openUrl(
carContext,
session.cas,
carContext.getString(R.string.privacy_link)
)
}).build()
)
}.build(), carContext.getString(R.string.other)))
@@ -889,7 +911,8 @@ class AboutScreen(ctx: CarContext) : Screen(ctx) {
}
}
class AcceptPrivacyScreen(ctx: CarContext) : Screen(ctx) {
@ExperimentalCarApi
class AcceptPrivacyScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx) {
val prefs = PreferenceDataSource(ctx)
override fun onGetTemplate(): Template {
val textWithoutLink = HtmlCompat.fromHtml(
@@ -910,7 +933,7 @@ class AcceptPrivacyScreen(ctx: CarContext) : Screen(ctx) {
addAction(Action.Builder()
.setTitle(carContext.getString(R.string.privacy))
.setOnClickListener(ParkedOnlyOnClickListener.create {
openUrl(carContext, carContext.getString(R.string.privacy_link))
openUrl(carContext, session.cas, carContext.getString(R.string.privacy_link))
}).build()
)
}.build()

View File

@@ -12,6 +12,7 @@ import androidx.browser.customtabs.CustomTabsIntent
import androidx.car.app.CarContext
import androidx.car.app.CarToast
import androidx.car.app.Screen
import androidx.car.app.annotations.ExperimentalCarApi
import androidx.car.app.constraints.ConstraintManager
import androidx.car.app.hardware.common.CarUnit
import androidx.car.app.model.CarColor
@@ -221,13 +222,14 @@ fun supportsCarApiLevel3(ctx: CarContext): Boolean {
return true
}
fun openUrl(carContext: CarContext, url: String) {
@ExperimentalCarApi
fun openUrl(carContext: CarContext, cas: CarAppService, url: String) {
val intent = CustomTabsIntent.Builder()
.setDefaultColorSchemeParams(
CustomTabColorSchemeParams.Builder()
.setToolbarColor(
ContextCompat.getColor(
carContext,
cas,
R.color.colorPrimary
)
)
@@ -237,7 +239,7 @@ fun openUrl(carContext: CarContext, url: String) {
intent.data = Uri.parse(url)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
carContext.startActivity(intent)
cas.startActivity(intent)
if (BuildConfig.FLAVOR_automotive != "automotive") {
// only show the toast "opened on phone" if we're running on a phone
CarToast.makeText(