Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix persistent hover overlay when in desktop/DeX mode or using a mouse/non-touch input #8316

Merged
merged 9 commits into from
Nov 9, 2022
Original file line number Diff line number Diff line change
Expand Up @@ -639,19 +639,7 @@ protected void initViews(final View rootView, final Bundle savedInstanceState) {
? View.VISIBLE
: View.GONE
);

if (DeviceUtils.isTv(getContext())) {
// remove ripple effects from detail controls
final int transparent = ContextCompat.getColor(requireContext(),
R.color.transparent_background_color);
binding.detailControlsPlaylistAppend.setBackgroundColor(transparent);
binding.detailControlsBackground.setBackgroundColor(transparent);
binding.detailControlsPopup.setBackgroundColor(transparent);
binding.detailControlsDownload.setBackgroundColor(transparent);
binding.detailControlsShare.setBackgroundColor(transparent);
binding.detailControlsOpenInBrowser.setBackgroundColor(transparent);
binding.detailControlsPlayWithKodi.setBackgroundColor(transparent);
}
accommodateForTvAndDesktopMode();
}

@Override
Expand Down Expand Up @@ -2106,6 +2094,30 @@ private void setupBrightness() {
}
}

/**
* Make changes to the UI to accommodate for better usability on bigger screens such as TVs
* or in Android's desktop mode (DeX etc).
*/
private void accommodateForTvAndDesktopMode() {
if (DeviceUtils.isTv(getContext())) {
// remove ripple effects from detail controls
final int transparent = ContextCompat.getColor(requireContext(),
R.color.transparent_background_color);
binding.detailControlsPlaylistAppend.setBackgroundColor(transparent);
binding.detailControlsBackground.setBackgroundColor(transparent);
binding.detailControlsPopup.setBackgroundColor(transparent);
binding.detailControlsDownload.setBackgroundColor(transparent);
binding.detailControlsShare.setBackgroundColor(transparent);
binding.detailControlsOpenInBrowser.setBackgroundColor(transparent);
binding.detailControlsPlayWithKodi.setBackgroundColor(transparent);
}
if (DeviceUtils.isDesktopMode(getContext())) {
// Remove the "hover" overlay (since it is visible on all mouse events and interferes
// with the video content being played)
binding.detailThumbnailRootLayout.setForeground(null);
}
}

private void checkLandscape() {
if ((!player.isPlaying() && player.getPlayQueue() != playQueue)
|| player.getPlayQueue() == null) {
Expand Down
79 changes: 79 additions & 0 deletions app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package org.schabi.newpipe.util;

import android.annotation.SuppressLint;
import android.app.UiModeManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Point;
import android.hardware.input.InputManager;
import android.os.BatteryManager;
import android.os.Build;
import android.provider.Settings;
import android.util.TypedValue;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowManager;
Expand All @@ -22,6 +25,10 @@
import org.schabi.newpipe.App;
import org.schabi.newpipe.R;

import java.lang.reflect.Method;

import static android.content.Context.INPUT_SERVICE;

public final class DeviceUtils {

private static final String AMAZON_FEATURE_FIRE_TV = "amazon.hardware.fire_tv";
Expand Down Expand Up @@ -84,6 +91,78 @@ public static boolean isTv(final Context context) {
return DeviceUtils.isTV;
}

/**
* Checks if the device is in desktop or DeX mode. This function should only
* be invoked once on view load as it is using reflection for the DeX checks.
* @param context the context to use for services and config.
* @return true if the Android device is in desktop mode or using DeX.
*/
@SuppressWarnings("JavaReflectionMemberAccess")
public static boolean isDesktopMode(@NonNull final Context context) {
// Adapted from https://stackoverflow.com/a/64615568
// to check for all input devices that have an active cursor
final InputManager im = (InputManager) context.getSystemService(INPUT_SERVICE);
for (final int id : im.getInputDeviceIds()) {
final InputDevice inputDevice = im.getInputDevice(id);
if (inputDevice.supportsSource(InputDevice.SOURCE_BLUETOOTH_STYLUS)
|| inputDevice.supportsSource(InputDevice.SOURCE_MOUSE)
|| inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)
|| inputDevice.supportsSource(InputDevice.SOURCE_TOUCHPAD)
|| inputDevice.supportsSource(InputDevice.SOURCE_TRACKBALL)) {
return true;
}
}

final UiModeManager uiModeManager =
ContextCompat.getSystemService(context, UiModeManager.class);
if (uiModeManager != null
&& uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_DESK) {
return true;
}

// DeX check for standalone and multi-window mode, from:
// https://developer.samsung.com/samsung-dex/modify-optimizing.html
try {
final Configuration config = context.getResources().getConfiguration();
final Class<?> configClass = config.getClass();
final int semDesktopModeEnabledConst =
configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass);
final int currentMode =
configClass.getField("semDesktopModeEnabled").getInt(config);
if (semDesktopModeEnabledConst == currentMode) {
return true;
}
} catch (final NoSuchFieldException | IllegalAccessException ignored) {
// Device doesn't seem to support DeX
}

@SuppressLint("WrongConstant") final Object desktopModeManager = context
.getApplicationContext()
.getSystemService("desktopmode");

if (desktopModeManager != null) {
try {
final Method getDesktopModeStateMethod = desktopModeManager.getClass()
.getDeclaredMethod("getDesktopModeState");
final Object desktopModeState = getDesktopModeStateMethod
.invoke(desktopModeManager);
final Class<?> desktopModeStateClass = desktopModeState.getClass();
final Method getEnabledMethod = desktopModeStateClass
.getDeclaredMethod("getEnabled");
final int enabledStatus = (int) getEnabledMethod.invoke(desktopModeState);
if (enabledStatus == desktopModeStateClass
.getDeclaredField("ENABLED").getInt(desktopModeStateClass)) {
return true;
}
} catch (final Exception ignored) {
// Device does not support DeX 3.0 or something went wrong when trying to determine
// if it supports this feature
}
}

return false;
}

public static boolean isTablet(@NonNull final Context context) {
final String tabletModeSetting = PreferenceManager.getDefaultSharedPreferences(context)
.getString(context.getString(R.string.tablet_mode_key), "");
Expand Down