FreeRDP
Loading...
Searching...
No Matches
SessionActivity.java
1/*
2 Android Session Activity
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5 Copyright 2026 Ibrahim Sevinc <ibrahim.sevinc.mail@gmail.com>
6
7 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
8 If a copy of the MPL was not distributed with this file, You can obtain one at
9 http://mozilla.org/MPL/2.0/.
10 */
11
12package com.freerdp.freerdpcore.presentation;
13
14import android.content.Context;
15import android.content.Intent;
16import android.content.res.Configuration;
17import android.graphics.Bitmap;
18import android.graphics.Bitmap.Config;
19import android.graphics.Rect;
20import android.graphics.drawable.BitmapDrawable;
21import android.inputmethodservice.KeyboardView;
22import android.net.Uri;
23import android.os.Build;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Looper;
27import android.os.Message;
28
29import androidx.activity.OnBackPressedCallback;
30import androidx.annotation.NonNull;
31import androidx.annotation.RequiresApi;
32import androidx.appcompat.app.AppCompatActivity;
33import androidx.core.graphics.Insets;
34import androidx.core.view.ViewCompat;
35import androidx.core.view.WindowCompat;
36import androidx.core.view.WindowInsetsCompat;
37import androidx.core.view.WindowInsetsControllerCompat;
38import androidx.lifecycle.ViewModelProvider;
39
40import android.util.Log;
41import android.view.KeyEvent;
42import android.view.MotionEvent;
43import android.view.ScaleGestureDetector;
44import android.view.View;
45import android.view.ViewTreeObserver.OnGlobalLayoutListener;
46import android.view.RoundedCorner;
47import android.view.WindowInsets;
48import android.view.WindowManager;
49import android.widget.Toast;
50
51import com.freerdp.freerdpcore.R;
52import com.freerdp.freerdpcore.application.GlobalApp;
53import com.freerdp.freerdpcore.application.SessionState;
54import com.freerdp.freerdpcore.domain.BookmarkBase;
55import com.freerdp.freerdpcore.domain.ConnectionReference;
56import com.freerdp.freerdpcore.services.LibFreeRDP;
57import com.freerdp.freerdpcore.utils.ClipboardManagerProxy;
58
59public class SessionActivity extends AppCompatActivity
60 implements LibFreeRDP.UIEventListener, ClipboardManagerProxy.OnClipboardChangedListener
61{
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";
65 static volatile SessionActivity activeSession;
66 private Bitmap bitmap;
67 private SessionState session;
68 private SessionView sessionView;
69 private TouchPointerView touchPointerView;
70
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;
75
76 private final Handler uiHandler = new Handler(Looper.getMainLooper()) {
77 @Override public void handleMessage(Message msg)
78 {
79 switch (msg.what)
80 {
81 case GRAPHICS_CHANGED:
82 {
83 sessionView.onSurfaceChange(session);
84 scrollView.requestLayout();
85 break;
86 }
87 case REFRESH_SESSIONVIEW:
88 {
89 sessionView.invalidateRegion();
90 break;
91 }
92 case DISPLAY_TOAST:
93 {
94 Toast errorToast = Toast.makeText(getApplicationContext(), msg.obj.toString(),
95 Toast.LENGTH_LONG);
96 errorToast.show();
97 break;
98 }
99 case POINTER_SET:
100 {
101 Bundle data = msg.getData();
102 if (data != null && data.containsKey("pixels"))
103 {
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);
110 }
111 else
112 {
113 sessionView.setRemoteCursor(null, 0, 0, 0, 0);
114 }
115 break;
116 }
117 }
118 }
119 };
120
121 private int screen_width;
122 private int screen_height;
123
124 private boolean connectCancelledByUser = false;
125 private boolean sessionRunning = false;
126 private long backPressedTime = 0;
127
128 private SessionViewModel sessionViewModel;
129 private ScrollView2D scrollView;
130 private ClipboardManagerProxy mClipboardManager;
131 private SessionInputManager inputManager;
132 private SessionDialogs dialogs;
133
134 private FloatingToolbar floatingToolbar;
135
136 private void hideSystemBars()
137 {
138 boolean hideStatusBar = ApplicationSettingsActivity.getHideStatusBar(this);
139 boolean hideNavBar = ApplicationSettingsActivity.getHideNavigationBar(this);
140
141 WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
142
143 if (getSupportActionBar() != null)
144 getSupportActionBar().hide();
145
146 WindowInsetsControllerCompat controller =
147 WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
148 controller.setAppearanceLightStatusBars(false);
149 controller.setAppearanceLightNavigationBars(false);
150
151 getWindow().setStatusBarColor(android.graphics.Color.TRANSPARENT);
152 getWindow().setNavigationBarColor(android.graphics.Color.TRANSPARENT);
153 getWindow().setNavigationBarContrastEnforced(false);
154
155 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
156 {
157 int toHide = 0;
158 if (hideStatusBar)
159 toHide |= WindowInsetsCompat.Type.statusBars();
160 if (hideNavBar)
161 toHide |= WindowInsetsCompat.Type.navigationBars();
162
163 if (toHide != 0)
164 {
165 controller.hide(toHide);
166 controller.setSystemBarsBehavior(
167 WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
168 }
169 else
170 {
171 controller.show(WindowInsetsCompat.Type.systemBars());
172 }
173 }
174 else
175 {
176 // API 29: layout flags must be set explicitly to keep drawing behind system bars.
177 int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
178 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
179 if (hideStatusBar)
180 flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
181 if (hideNavBar)
182 flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
183 if ((flags & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) !=
184 0)
185 flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
186
187 getWindow().getDecorView().setSystemUiVisibility(flags);
188 }
189
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;
194 else
195 lp.layoutInDisplayCutoutMode =
196 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
197 getWindow().setAttributes(lp);
198 }
199
200 @Override public void onCreate(Bundle savedInstanceState)
201 {
202 super.onCreate(savedInstanceState);
203
204 hideSystemBars();
205
206 this.setContentView(R.layout.session);
207
208 Log.v(TAG, "Session.onCreate");
209
210 // ATTENTION: We use the onGlobalLayout notification to start our
211 // session.
212 // This is because only then we can know the exact size of our session
213 // when using fit screen
214 // accounting for any status bars etc. that Android might throws on us.
215 // A bit weird looking
216 // but this is the only way ...
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()
224 {
225 screen_width = scrollView.getWidth() - scrollView.getPaddingLeft() -
226 scrollView.getPaddingRight();
227 screen_height = scrollView.getHeight() - scrollView.getPaddingTop() -
228 scrollView.getPaddingBottom();
229
230 // start session
231 if (!sessionRunning && getIntent() != null)
232 {
233 processIntent(getIntent());
234 sessionRunning = true;
235 }
236 }
237 });
238
239 sessionView = findViewById(R.id.sessionView);
240 sessionView.requestFocus();
241
242 touchPointerView = findViewById(R.id.touchPointerView);
243
244 floatingToolbar = new FloatingToolbar(this, new FloatingToolbar.Listener() {
245 @Override public void onToggleTouchPointer()
246 {
247 if (inputManager != null)
248 inputManager.toggleTouchPointer();
249 }
250 @Override public void onToggleSysKeyboard()
251 {
252 if (inputManager != null)
253 inputManager.toggleSystemKeyboard();
254 }
255 @Override public void onToggleExtKeyboard()
256 {
257 if (inputManager != null)
258 inputManager.toggleExtendedKeyboard();
259 }
260 });
261
262 KeyboardView keyboardView = findViewById(R.id.extended_keyboard);
263 KeyboardView modifiersKeyboardView = findViewById(R.id.extended_keyboard_header);
264
265 scrollView = findViewById(R.id.sessionScrollView);
266 sessionViewModel = new ViewModelProvider(this).get(SessionViewModel.class);
267 sessionViewModel.getState().observe(this, this::onConnectionStateChanged);
268
269 dialogs = new SessionDialogs(this, new SessionDialogs.OnUserCancelListener() {
270 @Override public void onUserCancel()
271 {
272 connectCancelledByUser = true;
273 }
274 });
275
276 // Wire up the input manager (instance is attached later in bindSession()).
277 inputManager = new SessionInputManager(this, scrollView, sessionView, touchPointerView,
278 keyboardView, modifiersKeyboardView);
279 sessionView.setSessionViewListener(inputManager);
280 touchPointerView.setTouchPointerListener(inputManager);
281 sessionView.setScaleGestureDetector(
282 new ScaleGestureDetector(this, inputManager.getPinchZoomListener()));
283
284 mClipboardManager = ClipboardManagerProxy.getClipboardManager(this);
285 mClipboardManager.addClipboardChangedListener(this);
286
287 getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
288 @Override public void handleOnBackPressed()
289 {
290 handleBackPressed();
291 }
292 });
293
294 hideSystemBars();
295 }
296
297 @Override public void onWindowFocusChanged(boolean hasFocus)
298 {
299 super.onWindowFocusChanged(hasFocus);
300 if (hasFocus)
301 {
302 hideSystemBars();
303 mClipboardManager.getPrimaryClipManually();
304 }
305 }
306
307 @Override protected void onStart()
308 {
309 super.onStart();
310 Log.v(TAG, "Session.onStart");
311 }
312
313 @Override protected void onRestart()
314 {
315 super.onRestart();
316 Log.v(TAG, "Session.onRestart");
317 }
318
319 @Override protected void onResume()
320 {
321 super.onResume();
322 Log.v(TAG, "Session.onResume");
323 activeSession = this;
324 }
325
326 @Override protected void onPause()
327 {
328 super.onPause();
329 Log.v(TAG, "Session.onPause");
330 if (activeSession == this)
331 activeSession = null;
332 // hide any visible keyboards
333 inputManager.hideKeyboards();
334 }
335
336 @Override protected void onStop()
337 {
338 super.onStop();
339 Log.v(TAG, "Session.onStop");
340 }
341
342 @Override protected void onDestroy()
343 {
344 if (connectThread != null)
345 {
346 connectThread.interrupt();
347 }
348 super.onDestroy();
349 Log.v(TAG, "Session.onDestroy");
350
351 // Cancel running disconnect timers.
352 GlobalApp.cancelDisconnectTimer();
353
354 // Disconnect only this activity's session.
355 if (session != null)
356 LibFreeRDP.disconnect(session.getInstance());
357
358 // unregister freerdp session listener
359 sessionViewModel.unregister();
360
361 // remove clipboard listener
362 mClipboardManager.removeClipboardboardChangedListener(this);
363
364 // free session
365 GlobalApp.freeSession(session.getInstance());
366
367 session = null;
368 }
369
370 @Override public void onConfigurationChanged(Configuration newConfig)
371 {
372 super.onConfigurationChanged(newConfig);
373
374 // reload keyboard resources (changed from landscape)
375 inputManager.reloadKeyboards();
376
377 hideSystemBars();
378
379 // screen_width/screen_height will be updated by the next onGlobalLayout callback;
380 if (session != null && session.getBookmark() != null &&
381 session.getBookmark().getActiveScreenSettings().isFitScreen())
382 {
383 scrollView.post(() -> {
384 if (screen_width > 0 && screen_height > 0)
385 LibFreeRDP.sendMonitorLayout(session.getInstance(), screen_width,
386 screen_height);
387 });
388 }
389 }
390
391 private WindowInsetsCompat onWindowInsetsChanged(View rootView, WindowInsetsCompat windowInsets)
392 {
393 boolean fitSafeArea = ApplicationSettingsActivity.getFitRoundedCorners(this);
394 boolean hideStatusBar = ApplicationSettingsActivity.getHideStatusBar(this);
395 boolean hideNavBar = ApplicationSettingsActivity.getHideNavigationBar(this);
396
397 int insetsTop = windowInsets
398 .getInsets(WindowInsetsCompat.Type.statusBars() |
399 WindowInsetsCompat.Type.displayCutout())
400 .top;
401 rootView.setPadding(0, hideStatusBar ? 0 : insetsTop, 0, 0);
402 Insets navInsets = hideNavBar
403 ? Insets.NONE
404 : windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
405 if (floatingToolbar != null)
406 floatingToolbar.setInsets(navInsets.left, hideStatusBar ? 0 : insetsTop,
407 navInsets.right, navInsets.bottom);
408
409 int safeLeft = 0, safeTop = 0, safeRight = 0, safeBottom = 0;
410 if (fitSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
411 {
412 WindowInsets platformInsets = windowInsets.toWindowInsets();
413 if (platformInsets != null)
414 {
415 boolean landscape = getResources().getConfiguration().orientation ==
416 Configuration.ORIENTATION_LANDSCAPE;
417
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);
422
423 if (landscape)
424 {
425 safeLeft = Math.max(0, Math.max(radTL, radBL) - rootView.getPaddingLeft());
426 safeRight = Math.max(0, Math.max(radTR, radBR) - rootView.getPaddingRight());
427 }
428 else
429 {
430 safeTop = Math.max(0, Math.max(radTL, radTR) - rootView.getPaddingTop());
431 safeBottom = Math.max(0, Math.max(radBL, radBR) - rootView.getPaddingBottom());
432 }
433 }
434 }
435
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);
441
442 return WindowInsetsCompat.CONSUMED;
443 }
444
445 @RequiresApi(Build.VERSION_CODES.S)
446 private static int cornerRadius(WindowInsets insets, int position)
447 {
448 RoundedCorner corner = insets.getRoundedCorner(position);
449 return (corner != null) ? corner.getRadius() : 0;
450 }
451
452 private void processIntent(Intent intent)
453 {
454 // get either session instance or create one from a bookmark/uri
455 Bundle bundle = intent.getExtras();
456 Uri openUri = intent.getData();
457 if (openUri != null)
458 {
459 // Launched from URI, e.g:
460 // freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
461 connect(openUri);
462 }
463 else if (bundle.containsKey(PARAM_INSTANCE))
464 {
465 int inst = bundle.getInt(PARAM_INSTANCE);
466 session = GlobalApp.getSession(inst);
467 bitmap = session.getSurface().getBitmap();
468 bindSession();
469 }
470 else if (bundle.containsKey(PARAM_CONNECTION_REFERENCE))
471 {
472 String refStr = bundle.getString(PARAM_CONNECTION_REFERENCE);
473 if (ConnectionReference.isHostnameReference(refStr))
474 {
475 BookmarkBase bookmark = new BookmarkBase();
476 bookmark.setHostname(ConnectionReference.getHostname(refStr));
477 connect(bookmark);
478 }
479 else if (ConnectionReference.isBookmarkReference(refStr))
480 {
481 sessionViewModel.loadBookmarkById(ConnectionReference.getBookmarkId(refStr),
482 bookmark -> {
483 if (bookmark != null)
484 connect(bookmark);
485 else
486 closeSessionActivity(RESULT_CANCELED);
487 });
488 }
489 else
490 {
491 closeSessionActivity(RESULT_CANCELED);
492 }
493 }
494 else
495 {
496 // no session found - exit
497 closeSessionActivity(RESULT_CANCELED);
498 }
499 }
500
501 private void connect(BookmarkBase bookmark)
502 {
503 session = GlobalApp.createSession(bookmark, getApplicationContext());
504
505 BookmarkBase.ScreenSettings screenSettings =
506 session.getBookmark().getActiveScreenSettings();
507 Log.v(TAG, "Screen Resolution: " + screenSettings.getResolutionString());
508 if (screenSettings.isAutomatic())
509 {
510 // Instead of enforcing obsolete ratios based on screen categories,
511 // directly map to actual device metrics without arbitrary multi-scaling.
512 screenSettings.setHeight(screen_height);
513 screenSettings.setWidth(screen_width);
514 }
515 if (screenSettings.isFitScreen())
516 {
517 screenSettings.setHeight(screen_height);
518 screenSettings.setWidth(screen_width);
519 }
520
521 connectWithTitle(bookmark.getLabel());
522 }
523
524 private void connect(Uri openUri)
525 {
526 session = GlobalApp.createSession(openUri, getApplicationContext());
527
528 connectWithTitle(openUri.getAuthority());
529 }
530
531 static class ConnectThread extends Thread
532 {
533 private final SessionState runnableSession;
534 private final Context context;
535
536 public ConnectThread(@NonNull Context context, @NonNull SessionState session)
537 {
538 this.context = context;
539 runnableSession = session;
540 }
541
542 public void run()
543 {
544 runnableSession.connect(context.getApplicationContext());
545 }
546 }
547
548 private ConnectThread connectThread = null;
549
550 private void connectWithTitle(String title)
551 {
552 session.setUIEventListener(this);
553
554 sessionViewModel.register(session.getInstance());
555
556 dialogs.showProgress(title, () -> {
557 connectCancelledByUser = true;
558 LibFreeRDP.cancelConnection(session.getInstance());
559 });
560
561 connectThread = new ConnectThread(getApplicationContext(), session);
562 connectThread.start();
563 }
564
565 // binds the current session to the activity by wiring it up with the
566 // sessionView and updating all internal objects accordingly
567 private void bindSession()
568 {
569 Log.v(TAG, "bindSession called");
570 session.setUIEventListener(this);
571 sessionView.onSurfaceChange(session);
572 scrollView.requestLayout();
573
574 Bitmap surface = session.getSurface() != null ? session.getSurface().getBitmap() : null;
575 inputManager.attachSession(session.getInstance(), surface);
576 inputManager.setScreenSize(screen_width, screen_height);
577 hideSystemBars();
578 View rootView = findViewById(R.id.session_root_view);
579 if (rootView != null)
580 ViewCompat.requestApplyInsets(rootView);
581 }
582
583 private void closeSessionActivity(int resultCode)
584 {
585 // Go back to home activity (and send intent data back to home)
586 setResult(resultCode, getIntent());
587 finish();
588 }
589
590 public void handleBackPressed()
591 {
592 // hide keyboards (if any visible) or send alt+f4 to the session
593 if (inputManager.isAnyKeyboardVisible())
594 {
595 inputManager.hideKeyboards();
596 return;
597 }
598 if (inputManager.handleBackAsAltF4())
599 {
600 return;
601 }
602 if (System.currentTimeMillis() - backPressedTime < 2000)
603 {
604 connectCancelledByUser = true;
605 LibFreeRDP.disconnect(session.getInstance());
606 }
607 else
608 {
609 backPressedTime = System.currentTimeMillis();
610 Toast.makeText(this, R.string.session_double_back_to_exit, Toast.LENGTH_SHORT).show();
611 }
612 }
613
614 @Override public boolean onKeyLongPress(int keyCode, KeyEvent event)
615 {
616 if (inputManager.onAndroidKeyLongPress(keyCode))
617 return true;
618 return super.onKeyLongPress(keyCode, event);
619 }
620
621 boolean handleKeyEvent(KeyEvent event)
622 {
623 return inputManager != null && inputManager.onAndroidKeyEvent(event);
624 }
625
626 // android keyboard input handling
627 // We always use the unicode value to process input from the android
628 // keyboard except if key modifiers
629 // (like Win, Alt, Ctrl) are activated. In this case we will send the
630 // virtual key code to allow key
631 // combinations (like Win + E to open the explorer).
632 @Override public boolean onKeyDown(int keycode, KeyEvent event)
633 {
634 if (keycode == KeyEvent.KEYCODE_BACK)
635 return super.onKeyDown(keycode, event);
636 return inputManager.onAndroidKeyEvent(event);
637 }
638
639 @Override public boolean onKeyUp(int keycode, KeyEvent event)
640 {
641 if (keycode == KeyEvent.KEYCODE_BACK)
642 return super.onKeyUp(keycode, event);
643 return inputManager.onAndroidKeyEvent(event);
644 }
645
646 // onKeyMultiple is called for input of some special characters like umlauts
647 // and some symbol characters
648 @Override public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
649 {
650 return inputManager.onAndroidKeyEvent(event);
651 }
652
653 // ****************************************************************************
654 // KeyboardMapper.KeyProcessingListener — delegated to SessionInputManager
655
656 // ****************************************************************************
657 // LibFreeRDP UI event listener implementation
658 @Override public void OnSettingsChanged(int width, int height, int bpp)
659 {
660
661 if (bpp > 16)
662 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
663 else
664 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
665
666 session.setSurface(new BitmapDrawable(getResources(), bitmap));
667
668 if (inputManager != null)
669 inputManager.setBitmap(bitmap);
670
671 if (session.getBookmark() == null)
672 {
673 // Return immediately if we launch from URI
674 return;
675 }
676 // check this settings and initial settings - if they are not equal the
677 // server doesn't support our settings
678 // FIXME: the additional check (settings.getWidth() != width + 1) is for
679 // the RDVH bug fix to avoid accidental notifications
680 // (refer to android_freerdp.c for more info on this problem)
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)));
686 }
687
688 @Override public void OnGraphicsUpdate(int x, int y, int width, int height)
689 {
690 LibFreeRDP.updateGraphics(session.getInstance(), bitmap, x, y, width, height);
691
692 sessionView.addInvalidRegion(new Rect(x, y, x + width, y + height));
693
694 /*
695 * since sessionView can only be modified from the UI thread any
696 * modifications to it need to be scheduled
697 */
698
699 uiHandler.sendEmptyMessage(REFRESH_SESSIONVIEW);
700 }
701
702 @Override public void OnGraphicsResize(int width, int height, int bpp)
703 {
704 // replace bitmap
705 if (bpp > 16)
706 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
707 else
708 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
709 session.setSurface(new BitmapDrawable(getResources(), bitmap));
710
711 if (inputManager != null)
712 inputManager.setBitmap(bitmap);
713
714 /*
715 * since sessionView can only be modified from the UI thread any
716 * modifications to it need to be scheduled
717 */
718 uiHandler.sendEmptyMessage(GRAPHICS_CHANGED);
719 }
720
721 @Override
722 public boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
723 StringBuilder password)
724 {
725 return dialogs.promptCredentials(username, domain, password);
726 }
727
728 @Override
729 public boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
730 StringBuilder password)
731 {
732 return dialogs.promptCredentials(username, domain, password);
733 }
734
735 @Override
736 public int OnVerifiyCertificateEx(String host, long port, String commonName, String subject,
737 String issuer, String fingerprint, long flags)
738 {
739 if (ApplicationSettingsActivity.getAcceptAllCertificates(this))
740 return 0;
741 return dialogs.verifyCertificate(host, port, subject, issuer, fingerprint, flags);
742 }
743
744 @Override
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)
749 {
750 if (ApplicationSettingsActivity.getAcceptAllCertificates(this))
751 return 0;
752 return dialogs.verifyChangedCertificate(host, port, subject, issuer, fingerprint, flags);
753 }
754
755 @Override public void OnRemoteClipboardChanged(String data)
756 {
757 Log.v(TAG, "OnRemoteClipboardChanged: " + data);
758 mClipboardManager.setClipboardData(data);
759 }
760
761 @Override public void OnRemoteClipboardImageChanged(byte[] data)
762 {
763 Log.v(TAG, "OnRemoteClipboardImageChanged: " + data.length + " bytes");
764 mClipboardManager.setClipboardImage(data);
765 }
766
767 @Override public void OnPointerSet(int[] pixels, int width, int height, int hotX, int hotY)
768 {
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);
776 msg.setData(data);
777 uiHandler.sendMessage(msg);
778 }
779
780 @Override public void OnPointerSetNull()
781 {
782 uiHandler.sendEmptyMessage(POINTER_SET);
783 }
784
785 @Override public void OnPointerSetDefault()
786 {
787 sessionView.setDefaultCursor();
788 }
789
790 // ****************************************************************************
791 // SessionView.SessionViewListener and TouchPointerView.TouchPointerListener
792 // — delegated to SessionInputManager
793
794 @Override public boolean onGenericMotionEvent(MotionEvent e)
795 {
796 super.onGenericMotionEvent(e);
797 return inputManager != null && inputManager.onGenericMotionEvent(e);
798 }
799
800 // ****************************************************************************
801 // ClipboardManagerProxy.OnClipboardChangedListener
802 @Override public void onClipboardChanged(String data)
803 {
804 Log.v(TAG, "onClipboardChanged: " + data);
805 if (session != null)
806 LibFreeRDP.sendClipboardData(session.getInstance(), data);
807 }
808
809 @Override public void onClipboardImageChanged(byte[] data, String mimeType)
810 {
811 if (session != null && data != null)
812 LibFreeRDP.sendClipboardImageData(session.getInstance(), data, mimeType);
813 }
814
815 private void onConnectionStateChanged(SessionViewModel.ConnectionState state)
816 {
817 if (session == null)
818 return;
819 switch (state)
820 {
821 case CONNECTED:
822 onSessionConnected();
823 break;
824 case FAILED:
825 onSessionFailed();
826 break;
827 case DISCONNECTED:
828 onSessionDisconnected();
829 break;
830 default:
831 break;
832 }
833 }
834
835 private void onSessionConnected()
836 {
837 Log.v(TAG, "onSessionConnected");
838
839 if (connectCancelledByUser)
840 {
841 LibFreeRDP.disconnect(session.getInstance());
842 closeSessionActivity(RESULT_CANCELED);
843 return;
844 }
845
846 // bind session
847 bindSession();
848
849 if (ApplicationSettingsActivity.getKeepScreenOnWhenConnected(this))
850 {
851 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
852 }
853
854 dialogs.dismissProgress();
855
856 if (session.getBookmark() == null)
857 {
858 // Return immediately if we launch from URI
859 return;
860 }
861
862 // add hostname to history if quick connect was used
863 Bundle bundle = getIntent().getExtras();
864 if (bundle != null && bundle.containsKey(PARAM_CONNECTION_REFERENCE))
865 {
866 if (ConnectionReference.isHostnameReference(
867 bundle.getString(PARAM_CONNECTION_REFERENCE)))
868 {
869 assert session.getBookmark().getType() == BookmarkBase.TYPE_MANUAL;
870 sessionViewModel.recordQuickConnectHistory(session.getBookmark().getHostname());
871 }
872 }
873 }
874
875 private void onSessionFailed()
876 {
877 Log.v(TAG, "onSessionFailed");
878
879 // cancel any pending input events
880 if (inputManager != null)
881 inputManager.cancelPendingEvents();
882
883 dialogs.dismissProgress();
884
885 // post error message on UI thread
886 if (!connectCancelledByUser)
887 uiHandler.sendMessage(Message.obtain(
888 null, DISPLAY_TOAST, getResources().getText(R.string.error_connection_failure)));
889
890 closeSessionActivity(RESULT_CANCELED);
891 }
892
893 private void onSessionDisconnected()
894 {
895 Log.v(TAG, "onSessionDisconnected");
896
897 // cancel any pending input events
898 if (inputManager != null)
899 inputManager.cancelPendingEvents();
900
901 if (ApplicationSettingsActivity.getKeepScreenOnWhenConnected(this))
902 {
903 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
904 }
905
906 dialogs.dismissProgress();
907
908 session.setUIEventListener(null);
909 closeSessionActivity(RESULT_OK);
910 }
911}
boolean promptCredentials(StringBuilder username, StringBuilder domain, StringBuilder password)