60 implements LibFreeRDP.UIEventListener, ClipboardManagerProxy.OnClipboardChangedListener
62 public static final String PARAM_CONNECTION_REFERENCE =
"conRef";
63 public static final String PARAM_INSTANCE =
"instance";
64 private static final String TAG =
"FreeRDP.SessionActivity";
66 private Bitmap bitmap;
67 private SessionState session;
71 private static final int REFRESH_SESSIONVIEW = 1;
72 private static final int DISPLAY_TOAST = 2;
73 private static final int GRAPHICS_CHANGED = 6;
74 private static final int POINTER_SET = 7;
76 private final Handler uiHandler =
new Handler(Looper.getMainLooper()) {
77 @Override
public void handleMessage(Message msg)
81 case GRAPHICS_CHANGED:
83 sessionView.onSurfaceChange(session);
84 scrollView.requestLayout();
87 case REFRESH_SESSIONVIEW:
89 sessionView.invalidateRegion();
94 Toast errorToast = Toast.makeText(getApplicationContext(), msg.obj.toString(),
101 Bundle data = msg.getData();
102 if (data !=
null && data.containsKey(
"pixels"))
104 int[] pixels = data.getIntArray(
"pixels");
105 int width = data.getInt(
"width");
106 int height = data.getInt(
"height");
107 int hotX = data.getInt(
"hotX");
108 int hotY = data.getInt(
"hotY");
109 sessionView.setRemoteCursor(pixels, width, height, hotX, hotY);
113 sessionView.setRemoteCursor(
null, 0, 0, 0, 0);
121 private int screen_width;
122 private int screen_height;
124 private boolean connectCancelledByUser =
false;
125 private boolean sessionRunning =
false;
126 private long backPressedTime = 0;
130 private ClipboardManagerProxy mClipboardManager;
136 private void hideSystemBars()
141 WindowCompat.setDecorFitsSystemWindows(getWindow(),
false);
143 if (getSupportActionBar() !=
null)
144 getSupportActionBar().hide();
146 WindowInsetsControllerCompat controller =
147 WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
148 controller.setAppearanceLightStatusBars(
false);
149 controller.setAppearanceLightNavigationBars(
false);
151 getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
152 getWindow().setNavigationBarColor(android.graphics.Color.TRANSPARENT);
153 getWindow().setNavigationBarContrastEnforced(
false);
155 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
159 toHide |= WindowInsetsCompat.Type.statusBars();
161 toHide |= WindowInsetsCompat.Type.navigationBars();
165 controller.hide(toHide);
166 controller.setSystemBarsBehavior(
167 WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
171 controller.show(WindowInsetsCompat.Type.systemBars());
177 int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
178 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
180 flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
182 flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
183 if ((flags & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) !=
185 flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
187 getWindow().getDecorView().setSystemUiVisibility(flags);
190 WindowManager.LayoutParams lp = getWindow().getAttributes();
191 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
192 lp.layoutInDisplayCutoutMode =
193 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
195 lp.layoutInDisplayCutoutMode =
196 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
197 getWindow().setAttributes(lp);
200 @Override
public void onCreate(Bundle savedInstanceState)
202 super.onCreate(savedInstanceState);
206 this.setContentView(R.layout.session);
208 Log.v(TAG,
"Session.onCreate");
217 final View activityRootView = findViewById(R.id.session_root_view);
218 activityRootView.setFitsSystemWindows(
false);
219 ViewCompat.setOnApplyWindowInsetsListener(activityRootView,
220 (v, insets) -> onWindowInsetsChanged(v, insets));
221 activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
222 new OnGlobalLayoutListener() {
223 @Override
public void onGlobalLayout()
225 screen_width = scrollView.getWidth() - scrollView.getPaddingLeft() -
226 scrollView.getPaddingRight();
227 screen_height = scrollView.getHeight() - scrollView.getPaddingTop() -
228 scrollView.getPaddingBottom();
231 if (!sessionRunning && getIntent() !=
null)
233 processIntent(getIntent());
234 sessionRunning =
true;
239 sessionView = findViewById(R.id.sessionView);
240 sessionView.requestFocus();
242 touchPointerView = findViewById(R.id.touchPointerView);
245 @Override public void onToggleTouchPointer()
247 if (inputManager != null)
248 inputManager.toggleTouchPointer();
250 @Override
public void onToggleSysKeyboard()
252 if (inputManager !=
null)
253 inputManager.toggleSystemKeyboard();
255 @Override
public void onToggleExtKeyboard()
257 if (inputManager !=
null)
258 inputManager.toggleExtendedKeyboard();
262 KeyboardView keyboardView = findViewById(R.id.extended_keyboard);
263 KeyboardView modifiersKeyboardView = findViewById(R.id.extended_keyboard_header);
265 scrollView = findViewById(R.id.sessionScrollView);
267 sessionViewModel.getState().observe(
this, this::onConnectionStateChanged);
270 @Override public void onUserCancel()
272 connectCancelledByUser = true;
278 keyboardView, modifiersKeyboardView);
279 sessionView.setSessionViewListener(inputManager);
280 touchPointerView.setTouchPointerListener(inputManager);
281 sessionView.setScaleGestureDetector(
282 new ScaleGestureDetector(
this, inputManager.getPinchZoomListener()));
284 mClipboardManager = ClipboardManagerProxy.getClipboardManager(
this);
285 mClipboardManager.addClipboardChangedListener(
this);
287 getOnBackPressedDispatcher().addCallback(
this,
new OnBackPressedCallback(
true) {
288 @Override
public void handleOnBackPressed()
297 @Override
public void onWindowFocusChanged(
boolean hasFocus)
299 super.onWindowFocusChanged(hasFocus);
303 mClipboardManager.getPrimaryClipManually();
307 @Override
protected void onStart()
310 Log.v(TAG,
"Session.onStart");
313 @Override
protected void onRestart()
316 Log.v(TAG,
"Session.onRestart");
319 @Override
protected void onResume()
322 Log.v(TAG,
"Session.onResume");
323 activeSession =
this;
326 @Override
protected void onPause()
329 Log.v(TAG,
"Session.onPause");
330 if (activeSession ==
this)
331 activeSession =
null;
333 inputManager.hideKeyboards();
336 @Override
protected void onStop()
339 Log.v(TAG,
"Session.onStop");
342 @Override
protected void onDestroy()
344 if (connectThread !=
null)
346 connectThread.interrupt();
349 Log.v(TAG,
"Session.onDestroy");
352 GlobalApp.cancelDisconnectTimer();
356 LibFreeRDP.disconnect(session.getInstance());
359 sessionViewModel.unregister();
362 mClipboardManager.removeClipboardboardChangedListener(
this);
365 GlobalApp.freeSession(session.getInstance());
370 @Override
public void onConfigurationChanged(Configuration newConfig)
372 super.onConfigurationChanged(newConfig);
375 inputManager.reloadKeyboards();
380 if (session !=
null && session.getBookmark() !=
null &&
381 session.getBookmark().getActiveScreenSettings().isFitScreen())
383 scrollView.post(() -> {
384 if (screen_width > 0 && screen_height > 0)
385 LibFreeRDP.sendMonitorLayout(session.getInstance(), screen_width,
391 private WindowInsetsCompat onWindowInsetsChanged(View rootView, WindowInsetsCompat windowInsets)
393 boolean fitSafeArea = ApplicationSettingsActivity.getFitRoundedCorners(
this);
394 boolean hideStatusBar = ApplicationSettingsActivity.getHideStatusBar(
this);
395 boolean hideNavBar = ApplicationSettingsActivity.getHideNavigationBar(
this);
397 int insetsTop = windowInsets
398 .getInsets(WindowInsetsCompat.Type.statusBars() |
399 WindowInsetsCompat.Type.displayCutout())
401 rootView.setPadding(0, hideStatusBar ? 0 : insetsTop, 0, 0);
402 Insets navInsets = hideNavBar
404 : windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
405 if (floatingToolbar !=
null)
406 floatingToolbar.setInsets(navInsets.left, hideStatusBar ? 0 : insetsTop,
407 navInsets.right, navInsets.bottom);
409 int safeLeft = 0, safeTop = 0, safeRight = 0, safeBottom = 0;
410 if (fitSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
412 WindowInsets platformInsets = windowInsets.toWindowInsets();
413 if (platformInsets !=
null)
415 boolean landscape = getResources().getConfiguration().orientation ==
416 Configuration.ORIENTATION_LANDSCAPE;
418 int radTL = cornerRadius(platformInsets, RoundedCorner.POSITION_TOP_LEFT);
419 int radBL = cornerRadius(platformInsets, RoundedCorner.POSITION_BOTTOM_LEFT);
420 int radTR = cornerRadius(platformInsets, RoundedCorner.POSITION_TOP_RIGHT);
421 int radBR = cornerRadius(platformInsets, RoundedCorner.POSITION_BOTTOM_RIGHT);
425 safeLeft = Math.max(0, Math.max(radTL, radBL) - rootView.getPaddingLeft());
426 safeRight = Math.max(0, Math.max(radTR, radBR) - rootView.getPaddingRight());
430 safeTop = Math.max(0, Math.max(radTL, radTR) - rootView.getPaddingTop());
431 safeBottom = Math.max(0, Math.max(radBL, radBR) - rootView.getPaddingBottom());
436 scrollView.setPadding(Math.max(safeLeft, navInsets.left), safeTop,
437 Math.max(safeRight, navInsets.right),
438 Math.max(safeBottom, navInsets.bottom));
439 if (inputManager !=
null)
440 inputManager.setSafeInsets(safeLeft, safeTop);
442 return WindowInsetsCompat.CONSUMED;
445 @RequiresApi(Build.VERSION_CODES.S)
446 private static
int cornerRadius(WindowInsets insets,
int position)
448 RoundedCorner corner = insets.getRoundedCorner(position);
449 return (corner !=
null) ? corner.getRadius() : 0;
452 private void processIntent(Intent intent)
455 Bundle bundle = intent.getExtras();
456 Uri openUri = intent.getData();
463 else if (bundle.containsKey(PARAM_INSTANCE))
465 int inst = bundle.getInt(PARAM_INSTANCE);
466 session = GlobalApp.getSession(inst);
467 bitmap = session.getSurface().getBitmap();
470 else if (bundle.containsKey(PARAM_CONNECTION_REFERENCE))
472 String refStr = bundle.getString(PARAM_CONNECTION_REFERENCE);
473 if (ConnectionReference.isHostnameReference(refStr))
475 BookmarkBase bookmark =
new BookmarkBase();
476 bookmark.setHostname(ConnectionReference.getHostname(refStr));
479 else if (ConnectionReference.isBookmarkReference(refStr))
481 sessionViewModel.loadBookmarkById(ConnectionReference.getBookmarkId(refStr),
483 if (bookmark != null)
486 closeSessionActivity(RESULT_CANCELED);
491 closeSessionActivity(RESULT_CANCELED);
497 closeSessionActivity(RESULT_CANCELED);
501 private void connect(BookmarkBase bookmark)
503 session = GlobalApp.createSession(bookmark, getApplicationContext());
505 BookmarkBase.ScreenSettings screenSettings =
506 session.getBookmark().getActiveScreenSettings();
507 Log.v(TAG,
"Screen Resolution: " + screenSettings.getResolutionString());
508 if (screenSettings.isAutomatic())
512 screenSettings.setHeight(screen_height);
513 screenSettings.setWidth(screen_width);
515 if (screenSettings.isFitScreen())
517 screenSettings.setHeight(screen_height);
518 screenSettings.setWidth(screen_width);
521 connectWithTitle(bookmark.getLabel());
524 private void connect(Uri openUri)
526 session = GlobalApp.createSession(openUri, getApplicationContext());
528 connectWithTitle(openUri.getAuthority());
531 static class ConnectThread
extends Thread
533 private final SessionState runnableSession;
534 private final Context context;
536 public ConnectThread(@NonNull Context context, @NonNull SessionState session)
538 this.context = context;
539 runnableSession = session;
544 runnableSession.connect(context.getApplicationContext());
548 private ConnectThread connectThread =
null;
550 private void connectWithTitle(String title)
552 session.setUIEventListener(
this);
554 sessionViewModel.register(session.getInstance());
556 dialogs.showProgress(title, () -> {
557 connectCancelledByUser =
true;
558 LibFreeRDP.cancelConnection(session.getInstance());
561 connectThread =
new ConnectThread(getApplicationContext(), session);
562 connectThread.start();
567 private void bindSession()
569 Log.v(TAG,
"bindSession called");
570 session.setUIEventListener(
this);
571 sessionView.onSurfaceChange(session);
572 scrollView.requestLayout();
574 Bitmap surface = session.getSurface() !=
null ? session.getSurface().getBitmap() :
null;
575 inputManager.attachSession(session.getInstance(), surface);
576 inputManager.setScreenSize(screen_width, screen_height);
578 View rootView = findViewById(R.id.session_root_view);
579 if (rootView !=
null)
580 ViewCompat.requestApplyInsets(rootView);
583 private void closeSessionActivity(
int resultCode)
586 setResult(resultCode, getIntent());
590 public void handleBackPressed()
593 if (inputManager.isAnyKeyboardVisible())
595 inputManager.hideKeyboards();
598 if (inputManager.handleBackAsAltF4())
602 if (System.currentTimeMillis() - backPressedTime < 2000)
604 connectCancelledByUser =
true;
605 LibFreeRDP.disconnect(session.getInstance());
609 backPressedTime = System.currentTimeMillis();
610 Toast.makeText(
this, R.string.session_double_back_to_exit, Toast.LENGTH_SHORT).show();
614 @Override
public boolean onKeyLongPress(
int keyCode, KeyEvent event)
616 if (inputManager.onAndroidKeyLongPress(keyCode))
618 return super.onKeyLongPress(keyCode, event);
621 boolean handleKeyEvent(KeyEvent event)
623 return inputManager !=
null && inputManager.onAndroidKeyEvent(event);
632 @Override
public boolean onKeyDown(
int keycode, KeyEvent event)
634 if (keycode == KeyEvent.KEYCODE_BACK)
635 return super.onKeyDown(keycode, event);
636 return inputManager.onAndroidKeyEvent(event);
639 @Override
public boolean onKeyUp(
int keycode, KeyEvent event)
641 if (keycode == KeyEvent.KEYCODE_BACK)
642 return super.onKeyUp(keycode, event);
643 return inputManager.onAndroidKeyEvent(event);
648 @Override
public boolean onKeyMultiple(
int keyCode,
int repeatCount, KeyEvent event)
650 return inputManager.onAndroidKeyEvent(event);
658 @Override
public void OnSettingsChanged(
int width,
int height,
int bpp)
662 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
664 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
666 session.setSurface(
new BitmapDrawable(getResources(), bitmap));
668 if (inputManager !=
null)
669 inputManager.setBitmap(bitmap);
671 if (session.getBookmark() ==
null)
681 BookmarkBase.ScreenSettings settings = session.getBookmark().getActiveScreenSettings();
682 if ((settings.getWidth() != width && settings.getWidth() != width + 1) ||
683 settings.getHeight() != height || settings.getColors() != bpp)
684 uiHandler.sendMessage(Message.obtain(
685 null, DISPLAY_TOAST, getResources().getText(R.string.info_capabilities_changed)));
688 @Override
public void OnGraphicsUpdate(
int x,
int y,
int width,
int height)
690 LibFreeRDP.updateGraphics(session.getInstance(), bitmap, x, y, width, height);
692 sessionView.addInvalidRegion(
new Rect(x, y, x + width, y + height));
699 uiHandler.sendEmptyMessage(REFRESH_SESSIONVIEW);
702 @Override
public void OnGraphicsResize(
int width,
int height,
int bpp)
706 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
708 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
709 session.setSurface(
new BitmapDrawable(getResources(), bitmap));
711 if (inputManager !=
null)
712 inputManager.setBitmap(bitmap);
718 uiHandler.sendEmptyMessage(GRAPHICS_CHANGED);
722 public boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
723 StringBuilder password)
729 public boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
730 StringBuilder password)
732 return dialogs.promptCredentials(username, domain, password);
736 public int OnVerifiyCertificateEx(String host,
long port, String commonName, String subject,
737 String issuer, String fingerprint,
long flags)
739 if (ApplicationSettingsActivity.getAcceptAllCertificates(
this))
741 return dialogs.verifyCertificate(host, port, subject, issuer, fingerprint, flags);
745 public int OnVerifyChangedCertificateEx(String host,
long port, String commonName,
746 String subject, String issuer, String fingerprint,
747 String oldSubject, String oldIssuer,
748 String oldFingerprint,
long flags)
750 if (ApplicationSettingsActivity.getAcceptAllCertificates(
this))
752 return dialogs.verifyChangedCertificate(host, port, subject, issuer, fingerprint, flags);
755 @Override
public void OnRemoteClipboardChanged(String data)
757 Log.v(TAG,
"OnRemoteClipboardChanged: " + data);
758 mClipboardManager.setClipboardData(data);
761 @Override
public void OnRemoteClipboardImageChanged(
byte[] data)
763 Log.v(TAG,
"OnRemoteClipboardImageChanged: " + data.length +
" bytes");
764 mClipboardManager.setClipboardImage(data);
767 @Override
public void OnPointerSet(
int[] pixels,
int width,
int height,
int hotX,
int hotY)
769 Bundle data =
new Bundle();
770 data.putIntArray(
"pixels", pixels);
771 data.putInt(
"width", width);
772 data.putInt(
"height", height);
773 data.putInt(
"hotX", hotX);
774 data.putInt(
"hotY", hotY);
775 Message msg = uiHandler.obtainMessage(POINTER_SET);
777 uiHandler.sendMessage(msg);
780 @Override
public void OnPointerSetNull()
782 uiHandler.sendEmptyMessage(POINTER_SET);
785 @Override
public void OnPointerSetDefault()
787 sessionView.setDefaultCursor();
794 @Override
public boolean onGenericMotionEvent(MotionEvent e)
796 super.onGenericMotionEvent(e);
797 return inputManager !=
null && inputManager.onGenericMotionEvent(e);
802 @Override
public void onClipboardChanged(String data)
804 Log.v(TAG,
"onClipboardChanged: " + data);
806 LibFreeRDP.sendClipboardData(session.getInstance(), data);
809 @Override
public void onClipboardImageChanged(
byte[] data, String mimeType)
811 if (session !=
null && data !=
null)
812 LibFreeRDP.sendClipboardImageData(session.getInstance(), data, mimeType);
815 private void onConnectionStateChanged(SessionViewModel.ConnectionState state)
822 onSessionConnected();
828 onSessionDisconnected();
835 private void onSessionConnected()
837 Log.v(TAG,
"onSessionConnected");
839 if (connectCancelledByUser)
841 LibFreeRDP.disconnect(session.getInstance());
842 closeSessionActivity(RESULT_CANCELED);
849 if (ApplicationSettingsActivity.getKeepScreenOnWhenConnected(
this))
851 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
854 dialogs.dismissProgress();
856 if (session.getBookmark() ==
null)
863 Bundle bundle = getIntent().getExtras();
864 if (bundle !=
null && bundle.containsKey(PARAM_CONNECTION_REFERENCE))
866 if (ConnectionReference.isHostnameReference(
867 bundle.getString(PARAM_CONNECTION_REFERENCE)))
869 assert session.getBookmark().getType() == BookmarkBase.TYPE_MANUAL;
870 sessionViewModel.recordQuickConnectHistory(session.getBookmark().getHostname());
875 private void onSessionFailed()
877 Log.v(TAG,
"onSessionFailed");
880 if (inputManager !=
null)
881 inputManager.cancelPendingEvents();
886 if (!connectCancelledByUser)
887 uiHandler.sendMessage(Message.obtain(
888 null, DISPLAY_TOAST, getResources().getText(R.string.error_connection_failure)));
890 closeSessionActivity(RESULT_CANCELED);
893 private void onSessionDisconnected()
895 Log.v(TAG,
"onSessionDisconnected");
898 if (inputManager !=
null)
899 inputManager.cancelPendingEvents();
901 if (ApplicationSettingsActivity.getKeepScreenOnWhenConnected(
this))
903 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
908 session.setUIEventListener(
null);
909 closeSessionActivity(RESULT_OK);