From 2fe699d7c121d7a83434d2a0cf519a61a4b6ef74 Mon Sep 17 00:00:00 2001 From: Ryan Pavlik Date: Fri, 13 Nov 2020 15:02:07 -0600 Subject: [PATCH] xrt/android: Actually start using dependency injection in Java/Kotlin code. --- src/xrt/auxiliary/android/build.gradle | 6 + .../monado/auxiliary/MonadoView.java | 3 + .../monado/auxiliary/NameAndLogoProvider.kt | 29 ++++ .../monado/auxiliary/UiProvider.kt | 39 ++++++ src/xrt/ipc/android/build.gradle | 7 + .../org/freedesktop/monado/ipc/Client.java | 2 +- .../freedesktop/monado/ipc/MonadoService.kt | 4 +- src/xrt/targets/openxr_android/build.gradle | 12 ++ .../src/main/AndroidManifest.xml | 1 + .../monado/openxr_runtime/AboutActivity.java | 3 + .../MonadoOpenXrAndroidModule.kt | 33 +++++ .../MonadoOpenXrApplication.java | 19 +++ .../MonadoOpenXrBrandingUiProvider.kt | 28 ++++ .../openxr_runtime/MonadoOpenXrUiProvider.kt | 41 ++++++ .../monado/openxr_runtime/VrModeStatus.java | 19 ++- .../res/raw/ic_notif_xr_letters_custom.svg | 129 ++++++++++++++++++ 16 files changed, 372 insertions(+), 3 deletions(-) create mode 100644 src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/NameAndLogoProvider.kt create mode 100644 src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/UiProvider.kt create mode 100644 src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrAndroidModule.kt create mode 100644 src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrApplication.java create mode 100644 src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrBrandingUiProvider.kt create mode 100644 src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrUiProvider.kt create mode 100644 src/xrt/targets/openxr_android/src/main/res/raw/ic_notif_xr_letters_custom.svg diff --git a/src/xrt/auxiliary/android/build.gradle b/src/xrt/auxiliary/android/build.gradle index 09adeff78..14b2a2894 100644 --- a/src/xrt/auxiliary/android/build.gradle +++ b/src/xrt/auxiliary/android/build.gradle @@ -4,6 +4,7 @@ plugins { id 'com.android.library' id 'kotlin-android' + id 'dagger.hilt.android.plugin' } android { @@ -39,4 +40,9 @@ dependencies { implementation "androidx.annotation:annotation:$androidxAnnotationVersion" implementation "androidx.core:core-ktx:$androidxCoreVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" + + // for Hilt dependency injection + implementation "com.google.dagger:hilt-android:$hiltVersion" + // Only a Java file has annotations right now, so we don't yet need kapt + annotationProcessor "com.google.dagger:hilt-compiler:$hiltVersion" } diff --git a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/MonadoView.java b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/MonadoView.java index 9feffcd38..4eb3a8efc 100644 --- a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/MonadoView.java +++ b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/MonadoView.java @@ -27,7 +27,10 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Calendar; +import dagger.hilt.android.AndroidEntryPoint; + @Keep +@AndroidEntryPoint public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, SurfaceHolder.Callback2 { private static final String TAG = "MonadoView"; @SuppressWarnings("deprecation") diff --git a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/NameAndLogoProvider.kt b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/NameAndLogoProvider.kt new file mode 100644 index 000000000..5c4bce56d --- /dev/null +++ b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/NameAndLogoProvider.kt @@ -0,0 +1,29 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Interface for target-specific branding things on Android. + * @author Ryan Pavlik + * @ingroup aux_android + */ + +package org.freedesktop.monado.auxiliary + +import android.graphics.drawable.Drawable + +/** + * Branding-related UI stuff. This interface must be provided by any Android "XRT Target". + * + * Intended for use in dependency injection. + */ +interface NameAndLogoProvider { + /** + * Gets a localized runtime name string for the runtime/Monado-incorporating target. + */ + fun getLocalizedRuntimeName(): CharSequence + + /** + * Gets a drawable for use in the about activity and elsewhere, for the runtime/Monado-incorporating target. + */ + fun getLogoDrawable(): Drawable? +} diff --git a/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/UiProvider.kt b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/UiProvider.kt new file mode 100644 index 000000000..3a9c89c7e --- /dev/null +++ b/src/xrt/auxiliary/android/src/main/java/org/freedesktop/monado/auxiliary/UiProvider.kt @@ -0,0 +1,39 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Interface for target-specific UI-related things on Android. + * @author Ryan Pavlik + * @ingroup aux_android + */ +package org.freedesktop.monado.auxiliary + +import android.app.PendingIntent +import android.content.Context +import android.graphics.drawable.Drawable +import android.graphics.drawable.Icon + +/** + * Non-branding-related UI stuff. This interface must be provided by any Android "XRT Target". + * + * Intended for use in dependency injection. + */ +interface UiProvider { + /** + * Gets a drawable for use in a notification, for the runtime/Monado-incorporating target. + * + * Optional - you can return null. + */ + fun getNotificationIcon(): Icon? = null + + /** + * Make a {@code PendingIntent} to launch an "About" activity for the runtime/target. + */ + fun makeAboutActivityPendingIntent(): PendingIntent + + /** + * Make a {@code PendingIntent} to launch a configuration activity, if provided by the target. + */ + fun makeConfigureActivityPendingIntent(): PendingIntent? = null + +} diff --git a/src/xrt/ipc/android/build.gradle b/src/xrt/ipc/android/build.gradle index c9cd398b6..5a5b0a762 100644 --- a/src/xrt/ipc/android/build.gradle +++ b/src/xrt/ipc/android/build.gradle @@ -4,6 +4,9 @@ plugins { id 'com.android.library' id 'kotlin-android' + + id 'kotlin-kapt' + id 'dagger.hilt.android.plugin' } android { @@ -49,4 +52,8 @@ dependencies { implementation "androidx.annotation:annotation:$androidxAnnotationVersion" implementation "androidx.core:core-ktx:$androidxCoreVersion" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion" + + // for Hilt dependency injection + api "com.google.dagger:hilt-android:$hiltVersion" + kapt "com.google.dagger:hilt-compiler:$hiltVersion" } diff --git a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/Client.java b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/Client.java index 349862871..4c39a548a 100644 --- a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/Client.java +++ b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/Client.java @@ -157,7 +157,7 @@ public class Client implements ServiceConnection { return false; } - intent = new Intent(BuildConfig.SERVICE_ACTION) + Intent intent = new Intent(BuildConfig.SERVICE_ACTION) .setPackage(packageName); context.startForegroundService(intent); diff --git a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt index 29b6d38ec..ba3f9af1f 100644 --- a/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt +++ b/src/xrt/ipc/android/src/main/java/org/freedesktop/monado/ipc/MonadoService.kt @@ -11,16 +11,18 @@ package org.freedesktop.monado.ipc import android.app.Service import android.content.Intent import android.os.IBinder +import dagger.hilt.android.AndroidEntryPoint /** * Minimal implementation of a Service. * * This is needed so that the APK can expose the binder service implemented in MonadoImpl. */ +@AndroidEntryPoint class MonadoService : Service() { val monado: MonadoImpl = MonadoImpl() override fun onBind(intent: Intent): IBinder? { - return monado; + return monado } } diff --git a/src/xrt/targets/openxr_android/build.gradle b/src/xrt/targets/openxr_android/build.gradle index 9b7bb1a7f..a8b0ef46e 100644 --- a/src/xrt/targets/openxr_android/build.gradle +++ b/src/xrt/targets/openxr_android/build.gradle @@ -6,6 +6,13 @@ import groovy.xml.XmlUtil plugins { id 'com.android.application' id 'kotlin-android' + + // Hilt dependency injection + id 'kotlin-kapt' + id 'dagger.hilt.android.plugin' + + // SVG files in the "raw" resource directory will be transformed into vector drawables of the same name. + id 'com.quittle.svg-2-android-vector' } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' @@ -178,4 +185,9 @@ dependencies { implementation "androidx.cardview:cardview:1.*.*" implementation "androidx.recyclerview:recyclerview:1.1.*" + + // for Hilt dependency injection + implementation "com.google.dagger:hilt-android:$hiltVersion" + kapt "com.google.dagger:hilt-compiler:$hiltVersion" + } diff --git a/src/xrt/targets/openxr_android/src/main/AndroidManifest.xml b/src/xrt/targets/openxr_android/src/main/AndroidManifest.xml index b5b59f6b1..fc10253b4 100644 --- a/src/xrt/targets/openxr_android/src/main/AndroidManifest.xml +++ b/src/xrt/targets/openxr_android/src/main/AndroidManifest.xml @@ -39,6 +39,7 @@ android:required="false" /> + */ + +package org.freedesktop.monado.openxr_runtime + +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ApplicationComponent +import org.freedesktop.monado.auxiliary.NameAndLogoProvider +import org.freedesktop.monado.auxiliary.UiProvider + +/** + * This is implemented by Hilt/Dagger to do dependency injection. + * + * Each declaration essentially signals to Hilt/Dagger what subclass/implementation of a + * base/interface to use for each thing it must inject. + */ +@Module +@InstallIn(ApplicationComponent::class) +abstract class MonadoOpenXrAndroidModule { + @Binds + abstract fun bindUiProvider(uiProvider: MonadoOpenXrUiProvider): UiProvider + + @Binds + abstract fun bindNameAndLogo(monadoOpenXrBrandingUiProvider: MonadoOpenXrBrandingUiProvider): NameAndLogoProvider + +} diff --git a/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrApplication.java b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrApplication.java new file mode 100644 index 000000000..60022b11e --- /dev/null +++ b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrApplication.java @@ -0,0 +1,19 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Simple Application subclass. + * @author Ryan Pavlik + */ +package org.freedesktop.monado.openxr_runtime; + +import android.app.Application; + +import dagger.hilt.android.HiltAndroidApp; + +/** + * Subclass required for Hilt usage. + */ +@HiltAndroidApp +public class MonadoOpenXrApplication extends Application { +} diff --git a/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrBrandingUiProvider.kt b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrBrandingUiProvider.kt new file mode 100644 index 000000000..799111f44 --- /dev/null +++ b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrBrandingUiProvider.kt @@ -0,0 +1,28 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Simple implementation of NameAndLogoProvider. + * @author Ryan Pavlik + */ + +package org.freedesktop.monado.openxr_runtime + +import android.content.Context +import android.graphics.drawable.Drawable +import androidx.core.content.ContextCompat +import dagger.hilt.android.qualifiers.ApplicationContext +import org.freedesktop.monado.auxiliary.NameAndLogoProvider +import javax.inject.Inject + +class MonadoOpenXrBrandingUiProvider @Inject constructor(@ApplicationContext val context: Context) : NameAndLogoProvider { + /** + * Gets a localized runtime name string for the runtime/Monado-incorporating target. + */ + override fun getLocalizedRuntimeName(): CharSequence = context.packageManager.getApplicationLabel(context.applicationInfo) + + /** + * Gets a drawable for use in the about activity and elsewhere, for the runtime/Monado-incorporating target. + */ + override fun getLogoDrawable(): Drawable? = ContextCompat.getDrawable(context, R.drawable.ic_monado_logo) +} diff --git a/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrUiProvider.kt b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrUiProvider.kt new file mode 100644 index 000000000..bfb3f7ab7 --- /dev/null +++ b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/MonadoOpenXrUiProvider.kt @@ -0,0 +1,41 @@ +// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Simple implementation of UiProvider. + * @author Ryan Pavlik + */ + +package org.freedesktop.monado.openxr_runtime + +import android.app.PendingIntent +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import android.graphics.drawable.Icon +import dagger.hilt.android.qualifiers.ApplicationContext +import org.freedesktop.monado.auxiliary.UiProvider +import javax.inject.Inject + +class MonadoOpenXrUiProvider @Inject constructor(@ApplicationContext val context: Context) : UiProvider { + + /** + * Gets a drawable for use in a notification, for the runtime/Monado-incorporating target. + */ + override fun getNotificationIcon(): Icon? = + Icon.createWithResource(context, R.drawable.ic_notif_xr_letters_custom) + + /** + * Make a {@code PendingIntent} to launch an "About" activity for the runtime/target. + */ + override fun makeAboutActivityPendingIntent(): PendingIntent = + PendingIntent.getActivity(context, + 0, + Intent.makeMainActivity( + ComponentName.createRelative(context, + AboutActivity::class.qualifiedName!!)), + 0 + ) + + +} diff --git a/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/VrModeStatus.java b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/VrModeStatus.java index 16c9602c1..515740af6 100644 --- a/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/VrModeStatus.java +++ b/src/xrt/targets/openxr_android/src/main/java/org/freedesktop/monado/openxr_runtime/VrModeStatus.java @@ -32,14 +32,22 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import org.freedesktop.monado.auxiliary.NameAndLogoProvider; +import org.freedesktop.monado.auxiliary.UiProvider; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; + /** * A Fragment for displaying/affecting VR Listener status. */ +@AndroidEntryPoint public class VrModeStatus extends Fragment { public static final int STATUS_UNKNOWN = -2; public static final int STATUS_DISABLED = 0; @@ -47,6 +55,13 @@ public class VrModeStatus extends Fragment { public static final int STATUS_NOT_AVAIL = -1; private static final String TAG = "MonadoVrModeStatus"; private static final String ARG_STATUS = "status"; + + @Inject + UiProvider uiProvider; + + @Inject + NameAndLogoProvider nameAndLogoProvider; + private @Status int status_ = STATUS_UNKNOWN; @@ -164,7 +179,9 @@ public class VrModeStatus extends Fragment { button.setVisibility(View.VISIBLE); break; case STATUS_NOT_AVAIL: - textEnabledDisabled.setText(res.getString(R.string.vr_mode_not_avail, res.getString(R.string.app_name))); + textEnabledDisabled.setText( + res.getString(R.string.vr_mode_not_avail, + nameAndLogoProvider.getLocalizedRuntimeName())); textEnabledDisabled.setVisibility(View.VISIBLE); button.setVisibility(View.GONE); break; diff --git a/src/xrt/targets/openxr_android/src/main/res/raw/ic_notif_xr_letters_custom.svg b/src/xrt/targets/openxr_android/src/main/res/raw/ic_notif_xr_letters_custom.svg new file mode 100644 index 000000000..b4403051a --- /dev/null +++ b/src/xrt/targets/openxr_android/src/main/res/raw/ic_notif_xr_letters_custom.svg @@ -0,0 +1,129 @@ + + + + + XR Notification Icon + + + + + + + + + + + image/svg+xml + + XR Notification Icon + + 2020 + + + Collabora, Ltd. + + + + + Collabora, Ltd. + + + + + Ryan Pavlik <ryan.pavlik@collabora.com> + + + + + Copyright 2020 + + + + + + + + + + + + + + + +