[AA] Working GridView for Android Auto

With this commit we have the first functional version
of the AndroidAuto integration.
This commit is contained in:
Alexander Dörflinger
2025-04-09 12:57:06 +02:00
parent fdb2273af1
commit 93cae8d160
9 changed files with 111 additions and 81 deletions

View File

@@ -15,6 +15,12 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk android:minSdkVersion="32"
android:targetSdkVersion="34" />
<uses-feature
android:name="android.software.car.templates_host"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -31,6 +37,9 @@
android:value="AIzaSyB7C4QCJEBvS7mFa_DeIZdzqe2hddtl-vk" />
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<activity
android:name=".FlashlightActivity"
@@ -51,7 +60,6 @@
tools:ignore="LockedOrientationActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -71,9 +79,8 @@
<service android:name=".auto.ShareLocationCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.action.SERVICE" />
<action android:name="androidx.car.app.CarAppService"/>
<category android:name="androidx.car.app.category.MESSAGING"/>
<category android:name="androidx.intent.category.IOT" />
</intent-filter>
</service>

View File

@@ -476,6 +476,10 @@ public class ShareLocationActivity extends AppCompatActivity implements OnMapRea
@Override
public void onMapReady(@NonNull final GoogleMap googleMap) {
if (googleMap == null) {
Log.w(TAG, "onMapReady: Map null, cannot continue");
return;
}
mGmap = googleMap;
if (mIsNightMode) {
mGmap.setMapStyle(MapStyleOptions.loadRawResourceStyle(this, R.raw.map_style_night));

View File

@@ -7,8 +7,16 @@ import androidx.car.app.CarAppService;
import androidx.car.app.Session;
import androidx.car.app.validation.HostValidator;
/**
* The {@link CarAppService} extension to be called upon connection.
*/
public class ShareLocationCarAppService extends CarAppService {
/**
* Tag for debugging purposes.
*/
private static final String TAG = "ShareLocationCarAppServ";
@NonNull
@Override
public HostValidator createHostValidator() {
@@ -19,7 +27,13 @@ public class ShareLocationCarAppService extends CarAppService {
@NonNull
@Override
public Session onCreateSession() {
Log.d(TAG, "onCreateSession: ");
return new ShareLocationSession();
try {
Log.d(TAG, "onCreateSession: ");
return new ShareLocationSession();
} catch (Exception e) {
Log.e(TAG, "onCreateSession: ", e);
return new ShareLocationSession();
}
}
}

View File

@@ -1,10 +1,5 @@
package com.aldo.apps.familyhelpers.auto;
import static androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT;
import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SHARE_LOCATION_CANCEL_ACTION;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;
@@ -17,136 +12,127 @@ import androidx.car.app.Screen;
import androidx.car.app.annotations.ExperimentalCarApi;
import androidx.car.app.model.CarIcon;
import androidx.car.app.model.GridItem;
import androidx.car.app.model.GridTemplate;
import androidx.car.app.model.ItemList;
import androidx.car.app.model.ListTemplate;
import androidx.car.app.model.Template;
import androidx.car.app.notification.CarAppExtender;
import androidx.car.app.notification.CarNotificationManager;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.graphics.drawable.IconCompat;
import com.aldo.apps.familyhelpers.R;
import com.aldo.apps.familyhelpers.workers.LocationHelper;
import com.aldo.apps.familyhelpers.workers.ShareLocationBackgroundWorker;
/**
* {@link Screen} implementation to be shown in the Android Auto environment.
*/
public class ShareLocationScreen extends Screen {
private static final String AA_NOTIFICATION_CHANNEL = "ShareLocation_AA";
private static final int AA_NOTIFICATION_ID = 1;
/**
* Tag for debugging purpose.
*/
private static final String TAG = "ShareLocationScreen";
/**
* {@link LocationHelper} instance to observe the current sharing state.
*/
private LocationHelper mLocationHelper;
// Constructor for ShareLocationScreen
/**
* C'Tor.
*
* @param carContext The {@link CarContext} this was instantiated in.
*/
protected ShareLocationScreen(@NonNull CarContext carContext) {
super(carContext);
Log.d(TAG, "ShareLocationScreen() called with: carContext = [" + carContext + "]");
mLocationHelper = LocationHelper.getInstance(getCarContext());
}
@ExperimentalCarApi
@NonNull
@Override
public Template onGetTemplate() {
Log.d(TAG, "onGetTemplate() called");
mLocationHelper = LocationHelper.getInstance(getCarContext());
final GridItem startItem = new GridItem.Builder()
.setTitle(getString(R.string.android_auto_share_location_start))
.setText(getString(R.string.android_auto_share_location_start_desc))
.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_start_sharing))
.build(), GridItem.IMAGE_TYPE_LARGE)
.setImage(getIconForResource(R.drawable.ic_start_sharing), GridItem.IMAGE_TYPE_ICON)
.setOnClickListener(this::startLocationShareService)
.build();
final GridItem stopItem = new GridItem.Builder()
.setTitle(getString(R.string.android_auto_share_location_stop))
.setText(getString(R.string.android_auto_share_location_stop_desc))
.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_stop_sharing))
.build(), GridItem.IMAGE_TYPE_LARGE)
.setImage(getIconForResource(R.drawable.ic_stop_sharing), GridItem.IMAGE_TYPE_ICON)
.setOnClickListener(this::stopLocationSharingService)
.build();
final ItemList itemList = new ItemList.Builder()
.setNoItemsMessage("No Items yet, something went wrong I guess")
.setNoItemsMessage(getString(R.string.android_auto_share_location_no_items_yet))
.addItem(startItem)
.addItem(stopItem)
.build();
mLocationHelper.getSharingStateSubject().subscribe(this::createNotification, this::handleError);
return new ListTemplate.Builder()
return new GridTemplate.Builder()
.setLoading(false)
.setTitle("Share Location")
.setTitle(getString(R.string.android_auto_share_location_start_desc))
.setSingleList(itemList)
.build();
}
private void createNotification(final Boolean isSharing) {
Log.d(TAG, "createNotification() called with: isSharing = [" + isSharing + "]");
CarNotificationManager notificationManager = CarNotificationManager.from(getCarContext());
notificationManager.createNotificationChannel(
new NotificationChannelCompat.Builder(AA_NOTIFICATION_CHANNEL, IMPORTANCE_DEFAULT)
.build());
final PendingIntent serviceIntent;
@DrawableRes final int iconId;
final String title;
if (isSharing) {
// Create an intent to start the ShareLocationBackgroundWorker service
final Intent stopService = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
stopService.setAction(SHARE_LOCATION_CANCEL_ACTION);
serviceIntent = PendingIntent.getService(getCarContext(), 0, stopService, PendingIntent.FLAG_IMMUTABLE);
title = getString(R.string.android_auto_share_location_stop);
iconId = R.drawable.ic_stop_sharing;
} else {
// Create an intent to start the ShareLocationBackgroundWorker service
final Intent startServiceIntent = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
serviceIntent = PendingIntent.getService(getCarContext(), 0, startServiceIntent, PendingIntent.FLAG_IMMUTABLE);
title = getString(R.string.android_auto_share_location_start);
iconId = R.drawable.ic_start_sharing;
}
final NotificationCompat.Builder carNotification = new NotificationCompat.Builder(getCarContext(), AA_NOTIFICATION_CHANNEL)
.setContentTitle("Share Location")
.addAction(iconId, title, serviceIntent)
.extend(new CarAppExtender.Builder()
.build());
notificationManager.notify(AA_NOTIFICATION_ID, carNotification);
/**
* Helper method to get an Icon from Resources and tint it into the proper color.
*
* @param iconId The DrawableId of the icon.
*
* @return The CarIcon.
*/
private CarIcon getIconForResource(@DrawableRes final int iconId) {
final IconCompat iconCompat = IconCompat.createWithResource(getCarContext(), iconId);
iconCompat.setTint(getCarContext().getColor(R.color.md_theme_primary));
return new CarIcon.Builder(iconCompat).build();
}
/**
* Helper method to start the location sharing by starting the {@link ShareLocationBackgroundWorker}.
*/
private void startLocationShareService() {
Log.d(TAG, "startLocationShareService: ");
if (mLocationHelper != null && mLocationHelper.isCurrentlySharing()) {
CarToast.makeText(getCarContext(), "You already are sharing", CarToast.LENGTH_SHORT).show();
CarToast.makeText(getCarContext(), R.string.android_auto_share_location_already_sharing,
CarToast.LENGTH_SHORT).show();
return;
}
// Create an intent to start the ShareLocationBackgroundWorker service
final Intent startServiceIntent = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
final Intent serviceIntent = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
// Start the service using the car context
getCarContext().startForegroundService(startServiceIntent);
getCarContext().startForegroundService(serviceIntent);
}
/**
* Helper method to stop the location sharing by stopping the {@link ShareLocationBackgroundWorker}.
*/
private void stopLocationSharingService() {
Log.d(TAG, "stopLocationSharingService: ");
if (mLocationHelper != null && !mLocationHelper.isCurrentlySharing()) {
CarToast.makeText(getCarContext(), "You are not sharing", CarToast.LENGTH_SHORT).show();
CarToast.makeText(getCarContext(), R.string.android_auto_share_location_not_yet_sharing,
CarToast.LENGTH_SHORT).show();
return;
}
// Create an intent to start the ShareLocationBackgroundWorker service
final Intent startServiceIntent = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
final Intent serviceIntent = new Intent(getCarContext(), ShareLocationBackgroundWorker.class);
// Start the service using the car context
getCarContext().stopService(startServiceIntent);
getCarContext().stopService(serviceIntent);
}
/**
* Helper method to extract a string resource.
*
* @param stringId The String resource ID.
*
* @return The unwrapped string.
*/
private String getString(@StringRes final int stringId) {
return getCarContext().getString(stringId);
}
private void handleError(final Throwable error) {
Log.e(TAG, "handleErrot: ", error);
}
}

View File

@@ -7,7 +7,14 @@ import androidx.annotation.NonNull;
import androidx.car.app.Screen;
import androidx.car.app.Session;
/**
* The {@link Session} to be started by the CarAppService to show the {@link ShareLocationScreen}.
*/
public class ShareLocationSession extends Session {
/**
* Tag for debugging purposes.
*/
private static final String TAG = "ShareLocationSession";
@NonNull

View File

@@ -63,9 +63,13 @@
<string name="android_auto_actions_start_sharing">Ja, fang an zu teilen</string>
<string name="android_auto_actions_stop_sharing">Ja, hör auf zu teilen</string>
<string name="android_auto_share_location_start">Start</string>
<string name="android_auto_share_location_start_desc">Anfangen den Standort zu teilen</string>
<string name="android_auto_share_location_start_desc">Standrot teilen</string>
<string name="android_auto_share_location_stop">Stop</string>
<string name="android_auto_share_location_stop_desc">Aufhören den Standort zu teilen</string>
<string name="android_auto_share_location_stop_desc">Nicht mehr teilen</string>
<string name="pref_on_screen_flashlight_custom_color_summary">Wähle eine eigene Farbe für deine Bildschirm-Taschenlampe</string>
<string name="flashlight_on_screen_custom_color">Eigene Farbe</string>
<string name="android_auto_share_location_no_items_yet">Noch keine Einträge, seltsam...</string>
<string name="android_auto_share_location_already_sharing">Du teilst deinen Standort schon</string>
<string name="android_auto_share_location_not_yet_sharing">Du teilst deinen Standort noch nicht</string>
</resources>

View File

@@ -62,10 +62,15 @@
<string name="android_auto_message_not_sharing">Android Auto start has been detected, do you wanna share your location?</string>
<string name="android_auto_actions_start_sharing">Yes, start to share</string>
<string name="android_auto_actions_stop_sharing">Yes, stop sharing</string>
<string name="android_auto_share_location_start">Start</string>
<string name="android_auto_share_location_start_desc">Start to share location</string>
<string name="android_auto_share_location_start_desc">Share Location</string>
<string name="android_auto_share_location_stop">Stop</string>
<string name="android_auto_share_location_stop_desc">Stop to share the location</string>
<string name="android_auto_share_location_stop_desc">Stop Sharing</string>
<string name="android_auto_share_location_start">Start</string>
<string name="pref_on_screen_flashlight_custom_color_summary">Choose an own color to be displayed in the on-screen flashlight.</string>
<string name="flashlight_on_screen_custom_color">Custom Color</string>
<string name="android_auto_share_location_no_items_yet">No Items yet, something went wrong</string>
<string name="android_auto_share_location_already_sharing">You already share your location</string>
<string name="android_auto_share_location_not_yet_sharing">You are not yet sharing your location</string>
</resources>

View File

@@ -94,8 +94,11 @@
<string name="android_auto_actions_start_sharing">Yes, start to share</string>
<string name="android_auto_actions_stop_sharing">Yes, stop sharing</string>
<string name="android_auto_share_location_start">Start</string>
<string name="android_auto_share_location_start_desc">Start to share location</string>
<string name="android_auto_share_location_start_desc">Share Location</string>
<string name="android_auto_share_location_stop">Stop</string>
<string name="android_auto_share_location_stop_desc">Stop to share the location</string>
<string name="android_auto_share_location_stop_desc">Stop Sharing</string>
<string name="android_auto_share_location_no_items_yet">No Items yet, something went wrong</string>
<string name="android_auto_share_location_already_sharing">You already share your location</string>
<string name="android_auto_share_location_not_yet_sharing">You are not yet sharing your location</string>
</resources>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="notification" />
<uses name="template"/>
</automotiveApp>