FreeRDP
Loading...
Searching...
No Matches
SessionActivity.java
1/*
2 Android Session Activity
3
4 Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9 */
10
11package com.freerdp.freerdpcore.presentation;
12
13import android.app.AlertDialog;
14import android.app.Dialog;
15import android.app.ProgressDialog;
16import android.app.UiModeManager;
17import android.content.BroadcastReceiver;
18import android.content.Context;
19import android.content.DialogInterface;
20import android.content.Intent;
21import android.content.IntentFilter;
22import android.content.SharedPreferences;
23import android.content.res.Configuration;
24import android.graphics.Bitmap;
25import android.graphics.Bitmap.Config;
26import android.graphics.Point;
27import android.graphics.Rect;
28import android.graphics.drawable.BitmapDrawable;
29import android.inputmethodservice.Keyboard;
30import android.inputmethodservice.KeyboardView;
31import android.net.Uri;
32import android.os.Build;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.Message;
36
37import androidx.annotation.NonNull;
38import androidx.appcompat.app.AppCompatActivity;
39
40import android.text.InputType;
41import android.util.Log;
42import android.view.KeyEvent;
43import android.view.Menu;
44import android.view.MenuItem;
45import android.view.MotionEvent;
46import android.view.ScaleGestureDetector;
47import android.view.View;
48import android.view.ViewConfiguration;
49import android.view.ViewTreeObserver.OnGlobalLayoutListener;
50import android.view.WindowManager;
51import android.view.inputmethod.InputMethodManager;
52import android.view.inputmethod.InputMethodSubtype;
53import android.widget.EditText;
54import android.widget.Toast;
55import android.widget.ZoomControls;
56
57import com.freerdp.freerdpcore.R;
58import com.freerdp.freerdpcore.application.GlobalApp;
59import com.freerdp.freerdpcore.application.SessionState;
60import com.freerdp.freerdpcore.domain.BookmarkBase;
61import com.freerdp.freerdpcore.domain.ConnectionReference;
62import com.freerdp.freerdpcore.domain.ManualBookmark;
63import com.freerdp.freerdpcore.services.LibFreeRDP;
64import com.freerdp.freerdpcore.utils.ClipboardManagerProxy;
65import com.freerdp.freerdpcore.utils.KeyboardMapper;
66import com.freerdp.freerdpcore.utils.Mouse;
67
68import java.util.Collection;
69import java.util.Iterator;
70import java.util.List;
71
72public class SessionActivity extends AppCompatActivity
73 implements LibFreeRDP.UIEventListener, KeyboardView.OnKeyboardActionListener,
74 ScrollView2D.ScrollView2DListener, KeyboardMapper.KeyProcessingListener,
75 SessionView.SessionViewListener, TouchPointerView.TouchPointerListener,
76 ClipboardManagerProxy.OnClipboardChangedListener
77{
78 public static final String PARAM_CONNECTION_REFERENCE = "conRef";
79 public static final String PARAM_INSTANCE = "instance";
80 private static final float ZOOMING_STEP = 0.5f;
81 private static final int ZOOMCONTROLS_AUTOHIDE_TIMEOUT = 4000;
82 // timeout between subsequent scrolling requests when the touch-pointer is
83 // at the edge of the session view
84 private static final int SCROLLING_TIMEOUT = 50;
85 private static final int SCROLLING_DISTANCE = 20;
86 private static final String TAG = "FreeRDP.SessionActivity";
87 // variables for delayed move event sending
88 private static final int MAX_DISCARDED_MOVE_EVENTS = 3;
89 private static final int SEND_MOVE_EVENT_TIMEOUT = 150;
90 private Bitmap bitmap;
91 private SessionState session;
92 private SessionView sessionView;
93 private TouchPointerView touchPointerView;
94 private ProgressDialog progressDialog;
95 private KeyboardView keyboardView;
96 private KeyboardView modifiersKeyboardView;
97 private ZoomControls zoomControls;
98 private KeyboardMapper keyboardMapper;
99
100 private Keyboard specialkeysKeyboard;
101 private Keyboard numpadKeyboard;
102 private Keyboard cursorKeyboard;
103 private Keyboard modifiersKeyboard;
104
105 private AlertDialog dlgVerifyCertificate;
106 private AlertDialog dlgUserCredentials;
107 private View userCredView;
108
109 private UIHandler uiHandler;
110
111 private int screen_width;
112 private int screen_height;
113
114 private boolean connectCancelledByUser = false;
115 private boolean sessionRunning = false;
116 private boolean toggleMouseButtons = false;
117
118 private LibFreeRDPBroadcastReceiver libFreeRDPBroadcastReceiver;
119 private ScrollView2D scrollView;
120 // keyboard visibility flags
121 private boolean sysKeyboardVisible = false;
122 private boolean extKeyboardVisible = false;
123 private int discardedMoveEvents = 0;
124 private ClipboardManagerProxy mClipboardManager;
125 private boolean callbackDialogResult;
126 View mDecor;
127
128 private void createDialogs()
129 {
130 // build verify certificate dialog
131 dlgVerifyCertificate =
132 new AlertDialog.Builder(this)
133 .setTitle(R.string.dlg_title_verify_certificate)
134 .setPositiveButton(android.R.string.yes,
135 new DialogInterface.OnClickListener() {
136 @Override
137 public void onClick(DialogInterface dialog, int which)
138 {
139 callbackDialogResult = true;
140 synchronized (dialog)
141 {
142 dialog.notify();
143 }
144 }
145 })
146 .setNegativeButton(android.R.string.no,
147 new DialogInterface.OnClickListener() {
148 @Override
149 public void onClick(DialogInterface dialog, int which)
150 {
151 callbackDialogResult = false;
152 connectCancelledByUser = true;
153 synchronized (dialog)
154 {
155 dialog.notify();
156 }
157 }
158 })
159 .setCancelable(false)
160 .create();
161
162 // build the dialog
163 userCredView = getLayoutInflater().inflate(R.layout.credentials, null, true);
164 dlgUserCredentials =
165 new AlertDialog.Builder(this)
166 .setView(userCredView)
167 .setTitle(R.string.dlg_title_credentials)
168 .setPositiveButton(android.R.string.ok,
169 new DialogInterface.OnClickListener() {
170 @Override
171 public void onClick(DialogInterface dialog, int which)
172 {
173 callbackDialogResult = true;
174 synchronized (dialog)
175 {
176 dialog.notify();
177 }
178 }
179 })
180 .setNegativeButton(android.R.string.cancel,
181 new DialogInterface.OnClickListener() {
182 @Override
183 public void onClick(DialogInterface dialog, int which)
184 {
185 callbackDialogResult = false;
186 connectCancelledByUser = true;
187 synchronized (dialog)
188 {
189 dialog.notify();
190 }
191 }
192 })
193 .setCancelable(false)
194 .create();
195 }
196
197 private boolean hasHardwareMenuButton()
198 {
199 if (Build.VERSION.SDK_INT <= 10)
200 return true;
201
202 if (Build.VERSION.SDK_INT >= 14)
203 {
204 boolean rc = false;
205 final ViewConfiguration cfg = ViewConfiguration.get(this);
206
207 return cfg.hasPermanentMenuKey();
208 }
209
210 return false;
211 }
212
213 @Override public void onCreate(Bundle savedInstanceState)
214 {
215 super.onCreate(savedInstanceState);
216
217 // show status bar or make fullscreen?
218 if (ApplicationSettingsActivity.getHideStatusBar(this))
219 {
220 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
221 WindowManager.LayoutParams.FLAG_FULLSCREEN);
222 }
223
224 this.setContentView(R.layout.session);
225 if (hasHardwareMenuButton() || ApplicationSettingsActivity.getHideActionBar(this))
226 {
227 this.getSupportActionBar().hide();
228 }
229 else
230 this.getSupportActionBar().show();
231
232 Log.v(TAG, "Session.onCreate");
233
234 // ATTENTION: We use the onGlobalLayout notification to start our
235 // session.
236 // This is because only then we can know the exact size of our session
237 // when using fit screen
238 // accounting for any status bars etc. that Android might throws on us.
239 // A bit weird looking
240 // but this is the only way ...
241 final View activityRootView = findViewById(R.id.session_root_view);
242 activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
243 new OnGlobalLayoutListener() {
244 @Override public void onGlobalLayout()
245 {
246 screen_width = activityRootView.getWidth();
247 screen_height = activityRootView.getHeight();
248
249 // start session
250 if (!sessionRunning && getIntent() != null)
251 {
252 processIntent(getIntent());
253 sessionRunning = true;
254 }
255 }
256 });
257
258 sessionView = findViewById(R.id.sessionView);
259 sessionView.setScaleGestureDetector(
260 new ScaleGestureDetector(this, new PinchZoomListener()));
261 sessionView.setSessionViewListener(this);
262 sessionView.requestFocus();
263
264 touchPointerView = findViewById(R.id.touchPointerView);
265 touchPointerView.setTouchPointerListener(this);
266
267 keyboardMapper = new KeyboardMapper();
268 keyboardMapper.init(this);
269 keyboardMapper.reset(this);
270
271 modifiersKeyboard = new Keyboard(getApplicationContext(), R.xml.modifiers_keyboard);
272 specialkeysKeyboard = new Keyboard(getApplicationContext(), R.xml.specialkeys_keyboard);
273 numpadKeyboard = new Keyboard(getApplicationContext(), R.xml.numpad_keyboard);
274 cursorKeyboard = new Keyboard(getApplicationContext(), R.xml.cursor_keyboard);
275
276 // hide keyboard below the sessionView
277 keyboardView = findViewById(R.id.extended_keyboard);
278 keyboardView.setKeyboard(specialkeysKeyboard);
279 keyboardView.setOnKeyboardActionListener(this);
280
281 modifiersKeyboardView = findViewById(R.id.extended_keyboard_header);
282 modifiersKeyboardView.setKeyboard(modifiersKeyboard);
283 modifiersKeyboardView.setOnKeyboardActionListener(this);
284
285 scrollView = findViewById(R.id.sessionScrollView);
286 scrollView.setScrollViewListener(this);
287 uiHandler = new UIHandler();
288 libFreeRDPBroadcastReceiver = new LibFreeRDPBroadcastReceiver();
289
290 zoomControls = findViewById(R.id.zoomControls);
291 zoomControls.hide();
292 zoomControls.setOnZoomInClickListener(new View.OnClickListener() {
293 @Override public void onClick(View v)
294 {
295 resetZoomControlsAutoHideTimeout();
296 zoomControls.setIsZoomInEnabled(sessionView.zoomIn(ZOOMING_STEP));
297 zoomControls.setIsZoomOutEnabled(true);
298 }
299 });
300 zoomControls.setOnZoomOutClickListener(new View.OnClickListener() {
301 @Override public void onClick(View v)
302 {
303 resetZoomControlsAutoHideTimeout();
304 zoomControls.setIsZoomOutEnabled(sessionView.zoomOut(ZOOMING_STEP));
305 zoomControls.setIsZoomInEnabled(true);
306 }
307 });
308
309 toggleMouseButtons = false;
310
311 createDialogs();
312
313 // register freerdp events broadcast receiver
314 IntentFilter filter = new IntentFilter();
315 filter.addAction(GlobalApp.ACTION_EVENT_FREERDP);
316 registerReceiver(libFreeRDPBroadcastReceiver, filter, RECEIVER_EXPORTED);
317
318 mClipboardManager = ClipboardManagerProxy.getClipboardManager(this);
319 mClipboardManager.addClipboardChangedListener(this);
320
321 mDecor = getWindow().getDecorView();
322 mDecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
323 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
324 }
325
326 @Override public void onWindowFocusChanged(boolean hasFocus)
327 {
328 super.onWindowFocusChanged(hasFocus);
329 mClipboardManager.getPrimaryClipManually();
330 }
331
332 @Override protected void onStart()
333 {
334 super.onStart();
335 Log.v(TAG, "Session.onStart");
336 }
337
338 @Override protected void onRestart()
339 {
340 super.onRestart();
341 Log.v(TAG, "Session.onRestart");
342 }
343
344 @Override protected void onResume()
345 {
346 super.onResume();
347 Log.v(TAG, "Session.onResume");
348 }
349
350 @Override protected void onPause()
351 {
352 super.onPause();
353 Log.v(TAG, "Session.onPause");
354
355 // hide any visible keyboards
356 showKeyboard(false, false);
357 }
358
359 @Override protected void onStop()
360 {
361 super.onStop();
362 Log.v(TAG, "Session.onStop");
363 }
364
365 @Override protected void onDestroy()
366 {
367 if (connectThread != null)
368 {
369 connectThread.interrupt();
370 }
371 super.onDestroy();
372 Log.v(TAG, "Session.onDestroy");
373
374 // Cancel running disconnect timers.
375 GlobalApp.cancelDisconnectTimer();
376
377 // Disconnect all remaining sessions.
378 Collection<SessionState> sessions = GlobalApp.getSessions();
379 for (SessionState session : sessions)
380 LibFreeRDP.disconnect(session.getInstance());
381
382 // unregister freerdp events broadcast receiver
383 unregisterReceiver(libFreeRDPBroadcastReceiver);
384
385 // remove clipboard listener
386 mClipboardManager.removeClipboardboardChangedListener(this);
387
388 // free session
389 GlobalApp.freeSession(session.getInstance());
390
391 session = null;
392 }
393
394 @Override public void onConfigurationChanged(Configuration newConfig)
395 {
396 super.onConfigurationChanged(newConfig);
397
398 // reload keyboard resources (changed from landscape)
399 modifiersKeyboard = new Keyboard(getApplicationContext(), R.xml.modifiers_keyboard);
400 specialkeysKeyboard = new Keyboard(getApplicationContext(), R.xml.specialkeys_keyboard);
401 numpadKeyboard = new Keyboard(getApplicationContext(), R.xml.numpad_keyboard);
402 cursorKeyboard = new Keyboard(getApplicationContext(), R.xml.cursor_keyboard);
403
404 // apply loaded keyboards
405 keyboardView.setKeyboard(specialkeysKeyboard);
406 modifiersKeyboardView.setKeyboard(modifiersKeyboard);
407
408 mDecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
409 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
410 }
411
412 private void processIntent(Intent intent)
413 {
414 // get either session instance or create one from a bookmark/uri
415 Bundle bundle = intent.getExtras();
416 Uri openUri = intent.getData();
417 if (openUri != null)
418 {
419 // Launched from URI, e.g:
420 // freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
421 connect(openUri);
422 }
423 else if (bundle.containsKey(PARAM_INSTANCE))
424 {
425 int inst = bundle.getInt(PARAM_INSTANCE);
426 session = GlobalApp.getSession(inst);
427 bitmap = session.getSurface().getBitmap();
428 bindSession();
429 }
430 else if (bundle.containsKey(PARAM_CONNECTION_REFERENCE))
431 {
432 BookmarkBase bookmark = null;
433 String refStr = bundle.getString(PARAM_CONNECTION_REFERENCE);
434 if (ConnectionReference.isHostnameReference(refStr))
435 {
436 bookmark = new ManualBookmark();
437 bookmark.<ManualBookmark>get().setHostname(ConnectionReference.getHostname(refStr));
438 }
439 else if (ConnectionReference.isBookmarkReference(refStr))
440 {
441 if (ConnectionReference.isManualBookmarkReference(refStr))
442 bookmark = GlobalApp.getManualBookmarkGateway().findById(
443 ConnectionReference.getManualBookmarkId(refStr));
444 else
445 assert false;
446 }
447
448 if (bookmark != null)
449 connect(bookmark);
450 else
451 closeSessionActivity(RESULT_CANCELED);
452 }
453 else
454 {
455 // no session found - exit
456 closeSessionActivity(RESULT_CANCELED);
457 }
458 }
459
460 private void connect(BookmarkBase bookmark)
461 {
462 session = GlobalApp.createSession(bookmark, getApplicationContext());
463
464 BookmarkBase.ScreenSettings screenSettings =
465 session.getBookmark().getActiveScreenSettings();
466 Log.v(TAG, "Screen Resolution: " + screenSettings.getResolutionString());
467 if (screenSettings.isAutomatic())
468 {
469 if ((getResources().getConfiguration().screenLayout &
470 Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE)
471 {
472 // large screen device i.e. tablet: simply use screen info
473 screenSettings.setHeight(screen_height);
474 screenSettings.setWidth(screen_width);
475 }
476 else
477 {
478 // small screen device i.e. phone:
479 // Automatic uses the largest side length of the screen and
480 // makes a 16:10 resolution setting out of it
481 int screenMax = Math.max(screen_width, screen_height);
482 screenSettings.setHeight(screenMax);
483 screenSettings.setWidth((int)((float)screenMax * 1.6f));
484 }
485 }
486 if (screenSettings.isFitScreen())
487 {
488 screenSettings.setHeight(screen_height);
489 screenSettings.setWidth(screen_width);
490 }
491
492 connectWithTitle(bookmark.getLabel());
493 }
494
495 private void connect(Uri openUri)
496 {
497 session = GlobalApp.createSession(openUri, getApplicationContext());
498
499 connectWithTitle(openUri.getAuthority());
500 }
501
502 static class ConnectThread extends Thread
503 {
504 private final SessionState runnableSession;
505 private final Context context;
506
507 public ConnectThread(@NonNull Context context, @NonNull SessionState session)
508 {
509 this.context = context;
510 runnableSession = session;
511 }
512
513 public void run()
514 {
515 runnableSession.connect(context.getApplicationContext());
516 }
517 }
518
519 private ConnectThread connectThread = null;
520
521 private void connectWithTitle(String title)
522 {
523 session.setUIEventListener(this);
524
525 progressDialog = new ProgressDialog(this);
526 progressDialog.setTitle(title);
527 progressDialog.setMessage(getResources().getText(R.string.dlg_msg_connecting));
528 progressDialog.setButton(
529 ProgressDialog.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
530 @Override public void onClick(DialogInterface dialog, int which)
531 {
532 connectCancelledByUser = true;
533 LibFreeRDP.cancelConnection(session.getInstance());
534 }
535 });
536 progressDialog.setCancelable(false);
537 progressDialog.show();
538
539 connectThread = new ConnectThread(getApplicationContext(), session);
540 connectThread.start();
541 }
542
543 // binds the current session to the activity by wiring it up with the
544 // sessionView and updating all internal objects accordingly
545 private void bindSession()
546 {
547 Log.v(TAG, "bindSession called");
548 session.setUIEventListener(this);
549 sessionView.onSurfaceChange(session);
550 scrollView.requestLayout();
551 keyboardMapper.reset(this);
552 mDecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
553 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
554 }
555
556 private void setSoftInputState(boolean state)
557 {
558 InputMethodManager mgr = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
559
560 if (state)
561 {
562 mgr.showSoftInput(sessionView, InputMethodManager.SHOW_FORCED);
563 }
564 else
565 {
566 mgr.hideSoftInputFromWindow(sessionView.getWindowToken(), 0);
567 }
568 }
569
570 // displays either the system or the extended keyboard or non of them
571 private void showKeyboard(final boolean showSystemKeyboard, final boolean showExtendedKeyboard)
572 {
573 // no matter what we are doing ... hide the zoom controls
574 // onScrollChange notification showing the control again ...
575 // i think check for "preference_key_ui_hide_zoom_controls" preference should be there
576 uiHandler.removeMessages(UIHandler.SHOW_ZOOMCONTROLS);
577 uiHandler.sendEmptyMessage(UIHandler.HIDE_ZOOMCONTROLS);
578
579 InputMethodManager mgr = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
580
581 if (showSystemKeyboard)
582 {
583 // hide extended keyboard
584 keyboardView.setVisibility(View.GONE);
585 // show system keyboard
586 setSoftInputState(true);
587
588 // show modifiers keyboard
589 modifiersKeyboardView.setVisibility(View.VISIBLE);
590 }
591 else if (showExtendedKeyboard)
592 {
593 // hide system keyboard
594 setSoftInputState(false);
595
596 // show extended keyboard
597 keyboardView.setKeyboard(specialkeysKeyboard);
598 keyboardView.setVisibility(View.VISIBLE);
599 modifiersKeyboardView.setVisibility(View.VISIBLE);
600 }
601 else
602 {
603 // hide both
604 setSoftInputState(false);
605 keyboardView.setVisibility(View.GONE);
606 modifiersKeyboardView.setVisibility(View.GONE);
607
608 // clear any active key modifiers)
609 keyboardMapper.clearlAllModifiers();
610 }
611
612 sysKeyboardVisible = showSystemKeyboard;
613 extKeyboardVisible = showExtendedKeyboard;
614 }
615
616 private void closeSessionActivity(int resultCode)
617 {
618 // Go back to home activity (and send intent data back to home)
619 setResult(resultCode, getIntent());
620 finish();
621 }
622
623 // update the state of our modifier keys
624 private void updateModifierKeyStates()
625 {
626 // check if any key is in the keycodes list
627
628 List<Keyboard.Key> keys = modifiersKeyboard.getKeys();
629 for (Keyboard.Key curKey : keys)
630 {
631 // if the key is a sticky key - just set it to off
632 if (curKey.sticky)
633 {
634 switch (keyboardMapper.getModifierState(curKey.codes[0]))
635 {
636 case KeyboardMapper.KEYSTATE_ON:
637 curKey.on = true;
638 curKey.pressed = false;
639 break;
640
641 case KeyboardMapper.KEYSTATE_OFF:
642 curKey.on = false;
643 curKey.pressed = false;
644 break;
645
646 case KeyboardMapper.KEYSTATE_LOCKED:
647 curKey.on = true;
648 curKey.pressed = true;
649 break;
650 }
651 }
652 }
653
654 // refresh image
655 modifiersKeyboardView.invalidateAllKeys();
656 }
657
658 private void sendDelayedMoveEvent(int x, int y)
659 {
660 if (uiHandler.hasMessages(UIHandler.SEND_MOVE_EVENT))
661 {
662 uiHandler.removeMessages(UIHandler.SEND_MOVE_EVENT);
663 discardedMoveEvents++;
664 }
665 else
666 discardedMoveEvents = 0;
667
668 if (discardedMoveEvents > MAX_DISCARDED_MOVE_EVENTS)
669 LibFreeRDP.sendCursorEvent(session.getInstance(), x, y, Mouse.getMoveEvent());
670 else
671 uiHandler.sendMessageDelayed(Message.obtain(null, UIHandler.SEND_MOVE_EVENT, x, y),
672 SEND_MOVE_EVENT_TIMEOUT);
673 }
674
675 private void cancelDelayedMoveEvent()
676 {
677 uiHandler.removeMessages(UIHandler.SEND_MOVE_EVENT);
678 }
679
680 @Override public boolean onCreateOptionsMenu(Menu menu)
681 {
682 getMenuInflater().inflate(R.menu.session_menu, menu);
683 return true;
684 }
685
686 @Override public boolean onOptionsItemSelected(MenuItem item)
687 {
688 // refer to http://tools.android.com/tips/non-constant-fields why we
689 // can't use switch/case here ..
690 int itemId = item.getItemId();
691
692 if (itemId == R.id.session_touch_pointer)
693 {
694 // toggle touch pointer
695 if (touchPointerView.getVisibility() == View.VISIBLE)
696 {
697 touchPointerView.setVisibility(View.INVISIBLE);
698 sessionView.setTouchPointerPadding(0, 0);
699 }
700 else
701 {
702 touchPointerView.setVisibility(View.VISIBLE);
703 sessionView.setTouchPointerPadding(touchPointerView.getPointerWidth(),
704 touchPointerView.getPointerHeight());
705 }
706 }
707 else if (itemId == R.id.session_sys_keyboard)
708 {
709 showKeyboard(!sysKeyboardVisible, false);
710 }
711 else if (itemId == R.id.session_ext_keyboard)
712 {
713 showKeyboard(false, !extKeyboardVisible);
714 }
715 else if (itemId == R.id.session_disconnect)
716 {
717 showKeyboard(false, false);
718 LibFreeRDP.disconnect(session.getInstance());
719 }
720
721 return true;
722 }
723
724 @Override public void onBackPressed()
725 {
726 // hide keyboards (if any visible) or send alt+f4 to the session
727 if (sysKeyboardVisible || extKeyboardVisible)
728 showKeyboard(false, false);
729 else if (ApplicationSettingsActivity.getUseBackAsAltf4(this))
730 {
731 keyboardMapper.sendAltF4();
732 }
733 }
734
735 @Override public boolean onKeyLongPress(int keyCode, KeyEvent event)
736 {
737 if (keyCode == KeyEvent.KEYCODE_BACK)
738 {
739 LibFreeRDP.disconnect(session.getInstance());
740 return true;
741 }
742 return super.onKeyLongPress(keyCode, event);
743 }
744
745 // android keyboard input handling
746 // We always use the unicode value to process input from the android
747 // keyboard except if key modifiers
748 // (like Win, Alt, Ctrl) are activated. In this case we will send the
749 // virtual key code to allow key
750 // combinations (like Win + E to open the explorer).
751 @Override public boolean onKeyDown(int keycode, KeyEvent event)
752 {
753 return keyboardMapper.processAndroidKeyEvent(event);
754 }
755
756 @Override public boolean onKeyUp(int keycode, KeyEvent event)
757 {
758 return keyboardMapper.processAndroidKeyEvent(event);
759 }
760
761 // onKeyMultiple is called for input of some special characters like umlauts
762 // and some symbol characters
763 @Override public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event)
764 {
765 return keyboardMapper.processAndroidKeyEvent(event);
766 }
767
768 // ****************************************************************************
769 // KeyboardView.KeyboardActionEventListener
770 @Override public void onKey(int primaryCode, int[] keyCodes)
771 {
772 keyboardMapper.processCustomKeyEvent(primaryCode);
773 }
774
775 @Override public void onText(CharSequence text)
776 {
777 }
778
779 @Override public void swipeRight()
780 {
781 }
782
783 @Override public void swipeLeft()
784 {
785 }
786
787 @Override public void swipeDown()
788 {
789 }
790
791 @Override public void swipeUp()
792 {
793 }
794
795 @Override public void onPress(int primaryCode)
796 {
797 }
798
799 @Override public void onRelease(int primaryCode)
800 {
801 }
802
803 // ****************************************************************************
804 // KeyboardMapper.KeyProcessingListener implementation
805 @Override public void processVirtualKey(int virtualKeyCode, boolean down)
806 {
807 LibFreeRDP.sendKeyEvent(session.getInstance(), virtualKeyCode, down);
808 }
809
810 @Override public void processUnicodeKey(int unicodeKey)
811 {
812 LibFreeRDP.sendUnicodeKeyEvent(session.getInstance(), unicodeKey, true);
813 LibFreeRDP.sendUnicodeKeyEvent(session.getInstance(), unicodeKey, false);
814 }
815
816 @Override public void switchKeyboard(int keyboardType)
817 {
818 switch (keyboardType)
819 {
820 case KeyboardMapper.KEYBOARD_TYPE_FUNCTIONKEYS:
821 keyboardView.setKeyboard(specialkeysKeyboard);
822 break;
823
824 case KeyboardMapper.KEYBOARD_TYPE_NUMPAD:
825 keyboardView.setKeyboard(numpadKeyboard);
826 break;
827
828 case KeyboardMapper.KEYBOARD_TYPE_CURSOR:
829 keyboardView.setKeyboard(cursorKeyboard);
830 break;
831
832 default:
833 break;
834 }
835 }
836
837 @Override public void modifiersChanged()
838 {
839 updateModifierKeyStates();
840 }
841
842 // ****************************************************************************
843 // LibFreeRDP UI event listener implementation
844 @Override public void OnSettingsChanged(int width, int height, int bpp)
845 {
846
847 if (bpp > 16)
848 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
849 else
850 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
851
852 session.setSurface(new BitmapDrawable(getResources(), bitmap));
853
854 if (session.getBookmark() == null)
855 {
856 // Return immediately if we launch from URI
857 return;
858 }
859
860 // check this settings and initial settings - if they are not equal the
861 // server doesn't support our settings
862 // FIXME: the additional check (settings.getWidth() != width + 1) is for
863 // the RDVH bug fix to avoid accidental notifications
864 // (refer to android_freerdp.c for more info on this problem)
865 BookmarkBase.ScreenSettings settings = session.getBookmark().getActiveScreenSettings();
866 if ((settings.getWidth() != width && settings.getWidth() != width + 1) ||
867 settings.getHeight() != height || settings.getColors() != bpp)
868 uiHandler.sendMessage(
869 Message.obtain(null, UIHandler.DISPLAY_TOAST,
870 getResources().getText(R.string.info_capabilities_changed)));
871 }
872
873 @Override public void OnGraphicsUpdate(int x, int y, int width, int height)
874 {
875 LibFreeRDP.updateGraphics(session.getInstance(), bitmap, x, y, width, height);
876
877 sessionView.addInvalidRegion(new Rect(x, y, x + width, y + height));
878
879 /*
880 * since sessionView can only be modified from the UI thread any
881 * modifications to it need to be scheduled
882 */
883
884 uiHandler.sendEmptyMessage(UIHandler.REFRESH_SESSIONVIEW);
885 }
886
887 @Override public void OnGraphicsResize(int width, int height, int bpp)
888 {
889 // replace bitmap
890 if (bpp > 16)
891 bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
892 else
893 bitmap = Bitmap.createBitmap(width, height, Config.RGB_565);
894 session.setSurface(new BitmapDrawable(getResources(), bitmap));
895
896 /*
897 * since sessionView can only be modified from the UI thread any
898 * modifications to it need to be scheduled
899 */
900 uiHandler.sendEmptyMessage(UIHandler.GRAPHICS_CHANGED);
901 }
902
903 @Override
904 public boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
905 StringBuilder password)
906 {
907 // this is where the return code of our dialog will be stored
908 callbackDialogResult = false;
909
910 // set text fields
911 ((EditText)userCredView.findViewById(R.id.editTextUsername)).setText(username);
912 ((EditText)userCredView.findViewById(R.id.editTextDomain)).setText(domain);
913 ((EditText)userCredView.findViewById(R.id.editTextPassword)).setText(password);
914
915 // start dialog in UI thread
916 uiHandler.sendMessage(Message.obtain(null, UIHandler.SHOW_DIALOG, dlgUserCredentials));
917
918 // wait for result
919 try
920 {
921 synchronized (dlgUserCredentials)
922 {
923 dlgUserCredentials.wait();
924 }
925 }
926 catch (InterruptedException e)
927 {
928 }
929
930 // clear buffers
931 username.setLength(0);
932 domain.setLength(0);
933 password.setLength(0);
934
935 // read back user credentials
936 username.append(
937 ((EditText)userCredView.findViewById(R.id.editTextUsername)).getText().toString());
938 domain.append(
939 ((EditText)userCredView.findViewById(R.id.editTextDomain)).getText().toString());
940 password.append(
941 ((EditText)userCredView.findViewById(R.id.editTextPassword)).getText().toString());
942
943 return callbackDialogResult;
944 }
945
946 @Override
947 public boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
948 StringBuilder password)
949 {
950 // this is where the return code of our dialog will be stored
951 callbackDialogResult = false;
952
953 // set text fields
954 ((EditText)userCredView.findViewById(R.id.editTextUsername)).setText(username);
955 ((EditText)userCredView.findViewById(R.id.editTextDomain)).setText(domain);
956 ((EditText)userCredView.findViewById(R.id.editTextPassword)).setText(password);
957
958 // start dialog in UI thread
959 uiHandler.sendMessage(Message.obtain(null, UIHandler.SHOW_DIALOG, dlgUserCredentials));
960
961 // wait for result
962 try
963 {
964 synchronized (dlgUserCredentials)
965 {
966 dlgUserCredentials.wait();
967 }
968 }
969 catch (InterruptedException e)
970 {
971 }
972
973 // clear buffers
974 username.setLength(0);
975 domain.setLength(0);
976 password.setLength(0);
977
978 // read back user credentials
979 username.append(
980 ((EditText)userCredView.findViewById(R.id.editTextUsername)).getText().toString());
981 domain.append(
982 ((EditText)userCredView.findViewById(R.id.editTextDomain)).getText().toString());
983 password.append(
984 ((EditText)userCredView.findViewById(R.id.editTextPassword)).getText().toString());
985
986 return callbackDialogResult;
987 }
988
989 @Override
990 public int OnVerifiyCertificateEx(String host, long port, String commonName, String subject,
991 String issuer, String fingerprint, long flags)
992 {
993 // see if global settings says accept all
994 if (ApplicationSettingsActivity.getAcceptAllCertificates(this))
995 return 0;
996
997 // this is where the return code of our dialog will be stored
998 callbackDialogResult = false;
999
1000 // set message
1001 String msg = getResources().getString(R.string.dlg_msg_verify_certificate);
1002 String type = "RDP-Server";
1003 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_GATEWAY) != 0)
1004 type = "RDP-Gateway";
1005 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_REDIRECT) != 0)
1006 type = "RDP-Redirect";
1007 msg += "\n\n" + type + ": " + host + ":" + port;
1008
1009 msg += "\n\nSubject: " + subject + "\nIssuer: " + issuer;
1010
1011 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_FP_IS_PEM) != 0)
1012 msg += "\nCertificate: " + fingerprint;
1013 else
1014 msg += "\nFingerprint: " + fingerprint;
1015 dlgVerifyCertificate.setMessage(msg);
1016
1017 // start dialog in UI thread
1018 uiHandler.sendMessage(Message.obtain(null, UIHandler.SHOW_DIALOG, dlgVerifyCertificate));
1019
1020 // wait for result
1021 try
1022 {
1023 synchronized (dlgVerifyCertificate)
1024 {
1025 dlgVerifyCertificate.wait();
1026 }
1027 }
1028 catch (InterruptedException e)
1029 {
1030 }
1031
1032 return callbackDialogResult ? 1 : 0;
1033 }
1034
1035 @Override
1036 public int OnVerifyChangedCertificateEx(String host, long port, String commonName,
1037 String subject, String issuer, String fingerprint,
1038 String oldSubject, String oldIssuer,
1039 String oldFingerprint, long flags)
1040 {
1041 // see if global settings says accept all
1042 if (ApplicationSettingsActivity.getAcceptAllCertificates(this))
1043 return 0;
1044
1045 // this is where the return code of our dialog will be stored
1046 callbackDialogResult = false;
1047
1048 // set message
1049 String msg = getResources().getString(R.string.dlg_msg_verify_certificate);
1050 String type = "RDP-Server";
1051 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_GATEWAY) != 0)
1052 type = "RDP-Gateway";
1053 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_REDIRECT) != 0)
1054 type = "RDP-Redirect";
1055 msg += "\n\n" + type + ": " + host + ":" + port;
1056 msg += "\n\nSubject: " + subject + "\nIssuer: " + issuer;
1057 if ((flags & LibFreeRDP.VERIFY_CERT_FLAG_FP_IS_PEM) != 0)
1058 msg += "\nCertificate: " + fingerprint;
1059 else
1060 msg += "\nFingerprint: " + fingerprint;
1061 dlgVerifyCertificate.setMessage(msg);
1062
1063 // start dialog in UI thread
1064 uiHandler.sendMessage(Message.obtain(null, UIHandler.SHOW_DIALOG, dlgVerifyCertificate));
1065
1066 // wait for result
1067 try
1068 {
1069 synchronized (dlgVerifyCertificate)
1070 {
1071 dlgVerifyCertificate.wait();
1072 }
1073 }
1074 catch (InterruptedException e)
1075 {
1076 }
1077
1078 return callbackDialogResult ? 1 : 0;
1079 }
1080
1081 @Override public void OnRemoteClipboardChanged(String data)
1082 {
1083 Log.v(TAG, "OnRemoteClipboardChanged: " + data);
1084 mClipboardManager.setClipboardData(data);
1085 }
1086
1087 // ****************************************************************************
1088 // ScrollView2DListener implementation
1089 private void resetZoomControlsAutoHideTimeout()
1090 {
1091 uiHandler.removeMessages(UIHandler.HIDE_ZOOMCONTROLS);
1092 uiHandler.sendEmptyMessageDelayed(UIHandler.HIDE_ZOOMCONTROLS,
1093 ZOOMCONTROLS_AUTOHIDE_TIMEOUT);
1094 }
1095
1096 @Override public void onScrollChanged(ScrollView2D scrollView, int x, int y, int oldx, int oldy)
1097 {
1098 zoomControls.setIsZoomInEnabled(!sessionView.isAtMaxZoom());
1099 zoomControls.setIsZoomOutEnabled(!sessionView.isAtMinZoom());
1100
1101 if (sysKeyboardVisible || extKeyboardVisible)
1102 return;
1103
1104 if (!ApplicationSettingsActivity.getHideZoomControls(this))
1105 {
1106 uiHandler.sendEmptyMessage(UIHandler.SHOW_ZOOMCONTROLS);
1107 resetZoomControlsAutoHideTimeout();
1108 }
1109 }
1110
1111 // ****************************************************************************
1112 // SessionView.SessionViewListener
1113 @Override public void onSessionViewBeginTouch()
1114 {
1115 scrollView.setScrollEnabled(false);
1116 }
1117
1118 @Override public void onSessionViewEndTouch()
1119 {
1120 scrollView.setScrollEnabled(true);
1121 }
1122
1123 @Override public void onSessionViewLeftTouch(int x, int y, boolean down)
1124 {
1125 if (!down)
1126 cancelDelayedMoveEvent();
1127
1128 LibFreeRDP.sendCursorEvent(session.getInstance(), x, y,
1129 toggleMouseButtons ? Mouse.getRightButtonEvent(this, down)
1130 : Mouse.getLeftButtonEvent(this, down));
1131
1132 if (!down)
1133 toggleMouseButtons = false;
1134 }
1135
1136 public void onSessionViewRightTouch(int x, int y, boolean down)
1137 {
1138 if (!down)
1139 toggleMouseButtons = !toggleMouseButtons;
1140 }
1141
1142 @Override public void onSessionViewMove(int x, int y)
1143 {
1144 sendDelayedMoveEvent(x, y);
1145 }
1146
1147 @Override public void onSessionViewScroll(boolean down)
1148 {
1149 LibFreeRDP.sendCursorEvent(session.getInstance(), 0, 0, Mouse.getScrollEvent(this, down));
1150 }
1151
1152 // ****************************************************************************
1153 // TouchPointerView.TouchPointerListener
1154 @Override public void onTouchPointerClose()
1155 {
1156 touchPointerView.setVisibility(View.INVISIBLE);
1157 sessionView.setTouchPointerPadding(0, 0);
1158 }
1159
1160 private Point mapScreenCoordToSessionCoord(int x, int y)
1161 {
1162 int mappedX = (int)((float)(x + scrollView.getScrollX()) / sessionView.getZoom());
1163 int mappedY = (int)((float)(y + scrollView.getScrollY()) / sessionView.getZoom());
1164 if (bitmap != null)
1165 {
1166 if (mappedX > bitmap.getWidth())
1167 mappedX = bitmap.getWidth();
1168 if (mappedY > bitmap.getHeight())
1169 mappedY = bitmap.getHeight();
1170 }
1171 return new Point(mappedX, mappedY);
1172 }
1173
1174 @Override public void onTouchPointerLeftClick(int x, int y, boolean down)
1175 {
1176 Point p = mapScreenCoordToSessionCoord(x, y);
1177 LibFreeRDP.sendCursorEvent(session.getInstance(), p.x, p.y,
1178 Mouse.getLeftButtonEvent(this, down));
1179 }
1180
1181 @Override public void onTouchPointerRightClick(int x, int y, boolean down)
1182 {
1183 Point p = mapScreenCoordToSessionCoord(x, y);
1184 LibFreeRDP.sendCursorEvent(session.getInstance(), p.x, p.y,
1185 Mouse.getRightButtonEvent(this, down));
1186 }
1187
1188 @Override public void onTouchPointerMove(int x, int y)
1189 {
1190 Point p = mapScreenCoordToSessionCoord(x, y);
1191 LibFreeRDP.sendCursorEvent(session.getInstance(), p.x, p.y, Mouse.getMoveEvent());
1192
1193 if (ApplicationSettingsActivity.getAutoScrollTouchPointer(this) &&
1194 !uiHandler.hasMessages(UIHandler.SCROLLING_REQUESTED))
1195 {
1196 Log.v(TAG, "Starting auto-scroll");
1197 uiHandler.sendEmptyMessageDelayed(UIHandler.SCROLLING_REQUESTED, SCROLLING_TIMEOUT);
1198 }
1199 }
1200
1201 @Override public void onTouchPointerScroll(boolean down)
1202 {
1203 LibFreeRDP.sendCursorEvent(session.getInstance(), 0, 0, Mouse.getScrollEvent(this, down));
1204 }
1205
1206 @Override public void onTouchPointerToggleKeyboard()
1207 {
1208 showKeyboard(!sysKeyboardVisible, false);
1209 }
1210
1211 @Override public void onTouchPointerToggleExtKeyboard()
1212 {
1213 showKeyboard(false, !extKeyboardVisible);
1214 }
1215
1216 @Override public void onTouchPointerResetScrollZoom()
1217 {
1218 sessionView.setZoom(1.0f);
1219 scrollView.scrollTo(0, 0);
1220 }
1221
1222 @Override public boolean onGenericMotionEvent(MotionEvent e)
1223 {
1224 super.onGenericMotionEvent(e);
1225 switch (e.getAction())
1226 {
1227 case MotionEvent.ACTION_SCROLL:
1228 final float vScroll = e.getAxisValue(MotionEvent.AXIS_VSCROLL);
1229 if (vScroll < 0)
1230 {
1231 LibFreeRDP.sendCursorEvent(session.getInstance(), 0, 0,
1232 Mouse.getScrollEvent(this, false));
1233 }
1234 if (vScroll > 0)
1235 {
1236 LibFreeRDP.sendCursorEvent(session.getInstance(), 0, 0,
1237 Mouse.getScrollEvent(this, true));
1238 }
1239 break;
1240 }
1241 return true;
1242 }
1243
1244 // ****************************************************************************
1245 // ClipboardManagerProxy.OnClipboardChangedListener
1246 @Override public void onClipboardChanged(String data)
1247 {
1248 Log.v(TAG, "onClipboardChanged: " + data);
1249 LibFreeRDP.sendClipboardData(session.getInstance(), data);
1250 }
1251
1252 private class UIHandler extends Handler
1253 {
1254
1255 public static final int REFRESH_SESSIONVIEW = 1;
1256 public static final int DISPLAY_TOAST = 2;
1257 public static final int HIDE_ZOOMCONTROLS = 3;
1258 public static final int SEND_MOVE_EVENT = 4;
1259 public static final int SHOW_DIALOG = 5;
1260 public static final int GRAPHICS_CHANGED = 6;
1261 public static final int SCROLLING_REQUESTED = 7;
1262 public static final int SHOW_ZOOMCONTROLS = 8;
1263
1264 UIHandler()
1265 {
1266 super();
1267 }
1268
1269 @Override public void handleMessage(Message msg)
1270 {
1271 switch (msg.what)
1272 {
1273 case GRAPHICS_CHANGED:
1274 {
1275 sessionView.onSurfaceChange(session);
1276 scrollView.requestLayout();
1277 break;
1278 }
1279 case REFRESH_SESSIONVIEW:
1280 {
1281 sessionView.invalidateRegion();
1282 break;
1283 }
1284 case DISPLAY_TOAST:
1285 {
1286 Toast errorToast = Toast.makeText(getApplicationContext(), msg.obj.toString(),
1287 Toast.LENGTH_LONG);
1288 errorToast.show();
1289 break;
1290 }
1291 case HIDE_ZOOMCONTROLS:
1292 {
1293 if (zoomControls.isShown())
1294 zoomControls.hide();
1295 break;
1296 }
1297 case SHOW_ZOOMCONTROLS:
1298 {
1299 if (!zoomControls.isShown())
1300 zoomControls.show();
1301
1302 break;
1303 }
1304 case SEND_MOVE_EVENT:
1305 {
1306 LibFreeRDP.sendCursorEvent(session.getInstance(), msg.arg1, msg.arg2,
1307 Mouse.getMoveEvent());
1308 break;
1309 }
1310 case SHOW_DIALOG:
1311 {
1312 // create and show the dialog
1313 ((Dialog)msg.obj).show();
1314 break;
1315 }
1316 case SCROLLING_REQUESTED:
1317 {
1318 int scrollX = 0;
1319 int scrollY = 0;
1320 float[] pointerPos = touchPointerView.getPointerPosition();
1321
1322 if (pointerPos[0] > (screen_width - touchPointerView.getPointerWidth()))
1323 scrollX = SCROLLING_DISTANCE;
1324 else if (pointerPos[0] < 0)
1325 scrollX = -SCROLLING_DISTANCE;
1326
1327 if (pointerPos[1] > (screen_height - touchPointerView.getPointerHeight()))
1328 scrollY = SCROLLING_DISTANCE;
1329 else if (pointerPos[1] < 0)
1330 scrollY = -SCROLLING_DISTANCE;
1331
1332 scrollView.scrollBy(scrollX, scrollY);
1333
1334 // see if we reached the min/max scroll positions
1335 if (scrollView.getScrollX() == 0 ||
1336 scrollView.getScrollX() == (sessionView.getWidth() - scrollView.getWidth()))
1337 scrollX = 0;
1338 if (scrollView.getScrollY() == 0 ||
1339 scrollView.getScrollY() ==
1340 (sessionView.getHeight() - scrollView.getHeight()))
1341 scrollY = 0;
1342
1343 if (scrollX != 0 || scrollY != 0)
1344 uiHandler.sendEmptyMessageDelayed(SCROLLING_REQUESTED, SCROLLING_TIMEOUT);
1345 else
1346 Log.v(TAG, "Stopping auto-scroll");
1347 break;
1348 }
1349 }
1350 }
1351 }
1352
1353 private class PinchZoomListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
1354 {
1355 private float scaleFactor = 1.0f;
1356
1357 @Override public boolean onScaleBegin(ScaleGestureDetector detector)
1358 {
1359 scrollView.setScrollEnabled(false);
1360 return true;
1361 }
1362
1363 @Override public boolean onScale(ScaleGestureDetector detector)
1364 {
1365
1366 // calc scale factor
1367 scaleFactor *= detector.getScaleFactor();
1368 scaleFactor = Math.max(SessionView.MIN_SCALE_FACTOR,
1369 Math.min(scaleFactor, SessionView.MAX_SCALE_FACTOR));
1370 sessionView.setZoom(scaleFactor);
1371
1372 if (!sessionView.isAtMinZoom() && !sessionView.isAtMaxZoom())
1373 {
1374 // transform scroll origin to the new zoom space
1375 float transOriginX = scrollView.getScrollX() * detector.getScaleFactor();
1376 float transOriginY = scrollView.getScrollY() * detector.getScaleFactor();
1377
1378 // transform center point to the zoomed space
1379 float transCenterX =
1380 (scrollView.getScrollX() + detector.getFocusX()) * detector.getScaleFactor();
1381 float transCenterY =
1382 (scrollView.getScrollY() + detector.getFocusY()) * detector.getScaleFactor();
1383
1384 // scroll by the difference between the distance of the
1385 // transformed center/origin point and their old distance
1386 // (focusX/Y)
1387 scrollView.scrollBy((int)((transCenterX - transOriginX) - detector.getFocusX()),
1388 (int)((transCenterY - transOriginY) - detector.getFocusY()));
1389 }
1390
1391 return true;
1392 }
1393
1394 @Override public void onScaleEnd(ScaleGestureDetector de)
1395 {
1396 scrollView.setScrollEnabled(true);
1397 }
1398 }
1399
1400 private class LibFreeRDPBroadcastReceiver extends BroadcastReceiver
1401 {
1402 @Override public void onReceive(Context context, Intent intent)
1403 {
1404 // still got a valid session?
1405 if (session == null)
1406 return;
1407
1408 // is this event for the current session?
1409 if (session.getInstance() != intent.getExtras().getLong(GlobalApp.EVENT_PARAM, -1))
1410 return;
1411
1412 switch (intent.getExtras().getInt(GlobalApp.EVENT_TYPE, -1))
1413 {
1414 case GlobalApp.FREERDP_EVENT_CONNECTION_SUCCESS:
1415 OnConnectionSuccess(context);
1416 break;
1417
1418 case GlobalApp.FREERDP_EVENT_CONNECTION_FAILURE:
1419 OnConnectionFailure(context);
1420 break;
1421 case GlobalApp.FREERDP_EVENT_DISCONNECTED:
1422 OnDisconnected(context);
1423 break;
1424 }
1425 }
1426
1427 private void OnConnectionSuccess(Context context)
1428 {
1429 Log.v(TAG, "OnConnectionSuccess");
1430
1431 // bind session
1432 bindSession();
1433
1434 if (progressDialog != null)
1435 {
1436 progressDialog.dismiss();
1437 progressDialog = null;
1438 }
1439
1440 if (session.getBookmark() == null)
1441 {
1442 // Return immediately if we launch from URI
1443 return;
1444 }
1445
1446 // add hostname to history if quick connect was used
1447 Bundle bundle = getIntent().getExtras();
1448 if (bundle != null && bundle.containsKey(PARAM_CONNECTION_REFERENCE))
1449 {
1450 if (ConnectionReference.isHostnameReference(
1451 bundle.getString(PARAM_CONNECTION_REFERENCE)))
1452 {
1453 assert session.getBookmark().getType() == BookmarkBase.TYPE_MANUAL;
1454 String item = session.getBookmark().<ManualBookmark>get().getHostname();
1455 if (!GlobalApp.getQuickConnectHistoryGateway().historyItemExists(item))
1456 GlobalApp.getQuickConnectHistoryGateway().addHistoryItem(item);
1457 }
1458 }
1459 }
1460
1461 private void OnConnectionFailure(Context context)
1462 {
1463 Log.v(TAG, "OnConnectionFailure");
1464
1465 // remove pending move events
1466 uiHandler.removeMessages(UIHandler.SEND_MOVE_EVENT);
1467
1468 if (progressDialog != null)
1469 {
1470 progressDialog.dismiss();
1471 progressDialog = null;
1472 }
1473
1474 // post error message on UI thread
1475 if (!connectCancelledByUser)
1476 uiHandler.sendMessage(
1477 Message.obtain(null, UIHandler.DISPLAY_TOAST,
1478 getResources().getText(R.string.error_connection_failure)));
1479
1480 closeSessionActivity(RESULT_CANCELED);
1481 }
1482
1483 private void OnDisconnected(Context context)
1484 {
1485 Log.v(TAG, "OnDisconnected");
1486
1487 // remove pending move events
1488 uiHandler.removeMessages(UIHandler.SEND_MOVE_EVENT);
1489
1490 if (progressDialog != null)
1491 {
1492 progressDialog.dismiss();
1493 progressDialog = null;
1494 }
1495
1496 session.setUIEventListener(null);
1497 closeSessionActivity(RESULT_OK);
1498 }
1499 }
1500}