Commit 20f21c7b authored by Yuwei Huang's avatar Yuwei Huang Committed by Commit Bot

[Remoting Android] Support for resizing host to fit client resolution

This CL adds support for Android client to resize host to fit client
resolution.

Bug: 874626
Change-Id: I9c3c8139eab20834e79deb2ce662a8ab1234b09c
Reviewed-on: https://chromium-review.googlesource.com/1182187Reviewed-by: default avatarJoe Downing <joedow@chromium.org>
Commit-Queue: Yuwei Huang <yuweih@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585358}
parent f9e001f4
......@@ -27,6 +27,10 @@
<item android:id="@+id/actionbar_send_ctrl_alt_del"
android:title="@string/send_ctrl_alt_del"
app:showAsAction="withText"/>
<item android:id="@+id/resize_to_client"
android:checkable="true"
android:title="@string/resize_to_client"
app:showAsAction="never|withText"/>
<item android:id="@+id/actionbar_disconnect"
android:title="@string/disconnect_myself_button"
app:showAsAction="withText"/>
......
......@@ -12,6 +12,7 @@ import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.v4.content.ContextCompat;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
......@@ -93,4 +94,16 @@ public abstract class ChromotingUtil {
public static boolean openUrl(Activity parentActivity, Uri uri) {
return startActivitySafely(parentActivity, new Intent(Intent.ACTION_VIEW, uri));
}
/**
* Converts a measurement in px to dp (density independent pixel).
*
* @param metrics The metrics used for conversion.
* @param value The value in px to be converted.
* @return The converted result in dp.
*/
public static int pxToDp(DisplayMetrics metrics, int value) {
// +0.5f to round up the result.
return (int) (value / metrics.density + 0.5f);
}
}
......@@ -15,6 +15,7 @@ import android.os.Handler;
import android.support.v7.app.ActionBar.OnMenuVisibilityListener;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.KeyEvent;
import android.view.Menu;
......@@ -55,6 +56,7 @@ public class Desktop
/** Preference used to track the last input mode selected by the user. */
private static final String PREFERENCE_INPUT_MODE = "input_mode";
private static final String PREFERENCE_RESIZE_TO_CLIENT = "resize_to_client";
/** The amount of time to wait to hide the ActionBar after user input is seen. */
private static final int ACTIONBAR_AUTO_HIDE_DELAY_MS = 3000;
......@@ -76,6 +78,9 @@ public class Desktop
/** Indicates whether a Soft Input UI (such as a keyboard) is visible. */
private boolean mSoftInputVisible = false;
/** Indicates whether resize-to-client is enabled. */
private boolean mResizeToClientEnabled = false;
/** Holds the scheduled task object which will be called to hide the ActionBar. */
private Runnable mActionBarAutoHideTask;
......@@ -154,6 +159,7 @@ public class Desktop
mActivityLifecycleListener.onActivityCreated(this, savedInstanceState);
mInputMode = getInitialInputModeValue();
mResizeToClientEnabled = getStoredResizeToClientEnabled();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
attachSystemUiResizeListener();
......@@ -283,6 +289,8 @@ public class Desktop
// Wait to set the input mode until after the default tinting has been applied.
setInputMode(mInputMode);
setResizeToClientEnabled(mResizeToClientEnabled);
// Keyboard state must be set after the keyboard icon has been added to the menu.
setKeyboardState(getResources().getConfiguration());
......@@ -352,6 +360,26 @@ public class Desktop
new InputModeChangedEventParameter(mInputMode, mHostTouchCapability));
}
private boolean getStoredResizeToClientEnabled() {
return getPreferences(MODE_PRIVATE).getBoolean(PREFERENCE_RESIZE_TO_CLIENT, false);
}
private void setResizeToClientEnabled(boolean resizeToClientEnabled) {
mResizeToClientEnabled = resizeToClientEnabled;
Menu menu = mToolbar.getMenu();
MenuItem resizeToClientMenuItem = menu.findItem(R.id.resize_to_client);
resizeToClientMenuItem.setChecked(mResizeToClientEnabled);
if (mResizeToClientEnabled != getStoredResizeToClientEnabled()) {
getPreferences(MODE_PRIVATE)
.edit()
.putBoolean(PREFERENCE_RESIZE_TO_CLIENT, mResizeToClientEnabled)
.apply();
}
if (mResizeToClientEnabled) {
sendPreferredHostResolution();
}
}
@Override
public void onCapabilitiesChanged(List<String> newCapabilities) {
if (newCapabilities.contains(Capabilities.TOUCH_CAPABILITY)) {
......@@ -600,6 +628,9 @@ public class Desktop
mInjector.sendCtrlAltDel();
return true;
}
if (id == R.id.resize_to_client) {
setResizeToClientEnabled(!item.isChecked());
}
if (id == R.id.actionbar_help) {
HelpSingleton.getInstance().launchHelp(this, HelpContext.DESKTOP);
return true;
......@@ -669,6 +700,25 @@ public class Desktop
});
}
/**
* Sends preferred resolution to the host that matches the aspect ratio of the screen. This is
* calculated by the size of the DesktopView minus the safe insets.
* This method does nothing if resize-to-client has not been enabled by the user.
*/
public void sendPreferredHostResolution() {
if (!mResizeToClientEnabled) {
return;
}
Rect safeInsets = getSafeInsets();
int safeAreaWidth = mRemoteHostDesktop.getWidth() - safeInsets.left - safeInsets.right;
int safeAreaHeight = mRemoteHostDesktop.getHeight() - safeInsets.top - safeInsets.bottom;
DisplayMetrics metrics = getResources().getDisplayMetrics();
safeAreaWidth = ChromotingUtil.pxToDp(metrics, safeAreaWidth);
safeAreaHeight = ChromotingUtil.pxToDp(metrics, safeAreaHeight);
mClient.sendClientResolution(safeAreaWidth, safeAreaHeight, metrics.density);
}
/**
* Called once when a keyboard key is pressed, then again when that same key is released. This
* is not guaranteed to be notified of all soft keyboard events: certain keyboards might not
......
......@@ -173,6 +173,9 @@ public class DesktopCanvas {
return;
}
// Reset to identity so that screen dimensions and image dimensions match up.
mRenderData.transform.reset();
float widthRatio = getSafeScreenWidth() / mRenderData.imageWidth;
float heightRatio = getSafeScreenHeight() / mRenderData.imageHeight;
float screenToImageScale = Math.max(widthRatio, heightRatio);
......
......@@ -389,6 +389,8 @@ public class TouchInputHandler {
mPanGestureBounds = new Rect(
mEdgeSlopInPx, mEdgeSlopInPx, width - mEdgeSlopInPx, height - mEdgeSlopInPx);
resizeImageToFitScreen();
mDesktop.sendPreferredHostResolution();
}
private void handleHostSizeChanged(int width, int height) {
......
......@@ -292,6 +292,22 @@ public class Client implements InputStub {
nativeSendExtensionMessage(mNativeJniClient, type, data);
}
/**
* Sends client resolution to the host so that the host can resize itself to fit the client
* without showing letterboxes.
*
* @param dipsWidth The width of the screen in density independent pixels.
* @param dipsHeight The height of the screen in density independent pixels.
* @param density The pixel density of the screen.
*/
public void sendClientResolution(int dipsWidth, int dipsHeight, float density) {
if (!mConnected) {
return;
}
nativeSendClientResolution(mNativeJniClient, dipsWidth, dipsHeight, density);
}
private native long nativeInit();
private native void nativeDestroy(long nativeJniClient);
......@@ -336,4 +352,8 @@ public class Client implements InputStub {
/** Passes extension message to the native code. */
private native void nativeSendExtensionMessage(long nativeJniClient, String type, String data);
/** Sends client resolution to the host. */
private native void nativeSendClientResolution(
long nativeJniClient, int dipsWidth, int dipsHeight, float scale);
}
......@@ -100,7 +100,7 @@ class ChromotingSession::Core : public ClientUserInterface,
void SendKeyEvent(int usb_key_code, bool key_down);
void SendTextEvent(const std::string& text);
void SendTouchEvent(const protocol::TouchEvent& touch_event);
void SendClientResolution(int dips_width, int dips_height, int scale);
void SendClientResolution(int dips_width, int dips_height, float scale);
void EnableVideoChannel(bool enable);
void SendClientMessage(const std::string& type, const std::string& data);
......@@ -275,7 +275,7 @@ void ChromotingSession::Core::SendTouchEvent(
void ChromotingSession::Core::SendClientResolution(int dips_width,
int dips_height,
int scale) {
float scale) {
DCHECK(network_task_runner()->BelongsToCurrentThread());
protocol::ClientResolution client_resolution;
client_resolution.set_dips_width(dips_width);
......@@ -708,7 +708,7 @@ void ChromotingSession::SendTouchEvent(
void ChromotingSession::SendClientResolution(int dips_width,
int dips_height,
int scale) {
float scale) {
RunCoreTaskOnNetworkThread(FROM_HERE, &Core::SendClientResolution, dips_width,
dips_height, scale);
}
......
......@@ -118,7 +118,7 @@ class ChromotingSession : public ClientInputInjector {
// Sends the provided touch event payload to the host.
void SendTouchEvent(const protocol::TouchEvent& touch_event);
void SendClientResolution(int dips_width, int dips_height, int scale);
void SendClientResolution(int dips_width, int dips_height, float scale);
// Enables or disables the video channel.
void EnableVideoChannel(bool enable);
......
......@@ -287,6 +287,15 @@ void JniClient::SendExtensionMessage(
ConvertJavaStringToUTF8(env, data));
}
void JniClient::SendClientResolution(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller,
jint dips_width,
jint dips_height,
jfloat scale) {
session_->SendClientResolution(dips_width, dips_height, scale);
}
void JniClient::Destroy(JNIEnv* env, const JavaParamRef<jobject>& caller) {
delete this;
}
......
......@@ -145,6 +145,12 @@ class JniClient : public ChromotingSession::Delegate {
const base::android::JavaParamRef<jstring>& type,
const base::android::JavaParamRef<jstring>& data);
void SendClientResolution(JNIEnv* env,
const base::android::JavaParamRef<jobject>& caller,
jint dips_width,
jint dips_height,
jfloat scale);
// Deletes this object.
void Destroy(JNIEnv* env, const base::android::JavaParamRef<jobject>& caller);
......
......@@ -1166,7 +1166,7 @@ General fixes and stability improvements.
<message desc="Label for button to reconnect to the previous Me2Me host. This button appears on the 'session-finished' page." name="IDS_RECONNECT">
Reconnect
</message>
<message desc="Button for enabling or disabling the 'resize-to-client' functionality, whereby the host desktop is resized to match the client size as closely as possible." name="IDS_RESIZE_TO_CLIENT">
<message desc="Button for enabling or disabling the 'resize-to-client' functionality, whereby the host desktop is resized to match the client size as closely as possible." name="IDS_RESIZE_TO_CLIENT" formatter_data="android_java">
Resize desktop to fit
</message>
<message desc="Label for button to retry connecting to a Me2Me host, after failing to connect to that host. This button appears on the 'session-finished' page." name="IDS_RETRY">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment