Vectras-VM-Android/app/src/main/java/com/vectras/qemu/MainVNCActivity.java
Epic Studios e724a517a8 PHANTOM V2.5
PH2.5 (9)
2024-01-20 02:19:58 +02:00

1360 lines
48 KiB
Java

package com.vectras.qemu;
import android.androidVNC.AbstractScaling;
import android.androidVNC.VncCanvasActivity;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.GravityCompat;
import androidx.core.view.MenuItemCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.FragmentTransaction;
import android.annotation.SuppressLint;
import android.app.Activity;
import androidx.appcompat.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.BaseInputConnection;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.vectras.qemu.utils.FileUtils;
import com.vectras.vm.Fragment.ControlersOptionsFragment;
import com.vectras.vm.Fragment.LoggerDialogFragment;
import com.vectras.vm.MainActivity;
import com.vectras.vm.R;
import com.vectras.qemu.utils.Machine;
import com.vectras.qemu.utils.QmpClient;
import com.vectras.vm.utils.UIUtils;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONObject;
/**
* @author Dev
*/
public class MainVNCActivity extends VncCanvasActivity {
public static final int KEYBOARD = 10000;
public static final int QUIT = 10001;
public static final int HELP = 10002;
private static boolean monitorMode = false;
private boolean mouseOn = false;
private Object lockTime = new Object();
private boolean timeQuit = false;
private Thread timeListenerThread;
private ProgressDialog progDialog;
private static boolean firstConnection;
String[] functionsArray = {"F1", "F2", "F3", "F4",
"F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"};
public boolean ctrlClicked = false;
public boolean altClicked = false;
private ImageButton qmpBtn;
@Override
public void onCreate(Bundle b) {
if (MainSettingsManager.getFullscreen(this))
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(b);
this.vncCanvas.setFocusableInTouchMode(true);
setDefaulViewMode();
// setUIModeMobile();
Toolbar mainToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mainToolbar);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
onFitToScreen();
ImageButton shutdownBtn = findViewById(R.id.shutdownBtn);
ImageButton settingBtn = findViewById(R.id.btnSettings);
ImageButton keyboardBtn = findViewById(R.id.kbdBtn);
ImageButton controllersBtn = findViewById(R.id.btnMode);
ImageButton upBtn = findViewById(R.id.upBtn);
ImageButton leftBtn = findViewById(R.id.leftBtn);
ImageButton downBtn = findViewById(R.id.downBtn);
ImageButton rightBtn = findViewById(R.id.rightBtn);
ImageButton enterBtn = findViewById(R.id.enterBtn);
ImageButton escBtn = findViewById(R.id.escBtn);
ImageButton ctrlBtn = findViewById(R.id.ctrlBtn);
ImageButton altBtn = findViewById(R.id.altBtn);
ImageButton delBtn = findViewById(R.id.delBtn);
ImageButton btnLogs = findViewById(R.id.btnLogs);
Button ctrlAltDelBtn = findViewById(R.id.ctrlAltDelBtn);
qmpBtn = findViewById(R.id.btnQmp);
btnLogs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Create and show the dialog.
LoggerDialogFragment newFragment = new LoggerDialogFragment();
newFragment.show(ft, "Logger");
}
});
shutdownBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Machine.stopVM(activity);
}
});
keyboardBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
toggleKeyboardFlag = UIUtils.onKeyboard(activity, toggleKeyboardFlag, vncCanvas);
}
}, 200);
}
});
controllersBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Create and show the dialog.
ControlersOptionsFragment newFragment = new ControlersOptionsFragment();
newFragment.show(ft, "Controllers");
}
});
settingBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Dialog alertDialog = new Dialog(activity, R.style.MainDialogTheme);
alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
alertDialog.getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
alertDialog.setContentView(R.layout.dialog_setting);
alertDialog.show();
}
});
upBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KeyEvent.KEYCODE_DPAD_UP, false);
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
sendKey(KeyEvent.KEYCODE_DPAD_UP, true);
return true;
}
return false;
}
});
leftBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KeyEvent.KEYCODE_DPAD_LEFT, false);
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
sendKey(KeyEvent.KEYCODE_DPAD_LEFT, true);
return true;
}
return false;
}
});
downBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KeyEvent.KEYCODE_DPAD_DOWN, false);
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
sendKey(KeyEvent.KEYCODE_DPAD_DOWN, true);
return true;
}
return false;
}
});
rightBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
sendKey(KeyEvent.KEYCODE_DPAD_RIGHT, false);
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
sendKey(KeyEvent.KEYCODE_DPAD_RIGHT, true);
return true;
}
return false;
}
});
escBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyDownUp(KeyEvent.KEYCODE_ESCAPE);
}
});
enterBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyDownUp(KeyEvent.KEYCODE_ENTER);
}
});
ctrlBtn.setOnClickListener(new View.OnClickListener() {
@SuppressLint("UseCompatLoadingForDrawables")
@Override
public void onClick(View v) {
if (!ctrlClicked) {
sendKey(KeyEvent.KEYCODE_CTRL_LEFT, false);
ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
ctrlClicked = true;
} else {
sendKey(KeyEvent.KEYCODE_CTRL_LEFT, true);
ctrlBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
ctrlClicked = false;
}
}
});
altBtn.setOnClickListener(new View.OnClickListener() {
@SuppressLint("UseCompatLoadingForDrawables")
@Override
public void onClick(View v) {
if (!altClicked) {
sendKey(KeyEvent.KEYCODE_ALT_LEFT, false);
altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button2));
altClicked = true;
} else {
sendKey(KeyEvent.KEYCODE_ALT_LEFT, true);
altBtn.setBackground(getResources().getDrawable(R.drawable.controls_button1));
altClicked = false;
}
}
});
delBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyDownUp(KeyEvent.KEYCODE_DEL);
}
});
ctrlAltDelBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendCtrlAtlDelKey();
}
});
qmpBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (monitorMode) {
onVNC();
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_terminal_24));
} else {
onMonitor();
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_computer_24));
}
}
});
if (monitorMode) {
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_terminal_24));
} else {
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_computer_24));
}
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
R.layout.container_function, functionsArray);
ListView listView = findViewById(R.id.functions);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position == 0) {
keyDownUp(KeyEvent.KEYCODE_F1);
} else if (position == 1) {
keyDownUp(KeyEvent.KEYCODE_F2);
} else if (position == 2) {
keyDownUp(KeyEvent.KEYCODE_F3);
} else if (position == 3) {
keyDownUp(KeyEvent.KEYCODE_F4);
} else if (position == 4) {
keyDownUp(KeyEvent.KEYCODE_F5);
} else if (position == 5) {
keyDownUp(KeyEvent.KEYCODE_F6);
} else if (position == 6) {
keyDownUp(KeyEvent.KEYCODE_F7);
} else if (position == 7) {
keyDownUp(KeyEvent.KEYCODE_F8);
} else if (position == 8) {
keyDownUp(KeyEvent.KEYCODE_F9);
} else if (position == 9) {
keyDownUp(KeyEvent.KEYCODE_F10);
} else if (position == 10) {
keyDownUp(KeyEvent.KEYCODE_F11);
} else if (position == 11) {
keyDownUp(KeyEvent.KEYCODE_F12);
}
}
});
ImageButton hideBtn = findViewById(R.id.visibilityButton);
hideBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FrameLayout l = findViewById(R.id.mainControl);
if (l.getVisibility() == View.VISIBLE) {
l.setVisibility(View.GONE);
hideBtn.setImageResource(R.drawable.round_visibility_24);
} else {
l.setVisibility(View.VISIBLE);
hideBtn.setImageResource(R.drawable.round_visibility_off_24);
}
}
});
}
private void keyDownUp(int keyEventCode) {
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
}
private void sendKey(int keyEventCode, boolean up) {
if (up)
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
else dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
}
public void sendCtrlAtlDelKey() {
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_CTRL_LEFT));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ALT_LEFT));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_CTRL_LEFT));
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ALT_LEFT));
dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
private void setDefaulViewMode() {
// Fit to Screen
AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this);
showPanningState();
// screenMode = VNCScreenMode.FitToScreen;
setLayout(getResources().getConfiguration());
UIUtils.setOrientation(this);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setLayout(newConfig);
}
public enum VNCScreenMode {
Normal,
FitToScreen,
Fullscreen //fullscreen not implemented yet
}
public static VNCScreenMode screenMode = VNCScreenMode.FitToScreen;
private void setLayout(Configuration newConfig) {
boolean isLanscape =
(newConfig != null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
|| UIUtils.isLandscapeOrientation(this);
View vnc_canvas_layout = (View) this.findViewById(R.id.vnc_canvas_layout);
RelativeLayout.LayoutParams vnc_canvas_layout_params = null;
RelativeLayout.LayoutParams vnc_params = null;
//normal 1-1
if (screenMode == VNCScreenMode.Normal) {
if (isLanscape) {
vnc_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
// vnc_params.addRule(RelativeLayout.CENTER_IN_PARENT);
vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
vnc_canvas_layout_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
// vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT);
vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
} else {
vnc_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
vnc_canvas_layout_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
}
} else {
//fittoscreen
if (isLanscape) {
vnc_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
vnc_params.addRule(RelativeLayout.CENTER_IN_PARENT);
vnc_canvas_layout_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
);
vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_IN_PARENT);
} else {
final Display display = getWindow().getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int h = ViewGroup.LayoutParams.WRAP_CONTENT;
if (vncCanvas != null && vncCanvas.rfb != null
&& vncCanvas.rfb.framebufferWidth != 0
&& vncCanvas.rfb.framebufferHeight != 0) {
h = size.x * vncCanvas.rfb.framebufferHeight / vncCanvas.rfb.framebufferWidth;
}
vnc_params = new RelativeLayout.LayoutParams(
size.x,
h
);
vnc_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
vnc_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
vnc_canvas_layout_params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
vnc_canvas_layout_params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
vnc_canvas_layout_params.addRule(RelativeLayout.CENTER_HORIZONTAL);
}
}
this.vncCanvas.setLayoutParams(vnc_params);
vnc_canvas_layout.setLayoutParams(vnc_canvas_layout_params);
this.invalidateOptionsMenu();
}
public void stopTimeListener() {
Log.v(TAG, "Stopping Listener");
synchronized (this.lockTime) {
this.timeQuit = true;
this.lockTime.notifyAll();
}
}
public void onDestroy() {
super.onDestroy();
this.stopTimeListener();
}
public void onPause() {
MainService.updateServiceNotification("Vectras VM Running in Background");
super.onPause();
}
public void onResume() {
MainService.updateServiceNotification("Vectras VM Running");
if (monitorMode) {
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_terminal_24));
} else {
qmpBtn.setImageDrawable(getResources().getDrawable(R.drawable.round_computer_24));
}
super.onResume();
}
public void checkStatus() {
while (timeQuit != true) {
MainActivity.VMStatus status = Machine.checkSaveVMStatus(activity);
Log.v(TAG, "Status: " + status);
if (status == MainActivity.VMStatus.Unknown
|| status == MainActivity.VMStatus.Completed
|| status == MainActivity.VMStatus.Failed
) {
//Log.v(TAG, "Saving state is done: " + status);
stopTimeListener();
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Log.w("SaveVM", "Interrupted");
}
}
Log.v("SaveVM", "Save state complete");
}
public void startSaveVMListener() {
this.stopTimeListener();
timeQuit = false;
try {
Log.v("Listener", "Time Listener Started...");
checkStatus();
synchronized (lockTime) {
while (timeQuit == false) {
lockTime.wait();
}
lockTime.notifyAll();
}
} catch (Exception ex) {
ex.printStackTrace();
Log.v("SaveVM", "Time listener thread error: " + ex.getMessage());
}
Log.v("Listener", "Time listener thread exited...");
}
String TAG = "MainVNCActivity";
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
super.onOptionsItemSelected(item);
if (item.getItemId() == this.KEYBOARD || item.getItemId() == R.id.itemKeyboard) {
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
toggleKeyboardFlag = UIUtils.onKeyboard(activity, toggleKeyboardFlag, vncCanvas);
}
}, 200);
} else if (item.getItemId() == R.id.itemReset) {
Machine.resetVM(activity);
} else if (item.getItemId() == R.id.itemShutdown) {
UIUtils.hideKeyboard(this, vncCanvas);
Machine.stopVM(activity);
} else if (item.getItemId() == R.id.itemDrives) {
} else if (item.getItemId() == R.id.itemMonitor) {
if (this.monitorMode) {
this.onVNC();
} else {
this.onMonitor();
}
} else if (item.getItemId() == R.id.itemSaveState) {
this.promptPause(activity);
} else if (item.getItemId() == R.id.itemFitToScreen) {
return onFitToScreen();
} else if (item.getItemId() == R.id.itemFullScreen) {
return toggleFullScreen();
} else if (item.getItemId() == this.QUIT) {
} else if (item.getItemId() == R.id.itemCenterMouse) {
onMouseMode();
} else if (item.getItemId() == R.id.itemCalibrateMouse) {
calibration();
} else if (item.getItemId() == R.id.itemHelp) {
} else if (item.getItemId() == R.id.itemHideToolbar) {
this.onHideToolbar();
} else if (item.getItemId() == R.id.itemDisplay) {
this.onSelectMenuVNCDisplay();
} else if (item.getItemId() == R.id.itemViewLog) {
}
this.invalidateOptionsMenu();
return true;
}
private void onMouseMode() {
String[] items = {"Trackpad Mouse (Phone)",
"Bluetooth/USB Mouse (Desktop mode)", //Physical mouse for Chromebook, Android x86 PC, or Bluetooth Mouse
};
final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this);
mBuilder.setTitle("Mouse");
mBuilder.setSingleChoiceItems(items, Config.mouseMode.ordinal(), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case 0:
setUIModeMobile(true);
break;
case 1:
promptSetUIModeDesktop(MainVNCActivity.this, false);
break;
default:
break;
}
dialog.dismiss();
}
});
final AlertDialog alertDialog = mBuilder.create();
alertDialog.show();
}
public boolean checkVMResolutionFits() {
if (vncCanvas.rfb.framebufferWidth < vncCanvas.getWidth()
&& vncCanvas.rfb.framebufferHeight < vncCanvas.getHeight())
return true;
return false;
}
private void onDisplayMode() {
String[] items = {
"Normal (One-To-One)",
"Fit To Screen"
//"Full Screen" //Stretched
};
int currentScaleType = vncCanvas.getScaleType() == ImageView.ScaleType.FIT_CENTER ? 1 : 0;
final AlertDialog.Builder mBuilder = new AlertDialog.Builder(this);
mBuilder.setTitle("Display Mode");
mBuilder.setSingleChoiceItems(items, currentScaleType, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int i) {
switch (i) {
case 0:
onNormalScreen();
onMouse();
break;
case 1:
if (Config.mouseMode == Config.MouseMode.External) {
UIUtils.toastShort(MainVNCActivity.this, "Fit to Screen disabled under Desktop mode");
dialog.dismiss();
return;
}
onFitToScreen();
onMouse();
break;
default:
break;
}
dialog.dismiss();
}
});
final AlertDialog alertDialog = mBuilder.create();
alertDialog.show();
}
private void setUIModeMobile(boolean fitToScreen) {
try {
MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
Config.mouseMode = Config.MouseMode.Trackpad;
MainSettingsManager.setDesktopMode(this, false);
if (fitToScreen)
onFitToScreen();
else
onNormalScreen();
onMouse();
//UIUtils.toastShort(MainVNCActivity.this, "Trackpad Calibrating");
invalidateOptionsMenu();
} catch (Exception ex) {
if (Config.debug)
ex.printStackTrace();
}
}
private void promptSetUIModeDesktop(final Activity activity, final boolean mouseMethodAlt) {
final AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
alertDialog.setTitle("Desktop mode");
LinearLayout mLayout = new LinearLayout(this);
mLayout.setPadding(20, 20, 20, 20);
mLayout.setOrientation(LinearLayout.VERTICAL);
TextView textView = new TextView(activity);
textView.setVisibility(View.VISIBLE);
String desktopInstructions = this.getString(R.string.desktopInstructions);
if (!checkVMResolutionFits()) {
String resolutionWarning = "Warning: Machine resolution "
+ vncCanvas.rfb.framebufferWidth + "x" + vncCanvas.rfb.framebufferHeight +
" is too high for Desktop Mode. " +
"Scaling will be used and Mouse Alignment will not be accurate. " +
"Reduce display resolution for better experience\n\n";
desktopInstructions = resolutionWarning + desktopInstructions;
}
textView.setText(desktopInstructions);
LinearLayout.LayoutParams textViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
ScrollView scrollView = new ScrollView(this);
scrollView.addView(textView);
mLayout.addView(scrollView, textViewParams);
alertDialog.setView(mLayout);
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setUIModeDesktop();
alertDialog.dismiss();
}
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
alertDialog.dismiss();
}
});
alertDialog.show();
}
private void setUIModeDesktop() {
try {
MotionEvent a = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
Config.mouseMode = Config.MouseMode.External;
MainSettingsManager.setDesktopMode(this, true);
if (Config.showToast)
UIUtils.toastShort(MainVNCActivity.this, "External Mouse Enabled");
onNormalScreen();
AbstractScaling.getById(R.id.itemOneToOne).setScaleTypeForActivity(MainVNCActivity.this);
showPanningState();
onMouse();
} catch (Exception e) {
if (Config.debug)
e.printStackTrace();
}
//vncCanvas.reSize(false);
invalidateOptionsMenu();
}
public void setContentView() {
setContentView(R.layout.activity_vnc);
}
private boolean toggleFullScreen() {
UIUtils.toastShort(this, "VNC Fullscreen not supported");
return false;
}
private boolean onFitToScreen() {
try {
UIUtils.setOrientation(this);
ActionBar bar = this.getSupportActionBar();
if (bar != null && !MainSettingsManager.getAlwaysShowMenuToolbar(this)) {
bar.hide();
}
inputHandler = getInputHandlerById(R.id.itemInputTouchpad);
connection.setInputMode(inputHandler.getName());
connection.setFollowMouse(true);
mouseOn = true;
AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this);
showPanningState();
screenMode = VNCScreenMode.FitToScreen;
setLayout(null);
return true;
} catch (Exception ex) {
if (Config.debug)
ex.printStackTrace();
}
return false;
}
private boolean onNormalScreen() {
try {
//Force only landscape
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
ActionBar bar = MainVNCActivity.this.getSupportActionBar();
if (bar != null) {
bar.hide();
}
inputHandler = getInputHandlerById(R.id.itemInputTouchpad);
connection.setInputMode(inputHandler.getName());
connection.setFollowMouse(true);
mouseOn = true;
AbstractScaling.getById(R.id.itemOneToOne).setScaleTypeForActivity(this);
showPanningState();
screenMode = VNCScreenMode.Normal;
setLayout(null);
return true;
} catch (Exception ex) {
if (Config.debug)
ex.printStackTrace();
} finally {
}
return false;
}
private boolean onMouse() {
// Main: For now we disable other modes
if (Config.disableMouseModes)
mouseOn = false;
if (mouseOn == false) {
inputHandler = getInputHandlerById(R.id.itemInputTouchpad);
connection.setInputMode(inputHandler.getName());
connection.setFollowMouse(true);
mouseOn = true;
} else {
// XXX: Main
// we disable panning for now
// input1 = getInputHandlerById(R.id.itemFitToScreen);
// input1 = getInputHandlerById(R.id.itemInputTouchPanZoomMouse);
// connection.setFollowMouse(false);
// mouseOn = false;
}
//Start calibration
calibration();
return true;
}
//XXX: We need to adjust the mouse inside the Guest
// This is a known issue with QEMU under VNC mode
// this only fixes things temporarily.
// There is a workaround to choose USB Tablet for mouse emulation
// though it might not work for all Guest OSes
public void calibration() {
Thread t = new Thread(new Runnable() {
public void run() {
try {
int origX = vncCanvas.mouseX;
int origY = vncCanvas.mouseY;
MotionEvent event = null;
for (int i = 0; i < 4 * 20; i++) {
int x = 0 + i * 50;
int y = 0 + i * 50;
if (i % 4 == 1) {
x = vncCanvas.rfb.framebufferWidth;
} else if (i % 4 == 2) {
y = vncCanvas.rfb.framebufferHeight;
} else if (i % 4 == 3) {
x = 0;
}
event = MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE,
x, y, 0);
Thread.sleep(10);
vncCanvas.processPointerEvent(event, false, false);
}
Thread.sleep(50);
event = MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE,
origX, origY, 0);
vncCanvas.processPointerEvent(event, false, false);
} catch (Exception ex) {
}
}
});
t.start();
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
return this.setupMenu(menu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.clear();
return this.setupMenu(menu);
}
public boolean setupMenu(Menu menu) {
getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu);
int maxMenuItemsShown = 4;
int actionShow = MenuItemCompat.SHOW_AS_ACTION_IF_ROOM;
if (UIUtils.isLandscapeOrientation(this)) {
maxMenuItemsShown = 6;
actionShow = MenuItemCompat.SHOW_AS_ACTION_ALWAYS;
}
if (vncCanvas.scaling != null) {
menu.findItem(vncCanvas.scaling.getId()).setChecked(true);
}
if (this.monitorMode) {
menu.findItem(R.id.itemMonitor).setTitle("VM Display");
} else {
menu.findItem(R.id.itemMonitor).setTitle("QEMU Monitor");
}
//XXX: We don't need these for now
menu.removeItem(menu.findItem(R.id.itemEnterText).getItemId());
menu.removeItem(menu.findItem(R.id.itemSendKeyAgain).getItemId());
menu.removeItem(menu.findItem(R.id.itemSpecialKeys).getItemId());
menu.removeItem(menu.findItem(R.id.itemInputMode).getItemId());
menu.removeItem(menu.findItem(R.id.itemScaling).getItemId());
menu.removeItem(menu.findItem(R.id.itemCtrlAltDel).getItemId());
menu.removeItem(menu.findItem(R.id.itemCtrlC).getItemId());
menu.removeItem(menu.findItem(R.id.itemColorMode).getItemId());
menu.removeItem(menu.findItem(R.id.itemFullScreen).getItemId());
if (MainSettingsManager.getAlwaysShowMenuToolbar(activity) || Config.mouseMode == Config.MouseMode.External) {
menu.removeItem(menu.findItem(R.id.itemHideToolbar).getItemId());
maxMenuItemsShown--;
}
// Menu inputMenu = menu.findItem(R.id.itemInputMode).getSubMenu();
//
// inputModeMenuItems = new MenuItem[inputModeIds.length];
// for (int i = 0; i < inputModeIds.length; i++) {
// inputModeMenuItems[i] = inputMenu.findItem(inputModeIds[i]);
// }
// updateInputMenu();
// menu.removeItem(menu.findItem(R.id.itemCenterMouse).getItemId());
// Main: Disable Panning for now
// if (this.mouseOn) {
// menu.findItem(R.id.itemCenterMouse).setTitle("Pan (Mouse Off)");
// menu.findItem(R.id.itemCenterMouse).setIcon(R.drawable.pan);
// } else {
menu.findItem(R.id.itemCenterMouse).setTitle("Mouse");
//
// }
for (int i = 0; i < menu.size() && i < maxMenuItemsShown; i++) {
MenuItemCompat.setShowAsAction(menu.getItem(i), actionShow);
}
return true;
}
public static boolean toggleKeyboardFlag = true;
private void onMonitor() {
if (Config.showToast)
UIUtils.toastShort(this, "Connecting to QEMU Monitor");
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
monitorMode = true;
vncCanvas.sendMetaKey1(50, 6);
}
});
t.start();
}
private void onVNC() {
UIUtils.toastShort(this, "Connecting to VM");
Thread t = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
monitorMode = false;
vncCanvas.sendMetaKey1(49, 6);
}
});
t.start();
}
// FIXME: We need this to able to catch complex characters strings like
// grave and send it as text
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
vncCanvas.sendText(event.getCharacters().toString());
return true;
} else
return super.dispatchKeyEvent(event);
}
private void resumeVM() {
if (MainActivity.vmexecutor == null) {
return;
}
Thread t = new Thread(new Runnable() {
public void run() {
if (MainActivity.vmexecutor.paused == 1) {
try {
Thread.sleep(4000);
} catch (InterruptedException ex) {
Logger.getLogger(MainVNCActivity.class.getName()).log(Level.SEVERE, null, ex);
}
if (vncCanvas == null)
return;
MainActivity.vmexecutor.paused = 0;
String command = QmpClient.cont();
String msg = QmpClient.sendCommand(command);
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
setUIModeMobile(screenMode == VNCScreenMode.FitToScreen);
}
}, 500);
}
}
});
t.start();
}
private void onPauseVM() {
Thread t = new Thread(new Runnable() {
public void run() {
// Delete any previous state file
if (MainActivity.vmexecutor.save_state_name != null) {
File file = new File(MainActivity.vmexecutor.save_state_name);
if (file.exists()) {
file.delete();
}
}
UIUtils.toastShort(getApplicationContext(), "Please wait while saving VM State");
String uri = "fd:" + MainActivity.vmexecutor.get_fd(MainActivity.vmexecutor.save_state_name);
String command = QmpClient.stop();
String msg = QmpClient.sendCommand(command);
// if (msg != null)
// Log.i(TAG, msg);
command = QmpClient.migrate(false, false, uri);
msg = QmpClient.sendCommand(command);
if (msg != null) {
// Log.i(TAG, msg);
processMigrationResponse(msg);
}
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
VMListener a = new VMListener();
a.execute();
}
}, 0);
}
});
t.start();
}
private void processMigrationResponse(String response) {
String errorStr = null;
if (response.contains("error")) {
try {
JSONObject object = new JSONObject(response);
errorStr = object.getString("error");
} catch (Exception ex) {
if (Config.debug)
ex.printStackTrace();
}
}
if (errorStr != null && errorStr.contains("desc")) {
String descStr = null;
try {
JSONObject descObj = new JSONObject(errorStr);
descStr = descObj.getString("desc");
} catch (Exception ex) {
if (Config.debug)
ex.printStackTrace();
}
final String descStr1 = descStr;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
Machine.pausedErrorVM(activity, descStr1);
}
}, 100);
}
}
private class VMListener extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... arg0) {
startSaveVMListener();
return null;
}
@Override
protected void onPostExecute(Void test) {
// if (progDialog.isShowing()) {
// progDialog.dismiss();
// }
}
}
private void fullScreen() {
AbstractScaling.getById(R.id.itemFitToScreen).setScaleTypeForActivity(this);
showPanningState();
}
public void promptPause(final Activity activity) {
final AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
alertDialog.setTitle("Pause VM");
TextView stateView = new TextView(activity);
stateView.setText("This make take a while depending on the RAM size used");
stateView.setPadding(20, 20, 20, 20);
alertDialog.setView(stateView);
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Pause", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onPauseVM();
return;
}
});
alertDialog.show();
}
public void onBackPressed() {
super.onBackPressed();
FrameLayout l = findViewById(R.id.mainControl);
if (l != null) {
if (l.getVisibility() == View.VISIBLE) {
l.setVisibility(View.GONE);
} else
l.setVisibility(View.VISIBLE);
}
}
public void onHideToolbar() {
ActionBar bar = this.getSupportActionBar();
if (bar != null) {
bar.hide();
}
}
@Override
public void onConnected() {
this.resumeVM();
if (!firstConnection)
UIUtils.showHints(this);
firstConnection = true;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
if (Config.mouseMode == Config.MouseMode.External)
setUIModeDesktop();
else
setUIModeMobile(screenMode == VNCScreenMode.FitToScreen);
}
}, 1000);
}
public void onSelectMenuVNCDisplay() {
final AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(activity, R.style.MainDialogTheme).create();
alertDialog.setTitle("Display");
LinearLayout.LayoutParams volParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
LinearLayout t = createVNCDisplayPanel();
t.setLayoutParams(volParams);
ScrollView s = new ScrollView(activity);
s.addView(t);
alertDialog.setView(s);
alertDialog.setButton(Dialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
alertDialog.cancel();
}
});
alertDialog.show();
}
public LinearLayout createVNCDisplayPanel() {
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setPadding(20, 20, 20, 20);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
int currRate = getCurrentVNCRefreshRate();
LinearLayout buttonsLayout = new LinearLayout(this);
buttonsLayout.setOrientation(LinearLayout.HORIZONTAL);
buttonsLayout.setGravity(Gravity.CENTER_HORIZONTAL);
Button displayMode = new Button(this);
displayMode.setText("Display Mode");
displayMode.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
onDisplayMode();
}
});
buttonsLayout.addView(displayMode);
Button colors = new Button(this);
colors.setText("Color Mode");
colors.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
selectColorModel();
}
});
buttonsLayout.addView(colors);
layout.addView(buttonsLayout);
final TextView value = new TextView(this);
value.setText("Display Refresh Rate: " + currRate + " Hz");
layout.addView(value);
value.setLayoutParams(params);
SeekBar rate = new SeekBar(this);
rate.setMax(Config.MAX_DISPLAY_REFRESH_RATE);
rate.setProgress(currRate);
rate.setLayoutParams(params);
((SeekBar) rate).setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar s, int progress, boolean touch) {
value.setText("Refresh Rate: " + (progress + 1) + " Hz");
}
public void onStartTrackingTouch(SeekBar arg0) {
}
public void onStopTrackingTouch(SeekBar arg0) {
int progress = arg0.getProgress() + 1;
int refreshMs = 1000 / progress;
Log.v(TAG, "Changing display refresh rate (ms): " + refreshMs);
MainActivity.vmexecutor.setvncrefreshrate(refreshMs);
}
});
layout.addView(rate);
return layout;
}
public int getCurrentVNCRefreshRate() {
return 1000 / MainActivity.vmexecutor.getvncrefreshrate();
}
}