diff --git a/app/build.gradle b/app/build.gradle index a639ca8..299b6f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.2.1' implementation 'io.reactivex.rxjava3:rxjava:3.1.5' implementation 'androidx.car.app:app:1.4.0' + implementation 'androidx.car.app:app-projected:1.4.0' implementation 'androidx.concurrent:concurrent-futures:1.2.0' // Color Picker Preference diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 240ed16..ca5d4c0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,8 @@ + + diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java index 0850366..7dcd978 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java @@ -2,6 +2,7 @@ package com.aldo.apps.familyhelpers; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.POST_NOTIFICATIONS; +import static com.aldo.apps.familyhelpers.utils.GlobalConstants.CAR_SPEED_PERMISSION; import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SIGN_IN_PROVIDERS; import android.content.Intent; @@ -168,6 +169,9 @@ public class HelperGridActivity extends AppCompatActivity { Log.d(TAG, "launchHelper: Notifications not allowed, return..."); return; } + if (!requestVehicleSpeedPermission()) { + Log.d(TAG, "launchHelper: Permission not granted cannot read vehicle speed."); + } if (mLocationHelper.requestLocationPermissions(HelperGridActivity.this) && mLocationHelper.requestBackgroundLocationPermission(HelperGridActivity.this)) { Log.d(TAG, "launchHelper: Permission already granted"); @@ -232,6 +236,26 @@ public class HelperGridActivity extends AppCompatActivity { } } + /** + * Helper method to request the VehicleSpeed permission before launching the ShareLocationActivity. + * + * @return true if granted, flase otherwise. + */ + private boolean requestVehicleSpeedPermission() { + if (ContextCompat.checkSelfPermission(this, CAR_SPEED_PERMISSION) == + PackageManager.PERMISSION_GRANTED) { + // Permission already granted + return true; + } else { + if (shouldShowRequestPermissionRationale(CAR_SPEED_PERMISSION)) { + Toast.makeText(HelperGridActivity.this, "Consider granting this for the vehicle speed in Android Auto to be more accurate", + Toast.LENGTH_LONG).show(); + } + mRequestPermissionLauncher.launch(CAR_SPEED_PERMISSION); + return false; + } + } + /** * Helper method to request the CameraPermission as it is required to access the * flashlight settings. diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/auto/ShareLocationScreen.java b/app/src/main/java/com/aldo/apps/familyhelpers/auto/ShareLocationScreen.java index ed47340..5f5e4c7 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/auto/ShareLocationScreen.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/auto/ShareLocationScreen.java @@ -16,6 +16,10 @@ import androidx.car.app.model.GridTemplate; import androidx.car.app.model.ItemList; import androidx.car.app.model.Template; import androidx.core.graphics.drawable.IconCompat; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleObserver; +import androidx.lifecycle.LifecycleOwner; import com.aldo.apps.familyhelpers.R; import com.aldo.apps.familyhelpers.workers.LocationHelper; @@ -36,6 +40,26 @@ public class ShareLocationScreen extends Screen { */ private LocationHelper mLocationHelper; + /** + * {@link VehicleInformationHelper} to read vehicle information in AA use case. + */ + private VehicleInformationHelper mVehicleInformationHelper; + + /** + * {@link LifecycleObserver} to stop listening to Vehicle Speed updates when AA ends. + */ + private LifecycleObserver mLifecycleObserver = new LifecycleEventObserver() { + @Override + public void onStateChanged(@NonNull final LifecycleOwner lifecycleOwner, + @NonNull final Lifecycle.Event event) { + + if (event == Lifecycle.Event.ON_STOP) { + Log.d(TAG, "onStateChanged: Activity stopped, unsubscribe"); + mVehicleInformationHelper.unsubscribeFromSpeedValue(); + } + } + }; + /** * C'Tor. * @@ -44,6 +68,8 @@ public class ShareLocationScreen extends Screen { protected ShareLocationScreen(@NonNull CarContext carContext) { super(carContext); Log.d(TAG, "ShareLocationScreen() called with: carContext = [" + carContext + "]"); + mVehicleInformationHelper = new VehicleInformationHelper(carContext); + getLifecycle().addObserver(mLifecycleObserver); } @ExperimentalCarApi @@ -52,6 +78,7 @@ public class ShareLocationScreen extends Screen { public Template onGetTemplate() { Log.d(TAG, "onGetTemplate() called"); mLocationHelper = LocationHelper.getInstance(getCarContext()); + mVehicleInformationHelper.subscribeToSpeedValues(getCarContext()); final GridItem startItem = new GridItem.Builder() .setTitle(getString(R.string.android_auto_share_location_start)) @@ -67,10 +94,18 @@ public class ShareLocationScreen extends Screen { .setOnClickListener(this::stopLocationSharingService) .build(); + final GridItem startNavigation = new GridItem.Builder() + .setTitle(getString(R.string.android_auto_share_location_navigation_title)) + .setText(getString(R.string.android_auto_share_location_naviagation_summary)) + .setImage(getIconForResource(R.drawable.ic_navigation), GridItem.IMAGE_TYPE_ICON) + .setOnClickListener(this::startNavigationScreen) + .build(); + final ItemList itemList = new ItemList.Builder() .setNoItemsMessage(getString(R.string.android_auto_share_location_no_items_yet)) .addItem(startItem) .addItem(stopItem) + .addItem(startNavigation) .build(); return new GridTemplate.Builder() @@ -125,6 +160,13 @@ public class ShareLocationScreen extends Screen { getCarContext().stopService(serviceIntent); } + /** + * Helper method to start The Navigation screen. + */ + private void startNavigationScreen() { + CarToast.makeText(getCarContext(), "Not yet implemented", CarToast.LENGTH_LONG).show(); + } + /** * Helper method to extract a string resource. * diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/auto/VehicleInformationHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/auto/VehicleInformationHelper.java new file mode 100644 index 0000000..8fded6f --- /dev/null +++ b/app/src/main/java/com/aldo/apps/familyhelpers/auto/VehicleInformationHelper.java @@ -0,0 +1,82 @@ +package com.aldo.apps.familyhelpers.auto; + +import static com.aldo.apps.familyhelpers.utils.GlobalConstants.CAR_SPEED_PERMISSION; + +import android.content.pm.PackageManager; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.car.app.CarContext; +import androidx.car.app.hardware.CarHardwareManager; +import androidx.car.app.hardware.info.CarInfo; +import androidx.car.app.hardware.info.Speed; + +import com.aldo.apps.familyhelpers.workers.LocationHelper; + +/** + * Helper class to retrieve a set of {@link CarInfo} to be used in other functionalities. + */ +public class VehicleInformationHelper { + + /** + * Tag for debugging purposes. + */ + private static final String TAG = "VehicleInformationHelper"; + + /** + * The {@link CarInfo} as retrieved from the CarService. + */ + private CarInfo mCarInfo; + + /** + * The {@link LocationHelper} to apply the speed in. + */ + private LocationHelper mLocationHelper; + + /** + * C'Tor. + * + * @param carContext The {@link CarContext} from which this was instantiated. + */ + public VehicleInformationHelper(final CarContext carContext) { + if (carContext.checkCallingOrSelfPermission(CAR_SPEED_PERMISSION) != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "VehicleInformationHelper: Nothing to do, permission not granted"); + return; + } + mLocationHelper = LocationHelper.getInstance(carContext); + } + + /** + * Subscribe to the Vehicle Speed and handle it. + * + * @param carContext The {@link CarContext} from which this was called. + */ + public void subscribeToSpeedValues(final CarContext carContext) { + if (carContext.checkCallingOrSelfPermission(CAR_SPEED_PERMISSION) == PackageManager.PERMISSION_GRANTED) { + mCarInfo = carContext.getCarService(CarHardwareManager.class).getCarInfo(); + if (mCarInfo == null) { + Log.e(TAG, "subscribeToSpeedValues: No car info available"); + return; + } + mCarInfo.addSpeedListener(carContext.getMainExecutor(), this::handleReceivedSpeed); + } else { + Log.w(TAG, "subscribeToSpeedValues: No Speed permission granted, cannot subscribe"); + } + } + + /** + * When AA ends, unsubscribe from the speed value again. + */ + public void unsubscribeFromSpeedValue() { + mCarInfo.removeSpeedListener(this::handleReceivedSpeed); + } + + /** + * Handle the received speed value by publishing it to the {@link LocationHelper}. + * + * @param data The {@link Speed} data. + */ + private void handleReceivedSpeed(@NonNull final Speed data) { + mLocationHelper.updateVehicleSpeed(data.getDisplaySpeedMetersPerSecond()); + } +} diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java b/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java index ff70331..e415eb0 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/model/LocationObject.java @@ -126,6 +126,17 @@ public class LocationObject { return speed; } + /** + * Setter for the speed to be used if the speed is received from the vehicle. + * + * @param vehicleSpeed The received vehicle speed. + */ + public void setSpeed(final Float vehicleSpeed) { + if (vehicleSpeed != null) { + speed = vehicleSpeed; + } + } + /** * Returns the last received timestamp. * diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java b/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java index 9ae1918..820d7f1 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/utils/GlobalConstants.java @@ -77,6 +77,12 @@ public final class GlobalConstants { new AuthUI.IdpConfig.GoogleBuilder().build() ); + /** + * Name of the permission for the VehicleSpeed. + */ + public static final String CAR_SPEED_PERMISSION = "com.google.android.gms.permission.CAR_SPEED"; + + /** * Private C'tor to prevent instantiation. */ diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java index 3e4e3a0..5627825 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java @@ -19,6 +19,7 @@ import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.car.app.hardware.common.CarValue; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; @@ -96,7 +97,6 @@ public final class LocationHelper { /** * The {@link BehaviorSubject} for the {@link LocationObject} for clients to subscribe to. - * TODO: Check if needed. */ private final BehaviorSubject mLocationSubject = BehaviorSubject.create(); /** @@ -111,6 +111,11 @@ public final class LocationHelper { * The {@link BehaviorSubject} holding the state of current subscription. */ private final BehaviorSubject mSharingStateSubject = BehaviorSubject.createDefault(false); + + /** + * The {@link CarValue} of the vehicles speed in meters per second if available. + */ + private CarValue mVehicleSpeed; /** * Boolean flag to check whether a subscription for location updates is already running or not. */ @@ -176,6 +181,11 @@ public final class LocationHelper { return true; } + public void updateVehicleSpeed(final CarValue vehicleSpeed) { + Log.d(TAG, "updateVehicleSpeed() called with: vehicleSpeed = [" + vehicleSpeed + "]"); + mVehicleSpeed = vehicleSpeed; + } + /** * Request the permission for {@link android.Manifest.permission#ACCESS_BACKGROUND_LOCATION} if not * yet granted before. @@ -296,6 +306,10 @@ public final class LocationHelper { */ private void handleReceivedLocation(final Location location) { final LocationObject locationObject = LocationObject.fromLocation(location); + if (mVehicleSpeed != null && mVehicleSpeed.getStatus() == CarValue.STATUS_SUCCESS) { + Log.d(TAG, "handleReceivedLocation: VehicleSpeed available"); + locationObject.setSpeed(mVehicleSpeed.getValue()); + } Log.d(TAG, "onLocationChanged: Received update with " + locationObject); mLocationSubject.onNext(locationObject); mDbHelper.insertOrUpdateLocation(locationObject); diff --git a/app/src/main/res/drawable/ic_navigation.xml b/app/src/main/res/drawable/ic_navigation.xml new file mode 100644 index 0000000..b76cef4 --- /dev/null +++ b/app/src/main/res/drawable/ic_navigation.xml @@ -0,0 +1,7 @@ + + + diff --git a/app/src/main/res/drawable/ic_start_sharing.xml b/app/src/main/res/drawable/ic_start_sharing.xml index b1e9e5d..04269e2 100644 --- a/app/src/main/res/drawable/ic_start_sharing.xml +++ b/app/src/main/res/drawable/ic_start_sharing.xml @@ -1,16 +1,4 @@ - - - - - + diff --git a/app/src/main/res/drawable/ic_stop_sharing.xml b/app/src/main/res/drawable/ic_stop_sharing.xml index fc1c269..ff6f9a6 100644 --- a/app/src/main/res/drawable/ic_stop_sharing.xml +++ b/app/src/main/res/drawable/ic_stop_sharing.xml @@ -1,8 +1,5 @@ - - + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c386bb8..36e69e6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -63,13 +63,15 @@ Ja, fang an zu teilen Ja, hör auf zu teilen Start - Standrot teilen + Standort teilen Stop Nicht mehr teilen Wähle eine eigene Farbe für deine Bildschirm-Taschenlampe Eigene Farbe - Noch keine Einträge, seltsam... + Noch keine Einträge, seltsam… Du teilst deinen Standort schon Du teilst deinen Standort noch nicht + Navigation + Teilen und navigieren \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 73a6dcb..156a860 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -71,6 +71,8 @@ No Items yet, something went wrong You already share your location You are not yet sharing your location + Navigate + Share and navigate \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 253aa5c..0366348 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,5 +100,7 @@ No Items yet, something went wrong You already share your location You are not yet sharing your location + Navigate + Share and navigate \ No newline at end of file