t/oxr_android: Add VrModeStatus fragment.

This commit is contained in:
Ryan Pavlik 2020-10-28 17:23:30 -05:00
parent ce3750bfe1
commit 03a129a4ea
3 changed files with 250 additions and 2 deletions

View file

@ -0,0 +1,196 @@
// Copyright 2020, Collabora, Ltd.
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Fragment to display the VR Mode status and actions.
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
*/
package org.freedesktop.monado.openxr_runtime;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
import android.provider.Settings;
import android.service.vr.VrListenerService;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
* A simple {@link Fragment} subclass.
* Use the {@link VrModeStatus#newInstance} factory method to
* create an instance of this fragment.
*/
public class VrModeStatus extends Fragment {
public static final int STATUS_UNKNOWN = -2;
public static final int STATUS_DISABLED = 0;
public static final int STATUS_ENABLED = 1;
public static final int STATUS_NOT_AVAIL = -1;
private static final String TAG = "MonadoVrModeStatus";
private static final String ARG_STATUS = "status";
private @Status
int status_ = STATUS_UNKNOWN;
public VrModeStatus() {
// Required empty public constructor
}
/**
* Get the ComponentName for a VrListenerService
*
* @param resolveInfo a ResolveInfo from PackageManager.queryIntentServices() with an
* android.service.vr.VrListenerService Intent and
* PackageManager.GET_META_DATA
* @return the ComponentName, or null if resolveInfo.serviceInfo is null
*/
private static ComponentName getVrListener(@NonNull ResolveInfo resolveInfo) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
if (serviceInfo == null) {
return null;
}
return new ComponentName(serviceInfo.packageName, serviceInfo.name);
}
private static @Nullable
ComponentName findOurselves(@NonNull List<ResolveInfo> resolutions, @NonNull String packageName) {
for (ResolveInfo resolveInfo : resolutions) {
ComponentName componentName = getVrListener(resolveInfo);
if (componentName == null) continue;
Log.i(TAG, "Looking at VrListener: " + componentName.getPackageName());
if (componentName.getPackageName().equals(packageName)) {
// we found us
return componentName;
}
}
return null;
}
/**
* Determine the VR mode status of this package.
*
* @param context A context to look up package manager info about this package.
* @param packageName The current package name (usually BuildConfig.APPLICATION_ID)
* @return the VR mode status
*/
public static @Status
int detectStatus(@NonNull Context context, @NonNull String packageName) {
Intent intent = new Intent(VrListenerService.SERVICE_INTERFACE);
PackageManager packageManager = context.getPackageManager();
// Suppression because we only care about finding out about our own package.
@SuppressLint("QueryPermissionsNeeded")
List<ResolveInfo> resolutions = packageManager.queryIntentServices(intent,
PackageManager.GET_META_DATA);
if (resolutions == null || resolutions.isEmpty()) {
return STATUS_NOT_AVAIL;
}
ComponentName us = findOurselves(resolutions, packageName);
if (us == null) {
Log.w(TAG, "Could not find ourselves in the list of VrListenerService implementations! " + packageName);
return STATUS_NOT_AVAIL;
}
try {
boolean enabled = VrListenerService.isVrModePackageEnabled(context, us);
return enabled ? STATUS_ENABLED : STATUS_DISABLED;
} catch (Exception e) {
return STATUS_NOT_AVAIL;
}
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param status The VR Mode status. See detectStatus()
* @return A new instance of fragment VrModeStatus.
*/
public static VrModeStatus newInstance(@Status int status) {
VrModeStatus fragment = new VrModeStatus();
Bundle args = new Bundle();
args.putInt(ARG_STATUS, status);
fragment.setArguments(args);
return fragment;
}
private static void launchVrSettings(Context context) {
Intent intent = new Intent(Settings.ACTION_VR_LISTENER_SETTINGS);
try {
context.startActivity(intent);
} catch (AndroidRuntimeException exception) {
Log.w("Monado", "Got exception trying to start VR listener settings: " + exception.toString());
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
status_ = getArguments().getInt(ARG_STATUS);
}
}
private void updateState(View v) {
TextView textEnabledDisabled = v.findViewById(R.id.textEnabledDisabled);
Button button = v.findViewById(R.id.btnLaunchVrSettings);
switch (status_) {
case STATUS_DISABLED:
textEnabledDisabled.setText(R.string.vr_mode_disabled);
textEnabledDisabled.setVisibility(View.VISIBLE);
button.setVisibility(View.VISIBLE);
break;
case STATUS_ENABLED:
textEnabledDisabled.setText(R.string.vr_mode_enabled);
textEnabledDisabled.setVisibility(View.VISIBLE);
button.setVisibility(View.VISIBLE);
break;
case STATUS_NOT_AVAIL:
textEnabledDisabled.setText(R.string.vr_mode_not_avail);
textEnabledDisabled.setVisibility(View.VISIBLE);
button.setVisibility(View.GONE);
break;
case STATUS_UNKNOWN:
textEnabledDisabled.setVisibility(View.GONE);
button.setVisibility(View.GONE);
break;
default:
throw new IllegalStateException("Unexpected value: " + status_);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_vr_mode_status, container, false);
updateState(view);
view.findViewById(R.id.btnLaunchVrSettings).setOnClickListener(v -> launchVrSettings(v.getContext()));
return view;
}
@IntDef(value = {STATUS_UNKNOWN, STATUS_DISABLED, STATUS_ENABLED, STATUS_NOT_AVAIL})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {
}
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".VrModeStatus">
<!--
Copyright 2020, Collabora, Ltd.
SPDX-License-Identifier: BSL-1.0
-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textEnabledDisabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@string/vr_mode_enabled"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnLaunchVrSettings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="@string/launch_settings"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textEnabledDisabled" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View file

@ -4,6 +4,13 @@
SPDX-License-Identifier: BSL-1.0
-->
<string name="app_name">FD.O Monado XR</string>
<string name="owered_by_monado">An XR Runtime, powered by Monado</string>
<string name="notice">NOTICE text goes here</string>
<string name="notice">"NOTICE text goes here\n\n\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam bibendum enim urna, consectetur mattis urna consectetur et. Aliquam erat volutpat. Nam dictum mi ac metus ullamcorper, eu finibus diam porta. Sed semper arcu mauris, eu porttitor arcu aliquet a. Donec et mauris id nulla aliquet eleifend. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque elementum velit non sem rutrum euismod.\n\nFusce venenatis justo ut suscipit sodales. Nunc tempor nunc nec est rhoncus varius. Quisque eget posuere sem, ac euismod nibh. Mauris viverra nisi varius varius hendrerit. Donec nibh est, ornare et ante eu, ultricies aliquet dolor. Nam laoreet velit tellus. In non felis sit amet velit mattis porttitor. Pellentesque non quam non erat porta auctor ut eget justo. Praesent non sem vitae metus blandit lacinia fringilla id mauris. Fusce quam magna, sodales eu tincidunt quis, accumsan et purus. Integer ipsum quam, egestas in malesuada id, gravida sed justo. Sed at leo tempus, porta turpis id, hendrerit dui. Nunc lobortis tortor ac orci vehicula, in sollicitudin ex pellentesque. Praesent euismod vulputate felis sed consectetur. Ut vitae quam velit.\n\nQuisque sodales sem nunc, sed semper ligula gravida ac. Nulla sit amet justo et enim suscipit lacinia ut vitae quam. Duis magna ipsum, gravida et eros a, vehicula viverra nibh. Fusce eu nisl ut massa congue auctor. Duis metus neque, egestas in accumsan scelerisque, rutrum efficitur libero. Sed bibendum id ante at pellentesque. Cras eget velit eu diam lacinia fringilla. Proin eleifend sit amet orci ac congue. Proin sed consequat justo. Sed et posuere velit.\n\nMauris consequat dignissim dui, nec ultrices augue elementum nec. Cras feugiat, ante non fringilla rhoncus, dolor metus volutpat eros, at rhoncus purus libero et mi. Sed rutrum iaculis nibh finibus congue. Maecenas sed mi vel orci volutpat sodales. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis non tortor aliquam felis mattis ultricies. Etiam iaculis massa id lacus dapibus placerat. Praesent ullamcorper imperdiet risus et commodo.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent scelerisque tellus purus, vel placerat sem fringilla in. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque lobortis mi nec luctus ullamcorper. Praesent diam tortor, accumsan ut vulputate non, volutpat eget ipsum. Sed sit amet ligula dictum, mollis lorem at, venenatis enim. Nullam mi nisl, pharetra suscipit facilisis non, posuere in nunc. Aliquam augue libero, eleifend non sagittis a, varius et arcu. Quisque arcu velit, lobortis eu felis ut, facilisis maximus urna. Integer nisl nisi, interdum sit amet ante nec, laoreet ullamcorper lacus. Donec lobortis non orci ac scelerisque. "</string>
<string name="powered_by_monado">An XR Runtime, powered by <a href="https://monado.dev">Monado</a></string>
<!-- Strings for the VR Mode status fragment -->
<string name="vr_mode_enabled">VR Mode is <b>enabled</b> for this runtime.</string>
<string name="vr_mode_disabled">VR Mode is available but not enabled for this runtime.</string>
<string name="vr_mode_not_avail">VR Mode is not available on this device.</string>
<string name="launch_settings">Open Android VR Settings</string>
<string name="monado_logo">Monado logo</string>
</resources>