Merge "VDM demo: all repohooks fixed for client/" into main

This commit is contained in:
Treehugger Robot
2023-12-06 13:50:15 +00:00
committed by Android (Google) Code Review
11 changed files with 277 additions and 290 deletions

View File

@@ -47,25 +47,25 @@ final class AudioPlayer implements Consumer<RemoteEvent> {
SAMPLE_RATE, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);
private static final int AUDIOTRACK_BUFFER_SIZE = 4 * MIN_AUDIOTRACK_BUFFER_SIZE;
private final Object lock = new Object();
private AudioTrack audioTrack;
private final Object mLock = new Object();
private AudioTrack mAudioTrack;
@Inject
AudioPlayer() {}
private void startPlayback() {
synchronized (lock) {
if (audioTrack != null) {
synchronized (mLock) {
if (mAudioTrack != null) {
Log.w(TAG, "Received startPlayback command without stopping the playback first");
stopPlayback();
}
audioTrack =
mAudioTrack =
new AudioTrack.Builder()
.setAudioFormat(AUDIO_FORMAT)
.setAudioAttributes(AUDIO_ATTRIBUTES)
.setBufferSizeInBytes(AUDIOTRACK_BUFFER_SIZE)
.build();
audioTrack.play();
mAudioTrack.play();
}
}
@@ -76,14 +76,14 @@ final class AudioPlayer implements Consumer<RemoteEvent> {
return;
}
int bytesWritten = 0;
synchronized (lock) {
if (audioTrack == null) {
synchronized (mLock) {
if (mAudioTrack == null) {
Log.e(TAG, "Received audio frame, but audio track was not initialized yet");
return;
}
while (bytesToWrite > 0) {
int ret = audioTrack.write(data, bytesWritten, bytesToWrite);
int ret = mAudioTrack.write(data, bytesWritten, bytesToWrite);
if (ret <= 0) {
Log.e(TAG, "AudioTrack.write returned error code " + ret);
}
@@ -94,13 +94,13 @@ final class AudioPlayer implements Consumer<RemoteEvent> {
}
private void stopPlayback() {
synchronized (lock) {
if (audioTrack == null) {
synchronized (mLock) {
if (mAudioTrack == null) {
Log.w(TAG, "Received stopPlayback command for already stopped playback");
} else {
audioTrack.stop();
audioTrack.release();
audioTrack = null;
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
}

View File

@@ -30,12 +30,12 @@ import java.util.function.Consumer;
/** Recycler view that can resize a child dynamically. */
public final class ClientView extends RecyclerView {
private boolean isResizing = false;
private Consumer<Rect> resizeDoneCallback = null;
private Drawable resizingRect = null;
private Rect resizingBounds = new Rect();
private float resizeOffsetX = 0;
private float resizeOffsetY = 0;
private boolean mIsResizing = false;
private Consumer<Rect> mResizeDoneCallback = null;
private Drawable mResizingRect = null;
private final Rect mResizingBounds = new Rect();
private float mResizeOffsetX = 0;
private float mResizeOffsetY = 0;
public ClientView(Context context) {
super(context);
@@ -53,48 +53,44 @@ public final class ClientView extends RecyclerView {
}
private void init() {
resizingRect = getContext().getResources().getDrawable(R.drawable.resize_rect);
mResizingRect = getContext().getResources().getDrawable(R.drawable.resize_rect, null);
}
void startResizing(View viewToResize, MotionEvent origin, Consumer<Rect> callback) {
isResizing = true;
resizeDoneCallback = callback;
viewToResize.getGlobalVisibleRect(resizingBounds);
resizingRect.setBounds(resizingBounds);
getRootView().getOverlay().add(resizingRect);
resizeOffsetX = origin.getRawX() - resizingBounds.right;
resizeOffsetY = origin.getRawY() - resizingBounds.top;
mIsResizing = true;
mResizeDoneCallback = callback;
viewToResize.getGlobalVisibleRect(mResizingBounds);
mResizingRect.setBounds(mResizingBounds);
getRootView().getOverlay().add(mResizingRect);
mResizeOffsetX = origin.getRawX() - mResizingBounds.right;
mResizeOffsetY = origin.getRawY() - mResizingBounds.top;
}
private void stopResizing() {
if (!isResizing) {
if (!mIsResizing) {
return;
}
isResizing = false;
resizeOffsetX = resizeOffsetY = 0;
mIsResizing = false;
mResizeOffsetX = mResizeOffsetY = 0;
getRootView().getOverlay().clear();
if (resizeDoneCallback != null) {
resizeDoneCallback.accept(resizingBounds);
resizeDoneCallback = null;
if (mResizeDoneCallback != null) {
mResizeDoneCallback.accept(mResizingBounds);
mResizeDoneCallback = null;
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!isResizing) {
if (!mIsResizing) {
return super.dispatchTouchEvent(ev);
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
stopResizing();
break;
case MotionEvent.ACTION_MOVE:
resizingBounds.right = (int) (ev.getRawX() - resizeOffsetX);
resizingBounds.top = (int) (ev.getRawY() - resizeOffsetY);
resizingRect.setBounds(resizingBounds);
break;
default:
break;
case MotionEvent.ACTION_UP -> stopResizing();
case MotionEvent.ACTION_MOVE -> {
mResizingBounds.right = (int) (ev.getRawX() - mResizeOffsetX);
mResizingBounds.top = (int) (ev.getRawY() - mResizeOffsetY);
mResizingRect.setBounds(mResizingBounds);
}
}
return true;
}

View File

@@ -30,6 +30,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -46,34 +47,35 @@ import java.util.concurrent.atomic.AtomicInteger;
final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
private static final String TAG = "VdmClient";
private static final AtomicInteger nextDisplayIndex = new AtomicInteger(1);
private static final AtomicInteger sNextDisplayIndex = new AtomicInteger(1);
// Simple list of all active displays.
private final List<RemoteDisplay> displayRepository =
private final List<RemoteDisplay> mDisplayRepository =
Collections.synchronizedList(new ArrayList<>());
private final RemoteIo remoteIo;
private final ClientView recyclerView;
private final InputManager inputManager;
private final RemoteIo mRemoteIo;
private final ClientView mRecyclerView;
private final InputManager mInputManager;
DisplayAdapter(ClientView recyclerView, RemoteIo remoteIo, InputManager inputManager) {
this.recyclerView = recyclerView;
this.remoteIo = remoteIo;
this.inputManager = inputManager;
mRecyclerView = recyclerView;
mRemoteIo = remoteIo;
mInputManager = inputManager;
setHasStableIds(true);
}
void addDisplay(boolean homeSupported) {
Log.i(TAG, "Adding display " + nextDisplayIndex);
displayRepository.add(new RemoteDisplay(nextDisplayIndex.getAndIncrement(), homeSupported));
notifyItemInserted(displayRepository.size() - 1);
Log.i(TAG, "Adding display " + sNextDisplayIndex);
mDisplayRepository.add(
new RemoteDisplay(sNextDisplayIndex.getAndIncrement(), homeSupported));
notifyItemInserted(mDisplayRepository.size() - 1);
}
void removeDisplay(int displayId) {
Log.i(TAG, "Removing display " + displayId);
for (int i = 0; i < displayRepository.size(); ++i) {
if (displayId == displayRepository.get(i).getDisplayId()) {
displayRepository.remove(i);
for (int i = 0; i < mDisplayRepository.size(); ++i) {
if (displayId == mDisplayRepository.get(i).getDisplayId()) {
mDisplayRepository.remove(i);
notifyItemRemoved(i);
break;
}
@@ -97,24 +99,25 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
void clearDisplays() {
Log.i(TAG, "Clearing all displays");
int size = displayRepository.size();
displayRepository.clear();
int size = mDisplayRepository.size();
mDisplayRepository.clear();
notifyItemRangeRemoved(0, size);
}
private DisplayHolder getDisplayHolder(int displayId) {
for (int i = 0; i < displayRepository.size(); ++i) {
if (displayId == displayRepository.get(i).getDisplayId()) {
return (DisplayHolder) recyclerView.findViewHolderForAdapterPosition(i);
for (int i = 0; i < mDisplayRepository.size(); ++i) {
if (displayId == mDisplayRepository.get(i).getDisplayId()) {
return (DisplayHolder) mRecyclerView.findViewHolderForAdapterPosition(i);
}
}
return null;
}
@NonNull
@Override
public DisplayHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Disable recycling so layout changes are not present in new displays.
recyclerView.getRecycledViewPool().setMaxRecycledViews(viewType, 0);
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(viewType, 0);
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.display_fragment, parent, false);
@@ -133,12 +136,12 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
@Override
public long getItemId(int position) {
return displayRepository.get(position).getDisplayId();
return mDisplayRepository.get(position).getDisplayId();
}
@Override
public int getItemCount() {
return displayRepository.size();
return mDisplayRepository.size();
}
public class DisplayHolder extends ViewHolder {
@@ -216,8 +219,8 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
void close() {
if (displayController != null) {
Log.i(TAG, "Closing DisplayHolder for display " + displayId);
inputManager.removeFocusListener(focusListener);
inputManager.removeFocusableDisplay(displayId);
mInputManager.removeFocusListener(focusListener);
mInputManager.removeFocusableDisplay(displayId);
displayController.close();
displayController = null;
}
@@ -225,7 +228,7 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
@SuppressLint("ClickableViewAccessibility")
void onBind(int position) {
RemoteDisplay remoteDisplay = displayRepository.get(position);
RemoteDisplay remoteDisplay = mDisplayRepository.get(position);
displayId = remoteDisplay.getDisplayId();
Log.v(
TAG,
@@ -244,9 +247,9 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
displayFocusIndicator.setBackground(null);
}
};
inputManager.addFocusListener(focusListener);
mInputManager.addFocusListener(focusListener);
displayController = new DisplayController(displayId, remoteIo);
displayController = new DisplayController(displayId, mRemoteIo);
Log.v(TAG, "Creating new DisplayController for display " + displayId);
setDisplayTitle("");
@@ -256,12 +259,12 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
v -> ((DisplayAdapter) getBindingAdapter()).removeDisplay(displayId));
View backButton = itemView.findViewById(R.id.display_back);
backButton.setOnClickListener(v -> inputManager.sendBack(displayId));
backButton.setOnClickListener(v -> mInputManager.sendBack(displayId));
View homeButton = itemView.findViewById(R.id.display_home);
if (remoteDisplay.isHomeSupported()) {
homeButton.setVisibility(View.VISIBLE);
homeButton.setOnClickListener(v -> inputManager.sendHome(displayId));
homeButton.setOnClickListener(v -> mInputManager.sendHome(displayId));
} else {
homeButton.setVisibility(View.GONE);
}
@@ -283,7 +286,7 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
resizeButton.setOnTouchListener(
(v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
recyclerView.startResizing(
mRecyclerView.startResizing(
textureView, event, DisplayHolder.this::resizeDisplay);
return true;
}
@@ -294,7 +297,7 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
(v, event) -> {
if (event.getDevice().supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) {
textureView.getParent().requestDisallowInterceptTouchEvent(true);
inputManager.sendInputEvent(
mInputManager.sendInputEvent(
InputDeviceType.DEVICE_TYPE_TOUCHSCREEN, event, displayId);
}
return true;
@@ -302,19 +305,19 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
textureView.setSurfaceTextureListener(
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {}
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture texture) {}
@Override
public void onSurfaceTextureAvailable(
SurfaceTexture texture, int width, int height) {
@NonNull SurfaceTexture texture, int width, int height) {
Log.v(TAG, "Setting surface for display " + displayId);
inputManager.addFocusableDisplay(displayId);
mInputManager.addFocusableDisplay(displayId);
surface = new Surface(texture);
displayController.setSurface(surface, width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture texture) {
Log.v(TAG, "onSurfaceTextureDestroyed for display " + displayId);
if (displayController != null) {
displayController.pause();
@@ -324,7 +327,7 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
@Override
public void onSurfaceTextureSizeChanged(
SurfaceTexture texture, int width, int height) {
@NonNull SurfaceTexture texture, int width, int height) {
Log.v(TAG, "onSurfaceTextureSizeChanged for display " + displayId);
textureView.setRotation(0);
rotateButton.setEnabled(true);
@@ -336,7 +339,7 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
|| !event.getDevice().supportsSource(InputDevice.SOURCE_MOUSE)) {
return false;
}
inputManager.sendInputEventToFocusedDisplay(
mInputManager.sendInputEventToFocusedDisplay(
InputDeviceType.DEVICE_TYPE_MOUSE, event);
return true;
});
@@ -345,20 +348,20 @@ final class DisplayAdapter extends RecyclerView.Adapter<DisplayHolder> {
private static class RemoteDisplay {
// Local ID, not corresponding to the displayId of the relevant Display on the host device.
private final int displayId;
private final boolean homeSupported;
private final int mDisplayId;
private final boolean mHomeSupported;
RemoteDisplay(int displayId, boolean homeSupported) {
this.displayId = displayId;
this.homeSupported = homeSupported;
mDisplayId = displayId;
mHomeSupported = homeSupported;
}
int getDisplayId() {
return displayId;
return mDisplayId;
}
boolean isHomeSupported() {
return homeSupported;
return mHomeSupported;
}
}
}

View File

@@ -27,71 +27,71 @@ import com.example.android.vdmdemo.common.VideoManager;
final class DisplayController {
private static final int DPI = 300;
private final int displayId;
private final RemoteIo remoteIo;
private final int mDisplayId;
private final RemoteIo mRemoteIo;
private VideoManager videoManager = null;
private VideoManager mVideoManager = null;
private int dpi = DPI;
private RemoteEvent displayCapabilities;
private int mDpi = DPI;
private RemoteEvent mDisplayCapabilities;
DisplayController(int displayId, RemoteIo remoteIo) {
this.displayId = displayId;
this.remoteIo = remoteIo;
mDisplayId = displayId;
mRemoteIo = remoteIo;
}
void setDpi(int dpi) {
this.dpi = dpi;
mDpi = dpi;
}
int getDpi() {
return dpi;
return mDpi;
}
void close() {
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder()
.setDisplayId(displayId)
.setDisplayId(mDisplayId)
.setStopStreaming(StopStreaming.newBuilder())
.build());
if (videoManager != null) {
videoManager.stop();
if (mVideoManager != null) {
mVideoManager.stop();
}
}
void pause() {
if (videoManager == null) {
if (mVideoManager == null) {
return;
}
videoManager.stop();
videoManager = null;
mVideoManager.stop();
mVideoManager = null;
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder()
.setDisplayId(displayId)
.setDisplayId(mDisplayId)
.setStopStreaming(StopStreaming.newBuilder().setPause(true))
.build());
}
void sendDisplayCapabilities() {
remoteIo.sendMessage(displayCapabilities);
mRemoteIo.sendMessage(mDisplayCapabilities);
}
void setSurface(Surface surface, int width, int height) {
if (videoManager != null) {
videoManager.stop();
if (mVideoManager != null) {
mVideoManager.stop();
}
videoManager = VideoManager.createDecoder(displayId, remoteIo);
videoManager.startDecoding(surface, width, height);
displayCapabilities =
mVideoManager = VideoManager.createDecoder(mDisplayId, mRemoteIo);
mVideoManager.startDecoding(surface, width, height);
mDisplayCapabilities =
RemoteEvent.newBuilder()
.setDisplayId(displayId)
.setDisplayId(mDisplayId)
.setDisplayCapabilities(
DisplayCapabilities.newBuilder()
.setViewportWidth(width)
.setViewportHeight(height)
.setDensityDpi(dpi))
.setDensityDpi(mDpi))
.build();
sendDisplayCapabilities();
}

View File

@@ -43,7 +43,7 @@ public final class DpadFragment extends Hilt_DpadFragment {
R.id.dpad_center, R.id.dpad_down, R.id.dpad_left, R.id.dpad_up, R.id.dpad_right
};
@Inject InputManager inputManager;
@Inject InputManager mInputManager;
@SuppressLint("ClickableViewAccessibility")
@Override
@@ -87,7 +87,7 @@ public final class DpadFragment extends Hilt_DpadFragment {
Log.w(TAG, "onDpadButtonClick: Method called from a non Dpad button");
return false;
}
inputManager.sendInputEventToFocusedDisplay(
mInputManager.sendInputEventToFocusedDisplay(
InputDeviceType.DEVICE_TYPE_DPAD,
new KeyEvent(
/* downTime= */ System.currentTimeMillis(),

View File

@@ -27,6 +27,7 @@ import android.view.Surface;
import android.view.TextureView;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
@@ -48,25 +49,22 @@ import javax.inject.Inject;
*/
@AndroidEntryPoint(AppCompatActivity.class)
public class ImmersiveActivity extends Hilt_ImmersiveActivity {
private static final String TAG = "VdmImmersiveActivity";
// Approximately, see
// https://developer.android.com/reference/android/util/DisplayMetrics#density
private static final float DIP_TO_DPI = 160f;
@Inject ConnectionManager connectionManager;
@Inject RemoteIo remoteIo;
@Inject VirtualSensorController sensorController;
@Inject AudioPlayer audioPlayer;
@Inject InputManager inputManager;
@Inject ConnectionManager mConnectionManager;
@Inject RemoteIo mRemoteIo;
@Inject VirtualSensorController mSensorController;
@Inject AudioPlayer mAudioPlayer;
@Inject InputManager mInputManager;
private DisplayController displayController;
private final Consumer<RemoteEvent> remoteEventConsumer = this::processRemoteEvent;
private DisplayController mDisplayController;
private final Consumer<RemoteEvent> mRemoteEventConsumer = this::processRemoteEvent;
private final ConnectionManager.ConnectionCallback connectionCallback =
private final ConnectionManager.ConnectionCallback mConnectionCallback =
new ConnectionManager.ConnectionCallback() {
@Override
public void onConnected(String remoteDeviceName) {}
@Override
public void onDisconnected() {
@@ -91,20 +89,20 @@ public class ImmersiveActivity extends Hilt_ImmersiveActivity {
new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
inputManager.sendBack(DEFAULT_DISPLAY);
mInputManager.sendBack(DEFAULT_DISPLAY);
}
};
getOnBackPressedDispatcher().addCallback(this, callback);
displayController = new DisplayController(DEFAULT_DISPLAY, remoteIo);
displayController.setDpi((int) (getResources().getDisplayMetrics().density * DIP_TO_DPI));
mDisplayController = new DisplayController(DEFAULT_DISPLAY, mRemoteIo);
mDisplayController.setDpi((int) (getResources().getDisplayMetrics().density * DIP_TO_DPI));
TextureView textureView = findViewById(R.id.immersive_surface_view);
textureView.setOnTouchListener(
(v, event) -> {
if (event.getDevice().supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) {
textureView.getParent().requestDisallowInterceptTouchEvent(true);
inputManager.sendInputEvent(
mInputManager.sendInputEvent(
InputDeviceType.DEVICE_TYPE_TOUCHSCREEN, event, DEFAULT_DISPLAY);
}
return true;
@@ -112,53 +110,53 @@ public class ImmersiveActivity extends Hilt_ImmersiveActivity {
textureView.setSurfaceTextureListener(
new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {}
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture texture) {}
@Override
public void onSurfaceTextureAvailable(
SurfaceTexture texture, int width, int height) {
displayController.setSurface(new Surface(texture), width, height);
@NonNull SurfaceTexture texture, int width, int height) {
mDisplayController.setSurface(new Surface(texture), width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureSizeChanged(
SurfaceTexture texture, int width, int height) {}
@NonNull SurfaceTexture texture, int width, int height) {}
});
}
@Override
public void onStart() {
super.onStart();
connectionManager.addConnectionCallback(connectionCallback);
remoteIo.addMessageConsumer(audioPlayer);
remoteIo.addMessageConsumer(remoteEventConsumer);
mConnectionManager.addConnectionCallback(mConnectionCallback);
mRemoteIo.addMessageConsumer(mAudioPlayer);
mRemoteIo.addMessageConsumer(mRemoteEventConsumer);
}
@Override
public void onStop() {
super.onStop();
connectionManager.removeConnectionCallback(connectionCallback);
remoteIo.removeMessageConsumer(audioPlayer);
remoteIo.removeMessageConsumer(remoteEventConsumer);
mConnectionManager.removeConnectionCallback(mConnectionCallback);
mRemoteIo.removeMessageConsumer(mAudioPlayer);
mRemoteIo.removeMessageConsumer(mRemoteEventConsumer);
}
@Override
protected void onDestroy() {
super.onDestroy();
displayController.close();
sensorController.close();
mDisplayController.close();
mSensorController.close();
}
private void processRemoteEvent(RemoteEvent event) {
if (event.hasStopStreaming() && !event.getStopStreaming().getPause()) {
finish();
} else if (event.hasStartStreaming()) {
displayController.sendDisplayCapabilities();
mDisplayController.sendDisplayCapabilities();
}
}
@@ -166,14 +164,17 @@ public class ImmersiveActivity extends Hilt_ImmersiveActivity {
public boolean dispatchKeyEvent(KeyEvent event) {
int keyCode = event.getKeyCode();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
inputManager.sendHome(DEFAULT_DISPLAY);
case KeyEvent.KEYCODE_VOLUME_UP -> {
mInputManager.sendHome(DEFAULT_DISPLAY);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
}
case KeyEvent.KEYCODE_VOLUME_DOWN -> {
finish();
return true;
default:
}
default -> {
return super.dispatchKeyEvent(event);
}
}
}
}

View File

@@ -47,57 +47,57 @@ import javax.inject.Singleton;
final class InputManager {
private static final String TAG = "InputManager";
private final RemoteIo remoteIo;
private final Settings settings;
private final RemoteIo mRemoteIo;
private final Settings mSettings;
private final Object lock = new Object();
private final Object mLock = new Object();
@GuardedBy("lock")
private int focusedDisplayId = Display.INVALID_DISPLAY;
@GuardedBy("mLock")
private int mFocusedDisplayId = Display.INVALID_DISPLAY;
@GuardedBy("lock")
private boolean isTrackingFocus = false;
@GuardedBy("mLock")
private boolean mIsTrackingFocus = false;
interface FocusListener {
void onFocusChange(int focusedDisplayId);
}
@GuardedBy("lock")
private final List<FocusListener> focusListeners = new ArrayList<>();
@GuardedBy("mLock")
private final List<FocusListener> mFocusListeners = new ArrayList<>();
@GuardedBy("lock")
private final Set<Integer> focusableDisplays = new HashSet<>();
@GuardedBy("mLock")
private final Set<Integer> mFocusableDisplays = new HashSet<>();
@Inject
InputManager(RemoteIo remoteIo, Settings settings) {
this.remoteIo = remoteIo;
this.settings = settings;
mRemoteIo = remoteIo;
mSettings = settings;
}
void addFocusListener(FocusListener focusListener) {
synchronized (lock) {
focusListeners.add(focusListener);
synchronized (mLock) {
mFocusListeners.add(focusListener);
}
}
void removeFocusListener(FocusListener focusListener) {
synchronized (lock) {
focusListeners.remove(focusListener);
synchronized (mLock) {
mFocusListeners.remove(focusListener);
}
}
void addFocusableDisplay(int displayId) {
synchronized (lock) {
if (focusableDisplays.add(displayId)) {
synchronized (mLock) {
if (mFocusableDisplays.add(displayId)) {
setFocusedDisplayId(displayId);
}
}
}
void removeFocusableDisplay(int displayId) {
synchronized (lock) {
focusableDisplays.remove(Integer.valueOf(displayId));
if (displayId == focusedDisplayId) {
synchronized (mLock) {
mFocusableDisplays.remove(displayId);
if (displayId == mFocusedDisplayId) {
setFocusedDisplayId(updateFocusedDisplayId());
}
}
@@ -105,21 +105,21 @@ final class InputManager {
void updateFocusTracking() {
boolean shouldTrackFocus =
settings.dpadEnabled
|| settings.navTouchpadEnabled
|| settings.externalKeyboardEnabled
|| settings.externalMouseEnabled;
mSettings.dpadEnabled
|| mSettings.navTouchpadEnabled
|| mSettings.externalKeyboardEnabled
|| mSettings.externalMouseEnabled;
List<FocusListener> listenersToNotify = Collections.emptyList();
final List<FocusListener> listenersToNotify;
int focusedDisplayIdToNotify = Display.INVALID_DISPLAY;
synchronized (lock) {
if (shouldTrackFocus != isTrackingFocus) {
isTrackingFocus = shouldTrackFocus;
synchronized (mLock) {
if (shouldTrackFocus != mIsTrackingFocus) {
mIsTrackingFocus = shouldTrackFocus;
}
if (isTrackingFocus) {
focusedDisplayIdToNotify = focusedDisplayId;
if (mIsTrackingFocus) {
focusedDisplayIdToNotify = mFocusedDisplayId;
}
listenersToNotify = new ArrayList<>(focusListeners);
listenersToNotify = new ArrayList<>(mFocusListeners);
}
for (FocusListener focusListener : listenersToNotify) {
focusListener.onFocusChange(focusedDisplayIdToNotify);
@@ -152,30 +152,30 @@ final class InputManager {
*/
public void sendInputEventToFocusedDisplay(InputDeviceType deviceType, InputEvent inputEvent) {
int targetDisplay;
synchronized (lock) {
if (!isTrackingFocus || focusedDisplayId == Display.INVALID_DISPLAY) {
synchronized (mLock) {
if (!mIsTrackingFocus || mFocusedDisplayId == Display.INVALID_DISPLAY) {
return;
}
targetDisplay = focusedDisplayId;
targetDisplay = mFocusedDisplayId;
}
switch (deviceType) {
case DEVICE_TYPE_NAVIGATION_TOUCHPAD:
if (!settings.navTouchpadEnabled) {
if (!mSettings.navTouchpadEnabled) {
return;
}
break;
case DEVICE_TYPE_DPAD:
if (!settings.dpadEnabled) {
if (!mSettings.dpadEnabled) {
return;
}
break;
case DEVICE_TYPE_MOUSE:
if (!settings.externalMouseEnabled) {
if (!mSettings.externalMouseEnabled) {
return;
}
break;
case DEVICE_TYPE_KEYBOARD:
if (!settings.externalKeyboardEnabled) {
if (!mSettings.externalKeyboardEnabled) {
return;
}
break;
@@ -206,7 +206,7 @@ final class InputManager {
void sendHome(int displayId) {
setFocusedDisplayId(displayId);
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder()
.setDisplayId(displayId)
.setHomeEvent(RemoteHomeEvent.newBuilder())
@@ -296,7 +296,7 @@ final class InputManager {
}
private void sendInputEvent(RemoteInputEvent inputEvent, int displayId) {
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder().setDisplayId(displayId).setInputEvent(inputEvent).build());
}
@@ -305,21 +305,21 @@ final class InputManager {
}
private int updateFocusedDisplayId() {
synchronized (lock) {
if (focusableDisplays.contains(focusedDisplayId)) {
return focusedDisplayId;
synchronized (mLock) {
if (mFocusableDisplays.contains(mFocusedDisplayId)) {
return mFocusedDisplayId;
}
return Iterables.getFirst(focusableDisplays, Display.INVALID_DISPLAY);
return Iterables.getFirst(mFocusableDisplays, Display.INVALID_DISPLAY);
}
}
private void setFocusedDisplayId(int displayId) {
List<FocusListener> listenersToNotify = Collections.emptyList();
synchronized (lock) {
if (displayId != focusedDisplayId) {
focusedDisplayId = displayId;
if (isTrackingFocus) {
listenersToNotify = new ArrayList<>(focusListeners);
synchronized (mLock) {
if (displayId != mFocusedDisplayId) {
mFocusedDisplayId = displayId;
if (mIsTrackingFocus) {
listenersToNotify = new ArrayList<>(mFocusListeners);
}
}
}

View File

@@ -49,45 +49,45 @@ import javax.inject.Inject;
@AndroidEntryPoint(AppCompatActivity.class)
public class MainActivity extends Hilt_MainActivity {
@Inject RemoteIo remoteIo;
@Inject ConnectionManager connectionManager;
@Inject InputManager inputManager;
@Inject VirtualSensorController sensorController;
@Inject AudioPlayer audioPlayer;
@Inject Settings settings;
@Inject RemoteIo mRemoteIo;
@Inject ConnectionManager mConnectionManager;
@Inject InputManager mInputManager;
@Inject VirtualSensorController mSensorController;
@Inject AudioPlayer mAudioPlayer;
@Inject Settings mSettings;
private final Consumer<RemoteEvent> remoteEventConsumer = this::processRemoteEvent;
private DisplayAdapter displayAdapter;
private final InputManager.FocusListener focusListener = this::onDisplayFocusChange;
private final Consumer<RemoteEvent> mRemoteEventConsumer = this::processRemoteEvent;
private DisplayAdapter mDisplayAdapter;
private final InputManager.FocusListener mFocusListener = this::onDisplayFocusChange;
private final ConnectionManager.ConnectionCallback connectionCallback =
private final ConnectionManager.ConnectionCallback mConnectionCallback =
new ConnectionManager.ConnectionCallback() {
@Override
public void onConnecting(String remoteDeviceName) {
connectionManager.stopAdvertising();
mConnectionManager.stopAdvertising();
}
@Override
public void onConnected(String remoteDeviceName) {
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder()
.setDeviceCapabilities(
DeviceCapabilities.newBuilder()
.setDeviceName(Build.MODEL)
.addAllSensorCapabilities(
(sensorController
(mSensorController
.getSensorCapabilities())))
.build());
}
@Override
public void onDisconnected() {
if (displayAdapter != null) {
runOnUiThread(displayAdapter::clearDisplays);
if (mDisplayAdapter != null) {
runOnUiThread(mDisplayAdapter::clearDisplays);
}
connectionManager.startAdvertising();
mConnectionManager.startAdvertising();
}
};
@@ -104,36 +104,36 @@ public class MainActivity extends Hilt_MainActivity {
displaysView.setLayoutManager(
new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
displaysView.setItemAnimator(null);
displayAdapter = new DisplayAdapter(displaysView, remoteIo, inputManager);
displaysView.setAdapter(displayAdapter);
mDisplayAdapter = new DisplayAdapter(displaysView, mRemoteIo, mInputManager);
displaysView.setAdapter(mDisplayAdapter);
}
@Override
public void onStart() {
super.onStart();
connectionManager.addConnectionCallback(connectionCallback);
connectionManager.startAdvertising();
inputManager.addFocusListener(focusListener);
remoteIo.addMessageConsumer(audioPlayer);
remoteIo.addMessageConsumer(remoteEventConsumer);
mConnectionManager.addConnectionCallback(mConnectionCallback);
mConnectionManager.startAdvertising();
mInputManager.addFocusListener(mFocusListener);
mRemoteIo.addMessageConsumer(mAudioPlayer);
mRemoteIo.addMessageConsumer(mRemoteEventConsumer);
}
@Override
public void onStop() {
super.onStop();
inputManager.removeFocusListener(focusListener);
connectionManager.removeConnectionCallback(connectionCallback);
connectionManager.stopAdvertising();
remoteIo.removeMessageConsumer(remoteEventConsumer);
remoteIo.removeMessageConsumer(audioPlayer);
mInputManager.removeFocusListener(mFocusListener);
mConnectionManager.removeConnectionCallback(mConnectionCallback);
mConnectionManager.stopAdvertising();
mRemoteIo.removeMessageConsumer(mRemoteEventConsumer);
mRemoteIo.removeMessageConsumer(mAudioPlayer);
}
@Override
protected void onDestroy() {
super.onDestroy();
displayAdapter.clearDisplays();
connectionManager.disconnect();
sensorController.close();
mDisplayAdapter.clearDisplays();
mConnectionManager.disconnect();
mSensorController.close();
}
@Override
@@ -142,7 +142,7 @@ public class MainActivity extends Hilt_MainActivity {
|| !event.getDevice().supportsSource(InputDevice.SOURCE_KEYBOARD)) {
return false;
}
inputManager.sendInputEventToFocusedDisplay(InputDeviceType.DEVICE_TYPE_KEYBOARD, event);
mInputManager.sendInputEventToFocusedDisplay(InputDeviceType.DEVICE_TYPE_KEYBOARD, event);
return true;
}
@@ -153,18 +153,11 @@ public class MainActivity extends Hilt_MainActivity {
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
switch (item.getItemId()) {
case R.id.enable_dpad:
item.setChecked(settings.dpadEnabled);
break;
case R.id.enable_nav_touchpad:
item.setChecked(settings.navTouchpadEnabled);
break;
case R.id.enable_external_keyboard:
item.setChecked(settings.externalKeyboardEnabled);
break;
case R.id.enable_external_mouse:
item.setChecked(settings.externalMouseEnabled);
break;
case R.id.enable_dpad -> item.setChecked(mSettings.dpadEnabled);
case R.id.enable_nav_touchpad -> item.setChecked(mSettings.navTouchpadEnabled);
case R.id.enable_external_keyboard -> item.setChecked(
mSettings.externalKeyboardEnabled);
case R.id.enable_external_mouse -> item.setChecked(mSettings.externalMouseEnabled);
}
}
return true;
@@ -175,23 +168,17 @@ public class MainActivity extends Hilt_MainActivity {
item.setChecked(!item.isChecked());
switch (item.getItemId()) {
case R.id.enable_dpad:
settings.dpadEnabled = item.isChecked();
break;
case R.id.enable_nav_touchpad:
settings.navTouchpadEnabled = item.isChecked();
break;
case R.id.enable_external_keyboard:
settings.externalKeyboardEnabled = item.isChecked();
break;
case R.id.enable_external_mouse:
settings.externalMouseEnabled = item.isChecked();
break;
default:
case R.id.enable_dpad -> mSettings.dpadEnabled = item.isChecked();
case R.id.enable_nav_touchpad -> mSettings.navTouchpadEnabled = item.isChecked();
case R.id.enable_external_keyboard ->
mSettings.externalKeyboardEnabled = item.isChecked();
case R.id.enable_external_mouse -> mSettings.externalMouseEnabled = item.isChecked();
default -> {
return super.onOptionsItemSelected(item);
}
}
inputManager.updateFocusTracking();
mInputManager.updateFocusTracking();
return true;
}
@@ -202,27 +189,27 @@ public class MainActivity extends Hilt_MainActivity {
} else {
runOnUiThread(
() ->
displayAdapter.addDisplay(
mDisplayAdapter.addDisplay(
event.getStartStreaming().getHomeEnabled()));
}
} else if (event.hasStopStreaming()) {
runOnUiThread(() -> displayAdapter.removeDisplay(event.getDisplayId()));
runOnUiThread(() -> mDisplayAdapter.removeDisplay(event.getDisplayId()));
} else if (event.hasDisplayRotation()) {
runOnUiThread(() -> displayAdapter.rotateDisplay(event));
runOnUiThread(() -> mDisplayAdapter.rotateDisplay(event));
} else if (event.hasDisplayChangeEvent()) {
runOnUiThread(() -> displayAdapter.processDisplayChange(event));
runOnUiThread(() -> mDisplayAdapter.processDisplayChange(event));
}
}
private void onDisplayFocusChange(int displayId) {
findViewById(R.id.dpad_fragment_container)
.setVisibility(
settings.dpadEnabled && displayId != Display.INVALID_DISPLAY
mSettings.dpadEnabled && displayId != Display.INVALID_DISPLAY
? View.VISIBLE
: View.GONE);
findViewById(R.id.nav_touchpad_fragment_container)
.setVisibility(
settings.navTouchpadEnabled && displayId != Display.INVALID_DISPLAY
mSettings.navTouchpadEnabled && displayId != Display.INVALID_DISPLAY
? View.VISIBLE
: View.GONE);
}

View File

@@ -35,7 +35,7 @@ import javax.inject.Inject;
@AndroidEntryPoint(Fragment.class)
public final class NavTouchpadFragment extends Hilt_NavTouchpadFragment {
@Inject InputManager inputManager;
@Inject InputManager mInputManager;
@SuppressLint("ClickableViewAccessibility")
@Override
@@ -46,7 +46,7 @@ public final class NavTouchpadFragment extends Hilt_NavTouchpadFragment {
TextView navTouchpad = view.findViewById(R.id.nav_touchpad);
navTouchpad.setOnTouchListener(
(v, event) -> {
inputManager.sendInputEventToFocusedDisplay(
mInputManager.sendInputEventToFocusedDisplay(
InputDeviceType.DEVICE_TYPE_NAVIGATION_TOUCHPAD, event);
return true;
});

View File

@@ -22,10 +22,10 @@ import javax.inject.Singleton;
/** Settings known to the VDM Demo Client application */
@Singleton
final class Settings {
boolean dpadEnabled = false;
boolean navTouchpadEnabled = false;
boolean externalKeyboardEnabled = false;
boolean externalMouseEnabled = false;
public boolean dpadEnabled = false;
public boolean navTouchpadEnabled = false;
public boolean externalKeyboardEnabled = false;
public boolean externalMouseEnabled = false;
@Inject
Settings() {}

View File

@@ -43,17 +43,17 @@ import javax.inject.Inject;
@ActivityScoped
final class VirtualSensorController implements AutoCloseable {
private final RemoteIo remoteIo;
private final Consumer<RemoteEvent> remoteEventConsumer = this::processRemoteEvent;
private final SensorManager sensorManager;
private final HandlerThread listenerThread;
private final Handler handler;
private final RemoteIo mRemoteIo;
private final Consumer<RemoteEvent> mRemoteEventConsumer = this::processRemoteEvent;
private final SensorManager mSensorManager;
private final HandlerThread mListenerThread;
private final Handler mHandler;
private final SensorEventListener sensorEventListener =
new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
remoteIo.sendMessage(
mRemoteIo.sendMessage(
RemoteEvent.newBuilder()
.setSensorEvent(
RemoteSensorEvent.newBuilder()
@@ -68,25 +68,25 @@ final class VirtualSensorController implements AutoCloseable {
@Inject
VirtualSensorController(@ApplicationContext Context context, RemoteIo remoteIo) {
this.sensorManager = context.getSystemService(SensorManager.class);
this.remoteIo = remoteIo;
mSensorManager = context.getSystemService(SensorManager.class);
mRemoteIo = remoteIo;
listenerThread = new HandlerThread("VirtualSensorListener");
listenerThread.start();
handler = new Handler(listenerThread.getLooper());
mListenerThread = new HandlerThread("VirtualSensorListener");
mListenerThread.start();
mHandler = new Handler(mListenerThread.getLooper());
remoteIo.addMessageConsumer(remoteEventConsumer);
remoteIo.addMessageConsumer(mRemoteEventConsumer);
}
@Override
public void close() {
sensorManager.unregisterListener(sensorEventListener);
listenerThread.quitSafely();
remoteIo.removeMessageConsumer(remoteEventConsumer);
mSensorManager.unregisterListener(sensorEventListener);
mListenerThread.quitSafely();
mRemoteIo.removeMessageConsumer(mRemoteEventConsumer);
}
public List<SensorCapabilities> getSensorCapabilities() {
return sensorManager.getSensorList(Sensor.TYPE_ALL).stream()
return mSensorManager.getSensorList(Sensor.TYPE_ALL).stream()
.map(
sensor ->
SensorCapabilities.newBuilder()
@@ -107,19 +107,19 @@ final class VirtualSensorController implements AutoCloseable {
return;
}
SensorConfiguration config = remoteEvent.getSensorConfiguration();
Sensor sensor = sensorManager.getDefaultSensor(config.getSensorType());
Sensor sensor = mSensorManager.getDefaultSensor(config.getSensorType());
if (sensor == null) {
return;
}
if (config.getEnabled()) {
sensorManager.registerListener(
mSensorManager.registerListener(
sensorEventListener,
sensor,
config.getSamplingPeriodUs(),
config.getBatchReportingLatencyUs(),
handler);
mHandler);
} else {
sensorManager.unregisterListener(sensorEventListener, sensor);
mSensorManager.unregisterListener(sensorEventListener, sensor);
}
}
}