Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,6 @@
package com.freerdp.freerdpcore.utils;
public class BuildFlags
{
private final static boolean USE_OPENSSL_DEFAULT_NAMES = @USE_OPENSSL_DEFAULT_NAMES@;
}

View File

@@ -0,0 +1,51 @@
plugins {
id 'com.gladed.androidgitversion' version '0.4.14'
}
androidGitVersion {
abis = ['armeabi':1, 'armeabi-v7a':2, 'arm64-v8a':3, 'mips':5, 'mips64':6, 'x86':8, 'x86_64':9 ]
prefix ''
}
println 'Version Name: ' + androidGitVersion.name()
println 'Version Code: ' + androidGitVersion.code()
apply plugin: 'com.android.application'
android {
compileSdkVersion = rootProject.ext.compileApi
buildToolsVersion = rootProject.ext.toolsVersion
defaultConfig {
applicationId "com.freerdp.afreerdp"
minSdkVersion rootProject.ext.minApi
targetSdkVersion rootProject.ext.targetApi
vectorDrawables.useSupportLibrary = true
versionName = androidGitVersion.name()
versionCode = androidGitVersion.code()
}
signingConfigs {
release {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
storeType "jks"
}
}
buildTypes {
release {
minifyEnabled false
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
jniDebuggable true
}
}
}
dependencies {
implementation project(':freeRDPCore')
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<lint></lint>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto">
<application
android:name="com.freerdp.afreerdp.application.GlobalApp"
android:icon="@drawable/icon_launcher_freerdp"
android:label="aFreeRDP"
android:resizeableActivity="true">
<!-- Main activity -->
<activity
android:exported="true"
android:name="com.freerdp.freerdpcore.presentation.HomeActivity"
android:alwaysRetainTaskState="true"
android:label="@string/app_title"
android:theme="@style/Theme.Main"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|screenLayout">
<intent-filter android:label="@string/app_title">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/rdp" />
<data android:mimeType="application/x-rdp" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="rdp" />
<data android:scheme="Rdp" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="file" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="content" />
<data android:host="*" />
<!-- Ugly hack to match all files with a dot in its name.
Remove if a better way is found / supported.
https://stackoverflow.com/questions/3400072/pathpattern-to-match-file-extension-does-not-work-if-a-period-exists-elsewhere-i/8599921#8599921
-->
<data android:pathPattern=".*\\.rdp" />
<data android:pathPattern=".*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rdp" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.rdp" />
</intent-filter>
</activity>
<!-- Session request handler activity - used for search and internally to start sessions -->
<!-- This should actually be defined in FreeRDPCore lib but Android manifest merging will -->
<!-- append the libs manifest to the apps manifest and therefore aliasing is not possible -->
<activity
android:exported="true"
android:name="com.freerdp.freerdpcore.services.SessionRequestHandlerActivity"
android:excludeFromRecents="true"
android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity-alias
android:exported="true"
android:name=".services.SessionRequestHandlerActivity"
android:targetActivity="com.freerdp.freerdpcore.services.SessionRequestHandlerActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity-alias>
<provider
android:name="com.freerdp.freerdpcore.services.FreeRDPSuggestionProvider"
android:authorities="com.freerdp.afreerdp.services.freerdpsuggestionprovider"
android:exported="false"></provider>
<meta-data android:name="com.samsung.android.keepalive.density" android:value="true"/>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -0,0 +1,147 @@
p {
border: none;
padding: 0in;
font-variant: normal;
font-family: "Helvetica";
font-style: normal;
font-weight: normal;
line-height: 100%;
text-align: center;
}
td p {
border: none;
padding: 0in;
font-variant: normal;
font-family: "Helvetica";
font-style: normal;
font-weight: normal;
line-height: 100%;
text-align: center;
}
h2 {
border: none;
padding: 0in;
direction: inherit;
font-variant: normal;
color: #ffffff;
line-height: 100%;
text-align: center;
}
h2.western {
font-style: normal;
}
h2.cjk {
font-family: "AR PL SungtiL GB";
font-style: normal;
}
h2.ctl {
font-family: "Lohit Devanagari";
font-style: normal;
}
h3 {
border: none;
padding: 0in;
direction: inherit;
font-variant: normal;
color: #ffffff;
line-height: 100%;
text-align: center;
page-break-before: auto;
page-break-after: auto;
}
h3.western {
font-style: normal;
}
h3.cjk {
font-family: "AR PL SungtiL GB";
font-style: normal;
}
h3.ctl {
font-family: "Lohit Devanagari";
font-style: normal;
}
h4 {
border: none;
padding: 0in;
direction: inherit;
font-variant: normal;
color: #ffffff;
line-height: 100%;
text-align: center;
page-break-before: auto;
page-break-after: auto;
}
h4.western {
font-style: normal;
}
h4.cjk {
font-family: "AR PL SungtiL GB";
font-style: normal;
}
h4.ctl {
font-family: "Lohit Devanagari";
font-style: normal;
}
pre {
direction: inherit;
font-variant: normal;
line-height: 100%;
text-align: center;
page-break-before: auto;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
pre.western {
font-size: 8pt;
font-style: normal;
font-weight: normal;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
pre.cjk {
font-family: "AR PL SungtiL GB", monospace;
font-size: 8pt;
font-style: normal;
font-weight: normal;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
pre.ctl {
font-style: normal;
font-weight: normal;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
}
a:link {
color: #0000ff
}

View File

@@ -0,0 +1,397 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="" content=""/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="../about.css">
</head>
<body lang="en-US" link="#0000ff" bgcolor="#e9ebf8" dir="ltr">
<div id="container" dir="ltr">
<h2 class="western"><br/>
<br/>
</h2>
<div id="introduction_headline" dir="ltr" style="background: #353639">
<h2 class="western"><font color="#ffffff">aFreeRDP <br/>
Remote
Desktop Client</font></h2>
</div>
<p><img src="../FreeRDP_Logo.png" name="Image1" align="bottom" width="25%" border="0"/>
</p>
<div id="introduction" dir="ltr" style="background: #ffffff">
<p><font color="#000000"><b>aFreeRDP</b> is an open source client
capable of natively using<br/>
Remote Desktop Protocol (RDP) in
order to remotely access your Windows desktop. </font>
</p>
</div>
<div id="article" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<br/>
<br/>
</p>
<div id="headline" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
Version Information</h3>
</div>
<table cellpadding="3" cellspacing="1" align="center">
<tr>
<td style="border: none; padding: 0in">
<p><b>aFreeRDP</b> Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%AFREERDP_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>System Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%SYSTEM_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>Model</p>
</td>
<td style="border: none; padding: 0in">
<p>%DEVICE_MODEL%</p>
</td>
</tr>
</table>
<div id="Section1" dir="ltr" style="background: #353639">
<h3 class="western" align="center" style="margin-left: 0.1in; margin-right: 0.1in"><a
name="headline"></a>
Credits</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font
color="#000000"><b>aFreeRDP</b></font><font color="#000000">
is a part of <a href="http://www.freerdp.com/">FreeRDP</a> </font>
</p>
<div id="Section2" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline1"></a>
Data protection</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000">Details
about data collection and usage by <b>aFreeRDP</b> are available at</font></p>
<p><a href="http://www.freerdp.com/privacy">http://www.freerdp.com/privacy</a>
</p>
<div id="Section3" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline2"></a>
Licenses</h3>
</div>
<div id="Section4" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline3"></a>
<font color="#ffffff">aFreeRDP</font></h4>
</div>
<pre class="western">This program is free software;
you can redistribute it and/or modify it under the terms
of the Mozilla Public License, v. 2.0.
You can obtain an online version of the License from
<a href="http://mozilla.org/MPL/2.0/">http://mozilla.org/MPL/2.0/</a>.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<br/>
<br/>
</p>
<div id="Section5" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline4"></a>
FreeRDP</h4>
</div>
<pre class="western">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section6" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline5"></a>
OpenSSL</h4>
</div>
<pre class="western">LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)&quot;
*
* 4. The names &quot;OpenSSL Toolkit&quot; and &quot;OpenSSL Project&quot; must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called &quot;OpenSSL&quot;
* nor may &quot;OpenSSL&quot; appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)&quot;
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are adhered to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* &quot;This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)&quot;
* The word 'cryptographic' can be left out if the routines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* &quot;This product includes software written by Tim Hudson (tjh@cryptsoft.com)&quot;
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publicly available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
A copy of the product's source code can be obtained from the project page at
<a href="https://www.openssl.org/">https://www.openssl.org/</a>.</pre>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,397 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="" content=""/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="../about.css">
</head>
<body lang="en-US" link="#0000ff" bgcolor="#e9ebf8" dir="ltr">
<div id="container" dir="ltr">
<h2 class="western"><br/>
<br/>
</h2>
<div id="introduction_headline" dir="ltr" style="background: #353639">
<h2 class="western"><font color="#ffffff">aFreeRDP <br/>
Remote
Desktop Client</font></h2>
</div>
<p><img src="../FreeRDP_Logo.png" name="Image1" align="bottom" width="25%" border="0"/>
</p>
<div id="introduction" dir="ltr" style="background: #ffffff">
<p><font color="#000000"><b>aFreeRDP</b> is an open source client
capable of natively using<br/>
Remote Desktop Protocol (RDP) in
order to remotely access your Windows desktop. </font>
</p>
</div>
<div id="article" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<br/>
<br/>
</p>
<div id="headline" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
Version Information</h3>
</div>
<table cellpadding="3" cellspacing="1" align="center">
<tr>
<td style="border: none; padding: 0in">
<p><b>aFreeRDP</b> Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%AFREERDP_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>System Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%SYSTEM_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>Model</p>
</td>
<td style="border: none; padding: 0in">
<p>%DEVICE_MODEL%</p>
</td>
</tr>
</table>
<div id="Section1" dir="ltr" style="background: #353639">
<h3 class="western" align="center" style="margin-left: 0.1in; margin-right: 0.1in"><a
name="headline"></a>
Credits</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font
color="#000000"><b>aFreeRDP</b></font><font color="#000000">
is a part of <a href="http://www.freerdp.com/">FreeRDP</a> </font>
</p>
<div id="Section2" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline1"></a>
Data protection</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000">Details
about data collection and usage by <b>aFreeRDP</b> are available at</font></p>
<p><a href="http://www.freerdp.com/privacy">http://www.freerdp.com/privacy</a>
</p>
<div id="Section3" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline2"></a>
Licenses</h3>
</div>
<div id="Section4" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline3"></a>
<font color="#ffffff">aFreeRDP</font></h4>
</div>
<pre class="western">This program is free software;
you can redistribute it and/or modify it under the terms
of the Mozilla Public License, v. 2.0.
You can obtain an online version of the License from
<a href="http://mozilla.org/MPL/2.0/">http://mozilla.org/MPL/2.0/</a>.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<br/>
<br/>
</p>
<div id="Section5" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline4"></a>
FreeRDP</h4>
</div>
<pre class="western">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section6" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline5"></a>
OpenSSL</h4>
</div>
<pre class="western">LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)&quot;
*
* 4. The names &quot;OpenSSL Toolkit&quot; and &quot;OpenSSL Project&quot; must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called &quot;OpenSSL&quot;
* nor may &quot;OpenSSL&quot; appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)&quot;
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are adhered to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* &quot;This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)&quot;
* The word 'cryptographic' can be left out if the routines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* &quot;This product includes software written by Tim Hudson (tjh@cryptsoft.com)&quot;
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publicly available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
A copy of the product's source code can be obtained from the project page at
<a href="https://www.openssl.org/">https://www.openssl.org/</a>.</pre>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View File

@@ -0,0 +1,410 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="" content=""/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<link rel="stylesheet" type="text/css" href="../about.css">
</head>
<body lang="de-AT" link="#0000ff" bgcolor="#e9ebf8" dir="ltr">
<div id="container" dir="ltr">
<p><br/>
<br/>
</p>
<div id="introduction_headline" dir="ltr" style="background: #353639">
<h2 class="western"><font color="#ffffff">aFreeRDP<br/>
Remote
Desktop Client</font>
</h2>
</div>
<div id="introduction" dir="ltr" style="background: #ffffff">
<p align="center"><font color="#000000"><img src="FreeRDP_Logo.png" name="FreeRDP-Logo"
align="top" hspace="6" vspace="6" width="10%"
border="0"/>
</font>
</p>
</div>
<div id="article" dir="ltr" style="background: #ffffff">
<p align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<font color="#000000"><b>aFreeRDP</b> ist ein Open Source Programm
mit nativer Unterstützung des Remote Desktop Protocol (RDP)<br/>
um
einen entfernten Zugriff auf Windows Desktops zu ermöglichen. </font>
</p>
<div id="headline" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
Versions Information</h3>
</div>
<table cellpadding="3" cellspacing="1" align="center">
<tr>
<td style="border: none; padding: 0in">
<p><b>aFreeRDP</b> Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%AFREERDP_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>System Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%SYSTEM_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>Model</p>
</td>
<td style="border: none; padding: 0in">
<p>%DEVICE_MODEL%</p>
</td>
</tr>
</table>
</div>
<div id="Section1" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="article"></a>
<br/>
<br/>
</p>
<div id="Section2" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline"></a>
Credits</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font
color="#000000"><b>aFreeRDP</b></font><font color="#000000">
ist ein Teil von <a href="http://www.freerdp.com/">FreeRDP</a> </font>
</p>
</div>
<div id="Section3" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="article1"></a>
<br/>
<br/>
</p>
<div id="Section4" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline1"></a>
Datenschutz</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000">Details
zu den Daten die aFreeRDP sammelt und verarbeitet sind unter</font></p>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000"><a
href="http://www.freerdp.com/privacy">http://www.freerdp.com/privacy</a>
zu finden.</font></p>
<div id="Section5" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline2"></a>
Lizenzen</h3>
</div>
<div id="Section6" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline3"></a>
<font color="#000000">aFreeRDP</font></h4>
</div>
<pre class="western">This program is free software;
you can redistribute it and/or modify it under the terms
of the Mozilla Public License, v. 2.0.
You can obtain an online version of the License from
<a href="http://mozilla.org/MPL/2.0/">http://mozilla.org/MPL/2.0/</a>.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section7" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline4"></a>
FreeRDP</h4>
</div>
<pre class="western">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
A copy of the product's source code can be obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section8" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline5"></a>
OpenSSL</h4>
</div>
<pre class="western">LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)&quot;
*
* 4. The names &quot;OpenSSL Toolkit&quot; and &quot;OpenSSL Project&quot; must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called &quot;OpenSSL&quot;
* nor may &quot;OpenSSL&quot; appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)&quot;
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* &quot;This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)&quot;
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* &quot;This product includes software written by Tim Hudson (tjh@cryptsoft.com)&quot;
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
A copy of the product's source code can be obtained from the project page at
<a href="https://www.openssl.org/">https://www.openssl.org/</a>.</pre>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,412 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title></title>
<meta name="" content=""/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"/>
<meta name="changed" content="2017-02-23T09:47:39.760769965">
<link rel="stylesheet" type="text/css" href="../about.css">
</head>
<body lang="de-AT" link="#0000ff" bgcolor="#e9ebf8" dir="ltr">
<div id="container" dir="ltr">
<p><br/>
<br/>
</p>
<div id="introduction_headline" dir="ltr" style="background: #353639">
<h2 class="western"><font color="#ffffff">aFreeRDP<br/>
Remote
Desktop Client</font>
</h2>
</div>
<div id="introduction" dir="ltr" style="background: #ffffff">
<p align="center"><font color="#000000"><img src="../FreeRDP_Logo.png" name="FreeRDP-Logo"
align="top" hspace="6" vspace="6" width="25%"
border="0"/>
</font>
</p>
</div>
<div id="article" dir="ltr" style="background: #ffffff">
<p align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<font color="#000000"><b>aFreeRDP</b> ist ein Open Source Programm
mit nativer Unterstützung des Remote Desktop Protocol (RDP)<br/>
um
einen entfernten Zugriff auf Windows Desktops zu ermöglichen. </font>
</p>
<div id="headline" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
Versions Information</h3>
</div>
<table cellpadding="3" cellspacing="1" align="center">
<tr>
<td style="border: none; padding: 0in">
<p><b>aFreeRDP</b> Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%AFREERDP_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>System Version</p>
</td>
<td style="border: none; padding: 0in">
<p>%SYSTEM_VERSION%</p>
</td>
</tr>
<tr>
<td style="border: none; padding: 0in">
<p>Model</p>
</td>
<td style="border: none; padding: 0in">
<p>%DEVICE_MODEL%</p>
</td>
</tr>
</table>
</div>
<div id="Section1" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="article"></a>
<br/>
<br/>
</p>
<div id="Section2" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline"></a>
Credits</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font
color="#000000"><b>aFreeRDP</b></font><font color="#000000">
ist ein Teil von <a href="http://www.freerdp.com/">FreeRDP</a> </font>
</p>
</div>
<div id="Section3" dir="ltr" style="background: #ffffff">
<p style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="article1"></a>
<br/>
<br/>
</p>
<div id="Section4" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline1"></a>
Datenschutz</h3>
</div>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000">Details
zu den Daten die aFreeRDP sammelt und verarbeitet sind unter</font></p>
<p style="margin-top: 0.1in; margin-bottom: 0.1in"><font color="#000000"><a
href="http://www.freerdp.com/privacy">http://www.freerdp.com/privacy</a>
zu finden.</font></p>
<div id="Section5" dir="ltr" style="background: #353639">
<h3 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline2"></a>
Lizenzen</h3>
</div>
<div id="Section6" dir="ltr" style="background: #353639">
<h4 class="western"><a name="headline3"></a>aFreeRDP</h4>
</div>
<pre class="western">This program is free software;
you can redistribute it and/or modify it under the terms
of the Mozilla Public License, v. 2.0.
You can obtain an online version of the License from
<a href="http://mozilla.org/MPL/2.0/">http://mozilla.org/MPL/2.0/</a>.
This program is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
A copy of the product's source code can be
obtained from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section7" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline4"></a>
FreeRDP</h4>
</div>
<pre class="western">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
<a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
A copy of the product's source code can be obtained
from the FreeRDP GitHub repository at
<a href="https://github.com/FreeRDP/FreeRDP">https://github.com/FreeRDP/FreeRDP</a>.</pre>
<div id="Section8" dir="ltr" style="background: #353639">
<h4 class="western" align="center"
style="margin-left: 0.1in; margin-right: 0.1in; margin-top: 0.1in; margin-bottom: 0.1in">
<a name="headline5"></a>
OpenSSL</h4>
</div>
<pre class="western">LICENSE ISSUES
==============
The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
the OpenSSL License and the original SSLeay license apply to the toolkit.
See below for the actual license texts.
OpenSSL License
---------------
/* ====================================================================
* Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)&quot;
*
* 4. The names &quot;OpenSSL Toolkit&quot; and &quot;OpenSSL Project&quot; must not be used to
* endorse or promote products derived from this software without
* prior written permission. For written permission, please contact
* openssl-core@openssl.org.
*
* 5. Products derived from this software may not be called &quot;OpenSSL&quot;
* nor may &quot;OpenSSL&quot; appear in their names without prior written
* permission of the OpenSSL Project.
*
* 6. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* &quot;This product includes software developed by the OpenSSL Project
* for use in the OpenSSL Toolkit (http://www.openssl.org/)&quot;
*
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This product includes cryptographic software written by Eric Young
* (eay@cryptsoft.com). This product includes software written by Tim
* Hudson (tjh@cryptsoft.com).
*
*/
Original SSLeay License
-----------------------
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* &quot;This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)&quot;
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* &quot;This product includes software written by Tim Hudson (tjh@cryptsoft.com)&quot;
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publicly available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/
A copy of the product's source code can be obtained from the project page at
<a href="https://www.openssl.org/">https://www.openssl.org/</a>.</pre>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,33 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Gesten</h1>
<p>
aFreeRDP ist für Touch Geräte entwickelt worden.
Diese Gesten lassen sie die häufigsten Operationen mit ihren Fingern
durchführen.</p>
<p><img src="gestures.png"></p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,38 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2>Gesten</h2>
<p>
aFreeRDP ist für Touch Geräte entwickelt worden.
Diese Gesten lassen sie die häufigsten Operationen mit ihren Fingern
durchführen.</p>
<p><img src="gestures_phone.png"></p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,49 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Toolbar</h1>
<p>
With the toolbar you'll be able to display and hide the main tools in your session.
This allows together with the touch pointer and the gestures an intuitiv workflow
for remote computing on touch sensitive screens.
</p>
<p><img src="toolbar.png"></p>
<div id="article">
<div id="headline">
<h3><span style="color:white">Tastatur</span></h3></div>
Zeige/verstecke die standard und die erweiterte Tastatur mit Funktionstasten
</div>
<div id="article">
<div id="headline"><h3><span style="color:white">Touch Zeiger</span></h3></div>
Zeige/verstecke den gesten gesteuerten Zeiger
</div>
<div id="article">
<div id="headline"><h3><span style="color:white">Beenden</span></h3></div>
Beende die aktuelle Sitzung. Seihen sie sich bewusst, dass das Beenden kein Logout
ist.
</div>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,49 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2>Toolbar</h2>
<p>
With the toolbar you'll be able to display and hide the main tools in your session.
This allows together with the touch pointer and the gestures an intuitiv workflow
for remote computing on touch sensitive screens.
</p>
<p><img src="toolbar_phone.png"></p>
<div id="article">
<div id="headline">
<h4><span style="color:white">Tastatur</h4></span></div>
Zeige/verstecke die standard und die erweiterte Tastatur mit Funktionstasten
</div>
<div id="article">
<div id="headline"><h4><span style="color:white">Touch Zeiger</h4></div>
Zeige/verstecke den gesten gesteuerten Zeiger
</div>
<div id="article">
<div id="headline"><h4><span style="color:white">Beenden</span></h4></div>
Beende die aktuelle Sitzung. Seihen sie sich bewusst, dass das Beenden kein Logout
ist.
</div>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,29 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Touch Pointer</h1>
<p><img src="touch_pointer.png">
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -0,0 +1,30 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2>Touch Pointer</h2>
<p><img src="touch_pointer_phone.png">
</p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,100 @@
<style type="text/css">
@charset "utf-8";
#container{
text-align:center;
color:#FFFFFF;
}
body {
margin:0;
padding:<length> 0 0 0;
font: 100%/1.4 Helvetica;
background-image:url(../background.jpg);
background-position:center;
}
#headline {
background-color:#353639;
opacity:0.9;
color:FFF;
text-align:center;
}
#article {
background-color:#FFFFFF;
opacity: 0.8;
z-index:0;
margin-bottom:3%;
padding-bottom:0.1%;
border-radius: 15px;
border-top-left-radius:0px;
border-top-right-radius:0px;
color:#000;
margin: 10px auto;
position:relative;
}
#header {
height:auto;
width:100%;
background-color:#353639;
padding-bottom:5px;
padding-left:5px;
padding-right:5px;
padding-top:10px;
position: fixed;
top: 0;
left: 0;
height:40px;
overflow:visible;
min-width:400px;
z-index:20;
}
#content {
padding-top:70px;
z-index:-20;
max-width:420px;
}
/* ~~ Element/tag selectors ~~ */
ul, ol, dl { /* Due to variations between browsers, it's best practices to zero padding and margin on lists. For consistency, you can either specify the amounts you want here, or on the list items (LI, DT, DD) they contain. Remember that what you do here will cascade to the .nav list unless you write a more specific selector. */
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5, h6, p {
margin-top: 0; /* removing the top margin gets around an issue where margins can escape from their containing div. The remaining bottom margin will hold it away from any elements that follow. */
padding-right: 1px;
padding-left: 1px; /* adding the padding to the sides of the elements within the divs, instead of the divs themselves, gets rid of any box model math. A nested div with side padding can also be used as an alternate method.
*/
color:#000;
}
a img { /* this selector removes the default blue border displayed in some browsers around an image when it is surrounded by a link */
border: none;
}
/* ~~ Styling for your site's links must remain in this order - including the group of selectors that create the hover effect. ~~ */
/*a:link {
color:#414958;
text-decoration: underline; unless you style your links to look extremely unique, it's best to provide underlines for quick visual identification */
a:hover, a:active, a:focus { /* this group of selectors will give a keyboard navigator the same hover experience as the person using a mouse. */
text-decoration: none;
}
* {
-webkit-touch-callout: none;
-webkit-user-select: none; /* Disable selection/Copy of UIWebView */
}
</style>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Gestures</h1>
<p>
aFreeRDP is designed for touch sensitive devices.
These gestures let you do the most usual operations with your fingers.</p>
<p><img src="gestures.png"></p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,33 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2> Gestures</h2>
<p>
aFreeRDP is designed for touch sensitive devices.
These gestures let you do the most usual operations with your fingers.</p>
<p><img src="gestures_phone.png"></p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,49 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Toolbar</h1>
<p>
With the toolbar you'll be able to display and hide the main tools in your session.
This allows together with the touch pointer and the gestures an intuitiv workflow
for remote computing on touch sensitive screens.
</p>
<p><img src="toolbar.png"></p>
<div id="article">
<div id="headline">
<h3><span style="color:white">Keyboards</span></h3></div>
Display/hide the default keyboard as well as an extended keyboard with function keys
</div>
<div id="article">
<div id="headline"><h3><span style="color:white">Touch Pointer</span></h3></div>
Display/hide the gesture controlled cursor
</div>
<div id="article">
<div id="headline"><h3><span style="color:white">Disconnect</span></h3></div>
Disconnect your current session. Please be aware that a disconnect is not the same
as a log out.
</div>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,50 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2>Toolbar</h2>
<p>
With the toolbar you'll be able to display and hide the main tools in your session.
This allows together with the touch pointer and the gestures an intuitiv workflow
for remote computing on touch sensitive screens.
</p>
<p><img src="toolbar_phone.png"></p>
<div id="article">
<div id="headline">
<h4><span style="color:white">Keyboards</h4></span></div>
Display/hide the default keyboard as well as an extended keyboard with function keys
</div>
<div id="article">
<div id="headline"><h4><span style="color:white">Touch Pointer</h4></div>
Display/hide the gesture controlled cursor
</div>
</span>
<div id="article">
<div id="headline"><h4><span style="color:white">Disconnect</span></h4></div>
Disconnect your current session. Please be aware that a disconnect is not the same
as a log out.
</div>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@@ -0,0 +1,29 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures.html"><img src="nav_gestures.png"></a>
<a href="toolbar.html"><img src="nav_toolbar.png"></a>
<a href="touch_pointer.html"><img src="nav_touch_pointer.png"></a>
</div>
<div id="content">
<h1>Touch Pointer</h1>
<p><img src="touch_pointer.png">
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -0,0 +1,30 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;"/>
<title>Help</title>
<link rel="stylesheet" type="text/css" href="../help-phone.css">
</head>
<body>
<div id="container">
<center>
<div id="header">
<a href="gestures_phone.html"><img src="nav_gestures.png" height="90%"></a>
<a href="toolbar_phone.html"><img src="nav_toolbar.png" height="90%"></a>
<a href="touch_pointer_phone.html"><img src="nav_touch_pointer.png" height="90%""></a>
</div>
<div id="content">
<h2>Touch Pointer</h2>
<p><img src="touch_pointer_phone.png">
</p>
</div>
</center>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,5 @@
package com.freerdp.afreerdp.application;
public class GlobalApp extends com.freerdp.freerdpcore.application.GlobalApp
{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<solid android:color="#99D4FF" />
<stroke android:width="1dp" android:color="#A7A7A7" />
<corners android:radius="3dp" />
<padding android:bottom="3dp" android:left="3dp" android:right="3dp" android:top="3dp" />
</shape>
</item>
<item android:state_focused="true">
<shape>
<solid android:color="#E3E3E3" />
<stroke android:width="1dp" android:color="#585858" />
<corners android:radius="3dp" />
<padding android:bottom="3dp" android:left="3dp" android:right="3dp" android:top="3dp" />
</shape>
</item>
<item>
<shape>
<solid android:color="#E3E3E3" />
<stroke android:width="1dp" android:color="#585858" />
<corners android:radius="3dp" />
<padding android:bottom="3dp" android:left="3dp" android:right="3dp" android:top="3dp" />
</shape>
</item>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/separator_background_color" />
<stroke
android:width="1dp"
android:color="@color/separator_frame_color" />
<padding
android:bottom="1dp"
android:left="1dp"
android:right="1dp"
android:top="1dp" />
<!--
<color xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#C2D6E6"
/>
-->
</shape>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">Entfernte Rechner</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">Remote Computers</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">L\'ordinateur distant</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">원격 컴퓨터</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">Remote Computers</string>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Search strings -->
<string name="search_settings_description">遠端電腦</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="search_settings_description">远程计算机</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_title" translatable="false">aFreeRDP</string>
<!-- Search strings -->
<string name="search_label" translatable="false">aFreeRDP</string>
<string name="search_settings_description">Remote Computers</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
/*
Bookmark searchable definition
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-->
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_hint"
android:imeOptions="actionGo"
android:includeInGlobalSearch="true"
android:label="@string/search_label"
android:searchMode="queryRewriteFromText"
android:searchSettingsDescription="@string/search_settings_description"
android:searchSuggestAuthority="com.freerdp.afreerdp.services.freerdpsuggestionprovider"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestSelection=" ? "></searchable>

View File

@@ -0,0 +1,70 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
Properties releaseProperties = new Properties()
File file = new File('release.properties')
if (file.canRead()) {
releaseProperties.load(new FileInputStream(file))
}
if (!hasProperty('RELEASE_STORE_FILE')) {
RELEASE_STORE_FILE=releaseProperties.get('RELEASE_STORE_FILE', '~/.android/debug.keystore')
}
if (!hasProperty('RELEASE_KEY_ALIAS')) {
RELEASE_KEY_ALIAS=releaseProperties.get('RELEASE_KEY_ALIAS', 'androiddebugkey')
}
if (!hasProperty('RELEASE_KEY_PASSWORD')) {
RELEASE_KEY_PASSWORD=releaseProperties.get('RELEASE_KEY_PASSWORD', 'android')
}
if (!hasProperty('RELEASE_STORE_PASSWORD')) {
RELEASE_STORE_PASSWORD=releaseProperties.get('RELEASE_STORE_PASSWORD', 'android')
}
ext {
compileApi = releaseProperties.get('COMPILE_API', 35)
targetApi = releaseProperties.get('TARGET_API', 35)
minApi = releaseProperties.get('MIN_API', 23)
toolsVersion = releaseProperties.get('TOOLS_VERSION', '35.0.0')
println '----------------- Project configuration -------------------'
println 'RELEASE_STORE_FILE: ' + RELEASE_STORE_FILE
println 'RELEASE_KEY_ALIAS: ' + RELEASE_KEY_ALIAS
println 'compile API: ' + compileApi
println 'target API: ' + targetApi
println 'min API: ' + minApi
println 'tools version: ' + toolsVersion
println '-----------------------------------------------------------'
}
buildscript {
repositories {
mavenCentral()
google()
maven {
url 'https://maven.google.com'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.13.0'
}
}
allprojects {
repositories {
mavenCentral()
google()
maven {
url 'https://maven.google.com'
}
}
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace 'com.freerdp.' + project.name.toLowerCase()
}
}
}
}
}
}

View File

@@ -0,0 +1,66 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion = rootProject.ext.compileApi
buildToolsVersion = rootProject.ext.toolsVersion
packagingOptions {
jniLibs {
pickFirsts += ['lib/arm64-v8a/libfreerdp3.so', 'lib/armeabi-v7a/libfreerdp3.so', 'lib/x86/libfreerdp3.so', 'lib/x86_64/libfreerdp3.so', 'lib/arm64-v8a/libfreerdp-client3.so', 'lib/armeabi-v7a/libfreerdp-client3.so', 'lib/x86/libfreerdp-client3.so', 'lib/x86_64/libfreerdp-client3.so', 'lib/arm64-v8a/libwinpr3.so', 'lib/armeabi-v7a/libwinpr3.so', 'lib/x86/libwinpr3.so', 'lib/x86_64/libwinpr3.so']
}
}
defaultConfig {
minSdkVersion rootProject.ext.minApi
targetSdkVersion rootProject.ext.targetApi
vectorDrawables.useSupportLibrary = true
ndkVersion = "29.0.13113456"
ndk {
File jniLibsDirectory = new File(project.projectDir, "src/main/jniLibs")
ArrayList<String> abiFiltersList = new ArrayList<String>()
if (new File(jniLibsDirectory, "arm64-v8a/libfreerdp3.so").exists())
abiFiltersList.add("arm64-v8a")
if (new File(jniLibsDirectory, "armeabi-v7a/libfreerdp3.so").exists())
abiFiltersList.add("armeabi-v7a")
if (new File(jniLibsDirectory, "x86_64/libfreerdp3.so").exists())
abiFiltersList.add("x86_64")
if (new File(jniLibsDirectory, "x86/libfreerdp3.so").exists())
abiFiltersList.add("x86")
//noinspection ChromeOsAbiSupport
abiFilters = abiFiltersList
}
externalNativeBuild {
cmake {
arguments "-DWITH_CLIENT_CHANNELS=ON"
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
jniDebuggable true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
}
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:support-vector-drawable:28.0.0'
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<lint></lint>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="auto">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:theme="@style/Theme.Main"
android:resizeableActivity="true"
>
<meta-data android:name="com.samsung.android.keepalive.density" android:value="true"/>
<!-- Activity to create shortcuts -->
<activity
android:exported="true"
android:name=".presentation.ShortcutsActivity"
android:label="@string/title_create_shortcut"
android:theme="@style/Theme.Main">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<!-- It is recommended that you use an activity-alias to provide the "CREATE_SHORTCUT" -->
<!-- intent-filter. This gives you a way to set the text (and optionally the -->
<!-- icon) that will be seen in the launcher's create-shortcut user interface. -->
<activity-alias
android:exported="true"
android:name=".presentation.CreateShortcuts"
android:label="@string/title_create_shortcut"
android:targetActivity=".presentation.ShortcutsActivity">
<!-- This intent-filter allows your shortcuts to be created in the launcher. -->
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
<activity
android:exported="false"
android:name=".presentation.BookmarkActivity"
android:label="@string/title_bookmark_settings"
android:theme="@style/Theme.Settings">
<intent-filter>
<action android:name="freerdp.intent.action.BOOKMARK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="preferences" />
</intent-filter>
</activity>
<activity
android:exported="true"
android:name=".presentation.ApplicationSettingsActivity"
android:label="@string/title_application_settings"
android:theme="@style/Theme.Settings"
android:windowSoftInputMode="stateHidden" />
<activity
android:exported="true"
android:name=".presentation.SessionActivity"
android:theme="@style/Theme.Main"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|smallestScreenSize|density|screenLayout|navigation"
android:windowSoftInputMode="adjustResize">
<!--android:configChanges="orientation|keyboardHidden|screenSize|keyboard"-->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="*"
android:scheme="freerdp" />
</intent-filter>
</activity>
<activity
android:exported="true"
android:name=".presentation.AboutActivity"
android:label="@string/title_about"
android:theme="@style/Theme.Main" />
<activity
android:exported="true"
android:name=".presentation.HelpActivity"
android:label="@string/title_help"
android:theme="@style/Theme.Main" />
<receiver
android:exported="true"
android:name=".application.NetworkStateReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@@ -0,0 +1,83 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# Android Client
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
# Copyright 2022 Ely Ronnen <elyronnen@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.13)
if(NOT FREERDP_DEFAULT_PROJECT_VERSION)
set(FREERDP_DEFAULT_PROJECT_VERSION "1.0.0.0")
endif()
project("freerdp-android" LANGUAGES C VERSION ${FREERDP_DEFAULT_PROJECT_VERSION})
message("project ${PROJECT_NAME} is using version ${PROJECT_VERSION}")
set(MODULE_NAME "freerdp-android")
set(MODULE_PREFIX "FREERDP_CLIENT_ANDROID")
set(FREERDP_IMPORT_DIR_RELATIVE ../jniLibs/${CMAKE_ANDROID_ARCH_ABI})
get_filename_component(FREERDP_IMPORT_DIR ${FREERDP_IMPORT_DIR_RELATIVE} ABSOLUTE)
include_directories(
SYSTEM ${FREERDP_IMPORT_DIR}/include/freerdp3 ${FREERDP_IMPORT_DIR}/include/winpr3
${FREERDP_IMPORT_DIR}/include/openssl
)
set(${MODULE_PREFIX}_SRCS
android_event.c
android_event.h
android_freerdp.c
android_freerdp.h
android_jni_utils.c
android_jni_utils.h
android_jni_callback.c
android_jni_callback.h
)
if(WITH_CLIENT_CHANNELS)
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} android_cliprdr.c android_cliprdr.h)
endif()
add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
add_library(freerdp3-lib SHARED IMPORTED)
set_property(TARGET freerdp3-lib PROPERTY IMPORTED_LOCATION ${FREERDP_IMPORT_DIR}/libfreerdp3.so)
add_library(freerdp-client3-lib SHARED IMPORTED)
set_property(TARGET freerdp-client3-lib PROPERTY IMPORTED_LOCATION ${FREERDP_IMPORT_DIR}/libfreerdp-client3.so)
add_library(winpr3-lib SHARED IMPORTED)
set_property(TARGET winpr3-lib PROPERTY IMPORTED_LOCATION ${FREERDP_IMPORT_DIR}/libwinpr3.so)
find_library(log-lib log)
find_library(dl-lib dl)
find_library(jnigraphics-lib jnigraphics)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries(
${MODULE_NAME}
freerdp3-lib
freerdp-client3-lib
winpr3-lib
${log-lib}
${dl-lib}
${jnigraphics-lib}
)

View File

@@ -0,0 +1,501 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Clipboard Redirection
*
* Copyright 2013 Felix Long
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <jni.h>
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/client/channels.h>
#include <freerdp/client/cliprdr.h>
#include "android_cliprdr.h"
#include "android_jni_utils.h"
#include "android_jni_callback.h"
UINT android_cliprdr_send_client_format_list(CliprdrClientContext* cliprdr)
{
UINT rc = ERROR_INTERNAL_ERROR;
UINT32 formatId;
UINT32 numFormats;
UINT32* pFormatIds;
const char* formatName;
CLIPRDR_FORMAT* formats;
CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
if (!cliprdr)
return ERROR_INVALID_PARAMETER;
androidContext* afc = (androidContext*)cliprdr->custom;
if (!afc || !afc->cliprdr)
return ERROR_INVALID_PARAMETER;
pFormatIds = nullptr;
numFormats = ClipboardGetFormatIds(afc->clipboard, &pFormatIds);
formats = (CLIPRDR_FORMAT*)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
if (!formats)
goto fail;
for (UINT32 index = 0; index < numFormats; index++)
{
formatId = pFormatIds[index];
formatName = ClipboardGetFormatName(afc->clipboard, formatId);
formats[index].formatId = formatId;
formats[index].formatName = nullptr;
if ((formatId > CF_MAX) && formatName)
{
formats[index].formatName = _strdup(formatName);
if (!formats[index].formatName)
goto fail;
}
}
formatList.common.msgFlags = 0;
formatList.numFormats = numFormats;
formatList.formats = formats;
formatList.common.msgType = CB_FORMAT_LIST;
if (!afc->cliprdr->ClientFormatList)
goto fail;
rc = afc->cliprdr->ClientFormatList(afc->cliprdr, &formatList);
fail:
free(pFormatIds);
free(formats);
return rc;
}
static UINT android_cliprdr_send_client_format_data_request(CliprdrClientContext* cliprdr,
UINT32 formatId)
{
UINT rc = ERROR_INVALID_PARAMETER;
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = WINPR_C_ARRAY_INIT;
androidContext* afc;
if (!cliprdr)
goto fail;
afc = (androidContext*)cliprdr->custom;
if (!afc || !afc->clipboardRequestEvent || !cliprdr->ClientFormatDataRequest)
goto fail;
formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
formatDataRequest.common.msgFlags = 0;
formatDataRequest.requestedFormatId = formatId;
afc->requestedFormatId = formatId;
(void)ResetEvent(afc->clipboardRequestEvent);
rc = cliprdr->ClientFormatDataRequest(cliprdr, &formatDataRequest);
fail:
return rc;
}
static UINT android_cliprdr_send_client_capabilities(CliprdrClientContext* cliprdr)
{
CLIPRDR_CAPABILITIES capabilities;
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
if (!cliprdr || !cliprdr->ClientCapabilities)
return ERROR_INVALID_PARAMETER;
capabilities.cCapabilitiesSets = 1;
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET*)&(generalCapabilitySet);
generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
generalCapabilitySet.capabilitySetLength = 12;
generalCapabilitySet.version = CB_CAPS_VERSION_2;
generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
return cliprdr->ClientCapabilities(cliprdr, &capabilities);
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_monitor_ready(CliprdrClientContext* cliprdr,
const CLIPRDR_MONITOR_READY* monitorReady)
{
UINT rc;
androidContext* afc;
if (!cliprdr || !monitorReady)
return ERROR_INVALID_PARAMETER;
afc = (androidContext*)cliprdr->custom;
if (!afc)
return ERROR_INVALID_PARAMETER;
if ((rc = android_cliprdr_send_client_capabilities(cliprdr)) != CHANNEL_RC_OK)
return rc;
if ((rc = android_cliprdr_send_client_format_list(cliprdr)) != CHANNEL_RC_OK)
return rc;
afc->clipboardSync = TRUE;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_server_capabilities(CliprdrClientContext* cliprdr,
const CLIPRDR_CAPABILITIES* capabilities)
{
CLIPRDR_CAPABILITY_SET* capabilitySet;
androidContext* afc;
if (!cliprdr || !capabilities)
return ERROR_INVALID_PARAMETER;
afc = (androidContext*)cliprdr->custom;
if (!afc)
return ERROR_INVALID_PARAMETER;
for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
{
capabilitySet = &(capabilities->capabilitySets[index]);
if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
(capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
{
CLIPRDR_GENERAL_CAPABILITY_SET* generalCapabilitySet =
(CLIPRDR_GENERAL_CAPABILITY_SET*)capabilitySet;
afc->clipboardCapabilities = generalCapabilitySet->generalFlags;
break;
}
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_server_format_list(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_LIST* formatList)
{
UINT rc;
CLIPRDR_FORMAT* format;
androidContext* afc;
if (!cliprdr || !formatList)
return ERROR_INVALID_PARAMETER;
afc = (androidContext*)cliprdr->custom;
if (!afc)
return ERROR_INVALID_PARAMETER;
if (afc->serverFormats)
{
for (UINT32 index = 0; index < afc->numServerFormats; index++)
free(afc->serverFormats[index].formatName);
free(afc->serverFormats);
afc->serverFormats = nullptr;
afc->numServerFormats = 0;
}
if (formatList->numFormats < 1)
return CHANNEL_RC_OK;
afc->numServerFormats = formatList->numFormats;
afc->serverFormats = (CLIPRDR_FORMAT*)calloc(afc->numServerFormats, sizeof(CLIPRDR_FORMAT));
if (!afc->serverFormats)
return CHANNEL_RC_NO_MEMORY;
for (UINT32 index = 0; index < afc->numServerFormats; index++)
{
afc->serverFormats[index].formatId = formatList->formats[index].formatId;
afc->serverFormats[index].formatName = nullptr;
if (formatList->formats[index].formatName)
{
afc->serverFormats[index].formatName = _strdup(formatList->formats[index].formatName);
if (!afc->serverFormats[index].formatName)
return CHANNEL_RC_NO_MEMORY;
}
}
for (UINT32 index = 0; index < afc->numServerFormats; index++)
{
format = &(afc->serverFormats[index]);
if (format->formatId == CF_UNICODETEXT)
{
if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_UNICODETEXT)) !=
CHANNEL_RC_OK)
return rc;
break;
}
else if (format->formatId == CF_TEXT)
{
if ((rc = android_cliprdr_send_client_format_data_request(cliprdr, CF_TEXT)) !=
CHANNEL_RC_OK)
return rc;
break;
}
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
android_cliprdr_server_format_list_response(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_LIST_RESPONSE* formatListResponse)
{
if (!cliprdr || !formatListResponse)
return ERROR_INVALID_PARAMETER;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
android_cliprdr_server_lock_clipboard_data(CliprdrClientContext* cliprdr,
const CLIPRDR_LOCK_CLIPBOARD_DATA* lockClipboardData)
{
if (!cliprdr || !lockClipboardData)
return ERROR_INVALID_PARAMETER;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_server_unlock_clipboard_data(
CliprdrClientContext* cliprdr, const CLIPRDR_UNLOCK_CLIPBOARD_DATA* unlockClipboardData)
{
if (!cliprdr || !unlockClipboardData)
return ERROR_INVALID_PARAMETER;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
android_cliprdr_server_format_data_request(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_DATA_REQUEST* formatDataRequest)
{
UINT rc;
BYTE* data;
UINT32 size;
UINT32 formatId;
CLIPRDR_FORMAT_DATA_RESPONSE response = WINPR_C_ARRAY_INIT;
androidContext* afc;
if (!cliprdr || !formatDataRequest || !cliprdr->ClientFormatDataResponse)
return ERROR_INVALID_PARAMETER;
afc = (androidContext*)cliprdr->custom;
if (!afc)
return ERROR_INVALID_PARAMETER;
formatId = formatDataRequest->requestedFormatId;
data = (BYTE*)ClipboardGetData(afc->clipboard, formatId, &size);
response.common.msgFlags = CB_RESPONSE_OK;
response.common.dataLen = size;
response.requestedFormatData = data;
if (!data)
{
response.common.msgFlags = CB_RESPONSE_FAIL;
response.common.dataLen = 0;
response.requestedFormatData = nullptr;
}
rc = cliprdr->ClientFormatDataResponse(cliprdr, &response);
free(data);
return rc;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT
android_cliprdr_server_format_data_response(CliprdrClientContext* cliprdr,
const CLIPRDR_FORMAT_DATA_RESPONSE* formatDataResponse)
{
CLIPRDR_FORMAT* format = nullptr;
if (!cliprdr || !formatDataResponse)
return ERROR_INVALID_PARAMETER;
androidContext* afc = (androidContext*)cliprdr->custom;
if (!afc)
return ERROR_INVALID_PARAMETER;
freerdp* instance = ((rdpContext*)afc)->instance;
if (!instance)
return ERROR_INVALID_PARAMETER;
for (UINT32 index = 0; index < afc->numServerFormats; index++)
{
if (afc->requestedFormatId == afc->serverFormats[index].formatId)
format = &(afc->serverFormats[index]);
}
if (!format)
{
(void)SetEvent(afc->clipboardRequestEvent);
return ERROR_INTERNAL_ERROR;
}
UINT32 formatId = format->formatId;
if (format->formatName)
formatId = ClipboardRegisterFormat(afc->clipboard, format->formatName);
uint32_t size = formatDataResponse->common.dataLen;
if (!ClipboardSetData(afc->clipboard, formatId, formatDataResponse->requestedFormatData, size))
return ERROR_INTERNAL_ERROR;
(void)SetEvent(afc->clipboardRequestEvent);
if ((formatId == CF_TEXT) || (formatId == CF_UNICODETEXT))
{
JNIEnv* env = nullptr;
formatId = ClipboardRegisterFormat(afc->clipboard, "text/plain");
char* data = (char*)ClipboardGetData(afc->clipboard, formatId, &size);
jboolean attached = jni_attach_thread(&env);
size = strnlen(data, size);
jstring jdata = jniNewStringUTF(env, data, size);
freerdp_callback("OnRemoteClipboardChanged", "(JLjava/lang/String;)V", (jlong)instance,
jdata);
(*env)->DeleteLocalRef(env, jdata);
if (attached == JNI_TRUE)
jni_detach_thread();
}
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_server_file_contents_request(
CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_REQUEST* fileContentsRequest)
{
if (!cliprdr || !fileContentsRequest)
return ERROR_INVALID_PARAMETER;
return CHANNEL_RC_OK;
}
/**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT android_cliprdr_server_file_contents_response(
CliprdrClientContext* cliprdr, const CLIPRDR_FILE_CONTENTS_RESPONSE* fileContentsResponse)
{
if (!cliprdr || !fileContentsResponse)
return ERROR_INVALID_PARAMETER;
return CHANNEL_RC_OK;
}
BOOL android_cliprdr_init(androidContext* afc, CliprdrClientContext* cliprdr)
{
wClipboard* clipboard;
HANDLE hevent;
if (!afc || !cliprdr)
return FALSE;
if (!(hevent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
return FALSE;
if (!(clipboard = ClipboardCreate()))
{
(void)CloseHandle(hevent);
return FALSE;
}
afc->cliprdr = cliprdr;
afc->clipboard = clipboard;
afc->clipboardRequestEvent = hevent;
cliprdr->custom = (void*)afc;
cliprdr->MonitorReady = android_cliprdr_monitor_ready;
cliprdr->ServerCapabilities = android_cliprdr_server_capabilities;
cliprdr->ServerFormatList = android_cliprdr_server_format_list;
cliprdr->ServerFormatListResponse = android_cliprdr_server_format_list_response;
cliprdr->ServerLockClipboardData = android_cliprdr_server_lock_clipboard_data;
cliprdr->ServerUnlockClipboardData = android_cliprdr_server_unlock_clipboard_data;
cliprdr->ServerFormatDataRequest = android_cliprdr_server_format_data_request;
cliprdr->ServerFormatDataResponse = android_cliprdr_server_format_data_response;
cliprdr->ServerFileContentsRequest = android_cliprdr_server_file_contents_request;
cliprdr->ServerFileContentsResponse = android_cliprdr_server_file_contents_response;
return TRUE;
}
BOOL android_cliprdr_uninit(androidContext* afc, CliprdrClientContext* cliprdr)
{
if (!afc || !cliprdr)
return FALSE;
cliprdr->custom = nullptr;
afc->cliprdr = nullptr;
ClipboardDestroy(afc->clipboard);
(void)CloseHandle(afc->clipboardRequestEvent);
return TRUE;
}

View File

@@ -0,0 +1,33 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Clipboard Redirection
*
* Copyright 2013 Felix Long
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CLIENT_ANDROID_CLIPRDR_H
#define FREERDP_CLIENT_ANDROID_CLIPRDR_H
#include <freerdp/client/cliprdr.h>
#include <freerdp/api.h>
#include "android_freerdp.h"
FREERDP_LOCAL UINT android_cliprdr_send_client_format_list(CliprdrClientContext* cliprdr);
FREERDP_LOCAL BOOL android_cliprdr_init(androidContext* afc, CliprdrClientContext* cliprdr);
FREERDP_LOCAL BOOL android_cliprdr_uninit(androidContext* afc, CliprdrClientContext* cliprdr);
#endif /* FREERDP_CLIENT_ANDROID_CLIPRDR_H */

View File

@@ -0,0 +1,404 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Event System
*
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <freerdp/freerdp.h>
#include <freerdp/log.h>
#define TAG CLIENT_TAG("android")
#include "android_freerdp.h"
#include "android_cliprdr.h"
BOOL android_push_event(freerdp* inst, ANDROID_EVENT* event)
{
androidContext* aCtx = (androidContext*)inst->context;
if (aCtx->event_queue->count >= aCtx->event_queue->size)
{
size_t new_size = aCtx->event_queue->size;
do
{
if (new_size >= SIZE_MAX - 128ull)
return FALSE;
new_size += 128ull;
} while (new_size <= aCtx->event_queue->count);
void* new_events =
realloc((void*)aCtx->event_queue->events, sizeof(ANDROID_EVENT*) * new_size);
if (!new_events)
return FALSE;
aCtx->event_queue->events = new_events;
aCtx->event_queue->size = new_size;
}
aCtx->event_queue->events[(aCtx->event_queue->count)++] = event;
return SetEvent(aCtx->event_queue->isSet);
}
static ANDROID_EVENT* android_peek_event(ANDROID_EVENT_QUEUE* queue)
{
ANDROID_EVENT* event;
if (queue->count < 1)
return nullptr;
event = queue->events[0];
return event;
}
static ANDROID_EVENT* android_pop_event(ANDROID_EVENT_QUEUE* queue)
{
ANDROID_EVENT* event;
if (queue->count < 1)
return nullptr;
event = queue->events[0];
(queue->count)--;
for (size_t i = 0; i < queue->count; i++)
{
queue->events[i] = queue->events[i + 1];
}
return event;
}
static BOOL android_process_event(ANDROID_EVENT_QUEUE* queue, freerdp* inst)
{
rdpContext* context;
WINPR_ASSERT(queue);
WINPR_ASSERT(inst);
context = inst->context;
WINPR_ASSERT(context);
while (android_peek_event(queue))
{
BOOL rc = FALSE;
androidContext* afc = (androidContext*)context;
ANDROID_EVENT* event = android_pop_event(queue);
WINPR_ASSERT(event);
switch (event->type)
{
case EVENT_TYPE_KEY:
{
ANDROID_EVENT_KEY* key_event = (ANDROID_EVENT_KEY*)event;
rc = freerdp_input_send_keyboard_event(context->input, key_event->flags,
key_event->scancode);
}
break;
case EVENT_TYPE_KEY_UNICODE:
{
ANDROID_EVENT_KEY* key_event = (ANDROID_EVENT_KEY*)event;
rc = freerdp_input_send_unicode_keyboard_event(context->input, key_event->flags,
key_event->scancode);
}
break;
case EVENT_TYPE_CURSOR:
{
ANDROID_EVENT_CURSOR* cursor_event = (ANDROID_EVENT_CURSOR*)event;
rc = freerdp_input_send_mouse_event(context->input, cursor_event->flags,
cursor_event->x, cursor_event->y);
}
break;
case EVENT_TYPE_CLIPBOARD:
{
ANDROID_EVENT_CLIPBOARD* clipboard_event = (ANDROID_EVENT_CLIPBOARD*)event;
UINT32 formatId = ClipboardRegisterFormat(afc->clipboard, "text/plain");
UINT32 size = clipboard_event->data_length;
if (size)
ClipboardSetData(afc->clipboard, formatId, clipboard_event->data, size);
else
ClipboardEmpty(afc->clipboard);
rc = (android_cliprdr_send_client_format_list(afc->cliprdr) == CHANNEL_RC_OK);
}
break;
case EVENT_TYPE_DISCONNECT:
default:
break;
}
android_event_free(event);
if (!rc)
return FALSE;
}
return TRUE;
}
HANDLE android_get_handle(freerdp* inst)
{
androidContext* aCtx;
if (!inst || !inst->context)
return nullptr;
aCtx = (androidContext*)inst->context;
if (!aCtx->event_queue || !aCtx->event_queue->isSet)
return nullptr;
return aCtx->event_queue->isSet;
}
BOOL android_check_handle(freerdp* inst)
{
androidContext* aCtx;
if (!inst || !inst->context)
return FALSE;
aCtx = (androidContext*)inst->context;
if (!aCtx->event_queue || !aCtx->event_queue->isSet)
return FALSE;
if (WaitForSingleObject(aCtx->event_queue->isSet, 0) == WAIT_OBJECT_0)
{
if (!ResetEvent(aCtx->event_queue->isSet))
return FALSE;
if (!android_process_event(aCtx->event_queue, inst))
return FALSE;
}
return TRUE;
}
ANDROID_EVENT_KEY* android_event_key_new(int flags, UINT16 scancode)
{
ANDROID_EVENT_KEY* event = (ANDROID_EVENT_KEY*)calloc(1, sizeof(ANDROID_EVENT_KEY));
if (!event)
return nullptr;
event->type = EVENT_TYPE_KEY;
event->flags = flags;
event->scancode = scancode;
return event;
}
static void android_event_key_free(ANDROID_EVENT_KEY* event)
{
free(event);
}
ANDROID_EVENT_KEY* android_event_unicodekey_new(UINT16 flags, UINT16 key)
{
ANDROID_EVENT_KEY* event;
event = (ANDROID_EVENT_KEY*)calloc(1, sizeof(ANDROID_EVENT_KEY));
if (!event)
return nullptr;
event->type = EVENT_TYPE_KEY_UNICODE;
event->flags = flags;
event->scancode = key;
return event;
}
static void android_event_unicodekey_free(ANDROID_EVENT_KEY* event)
{
free(event);
}
ANDROID_EVENT_CURSOR* android_event_cursor_new(UINT16 flags, UINT16 x, UINT16 y)
{
ANDROID_EVENT_CURSOR* event;
event = (ANDROID_EVENT_CURSOR*)calloc(1, sizeof(ANDROID_EVENT_CURSOR));
if (!event)
return nullptr;
event->type = EVENT_TYPE_CURSOR;
event->x = x;
event->y = y;
event->flags = flags;
return event;
}
static void android_event_cursor_free(ANDROID_EVENT_CURSOR* event)
{
free(event);
}
ANDROID_EVENT* android_event_disconnect_new(void)
{
ANDROID_EVENT* event;
event = (ANDROID_EVENT*)calloc(1, sizeof(ANDROID_EVENT));
if (!event)
return nullptr;
event->type = EVENT_TYPE_DISCONNECT;
return event;
}
static void android_event_disconnect_free(ANDROID_EVENT* event)
{
free(event);
}
ANDROID_EVENT_CLIPBOARD* android_event_clipboard_new(const void* data, size_t data_length)
{
ANDROID_EVENT_CLIPBOARD* event;
event = (ANDROID_EVENT_CLIPBOARD*)calloc(1, sizeof(ANDROID_EVENT_CLIPBOARD));
if (!event)
return nullptr;
event->type = EVENT_TYPE_CLIPBOARD;
if (data)
{
event->data = calloc(data_length + 1, sizeof(char));
if (!event->data)
{
free(event);
return nullptr;
}
memcpy(event->data, data, data_length);
event->data_length = data_length + 1;
}
return event;
}
static void android_event_clipboard_free(ANDROID_EVENT_CLIPBOARD* event)
{
if (event)
{
free(event->data);
free(event);
}
}
BOOL android_event_queue_init(freerdp* inst)
{
androidContext* aCtx = (androidContext*)inst->context;
ANDROID_EVENT_QUEUE* queue;
queue = (ANDROID_EVENT_QUEUE*)calloc(1, sizeof(ANDROID_EVENT_QUEUE));
if (!queue)
{
WLog_ERR(TAG, "android_event_queue_init: memory allocation failed");
return FALSE;
}
queue->size = 16;
queue->count = 0;
queue->isSet = CreateEventA(nullptr, TRUE, FALSE, nullptr);
if (!queue->isSet)
{
free(queue);
return FALSE;
}
queue->events = (ANDROID_EVENT**)calloc(queue->size, sizeof(ANDROID_EVENT*));
if (!queue->events)
{
WLog_ERR(TAG, "android_event_queue_init: memory allocation failed");
(void)CloseHandle(queue->isSet);
free(queue);
return FALSE;
}
aCtx->event_queue = queue;
return TRUE;
}
void android_event_queue_uninit(freerdp* inst)
{
androidContext* aCtx;
ANDROID_EVENT_QUEUE* queue;
if (!inst || !inst->context)
return;
aCtx = (androidContext*)inst->context;
queue = aCtx->event_queue;
if (queue)
{
if (queue->isSet)
{
(void)CloseHandle(queue->isSet);
queue->isSet = nullptr;
}
if (queue->events)
{
free(queue->events);
queue->events = nullptr;
queue->size = 0;
queue->count = 0;
}
free(queue);
}
}
void android_event_free(ANDROID_EVENT* event)
{
if (!event)
return;
switch (event->type)
{
case EVENT_TYPE_KEY:
android_event_key_free((ANDROID_EVENT_KEY*)event);
break;
case EVENT_TYPE_KEY_UNICODE:
android_event_unicodekey_free((ANDROID_EVENT_KEY*)event);
break;
case EVENT_TYPE_CURSOR:
android_event_cursor_free((ANDROID_EVENT_CURSOR*)event);
break;
case EVENT_TYPE_DISCONNECT:
android_event_disconnect_free((ANDROID_EVENT*)event);
break;
case EVENT_TYPE_CLIPBOARD:
android_event_clipboard_free((ANDROID_EVENT_CLIPBOARD*)event);
break;
default:
break;
}
}

View File

@@ -0,0 +1,75 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Event System
*
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef FREERDP_CLIENT_ANDROID_EVENT_H
#define FREERDP_CLIENT_ANDROID_EVENT_H
#include <freerdp/freerdp.h>
#include <freerdp/api.h>
#define EVENT_TYPE_KEY 1
#define EVENT_TYPE_CURSOR 2
#define EVENT_TYPE_DISCONNECT 3
#define EVENT_TYPE_KEY_UNICODE 4
#define EVENT_TYPE_CLIPBOARD 5
typedef struct
{
int type;
} ANDROID_EVENT;
typedef struct
{
int type;
int flags;
UINT16 scancode;
} ANDROID_EVENT_KEY;
typedef struct
{
int type;
UINT16 flags;
UINT16 x;
UINT16 y;
} ANDROID_EVENT_CURSOR;
typedef struct
{
int type;
void* data;
int data_length;
} ANDROID_EVENT_CLIPBOARD;
typedef struct
{
int size;
int count;
HANDLE isSet;
ANDROID_EVENT** events;
} ANDROID_EVENT_QUEUE;
FREERDP_LOCAL BOOL android_push_event(freerdp* inst, ANDROID_EVENT* event);
FREERDP_LOCAL HANDLE android_get_handle(freerdp* inst);
FREERDP_LOCAL BOOL android_check_handle(freerdp* inst);
FREERDP_LOCAL ANDROID_EVENT_KEY* android_event_key_new(int flags, UINT16 scancode);
FREERDP_LOCAL ANDROID_EVENT_KEY* android_event_unicodekey_new(UINT16 flags, UINT16 key);
FREERDP_LOCAL ANDROID_EVENT_CURSOR* android_event_cursor_new(UINT16 flags, UINT16 x, UINT16 y);
FREERDP_LOCAL ANDROID_EVENT* android_event_disconnect_new(void);
FREERDP_LOCAL ANDROID_EVENT_CLIPBOARD* android_event_clipboard_new(const void* data,
size_t data_length);
FREERDP_LOCAL void android_event_free(ANDROID_EVENT* event);
FREERDP_LOCAL BOOL android_event_queue_init(freerdp* inst);
FREERDP_LOCAL void android_event_queue_uninit(freerdp* inst);
#endif /* FREERDP_CLIENT_ANDROID_EVENT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
/*
Android JNI Client Layer
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
#ifndef FREERDP_CLIENT_ANDROID_FREERDP_H
#define FREERDP_CLIENT_ANDROID_FREERDP_H
#include <jni.h>
#include <winpr/crt.h>
#include <winpr/clipboard.h>
#include <freerdp/freerdp.h>
#include <freerdp/client/cliprdr.h>
#include "android_event.h"
typedef struct
{
rdpClientContext common;
ANDROID_EVENT_QUEUE* event_queue;
HANDLE thread;
BOOL is_connected;
BOOL clipboardSync;
wClipboard* clipboard;
UINT32 numServerFormats;
UINT32 requestedFormatId;
HANDLE clipboardRequestEvent;
CLIPRDR_FORMAT* serverFormats;
CliprdrClientContext* cliprdr;
UINT32 clipboardCapabilities;
} androidContext;
#endif /* FREERDP_CLIENT_ANDROID_FREERDP_H */

View File

@@ -0,0 +1,27 @@
/*
FreeRDP: A Remote Desktop Protocol client.
Android FreeRDP JNI Definitions
Copyright 2010 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef FREERDP_CLIENT_ANDROID_FREERDP_JNI_H
#define FREERDP_CLIENT_ANDROID_FREERDP_JNI_H
#define JAVA_LIBFREERDP_CLASS "com/freerdp/freerdpcore/services/LibFreeRDP"
#define JAVA_CONTEXT_CLASS "android/content/Context"
#define JAVA_FILE_CLASS "java/io/File"
#endif /* FREERDP_CLIENT_ANDROID_FREERDP_JNI_H */

View File

@@ -0,0 +1,226 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android JNI Callback Helpers
*
* Copyright 2011-2013 Thincast Technologies GmbH, Author: Martin Fleisz
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include "android_jni_callback.h"
#include "android_freerdp_jni.h"
#include <freerdp/log.h>
#define TAG CLIENT_TAG("android.callback")
static JavaVM* jVM;
static jobject jLibFreeRDPObject;
static const char* jLibFreeRDPPath = JAVA_LIBFREERDP_CLASS;
static void jni_load_class(JNIEnv* env, const char* path, jobject* objptr)
{
jclass class;
jmethodID method;
jobject object;
WLog_DBG(TAG, "jni_load_class: %s", path);
class = (*env)->FindClass(env, path);
if (!class)
{
WLog_ERR(TAG, "jni_load_class: failed to find class %s", path);
goto finish;
}
method = (*env)->GetMethodID(env, class, "<init>", "()V");
if (!method)
{
WLog_ERR(TAG, "jni_load_class: failed to find class constructor of %s", path);
goto finish;
}
object = (*env)->NewObject(env, class, method);
if (!object)
{
WLog_ERR(TAG, "jni_load_class: failed create new object of %s", path);
goto finish;
}
(*objptr) = (*env)->NewGlobalRef(env, object);
finish:
while (0)
;
}
jint init_callback_environment(JavaVM* vm, JNIEnv* env)
{
jVM = vm;
jni_load_class(env, jLibFreeRDPPath, &jLibFreeRDPObject);
return JNI_VERSION_1_6;
}
/* attach current thread to jvm */
jboolean jni_attach_thread(JNIEnv** env)
{
if ((*jVM)->GetEnv(jVM, (void**)env, JNI_VERSION_1_4) != JNI_OK)
{
WLog_DBG(TAG, "android_java_callback: attaching current thread");
(*jVM)->AttachCurrentThread(jVM, env, nullptr);
if ((*jVM)->GetEnv(jVM, (void**)env, JNI_VERSION_1_4) != JNI_OK)
{
WLog_ERR(TAG, "android_java_callback: failed to obtain current JNI environment");
}
return JNI_TRUE;
}
return JNI_FALSE;
}
/* attach current thread to JVM */
void jni_detach_thread()
{
(*jVM)->DetachCurrentThread(jVM);
}
/* callback with void result */
static void java_callback_void(jobject obj, const char* callback, const char* signature,
va_list args)
{
jclass jObjClass;
jmethodID jCallback;
jboolean attached;
JNIEnv* env;
WLog_DBG(TAG, "java_callback: %s (%s)", callback, signature);
attached = jni_attach_thread(&env);
jObjClass = (*env)->GetObjectClass(env, obj);
if (!jObjClass)
{
WLog_ERR(TAG, "android_java_callback: failed to get class reference");
goto finish;
}
jCallback = (*env)->GetStaticMethodID(env, jObjClass, callback, signature);
if (!jCallback)
{
WLog_ERR(TAG, "android_java_callback: failed to get method id");
goto finish;
}
(*env)->CallStaticVoidMethodV(env, jObjClass, jCallback, args);
finish:
if (attached == JNI_TRUE)
jni_detach_thread();
}
/* callback with bool result */
static jboolean java_callback_bool(jobject obj, const char* callback, const char* signature,
va_list args)
{
jclass jObjClass;
jmethodID jCallback;
jboolean attached;
jboolean res = JNI_FALSE;
JNIEnv* env;
WLog_DBG(TAG, "java_callback: %s (%s)", callback, signature);
attached = jni_attach_thread(&env);
jObjClass = (*env)->GetObjectClass(env, obj);
if (!jObjClass)
{
WLog_ERR(TAG, "android_java_callback: failed to get class reference");
goto finish;
}
jCallback = (*env)->GetStaticMethodID(env, jObjClass, callback, signature);
if (!jCallback)
{
WLog_ERR(TAG, "android_java_callback: failed to get method id");
goto finish;
}
res = (*env)->CallStaticBooleanMethodV(env, jObjClass, jCallback, args);
finish:
if (attached == JNI_TRUE)
jni_detach_thread();
return res;
}
/* callback with int result */
static jint java_callback_int(jobject obj, const char* callback, const char* signature,
va_list args)
{
jclass jObjClass;
jmethodID jCallback;
jboolean attached;
jint res = -1;
JNIEnv* env;
WLog_DBG(TAG, "java_callback: %s (%s)", callback, signature);
attached = jni_attach_thread(&env);
jObjClass = (*env)->GetObjectClass(env, obj);
if (!jObjClass)
{
WLog_ERR(TAG, "android_java_callback: failed to get class reference");
goto finish;
}
jCallback = (*env)->GetStaticMethodID(env, jObjClass, callback, signature);
if (!jCallback)
{
WLog_ERR(TAG, "android_java_callback: failed to get method id");
goto finish;
}
res = (*env)->CallStaticIntMethodV(env, jObjClass, jCallback, args);
finish:
if (attached == JNI_TRUE)
jni_detach_thread();
return res;
}
/* callback to freerdp class */
void freerdp_callback(const char* callback, const char* signature, ...)
{
va_list vl = WINPR_C_ARRAY_INIT;
va_start(vl, signature);
java_callback_void(jLibFreeRDPObject, callback, signature, vl);
va_end(vl);
}
jboolean freerdp_callback_bool_result(const char* callback, const char* signature, ...)
{
va_list vl = WINPR_C_ARRAY_INIT;
va_start(vl, signature);
jboolean res = java_callback_bool(jLibFreeRDPObject, callback, signature, vl);
va_end(vl);
return res;
}
jint freerdp_callback_int_result(const char* callback, const char* signature, ...)
{
va_list vl = WINPR_C_ARRAY_INIT;
va_start(vl, signature);
jint res = java_callback_int(jLibFreeRDPObject, callback, signature, vl);
va_end(vl);
return res;
}

View File

@@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android JNI Callback Helpers
*
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2011-2013 Thincast Technologies GmbH, Author: Martin Fleisz
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef FREERDP_CLIENT_ANDROID_JNI_CALLBACK_H
#define FREERDP_CLIENT_ANDROID_JNI_CALLBACK_H
#include <jni.h>
#include <stdarg.h>
#include <freerdp/api.h>
FREERDP_LOCAL jint init_callback_environment(JavaVM* vm, JNIEnv* env);
FREERDP_LOCAL jboolean jni_attach_thread(JNIEnv** env);
FREERDP_LOCAL void jni_detach_thread(void);
FREERDP_LOCAL void freerdp_callback(const char* callback, const char* signature, ...);
FREERDP_LOCAL jboolean freerdp_callback_bool_result(const char* callback, const char* signature,
...);
FREERDP_LOCAL jint freerdp_callback_int_result(const char* callback, const char* signature, ...);
#endif /* FREERDP_CLIENT_ANDROID_JNI_CALLBACK_H */

View File

@@ -0,0 +1,191 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Event System
*
* Copyright 2013 Felix Long
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "android_jni_utils.h"
#include <locale.h>
#include <freerdp/channels/channels.h>
#include <freerdp/log.h>
#include "android_jni_callback.h"
#define TAG CLIENT_TAG("android.utils")
JavaVM* g_JavaVm;
JavaVM* getJavaVM()
{
return g_JavaVm;
}
JNIEnv* getJNIEnv()
{
JNIEnv* env = nullptr;
if ((*g_JavaVm)->GetEnv(g_JavaVm, (void**)&env, JNI_VERSION_1_4) != JNI_OK)
{
WLog_FATAL(TAG, "Failed to obtain JNIEnv");
return nullptr;
}
return env;
}
jobject create_string_builder(JNIEnv* env, char* initialStr)
{
jclass cls;
jmethodID methodId;
jobject obj;
// get class
cls = (*env)->FindClass(env, "java/lang/StringBuilder");
if (!cls)
return nullptr;
if (initialStr)
{
// get method id for constructor
methodId = (*env)->GetMethodID(env, cls, "<init>", "(Ljava/lang/String;)V");
if (!methodId)
return nullptr;
// create string that holds our initial string
jstring jstr = (*env)->NewStringUTF(env, initialStr);
// construct new StringBuilder
obj = (*env)->NewObject(env, cls, methodId, jstr);
}
else
{
// get method id for constructor
methodId = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (!methodId)
return nullptr;
// construct new StringBuilder
obj = (*env)->NewObject(env, cls, methodId);
}
return obj;
}
char* get_string_from_string_builder(JNIEnv* env, jobject strBuilder)
{
jclass cls;
jmethodID methodId;
jstring strObj;
const char* native_str;
char* result;
// get class
cls = (*env)->FindClass(env, "java/lang/StringBuilder");
if (!cls)
return nullptr;
// get method id for constructor
methodId = (*env)->GetMethodID(env, cls, "toString", "()Ljava/lang/String;");
if (!methodId)
return nullptr;
// get jstring representation of our buffer
strObj = (*env)->CallObjectMethod(env, strBuilder, methodId);
// read string
native_str = (*env)->GetStringUTFChars(env, strObj, nullptr);
if (!native_str)
return nullptr;
result = _strdup(native_str);
(*env)->ReleaseStringUTFChars(env, strObj, native_str);
return result;
}
jstring jniNewStringUTF(JNIEnv* env, const char* in, int len)
{
jstring out = nullptr;
jchar* unicode = nullptr;
jint result_size = 0;
unsigned char* utf8 = (unsigned char*)in;
if (!in)
{
return nullptr;
}
if (len < 0)
len = strlen(in);
unicode = (jchar*)malloc(sizeof(jchar) * (len + 1));
if (!unicode)
{
return nullptr;
}
for (jint i = 0; i < len; i++)
{
unsigned char one = utf8[i];
switch (one >> 4)
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
unicode[result_size++] = one;
break;
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
// case 0x0f:
/*
* Bit pattern 10xx or 1111, which are illegal start bytes.
* Note: 1111 is valid for normal UTF-8, but not the
* modified UTF-8 used here.
*/
break;
case 0x0f:
case 0x0e:
// Bit pattern 111x, so there are two additional bytes.
if (i < (len - 2))
{
unsigned char two = utf8[i + 1];
unsigned char three = utf8[i + 2];
if ((two & 0xc0) == 0x80 && (three & 0xc0) == 0x80)
{
i += 2;
unicode[result_size++] =
((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
}
}
break;
case 0x0c:
case 0x0d:
// Bit pattern 110x, so there is one additional byte.
if (i < (len - 1))
{
unsigned char two = utf8[i + 1];
if ((two & 0xc0) == 0x80)
{
i += 1;
unicode[result_size++] = ((one & 0x1f) << 6) | (two & 0x3f);
}
}
break;
}
}
out = (*env)->NewString(env, unicode, result_size);
free(unicode);
return out;
}

View File

@@ -0,0 +1,36 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Android Event System
*
* Copyright 2013 Felix Long
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#ifndef FREERDP_CLIENT_ANDROID_JNI_UTILS_H
#define FREERDP_CLIENT_ANDROID_JNI_UTILS_H
#include <jni.h>
#include <freerdp/api.h>
#ifdef __cplusplus
extern "C"
{
#endif
FREERDP_LOCAL JNIEnv* getJNIEnv();
FREERDP_LOCAL JavaVM* getJavaVM();
FREERDP_LOCAL char* get_string_from_string_builder(JNIEnv* env, jobject strBuilder);
FREERDP_LOCAL jobject create_string_builder(JNIEnv* env, char* initialStr);
FREERDP_LOCAL jstring jniNewStringUTF(JNIEnv* env, const char* in, int len);
FREERDP_LOCAL extern JavaVM* g_JavaVm;
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CLIENT_ANDROID_JNI_UTILS_H */

View File

@@ -0,0 +1,211 @@
/*
Android Main Application
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.application;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.util.Log;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.presentation.ApplicationSettingsActivity;
import com.freerdp.freerdpcore.services.BookmarkDB;
import com.freerdp.freerdpcore.services.HistoryDB;
import com.freerdp.freerdpcore.services.LibFreeRDP;
import com.freerdp.freerdpcore.services.ManualBookmarkGateway;
import com.freerdp.freerdpcore.services.QuickConnectHistoryGateway;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class GlobalApp extends Application implements LibFreeRDP.EventListener
{
// event notification defines
public static final String EVENT_TYPE = "EVENT_TYPE";
public static final String EVENT_PARAM = "EVENT_PARAM";
public static final String EVENT_STATUS = "EVENT_STATUS";
public static final String EVENT_ERROR = "EVENT_ERROR";
public static final String ACTION_EVENT_FREERDP = "com.freerdp.freerdp.event.freerdp";
public static final int FREERDP_EVENT_CONNECTION_SUCCESS = 1;
public static final int FREERDP_EVENT_CONNECTION_FAILURE = 2;
public static final int FREERDP_EVENT_DISCONNECTED = 3;
private static final String TAG = "GlobalApp";
public static boolean ConnectedTo3G = false;
private static Map<Long, SessionState> sessionMap;
private static BookmarkDB bookmarkDB;
private static ManualBookmarkGateway manualBookmarkGateway;
private static HistoryDB historyDB;
private static QuickConnectHistoryGateway quickConnectHistoryGateway;
// timer for disconnecting sessions after the screen was turned off
private static Timer disconnectTimer = null;
public static ManualBookmarkGateway getManualBookmarkGateway()
{
return manualBookmarkGateway;
}
public static QuickConnectHistoryGateway getQuickConnectHistoryGateway()
{
return quickConnectHistoryGateway;
}
// Disconnect handling for Screen on/off events
public void startDisconnectTimer()
{
final int timeoutMinutes = ApplicationSettingsActivity.getDisconnectTimeout(this);
if (timeoutMinutes > 0)
{
// start disconnect timeout...
disconnectTimer = new Timer();
disconnectTimer.schedule(new DisconnectTask(), (long)timeoutMinutes * 60 * 1000);
}
}
static public void cancelDisconnectTimer()
{
// cancel any pending timer events
if (disconnectTimer != null)
{
disconnectTimer.cancel();
disconnectTimer.purge();
disconnectTimer = null;
}
}
// RDP session handling
static public SessionState createSession(BookmarkBase bookmark, Context context)
{
SessionState session = new SessionState(LibFreeRDP.newInstance(context), bookmark);
sessionMap.put(session.getInstance(), session);
return session;
}
static public SessionState createSession(Uri openUri, Context context)
{
SessionState session = new SessionState(LibFreeRDP.newInstance(context), openUri);
sessionMap.put(session.getInstance(), session);
return session;
}
static public SessionState getSession(long instance)
{
return sessionMap.get(instance);
}
static public Collection<SessionState> getSessions()
{
// return a copy of the session items
return new ArrayList<>(sessionMap.values());
}
static public void freeSession(long instance)
{
if (GlobalApp.sessionMap.containsKey(instance))
{
GlobalApp.sessionMap.remove(instance);
LibFreeRDP.freeInstance(instance);
}
}
@Override public void onCreate()
{
super.onCreate();
/* Initialize preferences. */
ApplicationSettingsActivity.get(this);
sessionMap = Collections.synchronizedMap(new HashMap<Long, SessionState>());
LibFreeRDP.setEventListener(this);
bookmarkDB = new BookmarkDB(this);
manualBookmarkGateway = new ManualBookmarkGateway(bookmarkDB);
historyDB = new HistoryDB(this);
quickConnectHistoryGateway = new QuickConnectHistoryGateway(historyDB);
ConnectedTo3G = NetworkStateReceiver.isConnectedTo3G(this);
// init screen receiver here (this can't be declared in AndroidManifest - refer to:
// http://thinkandroid.wordpress.com/2010/01/24/handling-screen-off-and-screen-on-intents/
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(new ScreenReceiver(), filter, RECEIVER_EXPORTED);
}
// helper to send FreeRDP notifications
private void sendRDPNotification(int type, long param)
{
// send broadcast
Intent intent = new Intent(ACTION_EVENT_FREERDP);
intent.putExtra(EVENT_TYPE, type);
intent.putExtra(EVENT_PARAM, param);
sendBroadcast(intent);
}
@Override public void OnPreConnect(long instance)
{
Log.v(TAG, "OnPreConnect");
}
// //////////////////////////////////////////////////////////////////////
// Implementation of LibFreeRDP.EventListener
public void OnConnectionSuccess(long instance)
{
Log.v(TAG, "OnConnectionSuccess");
sendRDPNotification(FREERDP_EVENT_CONNECTION_SUCCESS, instance);
}
public void OnConnectionFailure(long instance)
{
Log.v(TAG, "OnConnectionFailure");
// send notification to session activity
sendRDPNotification(FREERDP_EVENT_CONNECTION_FAILURE, instance);
}
public void OnDisconnecting(long instance)
{
Log.v(TAG, "OnDisconnecting");
}
public void OnDisconnected(long instance)
{
Log.v(TAG, "OnDisconnected");
sendRDPNotification(FREERDP_EVENT_DISCONNECTED, instance);
}
// TimerTask for disconnecting sessions after screen was turned off
private static class DisconnectTask extends TimerTask
{
@Override public void run()
{
Log.v("DisconnectTask", "Doing action");
// disconnect any running rdp session
Collection<SessionState> sessions = GlobalApp.getSessions();
for (SessionState session : sessions)
{
LibFreeRDP.disconnect(session.getInstance());
}
}
}
}

View File

@@ -0,0 +1,68 @@
/*
Network State Receiver
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import androidx.annotation.NonNull;
public class NetworkStateReceiver extends BroadcastReceiver
{
public static boolean isConnectedTo3G(Context context)
{
ConnectivityManager connectivity =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = connectivity.getActiveNetworkInfo();
// no connection or background data disabled
if (info == null || !info.isConnected())
return false;
return (info.getType() != ConnectivityManager.TYPE_WIFI &&
info.getType() != ConnectivityManager.TYPE_WIMAX);
}
@Override public void onReceive(@NonNull Context context, @NonNull Intent intent)
{
String action = intent.getAction();
if (!action.equals("android.net.conn.CONNECTIVITY_CHANGE"))
{
return;
}
// check if we are connected via 3g or wlan
if (intent.getExtras() != null)
{
NetworkInfo info =
(NetworkInfo)intent.getExtras().get(ConnectivityManager.EXTRA_NETWORK_INFO);
// are we connected at all?
if (info != null)
{
if (info.isConnected())
{
// see if we are connected through 3G or WiFi
Log.d("app", "Connected via type " + info.getTypeName());
GlobalApp.ConnectedTo3G = (info.getType() != ConnectivityManager.TYPE_WIFI &&
info.getType() != ConnectivityManager.TYPE_WIMAX);
}
Log.v("NetworkState", info.toString());
}
}
}
}

View File

@@ -0,0 +1,30 @@
/*
Helper class to receive notifications when the screen is turned on/off
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class ScreenReceiver extends BroadcastReceiver
{
@Override public void onReceive(Context context, Intent intent)
{
GlobalApp app = (GlobalApp)context.getApplicationContext();
Log.v("ScreenReceiver", "Received action: " + intent.getAction());
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF))
app.startDisconnectTimer();
else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON))
GlobalApp.cancelDisconnectTimer();
}
}

View File

@@ -0,0 +1,129 @@
/*
Session State class
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.application;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.services.LibFreeRDP;
public class SessionState implements Parcelable
{
public static final Parcelable.Creator<SessionState> CREATOR =
new Parcelable.Creator<SessionState>() {
public SessionState createFromParcel(Parcel in)
{
return new SessionState(in);
}
@Override public SessionState[] newArray(int size)
{
return new SessionState[size];
}
};
private final long instance;
private final BookmarkBase bookmark;
private final Uri openUri;
private BitmapDrawable surface;
private LibFreeRDP.UIEventListener uiEventListener;
public SessionState(Parcel parcel)
{
instance = parcel.readLong();
bookmark = parcel.readParcelable(null);
openUri = parcel.readParcelable(null);
Bitmap bitmap = parcel.readParcelable(null);
surface = new BitmapDrawable(bitmap);
}
public SessionState(long instance, BookmarkBase bookmark)
{
this.instance = instance;
this.bookmark = bookmark;
this.openUri = null;
this.uiEventListener = null;
}
public SessionState(long instance, Uri openUri)
{
this.instance = instance;
this.bookmark = null;
this.openUri = openUri;
this.uiEventListener = null;
}
public void connect(Context context)
{
if (bookmark != null)
{
LibFreeRDP.setConnectionInfo(context, instance, bookmark);
}
else
{
LibFreeRDP.setConnectionInfo(context, instance, openUri);
}
LibFreeRDP.connect(instance);
}
public long getInstance()
{
return instance;
}
public BookmarkBase getBookmark()
{
return bookmark;
}
public Uri getOpenUri()
{
return openUri;
}
public LibFreeRDP.UIEventListener getUIEventListener()
{
return uiEventListener;
}
public void setUIEventListener(LibFreeRDP.UIEventListener uiEventListener)
{
this.uiEventListener = uiEventListener;
}
public BitmapDrawable getSurface()
{
return surface;
}
public void setSurface(BitmapDrawable surface)
{
this.surface = surface;
}
@Override public int describeContents()
{
return 0;
}
@Override public void writeToParcel(Parcel out, int flags)
{
out.writeLong(instance);
out.writeParcelable(bookmark, flags);
out.writeParcelable(openUri, flags);
out.writeParcelable(surface.getBitmap(), flags);
}
}

View File

@@ -0,0 +1,85 @@
/*
A RDP connection reference. References can use bookmark ids or hostnames to connect to a RDP
server.
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.domain;
public class ConnectionReference
{
public static final String PATH_MANUAL_BOOKMARK_ID = "MBMID/";
public static final String PATH_HOSTNAME = "HOST/";
public static final String PATH_PLACEHOLDER = "PLCHLD/";
public static final String PATH_FILE = "FILE/";
public static String getManualBookmarkReference(long bookmarkId)
{
return (PATH_MANUAL_BOOKMARK_ID + bookmarkId);
}
public static String getHostnameReference(String hostname)
{
return (PATH_HOSTNAME + hostname);
}
public static String getPlaceholderReference(String name)
{
return (PATH_PLACEHOLDER + name);
}
public static String getFileReference(String uri)
{
return (PATH_FILE + uri);
}
public static boolean isBookmarkReference(String refStr)
{
return refStr.startsWith(PATH_MANUAL_BOOKMARK_ID);
}
public static boolean isManualBookmarkReference(String refStr)
{
return refStr.startsWith(PATH_MANUAL_BOOKMARK_ID);
}
public static boolean isHostnameReference(String refStr)
{
return refStr.startsWith(PATH_HOSTNAME);
}
public static boolean isPlaceholderReference(String refStr)
{
return refStr.startsWith(PATH_PLACEHOLDER);
}
public static boolean isFileReference(String refStr)
{
return refStr.startsWith(PATH_FILE);
}
public static long getManualBookmarkId(String refStr)
{
return Integer.parseInt(refStr.substring(PATH_MANUAL_BOOKMARK_ID.length()));
}
public static String getHostname(String refStr)
{
return refStr.substring(PATH_HOSTNAME.length());
}
public static String getPlaceholder(String refStr)
{
return refStr.substring(PATH_PLACEHOLDER.length());
}
public static String getFile(String refStr)
{
return refStr.substring(PATH_FILE.length());
}
}

View File

@@ -0,0 +1,255 @@
/*
Manual Bookmark implementation
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.domain;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
public class ManualBookmark extends BookmarkBase
{
public static final Parcelable.Creator<ManualBookmark> CREATOR =
new Parcelable.Creator<ManualBookmark>() {
public ManualBookmark createFromParcel(Parcel in)
{
return new ManualBookmark(in);
}
@Override public ManualBookmark[] newArray(int size)
{
return new ManualBookmark[size];
}
};
private String hostname;
private int port;
private boolean enableGatewaySettings;
private GatewaySettings gatewaySettings;
public ManualBookmark(Parcel parcel)
{
super(parcel);
type = TYPE_MANUAL;
hostname = parcel.readString();
port = parcel.readInt();
enableGatewaySettings = (parcel.readInt() == 1);
gatewaySettings = parcel.readParcelable(GatewaySettings.class.getClassLoader());
}
public ManualBookmark()
{
super();
init();
}
private void init()
{
type = TYPE_MANUAL;
hostname = "";
port = 3389;
enableGatewaySettings = false;
gatewaySettings = new GatewaySettings();
}
public String getHostname()
{
return hostname;
}
public void setHostname(String hostname)
{
this.hostname = hostname;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public boolean getEnableGatewaySettings()
{
return enableGatewaySettings;
}
public void setEnableGatewaySettings(boolean enableGatewaySettings)
{
this.enableGatewaySettings = enableGatewaySettings;
}
public GatewaySettings getGatewaySettings()
{
return gatewaySettings;
}
public void setGatewaySettings(GatewaySettings gatewaySettings)
{
this.gatewaySettings = gatewaySettings;
}
@Override public int describeContents()
{
return 0;
}
@Override public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeString(hostname);
out.writeInt(port);
out.writeInt(enableGatewaySettings ? 1 : 0);
out.writeParcelable(gatewaySettings, flags);
}
@Override public void writeToSharedPreferences(SharedPreferences sharedPrefs)
{
super.writeToSharedPreferences(sharedPrefs);
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putString("bookmark.hostname", hostname);
editor.putInt("bookmark.port", port);
editor.putBoolean("bookmark.enable_gateway_settings", enableGatewaySettings);
editor.putString("bookmark.gateway_hostname", gatewaySettings.getHostname());
editor.putInt("bookmark.gateway_port", gatewaySettings.getPort());
editor.putString("bookmark.gateway_username", gatewaySettings.getUsername());
editor.putString("bookmark.gateway_password", gatewaySettings.getPassword());
editor.putString("bookmark.gateway_domain", gatewaySettings.getDomain());
editor.commit();
}
@Override public void readFromSharedPreferences(SharedPreferences sharedPrefs)
{
super.readFromSharedPreferences(sharedPrefs);
hostname = sharedPrefs.getString("bookmark.hostname", "");
port = sharedPrefs.getInt("bookmark.port", 3389);
enableGatewaySettings = sharedPrefs.getBoolean("bookmark.enable_gateway_settings", false);
gatewaySettings.setHostname(sharedPrefs.getString("bookmark.gateway_hostname", ""));
gatewaySettings.setPort(sharedPrefs.getInt("bookmark.gateway_port", 443));
gatewaySettings.setUsername(sharedPrefs.getString("bookmark.gateway_username", ""));
gatewaySettings.setPassword(sharedPrefs.getString("bookmark.gateway_password", ""));
gatewaySettings.setDomain(sharedPrefs.getString("bookmark.gateway_domain", ""));
}
// Cloneable
public Object clone()
{
return super.clone();
}
// Gateway Settings class
public static class GatewaySettings implements Parcelable
{
public static final Parcelable.Creator<GatewaySettings> CREATOR =
new Parcelable.Creator<GatewaySettings>() {
public GatewaySettings createFromParcel(Parcel in)
{
return new GatewaySettings(in);
}
@Override public GatewaySettings[] newArray(int size)
{
return new GatewaySettings[size];
}
};
private String hostname;
private int port;
private String username;
private String password;
private String domain;
public GatewaySettings()
{
hostname = "";
port = 443;
username = "";
password = "";
domain = "";
}
public GatewaySettings(Parcel parcel)
{
hostname = parcel.readString();
port = parcel.readInt();
username = parcel.readString();
password = parcel.readString();
domain = parcel.readString();
}
public String getHostname()
{
return hostname;
}
public void setHostname(String hostname)
{
this.hostname = hostname;
}
public int getPort()
{
return port;
}
public void setPort(int port)
{
this.port = port;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getDomain()
{
return domain;
}
public void setDomain(String domain)
{
this.domain = domain;
}
@Override public int describeContents()
{
return 0;
}
@Override public void writeToParcel(Parcel out, int flags)
{
out.writeString(hostname);
out.writeInt(port);
out.writeString(username);
out.writeString(password);
out.writeString(domain);
}
}
}

View File

@@ -0,0 +1,84 @@
/*
Placeholder for bookmark items with a special purpose (i.e. just displaying some text)
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.domain;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
public class PlaceholderBookmark extends BookmarkBase
{
public static final Parcelable.Creator<PlaceholderBookmark> CREATOR =
new Parcelable.Creator<PlaceholderBookmark>() {
public PlaceholderBookmark createFromParcel(Parcel in)
{
return new PlaceholderBookmark(in);
}
@Override public PlaceholderBookmark[] newArray(int size)
{
return new PlaceholderBookmark[size];
}
};
private String name;
public PlaceholderBookmark(Parcel parcel)
{
super(parcel);
type = TYPE_PLACEHOLDER;
name = parcel.readString();
}
public PlaceholderBookmark()
{
super();
type = TYPE_PLACEHOLDER;
name = "";
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
@Override public int describeContents()
{
return 0;
}
@Override public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
out.writeString(name);
}
@Override public void writeToSharedPreferences(SharedPreferences sharedPrefs)
{
super.writeToSharedPreferences(sharedPrefs);
}
@Override public void readFromSharedPreferences(SharedPreferences sharedPrefs)
{
super.readFromSharedPreferences(sharedPrefs);
}
// Cloneable
public Object clone()
{
return super.clone();
}
}

View File

@@ -0,0 +1,70 @@
/*
Quick Connect bookmark (used for quick connects using just a hostname)
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.domain;
import android.content.SharedPreferences;
import android.os.Parcel;
import android.os.Parcelable;
public class QuickConnectBookmark extends ManualBookmark
{
public static final Parcelable.Creator<QuickConnectBookmark> CREATOR =
new Parcelable.Creator<QuickConnectBookmark>() {
public QuickConnectBookmark createFromParcel(Parcel in)
{
return new QuickConnectBookmark(in);
}
@Override public QuickConnectBookmark[] newArray(int size)
{
return new QuickConnectBookmark[size];
}
};
public QuickConnectBookmark(Parcel parcel)
{
super(parcel);
type = TYPE_QUICKCONNECT;
}
public QuickConnectBookmark()
{
super();
type = TYPE_QUICKCONNECT;
}
@Override public int describeContents()
{
return 0;
}
@Override public void writeToParcel(Parcel out, int flags)
{
super.writeToParcel(out, flags);
}
@Override public void writeToSharedPreferences(SharedPreferences sharedPrefs)
{
super.writeToSharedPreferences(sharedPrefs);
}
@Override public void readFromSharedPreferences(SharedPreferences sharedPrefs)
{
super.readFromSharedPreferences(sharedPrefs);
}
// Cloneable
public Object clone()
{
return super.clone();
}
}

View File

@@ -0,0 +1,117 @@
package com.freerdp.freerdpcore.presentation;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.nfc.FormatException;
import android.os.Build;
import android.os.Bundle;
import androidx.core.text.TextUtilsCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.services.LibFreeRDP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.Locale;
public class AboutActivity extends AppCompatActivity
{
private static final String TAG = AboutActivity.class.toString();
private WebView mWebView;
@Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
mWebView = findViewById(R.id.activity_about_webview);
}
@Override protected void onResume()
{
populate();
super.onResume();
}
private void populate()
{
StringBuilder total = new StringBuilder();
String filename = "about_phone.html";
if ((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE)
{
filename = "about.html";
}
Locale def = Locale.getDefault();
String prefix = def.getLanguage().toLowerCase(def);
String dir = prefix + "_about_page/";
String file = dir + filename;
InputStream is;
try
{
is = getAssets().open(file);
is.close();
}
catch (IOException e)
{
Log.e(TAG, "Missing localized asset " + file, e);
dir = "about_page/";
file = dir + filename;
}
try
{
try (BufferedReader r =
new BufferedReader(new InputStreamReader(getAssets().open(file))))
{
String line;
while ((line = r.readLine()) != null)
{
total.append(line);
total.append("\n");
}
}
}
catch (IOException e)
{
Log.e(TAG, "Could not read about page " + file, e);
}
// append FreeRDP core version to app version
// get app version
String version;
try
{
version = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
}
catch (PackageManager.NameNotFoundException e)
{
version = "unknown";
}
version = version + " (" + LibFreeRDP.getVersion() + ")";
WebSettings settings = mWebView.getSettings();
settings.setDomStorageEnabled(true);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setSupportZoom(true);
final String base = "file:///android_asset/" + dir;
final String rawHtml = total.toString();
final String html = rawHtml.replaceAll("%AFREERDP_VERSION%", version)
.replaceAll("%SYSTEM_VERSION%", Build.VERSION.RELEASE)
.replaceAll("%DEVICE_MODEL%", Build.MODEL);
mWebView.loadDataWithBaseURL(base, html, "text/html", null, "about:blank");
}
}

View File

@@ -0,0 +1,307 @@
/*
Application Settings Activity
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import androidx.appcompat.app.AlertDialog;
import android.widget.Toast;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.utils.AppCompatPreferenceActivity;
import java.io.File;
import java.util.List;
import java.util.UUID;
public class ApplicationSettingsActivity extends AppCompatPreferenceActivity
{
private static boolean isXLargeTablet(Context context)
{
return (context.getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
@Override protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setupActionBar();
}
private void setupActionBar()
{
android.app.ActionBar actionBar = getActionBar();
if (actionBar != null)
{
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
@Override public boolean onIsMultiPane()
{
return isXLargeTablet(this);
}
@Override
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target)
{
loadHeadersFromResource(R.xml.settings_app_headers, target);
}
protected boolean isValidFragment(String fragmentName)
{
return PreferenceFragment.class.getName().equals(fragmentName) ||
ClientPreferenceFragment.class.getName().equals(fragmentName) ||
UiPreferenceFragment.class.getName().equals(fragmentName) ||
PowerPreferenceFragment.class.getName().equals(fragmentName) ||
SecurityPreferenceFragment.class.getName().equals(fragmentName);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class ClientPreferenceFragment
extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
{
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_app_client);
SharedPreferences preferences = get(getActivity());
preferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
if (isAdded())
{
final String clientNameKey = getString(R.string.preference_key_client_name);
get(getActivity());
if (key.equals(clientNameKey))
{
final String clientNameValue = sharedPreferences.getString(clientNameKey, "");
EditTextPreference pref = (EditTextPreference)findPreference(clientNameKey);
pref.setText(clientNameValue);
}
}
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class UiPreferenceFragment extends PreferenceFragment
{
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_app_ui);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class PowerPreferenceFragment extends PreferenceFragment
{
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_app_power);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class SecurityPreferenceFragment extends PreferenceFragment
{
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_app_security);
}
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference)
{
final String clear =
getString(R.string.preference_key_security_clear_certificate_cache);
if (preference.getKey().equals(clear))
{
showDialog();
return true;
}
else
{
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
}
private void showDialog()
{
new AlertDialog.Builder(getActivity())
.setTitle(R.string.dlg_title_clear_cert_cache)
.setMessage(R.string.dlg_msg_clear_cert_cache)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
clearCertificateCache();
dialog.dismiss();
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
})
.setIcon(android.R.drawable.ic_delete)
.show();
}
private boolean deleteDirectory(File dir)
{
if (dir.isDirectory())
{
String[] children = dir.list();
for (String file : children)
{
if (!deleteDirectory(new File(dir, file)))
return false;
}
}
return dir.delete();
}
private void clearCertificateCache()
{
Context context = getActivity();
if ((new File(context.getFilesDir() + "/.freerdp")).exists())
{
if (deleteDirectory(new File(context.getFilesDir() + "/.freerdp")))
Toast.makeText(context, R.string.info_reset_success, Toast.LENGTH_LONG).show();
else
Toast.makeText(context, R.string.info_reset_failed, Toast.LENGTH_LONG).show();
}
else
Toast.makeText(context, R.string.info_reset_success, Toast.LENGTH_LONG).show();
}
}
public static SharedPreferences get(Context context)
{
Context appContext = context.getApplicationContext();
PreferenceManager.setDefaultValues(appContext, R.xml.settings_app_client, false);
PreferenceManager.setDefaultValues(appContext, R.xml.settings_app_power, false);
PreferenceManager.setDefaultValues(appContext, R.xml.settings_app_security, false);
PreferenceManager.setDefaultValues(appContext, R.xml.settings_app_ui, false);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(appContext);
final String key = context.getString(R.string.preference_key_client_name);
final String value = preferences.getString(key, "");
if (value.isEmpty())
{
final String android_id = UUID.randomUUID().toString();
final String defaultValue = context.getString(R.string.preference_default_client_name);
final String name = defaultValue + "-" + android_id;
preferences.edit().putString(key, name.substring(0, 31)).apply();
}
return preferences;
}
public static int getDisconnectTimeout(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getInt(
context.getString(R.string.preference_key_power_disconnect_timeout), 0);
}
public static boolean getHideStatusBar(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(context.getString(R.string.preference_key_ui_hide_status_bar),
false);
}
public static boolean getHideActionBar(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(context.getString(R.string.preference_key_ui_hide_action_bar),
false);
}
public static boolean getUseBackAsAltf4(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_ui_use_back_as_altf4), true);
}
public static boolean getAcceptAllCertificates(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_accept_certificates), false);
}
public static boolean getHideZoomControls(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_ui_hide_zoom_controls), false);
}
public static boolean getSwapMouseButtons(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_ui_swap_mouse_buttons), false);
}
public static boolean getInvertScrolling(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_ui_invert_scrolling), true);
}
public static boolean getAskOnExit(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(context.getString(R.string.preference_key_ui_ask_on_exit),
false);
}
public static boolean getAutoScrollTouchPointer(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getBoolean(
context.getString(R.string.preference_key_ui_auto_scroll_touchpointer), false);
}
public static String getClientName(Context context)
{
SharedPreferences preferences = get(context);
return preferences.getString(context.getString(R.string.preference_key_client_name), "");
}
}

View File

@@ -0,0 +1,732 @@
/*
Bookmark editing activity
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.View;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.application.GlobalApp;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.ConnectionReference;
import com.freerdp.freerdpcore.domain.ManualBookmark;
import com.freerdp.freerdpcore.services.BookmarkBaseGateway;
import com.freerdp.freerdpcore.services.LibFreeRDP;
import com.freerdp.freerdpcore.utils.RDPFileParser;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
public class BookmarkActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener
{
public static final String PARAM_CONNECTION_REFERENCE = "conRef";
private static final String TAG = "BookmarkActivity";
private static final int PREFERENCES_BOOKMARK = 1;
private static final int PREFERENCES_CREDENTIALS = 2;
private static final int PREFERENCES_SCREEN = 3;
private static final int PREFERENCES_PERFORMANCE = 4;
private static final int PREFERENCES_ADVANCED = 5;
private static final int PREFERENCES_SCREEN3G = 6;
private static final int PREFERENCES_PERFORMANCE3G = 7;
private static final int PREFERENCES_GATEWAY = 8;
private static final int PREFERENCES_DEBUG = 9;
// bookmark needs to be static because the activity is started for each
// subview
// (we have to do this because Android has a bug where the style for
// Preferences
// is only applied to the first PreferenceScreen but not to subsequent ones)
private static BookmarkBase bookmark = null;
private static boolean settings_changed = false;
private static boolean new_bookmark = false;
private int current_preferences;
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
PreferenceManager mgr = getPreferenceManager();
// init shared preferences for activity
mgr.setSharedPreferencesName("TEMP");
mgr.setSharedPreferencesMode(MODE_PRIVATE);
if (bookmark == null)
{
// if we have a bookmark id set in the extras we are in edit mode
Bundle bundle = getIntent().getExtras();
if (bundle != null)
{
// See if we got a connection reference to a bookmark
if (bundle.containsKey(PARAM_CONNECTION_REFERENCE))
{
String refStr = bundle.getString(PARAM_CONNECTION_REFERENCE);
if (ConnectionReference.isManualBookmarkReference(refStr))
{
bookmark = GlobalApp.getManualBookmarkGateway().findById(
ConnectionReference.getManualBookmarkId(refStr));
new_bookmark = false;
}
else if (ConnectionReference.isHostnameReference(refStr))
{
bookmark = new ManualBookmark();
bookmark.<ManualBookmark>get().setLabel(
ConnectionReference.getHostname(refStr));
bookmark.<ManualBookmark>get().setHostname(
ConnectionReference.getHostname(refStr));
new_bookmark = true;
}
else if (ConnectionReference.isFileReference(refStr))
{
String file = ConnectionReference.getFile(refStr);
bookmark = new ManualBookmark();
bookmark.setLabel(file);
try
{
RDPFileParser rdpFile = new RDPFileParser(file);
updateBookmarkFromFile((ManualBookmark)bookmark, rdpFile);
bookmark.setLabel(new File(file).getName());
new_bookmark = true;
}
catch (IOException e)
{
Log.e(TAG, "Failed reading RDP file", e);
}
}
}
}
// last chance - ensure we really have a valid bookmark
if (bookmark == null)
bookmark = new ManualBookmark();
// hide gateway settings if we edit a non-manual bookmark
if (current_preferences == PREFERENCES_ADVANCED &&
bookmark.getType() != ManualBookmark.TYPE_MANUAL)
{
PreferenceScreen screen = getPreferenceScreen();
screen.removePreference(findPreference("bookmark.enable_gateway"));
screen.removePreference(findPreference("bookmark.gateway"));
}
updateH264Preferences();
// update preferences from bookmark
bookmark.writeToSharedPreferences(mgr.getSharedPreferences());
// no settings changed yet
settings_changed = false;
}
// load the requested settings resource
if (getIntent() == null || getIntent().getData() == null)
{
addPreferencesFromResource(R.xml.bookmark_settings);
current_preferences = PREFERENCES_BOOKMARK;
}
else if (getIntent().getData().toString().equals("preferences://screen_settings"))
{
addPreferencesFromResource(R.xml.screen_settings);
current_preferences = PREFERENCES_SCREEN;
}
else if (getIntent().getData().toString().equals("preferences://performance_flags"))
{
addPreferencesFromResource(R.xml.performance_flags);
current_preferences = PREFERENCES_PERFORMANCE;
}
else if (getIntent().getData().toString().equals("preferences://screen_settings_3g"))
{
addPreferencesFromResource(R.xml.screen_settings_3g);
current_preferences = PREFERENCES_SCREEN3G;
}
else if (getIntent().getData().toString().equals("preferences://performance_flags_3g"))
{
addPreferencesFromResource(R.xml.performance_flags_3g);
current_preferences = PREFERENCES_PERFORMANCE3G;
}
else if (getIntent().getData().toString().equals("preferences://advanced_settings"))
{
addPreferencesFromResource(R.xml.advanced_settings);
current_preferences = PREFERENCES_ADVANCED;
}
else if (getIntent().getData().toString().equals("preferences://credentials_settings"))
{
addPreferencesFromResource(R.xml.credentials_settings);
current_preferences = PREFERENCES_CREDENTIALS;
}
else if (getIntent().getData().toString().equals("preferences://gateway_settings"))
{
addPreferencesFromResource(R.xml.gateway_settings);
current_preferences = PREFERENCES_GATEWAY;
}
else if (getIntent().getData().toString().equals("preferences://debug_settings"))
{
addPreferencesFromResource(R.xml.debug_settings);
current_preferences = PREFERENCES_DEBUG;
}
else
{
addPreferencesFromResource(R.xml.bookmark_settings);
current_preferences = PREFERENCES_BOOKMARK;
}
// update UI with bookmark data
SharedPreferences spref = mgr.getSharedPreferences();
initSettings(spref);
// register for preferences changed notification
mgr.getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
// set the correct component names in our preferencescreen settings
setIntentComponentNames();
updateH264Preferences();
}
private void updateH264Preferences()
{
if (!LibFreeRDP.hasH264Support())
{
final int[] preferenceIdList = { R.string.preference_key_h264,
R.string.preference_key_h264_3g };
PreferenceManager mgr = getPreferenceManager();
for (int id : preferenceIdList)
{
final String key = getString(id);
Preference preference = mgr.findPreference(key);
if (preference != null)
{
preference.setEnabled(false);
}
}
}
}
private void updateBookmarkFromFile(ManualBookmark bookmark, RDPFileParser rdpFile)
{
String s;
Integer i;
s = rdpFile.getString("full address");
if (s != null)
{
// this gets complicated as it can include port
if (s.lastIndexOf(":") > s.lastIndexOf("]"))
{
try
{
String port = s.substring(s.lastIndexOf(":") + 1);
bookmark.setPort(Integer.parseInt(port));
}
catch (NumberFormatException e)
{
Log.e(TAG, "Malformed address");
}
s = s.substring(0, s.lastIndexOf(":"));
}
// or even be an ipv6 address
if (s.startsWith("[") && s.endsWith("]"))
s = s.substring(1, s.length() - 1);
bookmark.setHostname(s);
}
i = rdpFile.getInteger("server port");
if (i != null)
bookmark.setPort(i);
s = rdpFile.getString("username");
if (s != null)
bookmark.setUsername(s);
s = rdpFile.getString("domain");
if (s != null)
bookmark.setDomain(s);
i = rdpFile.getInteger("connect to console");
if (i != null)
bookmark.getAdvancedSettings().setConsoleMode(i == 1);
}
private void setIntentComponentNames()
{
// we set the component name for our sub-activity calls here because we
// don't know the package
// name of the main app in our library project.
ComponentName compName =
new ComponentName(getPackageName(), BookmarkActivity.class.getName());
ArrayList<String> prefKeys = new ArrayList<>();
prefKeys.add("bookmark.credentials");
prefKeys.add("bookmark.screen");
prefKeys.add("bookmark.performance");
prefKeys.add("bookmark.advanced");
prefKeys.add("bookmark.screen_3g");
prefKeys.add("bookmark.performance_3g");
prefKeys.add("bookmark.gateway_settings");
prefKeys.add("bookmark.debug");
for (String p : prefKeys)
{
Preference pref = findPreference(p);
if (pref != null)
pref.getIntent().setComponent(compName);
}
}
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
settings_changed = true;
switch (current_preferences)
{
case PREFERENCES_DEBUG:
debugSettingsChanged(sharedPreferences, key);
break;
case PREFERENCES_BOOKMARK:
bookmarkSettingsChanged(sharedPreferences, key);
break;
case PREFERENCES_ADVANCED:
advancedSettingsChanged(sharedPreferences, key);
break;
case PREFERENCES_CREDENTIALS:
credentialsSettingsChanged(sharedPreferences, key);
break;
case PREFERENCES_SCREEN:
case PREFERENCES_SCREEN3G:
screenSettingsChanged(sharedPreferences, key);
break;
case PREFERENCES_GATEWAY:
gatewaySettingsChanged(sharedPreferences, key);
break;
default:
break;
}
}
private void initSettings(SharedPreferences sharedPreferences)
{
switch (current_preferences)
{
case PREFERENCES_BOOKMARK:
initBookmarkSettings(sharedPreferences);
break;
case PREFERENCES_ADVANCED:
initAdvancedSettings(sharedPreferences);
break;
case PREFERENCES_CREDENTIALS:
initCredentialsSettings(sharedPreferences);
break;
case PREFERENCES_SCREEN:
initScreenSettings(sharedPreferences);
break;
case PREFERENCES_SCREEN3G:
initScreenSettings3G(sharedPreferences);
break;
case PREFERENCES_GATEWAY:
initGatewaySettings(sharedPreferences);
break;
case PREFERENCES_DEBUG:
initDebugSettings(sharedPreferences);
break;
default:
break;
}
}
private void initBookmarkSettings(SharedPreferences sharedPreferences)
{
bookmarkSettingsChanged(sharedPreferences, "bookmark.label");
bookmarkSettingsChanged(sharedPreferences, "bookmark.hostname");
bookmarkSettingsChanged(sharedPreferences, "bookmark.port");
bookmarkSettingsChanged(sharedPreferences, "bookmark.username");
bookmarkSettingsChanged(sharedPreferences, "bookmark.resolution");
}
private void bookmarkSettingsChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("bookmark.label") && findPreference(key) != null)
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
else if (key.equals("bookmark.hostname") && findPreference(key) != null)
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
else if (key.equals("bookmark.port") && findPreference(key) != null)
findPreference(key).setSummary(String.valueOf(sharedPreferences.getInt(key, -1)));
else if (key.equals("bookmark.username"))
{
String username = sharedPreferences.getString(key, "<none>");
if (username.length() == 0)
username = "<none>";
findPreference("bookmark.credentials").setSummary(username);
}
else if (key.equals("bookmark.resolution") || key.equals("bookmark.colors") ||
key.equals("bookmark.width") || key.equals("bookmark.height"))
{
String resolution = sharedPreferences.getString("bookmark.resolution", "800x600");
// compare english string from resolutions_values_array array,
// decode to localized
// text for display
if (resolution.equals("automatic"))
{
resolution = getResources().getString(R.string.resolution_automatic);
}
if (resolution.equals("custom"))
{
resolution = getResources().getString(R.string.resolution_custom);
}
if (resolution.equals("fitscreen"))
{
resolution = getResources().getString(R.string.resolution_fit);
}
resolution += "@" + sharedPreferences.getInt("bookmark.colors", 16);
findPreference("bookmark.screen").setSummary(resolution);
}
}
private void initAdvancedSettings(SharedPreferences sharedPreferences)
{
advancedSettingsChanged(sharedPreferences, "bookmark.enable_gateway_settings");
advancedSettingsChanged(sharedPreferences, "bookmark.enable_3g_settings");
advancedSettingsChanged(sharedPreferences, "bookmark.security");
advancedSettingsChanged(sharedPreferences, "bookmark.resolution_3g");
advancedSettingsChanged(sharedPreferences, "bookmark.remote_program");
advancedSettingsChanged(sharedPreferences, "bookmark.work_dir");
}
private void advancedSettingsChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("bookmark.enable_gateway_settings"))
{
boolean enabled = sharedPreferences.getBoolean(key, false);
findPreference("bookmark.gateway_settings").setEnabled(enabled);
}
else if (key.equals("bookmark.enable_3g_settings"))
{
boolean enabled = sharedPreferences.getBoolean(key, false);
findPreference("bookmark.screen_3g").setEnabled(enabled);
findPreference("bookmark.performance_3g").setEnabled(enabled);
}
else if (key.equals("bookmark.security"))
{
ListPreference listPreference = (ListPreference)findPreference(key);
CharSequence security = listPreference.getEntries()[sharedPreferences.getInt(key, 0)];
listPreference.setSummary(security);
}
else if (key.equals("bookmark.resolution_3g") || key.equals("bookmark.colors_3g") ||
key.equals("bookmark.width_3g") || key.equals("bookmark.height_3g"))
{
String resolution = sharedPreferences.getString("bookmark.resolution_3g", "800x600");
if (resolution.equals("automatic"))
resolution = getResources().getString(R.string.resolution_automatic);
else if (resolution.equals("custom"))
resolution = getResources().getString(R.string.resolution_custom);
resolution += "@" + sharedPreferences.getInt("bookmark.colors_3g", 16);
findPreference("bookmark.screen_3g").setSummary(resolution);
}
else if (key.equals("bookmark.remote_program"))
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
else if (key.equals("bookmark.work_dir"))
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
}
private void initCredentialsSettings(SharedPreferences sharedPreferences)
{
credentialsSettingsChanged(sharedPreferences, "bookmark.username");
credentialsSettingsChanged(sharedPreferences, "bookmark.password");
credentialsSettingsChanged(sharedPreferences, "bookmark.domain");
}
private void credentialsSettingsChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("bookmark.username"))
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
else if (key.equals("bookmark.password"))
{
if (sharedPreferences.getString(key, "").length() == 0)
findPreference(key).setSummary(
getResources().getString(R.string.settings_password_empty));
else
findPreference(key).setSummary(
getResources().getString(R.string.settings_password_present));
}
else if (key.equals("bookmark.domain"))
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
}
private void initScreenSettings(SharedPreferences sharedPreferences)
{
screenSettingsChanged(sharedPreferences, "bookmark.colors");
screenSettingsChanged(sharedPreferences, "bookmark.resolution");
screenSettingsChanged(sharedPreferences, "bookmark.width");
screenSettingsChanged(sharedPreferences, "bookmark.height");
}
private void initScreenSettings3G(SharedPreferences sharedPreferences)
{
screenSettingsChanged(sharedPreferences, "bookmark.colors_3g");
screenSettingsChanged(sharedPreferences, "bookmark.resolution_3g");
screenSettingsChanged(sharedPreferences, "bookmark.width_3g");
screenSettingsChanged(sharedPreferences, "bookmark.height_3g");
}
private void screenSettingsChanged(SharedPreferences sharedPreferences, String key)
{
// could happen during initialization because 3g and non-3g settings
// share this routine - just skip
if (findPreference(key) == null)
return;
if (key.equals("bookmark.colors") || key.equals("bookmark.colors_3g"))
{
ListPreference listPreference = (ListPreference)findPreference(key);
listPreference.setSummary(listPreference.getEntry());
}
else if (key.equals("bookmark.resolution") || key.equals("bookmark.resolution_3g"))
{
ListPreference listPreference = (ListPreference)findPreference(key);
listPreference.setSummary(listPreference.getEntry());
String value = listPreference.getValue();
boolean enabled = value.equalsIgnoreCase("custom");
if (key.equals("bookmark.resolution"))
{
findPreference("bookmark.width").setEnabled(enabled);
findPreference("bookmark.height").setEnabled(enabled);
}
else
{
findPreference("bookmark.width_3g").setEnabled(enabled);
findPreference("bookmark.height_3g").setEnabled(enabled);
}
}
else if (key.equals("bookmark.width") || key.equals("bookmark.width_3g"))
findPreference(key).setSummary(String.valueOf(sharedPreferences.getInt(key, 800)));
else if (key.equals("bookmark.height") || key.equals("bookmark.height_3g"))
findPreference(key).setSummary(String.valueOf(sharedPreferences.getInt(key, 600)));
}
private void initDebugSettings(SharedPreferences sharedPreferences)
{
debugSettingsChanged(sharedPreferences, "bookmark.debug_level");
debugSettingsChanged(sharedPreferences, "bookmark.async_channel");
debugSettingsChanged(sharedPreferences, "bookmark.async_update");
}
private void initGatewaySettings(SharedPreferences sharedPreferences)
{
gatewaySettingsChanged(sharedPreferences, "bookmark.gateway_hostname");
gatewaySettingsChanged(sharedPreferences, "bookmark.gateway_port");
gatewaySettingsChanged(sharedPreferences, "bookmark.gateway_username");
gatewaySettingsChanged(sharedPreferences, "bookmark.gateway_password");
gatewaySettingsChanged(sharedPreferences, "bookmark.gateway_domain");
}
private void debugSettingsChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("bookmark.debug_level"))
{
String level = sharedPreferences.getString(key, "INFO");
Preference pref = findPreference("bookmark.debug_level");
pref.setDefaultValue(level);
}
else if (key.equals("bookmark.async_channel"))
{
boolean enabled = sharedPreferences.getBoolean(key, false);
Preference pref = findPreference("bookmark.async_channel");
pref.setDefaultValue(enabled);
}
else if (key.equals("bookmark.async_update"))
{
boolean enabled = sharedPreferences.getBoolean(key, false);
Preference pref = findPreference("bookmark.async_update");
pref.setDefaultValue(enabled);
}
}
private void gatewaySettingsChanged(SharedPreferences sharedPreferences, String key)
{
if (key.equals("bookmark.gateway_hostname"))
{
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
}
else if (key.equals("bookmark.gateway_port"))
{
findPreference(key).setSummary(String.valueOf(sharedPreferences.getInt(key, 443)));
}
else if (key.equals("bookmark.gateway_username"))
{
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
}
else if (key.equals("bookmark.gateway_password"))
{
if (sharedPreferences.getString(key, "").length() == 0)
findPreference(key).setSummary(
getResources().getString(R.string.settings_password_empty));
else
findPreference(key).setSummary(
getResources().getString(R.string.settings_password_present));
}
else if (key.equals("bookmark.gateway_domain"))
findPreference(key).setSummary(sharedPreferences.getString(key, ""));
}
private boolean verifySettings(SharedPreferences sharedPreferences)
{
boolean verifyFailed = sharedPreferences.getString("bookmark.label", "").length() == 0;
// perform sanity checks on settings
// Label set
// Server and port specified
if (!verifyFailed && sharedPreferences.getString("bookmark.hostname", "").length() == 0)
verifyFailed = true;
// Server and port specified
if (!verifyFailed && sharedPreferences.getInt("bookmark.port", -1) <= 0)
verifyFailed = true;
// if an error occurred - display toast and return false
return (!verifyFailed);
}
private void finishAndResetBookmark()
{
bookmark = null;
getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
this);
finish();
}
@Override public void onBackPressed()
{
// only proceed if we are in the main preferences screen
if (current_preferences != PREFERENCES_BOOKMARK)
{
super.onBackPressed();
getPreferenceManager()
.getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
return;
}
SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences();
if (!verifySettings(sharedPreferences))
{
// ask the user if he wants to cancel or continue editing
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.error_bookmark_incomplete_title)
.setMessage(R.string.error_bookmark_incomplete)
.setPositiveButton(R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
finishAndResetBookmark();
}
})
.setNegativeButton(R.string.cont,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.cancel();
}
})
.show();
}
else
{
// ask the user if he wants to save or cancel editing if a setting
// has changed
if (new_bookmark || settings_changed)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.dlg_title_save_bookmark)
.setMessage(R.string.dlg_save_bookmark)
.setPositiveButton(
R.string.yes,
new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which)
{
// read shared prefs back to bookmark
bookmark.readFromSharedPreferences(
getPreferenceManager().getSharedPreferences());
BookmarkBaseGateway bookmarkGateway;
if (bookmark.getType() == BookmarkBase.TYPE_MANUAL)
{
bookmarkGateway = GlobalApp.getManualBookmarkGateway();
// remove any history entry for this
// bookmark
GlobalApp.getQuickConnectHistoryGateway().removeHistoryItem(
bookmark.<ManualBookmark>get().getHostname());
}
else
{
assert false;
return;
}
// insert or update bookmark and leave
// activity
if (bookmark.getId() > 0)
bookmarkGateway.update(bookmark);
else
bookmarkGateway.insert(bookmark);
finishAndResetBookmark();
}
})
.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
finishAndResetBookmark();
}
})
.show();
}
else
{
finishAndResetBookmark();
}
}
}
}

View File

@@ -0,0 +1,77 @@
/*
Activity that displays the help pages
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.webkit.WebSettings;
import android.webkit.WebView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;
public class HelpActivity extends AppCompatActivity
{
private static final String TAG = HelpActivity.class.toString();
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
WebView webview = new WebView(this);
setContentView(webview);
String filename;
if ((getResources().getConfiguration().screenLayout &
Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE)
filename = "gestures.html";
else
filename = "gestures_phone.html";
WebSettings settings = webview.getSettings();
settings.setDomStorageEnabled(true);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setSupportZoom(true);
settings.setJavaScriptEnabled(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccess(true);
final Locale def = Locale.getDefault();
final String prefix = def.getLanguage().toLowerCase(def);
final String base = "file:///android_asset/";
final String baseName = "help_page";
String dir = prefix + "_" + baseName + "/";
String file = dir + filename;
InputStream is;
try
{
is = getAssets().open(file);
is.close();
}
catch (IOException e)
{
Log.e(TAG, "Missing localized asset " + file, e);
dir = baseName + "/";
file = dir + filename;
}
webview.loadUrl(base + file);
}
}

View File

@@ -0,0 +1,399 @@
/*
Main/Home Activity
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ListView;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.application.GlobalApp;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.ConnectionReference;
import com.freerdp.freerdpcore.domain.PlaceholderBookmark;
import com.freerdp.freerdpcore.domain.QuickConnectBookmark;
import com.freerdp.freerdpcore.utils.BookmarkArrayAdapter;
import com.freerdp.freerdpcore.utils.SeparatedListAdapter;
import java.util.ArrayList;
public class HomeActivity extends AppCompatActivity
{
private final static String ADD_BOOKMARK_PLACEHOLDER = "add_bookmark";
private static final String TAG = "HomeActivity";
private static final String PARAM_SUPERBAR_TEXT = "superbar_text";
private ListView listViewBookmarks;
private Button clearTextButton;
private EditText superBarEditText;
private BookmarkArrayAdapter manualBookmarkAdapter;
private SeparatedListAdapter separatedListAdapter;
private PlaceholderBookmark addBookmarkPlaceholder;
private String sectionLabelBookmarks;
View mDecor;
@Override public void onCreate(Bundle savedInstanceState)
{
setTitle(R.string.title_home);
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
mDecor = getWindow().getDecorView();
mDecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
long heapSize = Runtime.getRuntime().maxMemory();
Log.i(TAG, "Max HeapSize: " + heapSize);
Log.i(TAG, "App data folder: " + getFilesDir().toString());
// load strings
sectionLabelBookmarks = getResources().getString(R.string.section_bookmarks);
// create add bookmark/quick connect bookmark placeholder
addBookmarkPlaceholder = new PlaceholderBookmark();
addBookmarkPlaceholder.setName(ADD_BOOKMARK_PLACEHOLDER);
addBookmarkPlaceholder.setLabel(
getResources().getString(R.string.list_placeholder_add_bookmark));
// check for passed .rdp file and open it in a new bookmark
Intent caller = getIntent();
Uri callParameter = caller.getData();
if (Intent.ACTION_VIEW.equals(caller.getAction()) && callParameter != null)
{
String refStr = ConnectionReference.getFileReference(callParameter.getPath());
Bundle bundle = new Bundle();
bundle.putString(BookmarkActivity.PARAM_CONNECTION_REFERENCE, refStr);
Intent bookmarkIntent =
new Intent(this.getApplicationContext(), BookmarkActivity.class);
bookmarkIntent.putExtras(bundle);
startActivity(bookmarkIntent);
}
// load views
clearTextButton = findViewById(R.id.clear_search_btn);
superBarEditText = findViewById(R.id.superBarEditText);
listViewBookmarks = findViewById(R.id.listViewBookmarks);
// set listeners for the list view
listViewBookmarks.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
String curSection = separatedListAdapter.getSectionForPosition(position);
Log.v(TAG, "Clicked on item id " + separatedListAdapter.getItemId(position) +
" in section " + curSection);
if (curSection.equals(sectionLabelBookmarks))
{
String refStr = view.getTag().toString();
if (ConnectionReference.isManualBookmarkReference(refStr) ||
ConnectionReference.isHostnameReference(refStr))
{
Bundle bundle = new Bundle();
bundle.putString(SessionActivity.PARAM_CONNECTION_REFERENCE, refStr);
Intent sessionIntent = new Intent(view.getContext(), SessionActivity.class);
sessionIntent.putExtras(bundle);
startActivity(sessionIntent);
// clear any search text
superBarEditText.setText("");
superBarEditText.clearFocus();
}
else if (ConnectionReference.isPlaceholderReference(refStr))
{
// is this the add bookmark placeholder?
if (ConnectionReference.getPlaceholder(refStr).equals(
ADD_BOOKMARK_PLACEHOLDER))
{
Intent bookmarkIntent =
new Intent(view.getContext(), BookmarkActivity.class);
startActivity(bookmarkIntent);
}
}
}
}
});
listViewBookmarks.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
// if the selected item is not a session item (tag == null) and not a quick connect
// entry (not a hostname connection reference) inflate the context menu
View itemView = ((AdapterContextMenuInfo)menuInfo).targetView;
String refStr = itemView.getTag() != null ? itemView.getTag().toString() : null;
if (refStr != null && !ConnectionReference.isHostnameReference(refStr) &&
!ConnectionReference.isPlaceholderReference(refStr))
{
getMenuInflater().inflate(R.menu.bookmark_context_menu, menu);
menu.setHeaderTitle(getResources().getString(R.string.menu_title_bookmark));
}
}
});
superBarEditText.addTextChangedListener(new SuperBarTextWatcher());
clearTextButton.setOnClickListener(new OnClickListener() {
@Override public void onClick(View v)
{
superBarEditText.setText("");
}
});
}
@Override public void onConfigurationChanged(Configuration newConfig)
{
// ignore orientation/keyboard change
super.onConfigurationChanged(newConfig);
mDecor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
@Override public boolean onSearchRequested()
{
superBarEditText.requestFocus();
return true;
}
@Override public boolean onContextItemSelected(MenuItem aItem)
{
// get connection reference
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo)aItem.getMenuInfo();
String refStr = menuInfo.targetView.getTag().toString();
// refer to http://tools.android.com/tips/non-constant-fields why we can't use switch/case
// here ..
int itemId = aItem.getItemId();
if (itemId == R.id.bookmark_connect)
{
Bundle bundle = new Bundle();
bundle.putString(SessionActivity.PARAM_CONNECTION_REFERENCE, refStr);
Intent sessionIntent = new Intent(this, SessionActivity.class);
sessionIntent.putExtras(bundle);
startActivity(sessionIntent);
return true;
}
else if (itemId == R.id.bookmark_edit)
{
Bundle bundle = new Bundle();
bundle.putString(BookmarkActivity.PARAM_CONNECTION_REFERENCE, refStr);
Intent bookmarkIntent =
new Intent(this.getApplicationContext(), BookmarkActivity.class);
bookmarkIntent.putExtras(bundle);
startActivity(bookmarkIntent);
return true;
}
else if (itemId == R.id.bookmark_delete)
{
if (ConnectionReference.isManualBookmarkReference(refStr))
{
long id = ConnectionReference.getManualBookmarkId(refStr);
GlobalApp.getManualBookmarkGateway().delete(id);
manualBookmarkAdapter.remove(id);
separatedListAdapter.notifyDataSetChanged();
}
else
{
assert false;
}
// clear super bar text
superBarEditText.setText("");
return true;
}
return false;
}
@Override protected void onResume()
{
super.onResume();
Log.v(TAG, "HomeActivity.onResume");
// create bookmark cursor adapter
manualBookmarkAdapter = new BookmarkArrayAdapter(
this, R.layout.bookmark_list_item, GlobalApp.getManualBookmarkGateway().findAll());
// add add bookmark item to manual adapter
manualBookmarkAdapter.insert(addBookmarkPlaceholder, 0);
// attach all adapters to the separatedListView adapter and assign it to the list view
separatedListAdapter = new SeparatedListAdapter(this);
separatedListAdapter.addSection(sectionLabelBookmarks, manualBookmarkAdapter);
listViewBookmarks.setAdapter(separatedListAdapter);
// if we have a filter text entered cause an update to be caused here
String filter = superBarEditText.getText().toString();
if (filter.length() > 0)
superBarEditText.setText(filter);
}
@Override protected void onPause()
{
super.onPause();
Log.v(TAG, "HomeActivity.onPause");
// reset adapters
listViewBookmarks.setAdapter(null);
separatedListAdapter = null;
manualBookmarkAdapter = null;
}
@Override public void onBackPressed()
{
// if back was pressed - ask the user if he really wants to exit
if (ApplicationSettingsActivity.getAskOnExit(this))
{
final CheckBox cb = new CheckBox(this);
cb.setChecked(!ApplicationSettingsActivity.getAskOnExit(this));
cb.setText(R.string.dlg_dont_show_again);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.dlg_title_exit)
.setMessage(R.string.dlg_msg_exit)
.setView(cb)
.setPositiveButton(R.string.yes,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
finish();
}
})
.setNegativeButton(R.string.no,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
})
.create()
.show();
}
else
{
super.onBackPressed();
}
}
@Override protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString(PARAM_SUPERBAR_TEXT, superBarEditText.getText().toString());
}
@Override protected void onRestoreInstanceState(Bundle inState)
{
super.onRestoreInstanceState(inState);
superBarEditText.setText(inState.getString(PARAM_SUPERBAR_TEXT));
}
@Override public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.home_menu, menu);
return true;
}
@Override public boolean onOptionsItemSelected(MenuItem item)
{
// refer to http://tools.android.com/tips/non-constant-fields why we can't use switch/case
// here ..
int itemId = item.getItemId();
if (itemId == R.id.newBookmark)
{
Intent bookmarkIntent = new Intent(this, BookmarkActivity.class);
startActivity(bookmarkIntent);
}
else if (itemId == R.id.appSettings)
{
Intent settingsIntent = new Intent(this, ApplicationSettingsActivity.class);
startActivity(settingsIntent);
}
else if (itemId == R.id.help)
{
Intent helpIntent = new Intent(this, HelpActivity.class);
startActivity(helpIntent);
}
else if (itemId == R.id.about)
{
Intent aboutIntent = new Intent(this, AboutActivity.class);
startActivity(aboutIntent);
}
return true;
}
private class SuperBarTextWatcher implements TextWatcher
{
@Override public void afterTextChanged(Editable s)
{
if (separatedListAdapter != null)
{
String text = s.toString();
if (text.length() > 0)
{
ArrayList<BookmarkBase> computers_list =
GlobalApp.getQuickConnectHistoryGateway().findHistory(text);
computers_list.addAll(
GlobalApp.getManualBookmarkGateway().findByLabelOrHostnameLike(text));
manualBookmarkAdapter.replaceItems(computers_list);
QuickConnectBookmark qcBm = new QuickConnectBookmark();
qcBm.setLabel(text);
qcBm.setHostname(text);
manualBookmarkAdapter.insert(qcBm, 0);
}
else
{
manualBookmarkAdapter.replaceItems(
GlobalApp.getManualBookmarkGateway().findAll());
manualBookmarkAdapter.insert(addBookmarkPlaceholder, 0);
}
separatedListAdapter.notifyDataSetChanged();
}
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
}
}

View File

@@ -0,0 +1,445 @@
/*
Android Session view
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.text.InputType;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import androidx.annotation.NonNull;
import com.freerdp.freerdpcore.application.SessionState;
import com.freerdp.freerdpcore.services.LibFreeRDP;
import com.freerdp.freerdpcore.utils.DoubleGestureDetector;
import com.freerdp.freerdpcore.utils.GestureDetector;
import com.freerdp.freerdpcore.utils.Mouse;
import java.util.Stack;
public class SessionView extends View
{
public static final float MAX_SCALE_FACTOR = 3.0f;
public static final float MIN_SCALE_FACTOR = 1.0f;
private static final String TAG = "SessionView";
private static final float SCALE_FACTOR_DELTA = 0.0001f;
private static final float TOUCH_SCROLL_DELTA = 10.0f;
private int width;
private int height;
private BitmapDrawable surface;
private Stack<Rect> invalidRegions;
private int touchPointerPaddingWidth = 0;
private int touchPointerPaddingHeight = 0;
private SessionViewListener sessionViewListener = null;
// helpers for scaling gesture handling
private float scaleFactor = 1.0f;
private Matrix scaleMatrix;
private Matrix invScaleMatrix;
private RectF invalidRegionF;
private GestureDetector gestureDetector;
private SessionState currentSession;
// private static final String TAG = "FreeRDP.SessionView";
private DoubleGestureDetector doubleGestureDetector;
public SessionView(Context context)
{
super(context);
initSessionView(context);
}
public SessionView(Context context, AttributeSet attrs)
{
super(context, attrs);
initSessionView(context);
}
public SessionView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initSessionView(context);
}
private void initSessionView(Context context)
{
invalidRegions = new Stack<>();
gestureDetector = new GestureDetector(context, new SessionGestureListener(), null, true);
doubleGestureDetector =
new DoubleGestureDetector(context, null, new SessionDoubleGestureListener());
scaleFactor = 1.0f;
scaleMatrix = new Matrix();
invScaleMatrix = new Matrix();
invalidRegionF = new RectF();
setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
/* External Mouse Hover */
@Override public boolean onHoverEvent(MotionEvent event)
{
if (event.getAction() == MotionEvent.ACTION_HOVER_MOVE)
{
// Handle hover move event
float x = event.getX();
float y = event.getY();
// Perform actions based on the hover position (x, y)
MotionEvent mappedEvent = mapTouchEvent(event);
LibFreeRDP.sendCursorEvent(currentSession.getInstance(), (int)mappedEvent.getX(),
(int)mappedEvent.getY(), Mouse.getMoveEvent());
}
// Return true to indicate that you've handled the event
return true;
}
public void setScaleGestureDetector(ScaleGestureDetector scaleGestureDetector)
{
doubleGestureDetector.setScaleGestureDetector(scaleGestureDetector);
}
public void setSessionViewListener(SessionViewListener sessionViewListener)
{
this.sessionViewListener = sessionViewListener;
}
public void addInvalidRegion(Rect invalidRegion)
{
// correctly transform invalid region depending on current scaling
invalidRegionF.set(invalidRegion);
scaleMatrix.mapRect(invalidRegionF);
invalidRegionF.roundOut(invalidRegion);
invalidRegions.add(invalidRegion);
}
public void invalidateRegion()
{
invalidate(invalidRegions.pop());
}
public void onSurfaceChange(SessionState session)
{
surface = session.getSurface();
Bitmap bitmap = surface.getBitmap();
width = bitmap.getWidth();
height = bitmap.getHeight();
surface.setBounds(0, 0, width, height);
setMinimumWidth(width);
setMinimumHeight(height);
requestLayout();
currentSession = session;
}
public float getZoom()
{
return scaleFactor;
}
public void setZoom(float factor)
{
// calc scale matrix and inverse scale matrix (to correctly transform the view and moues
// coordinates)
scaleFactor = factor;
scaleMatrix.setScale(scaleFactor, scaleFactor);
invScaleMatrix.setScale(1.0f / scaleFactor, 1.0f / scaleFactor);
// update layout
requestLayout();
}
public boolean isAtMaxZoom()
{
return (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA));
}
public boolean isAtMinZoom()
{
return (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA));
}
public boolean zoomIn(float factor)
{
boolean res = true;
scaleFactor += factor;
if (scaleFactor > (MAX_SCALE_FACTOR - SCALE_FACTOR_DELTA))
{
scaleFactor = MAX_SCALE_FACTOR;
res = false;
}
setZoom(scaleFactor);
return res;
}
public boolean zoomOut(float factor)
{
boolean res = true;
scaleFactor -= factor;
if (scaleFactor < (MIN_SCALE_FACTOR + SCALE_FACTOR_DELTA))
{
scaleFactor = MIN_SCALE_FACTOR;
res = false;
}
setZoom(scaleFactor);
return res;
}
public void setTouchPointerPadding(int width, int height)
{
touchPointerPaddingWidth = width;
touchPointerPaddingHeight = height;
requestLayout();
}
public int getTouchPointerPaddingWidth()
{
return touchPointerPaddingWidth;
}
public int getTouchPointerPaddingHeight()
{
return touchPointerPaddingHeight;
}
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
Log.v(TAG, width + "x" + height);
this.setMeasuredDimension((int)(width * scaleFactor) + touchPointerPaddingWidth,
(int)(height * scaleFactor) + touchPointerPaddingHeight);
}
@Override public void onDraw(@NonNull Canvas canvas)
{
super.onDraw(canvas);
canvas.save();
canvas.concat(scaleMatrix);
canvas.drawColor(Color.BLACK);
if (surface != null)
{
surface.draw(canvas);
}
canvas.restore();
}
// dirty hack: we call back to our activity and call onBackPressed as this doesn't reach us when
// the soft keyboard is shown ...
@Override public boolean dispatchKeyEventPreIme(KeyEvent event)
{
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK &&
event.getAction() == KeyEvent.ACTION_DOWN)
((SessionActivity)this.getContext()).onBackPressed();
return super.dispatchKeyEventPreIme(event);
}
// perform mapping on the touch event's coordinates according to the current scaling
private MotionEvent mapTouchEvent(MotionEvent event)
{
MotionEvent mappedEvent = MotionEvent.obtain(event);
float[] coordinates = { mappedEvent.getX(), mappedEvent.getY() };
invScaleMatrix.mapPoints(coordinates);
mappedEvent.setLocation(coordinates[0], coordinates[1]);
return mappedEvent;
}
// perform mapping on the double touch event's coordinates according to the current scaling
private MotionEvent mapDoubleTouchEvent(MotionEvent event)
{
MotionEvent mappedEvent = MotionEvent.obtain(event);
float[] coordinates = { (mappedEvent.getX(0) + mappedEvent.getX(1)) / 2,
(mappedEvent.getY(0) + mappedEvent.getY(1)) / 2 };
invScaleMatrix.mapPoints(coordinates);
mappedEvent.setLocation(coordinates[0], coordinates[1]);
return mappedEvent;
}
@Override public boolean onTouchEvent(MotionEvent event)
{
boolean res = gestureDetector.onTouchEvent(event);
res |= doubleGestureDetector.onTouchEvent(event);
return res;
}
public interface SessionViewListener {
void onSessionViewBeginTouch();
void onSessionViewEndTouch();
void onSessionViewLeftTouch(int x, int y, boolean down);
void onSessionViewRightTouch(int x, int y, boolean down);
void onSessionViewMove(int x, int y);
void onSessionViewScroll(boolean down);
}
private class SessionGestureListener extends GestureDetector.SimpleOnGestureListener
{
boolean longPressInProgress = false;
public boolean onDown(MotionEvent e)
{
return true;
}
public boolean onUp(MotionEvent e)
{
sessionViewListener.onSessionViewEndTouch();
return true;
}
public void onLongPress(MotionEvent e)
{
MotionEvent mappedEvent = mapTouchEvent(e);
sessionViewListener.onSessionViewBeginTouch();
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
longPressInProgress = true;
}
public void onLongPressUp(MotionEvent e)
{
MotionEvent mappedEvent = mapTouchEvent(e);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
longPressInProgress = false;
sessionViewListener.onSessionViewEndTouch();
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
if (longPressInProgress)
{
MotionEvent mappedEvent = mapTouchEvent(e2);
sessionViewListener.onSessionViewMove((int)mappedEvent.getX(),
(int)mappedEvent.getY());
return true;
}
return false;
}
public boolean onDoubleTap(MotionEvent e)
{
// send 2nd click for double click
MotionEvent mappedEvent = mapTouchEvent(e);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
return true;
}
public boolean onSingleTapUp(MotionEvent e)
{
// send single click
MotionEvent mappedEvent = mapTouchEvent(e);
sessionViewListener.onSessionViewBeginTouch();
switch (e.getButtonState())
{
case MotionEvent.BUTTON_PRIMARY:
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
break;
case MotionEvent.BUTTON_SECONDARY:
sessionViewListener.onSessionViewRightTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
sessionViewListener.onSessionViewRightTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
sessionViewListener.onSessionViewLeftTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
break;
}
sessionViewListener.onSessionViewEndTouch();
return true;
}
}
private class SessionDoubleGestureListener
implements DoubleGestureDetector.OnDoubleGestureListener
{
private MotionEvent prevEvent = null;
public boolean onDoubleTouchDown(MotionEvent e)
{
sessionViewListener.onSessionViewBeginTouch();
prevEvent = MotionEvent.obtain(e);
return true;
}
public boolean onDoubleTouchUp(MotionEvent e)
{
if (prevEvent != null)
{
prevEvent.recycle();
prevEvent = null;
}
sessionViewListener.onSessionViewEndTouch();
return true;
}
public boolean onDoubleTouchScroll(MotionEvent e1, MotionEvent e2)
{
// calc if user scrolled up or down (or if any scrolling happened at all)
float deltaY = e2.getY() - prevEvent.getY();
if (deltaY > TOUCH_SCROLL_DELTA)
{
sessionViewListener.onSessionViewScroll(true);
prevEvent.recycle();
prevEvent = MotionEvent.obtain(e2);
}
else if (deltaY < -TOUCH_SCROLL_DELTA)
{
sessionViewListener.onSessionViewScroll(false);
prevEvent.recycle();
prevEvent = MotionEvent.obtain(e2);
}
return true;
}
public boolean onDoubleTouchSingleTap(MotionEvent e)
{
// send single click
MotionEvent mappedEvent = mapDoubleTouchEvent(e);
sessionViewListener.onSessionViewRightTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), true);
sessionViewListener.onSessionViewRightTouch((int)mappedEvent.getX(),
(int)mappedEvent.getY(), false);
return true;
}
}
@Override public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
super.onCreateInputConnection(outAttrs);
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
return null;
}
}

View File

@@ -0,0 +1,160 @@
/*
Android Shortcut activity
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.TextView;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.application.GlobalApp;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.services.SessionRequestHandlerActivity;
import com.freerdp.freerdpcore.utils.BookmarkArrayAdapter;
import java.util.ArrayList;
public class ShortcutsActivity extends ListActivity
{
public static final String TAG = "ShortcutsActivity";
@Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction()))
{
// set listeners for the list view
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
String refStr = view.getTag().toString();
String defLabel =
((TextView)(view.findViewById(R.id.bookmark_text1))).getText().toString();
setupShortcut(refStr, defLabel);
}
});
}
else
{
// just exit
finish();
}
}
@Override public void onResume()
{
super.onResume();
// create bookmark cursor adapter
ArrayList<BookmarkBase> bookmarks = GlobalApp.getManualBookmarkGateway().findAll();
BookmarkArrayAdapter bookmarkAdapter =
new BookmarkArrayAdapter(this, android.R.layout.simple_list_item_2, bookmarks);
getListView().setAdapter(bookmarkAdapter);
}
public void onPause()
{
super.onPause();
getListView().setAdapter(null);
}
/**
* This function creates a shortcut and returns it to the caller. There are actually two
* intents that you will send back.
* <p>
* The first intent serves as a container for the shortcut and is returned to the launcher by
* setResult(). This intent must contain three fields:
* <p>
* <ul>
* <li>{@link android.content.Intent#EXTRA_SHORTCUT_INTENT} The shortcut intent.</li>
* <li>{@link android.content.Intent#EXTRA_SHORTCUT_NAME} The text that will be displayed with
* the shortcut.</li>
* <li>{@link android.content.Intent#EXTRA_SHORTCUT_ICON} The shortcut's icon, if provided as a
* bitmap, <i>or</i> {@link android.content.Intent#EXTRA_SHORTCUT_ICON_RESOURCE} if provided as
* a drawable resource.</li>
* </ul>
* <p>
* If you use a simple drawable resource, note that you must wrapper it using
* {@link android.content.Intent.ShortcutIconResource}, as shown below. This is required so
* that the launcher can access resources that are stored in your application's .apk file. If
* you return a bitmap, such as a thumbnail, you can simply put the bitmap into the extras
* bundle using {@link android.content.Intent#EXTRA_SHORTCUT_ICON}.
* <p>
* The shortcut intent can be any intent that you wish the launcher to send, when the user
* clicks on the shortcut. Typically this will be {@link android.content.Intent#ACTION_VIEW}
* with an appropriate Uri for your content, but any Intent will work here as long as it
* triggers the desired action within your Activity.
*/
private void setupShortcut(String strRef, String defaultLabel)
{
final String paramStrRef = strRef;
final String paramDefaultLabel = defaultLabel;
final Context paramContext = this;
// display edit dialog to the user so he can specify the shortcut name
final EditText input = new EditText(this);
input.setText(defaultLabel);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.dlg_title_create_shortcut)
.setMessage(R.string.dlg_msg_create_shortcut)
.setView(input)
.setPositiveButton(
android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which)
{
String label = input.getText().toString();
if (label.length() == 0)
label = paramDefaultLabel;
Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
shortcutIntent.setClassName(paramContext,
SessionRequestHandlerActivity.class.getName());
shortcutIntent.setData(Uri.parse(paramStrRef));
// Then, set up the container intent (the response to the caller)
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
Parcelable iconResource = Intent.ShortcutIconResource.fromContext(
paramContext, R.drawable.icon_launcher_freerdp);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
// Now, return the result to the launcher
setResult(RESULT_OK, intent);
finish();
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
})
.create()
.show();
}
}

View File

@@ -0,0 +1,381 @@
/*
Android Touch Pointer view
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.presentation;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.utils.GestureDetector;
public class TouchPointerView extends ImageView
{
private static final int POINTER_ACTION_CURSOR = 0;
private static final int POINTER_ACTION_CLOSE = 3;
// the touch pointer consists of 9 quadrants with the following functionality:
//
// -------------
// | 0 | 1 | 2 |
// -------------
// | 3 | 4 | 5 |
// -------------
// | 6 | 7 | 8 |
// -------------
//
// 0 ... contains the actual pointer (the tip must be centered in the quadrant)
// 1 ... is left empty
// 2, 3, 5, 6, 7, 8 ... function quadrants that issue a callback
// 4 ... pointer center used for left clicks and to drag the pointer
private static final int POINTER_ACTION_RCLICK = 2;
private static final int POINTER_ACTION_LCLICK = 4;
private static final int POINTER_ACTION_MOVE = 4;
private static final int POINTER_ACTION_SCROLL = 5;
private static final int POINTER_ACTION_RESET = 6;
private static final int POINTER_ACTION_KEYBOARD = 7;
private static final int POINTER_ACTION_EXTKEYBOARD = 8;
private static final float SCROLL_DELTA = 10.0f;
private static final int DEFAULT_TOUCH_POINTER_RESTORE_DELAY = 150;
private RectF pointerRect;
private final RectF[] pointerAreaRects = new RectF[9];
private Matrix translationMatrix;
private boolean pointerMoving = false;
private boolean pointerScrolling = false;
private TouchPointerListener listener = null;
private final UIHandler uiHandler = new UIHandler();
// gesture detection
private GestureDetector gestureDetector;
public TouchPointerView(Context context)
{
super(context);
initTouchPointer(context);
}
public TouchPointerView(Context context, AttributeSet attrs)
{
super(context, attrs);
initTouchPointer(context);
}
public TouchPointerView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initTouchPointer(context);
}
private void initTouchPointer(Context context)
{
gestureDetector =
new GestureDetector(context, new TouchPointerGestureListener(), null, true);
gestureDetector.setLongPressTimeout(500);
translationMatrix = new Matrix();
setScaleType(ScaleType.MATRIX);
setImageMatrix(translationMatrix);
// init rects
final float rectSizeWidth = (float)getDrawable().getIntrinsicWidth() / 3.0f;
final float rectSizeHeight = (float)getDrawable().getIntrinsicWidth() / 3.0f;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
int left = (int)(j * rectSizeWidth);
int top = (int)(i * rectSizeHeight);
int right = left + (int)rectSizeWidth;
int bottom = top + (int)rectSizeHeight;
pointerAreaRects[i * 3 + j] = new RectF(left, top, right, bottom);
}
}
pointerRect =
new RectF(0, 0, getDrawable().getIntrinsicWidth(), getDrawable().getIntrinsicHeight());
}
public void setTouchPointerListener(TouchPointerListener listener)
{
this.listener = listener;
}
public int getPointerWidth()
{
return getDrawable().getIntrinsicWidth();
}
public int getPointerHeight()
{
return getDrawable().getIntrinsicHeight();
}
public float[] getPointerPosition()
{
float[] curPos = new float[2];
translationMatrix.mapPoints(curPos);
return curPos;
}
private void movePointer(float deltaX, float deltaY)
{
translationMatrix.postTranslate(deltaX, deltaY);
setImageMatrix(translationMatrix);
}
private void ensureVisibility(int screen_width, int screen_height)
{
float[] curPos = new float[2];
translationMatrix.mapPoints(curPos);
if (curPos[0] > (screen_width - pointerRect.width()))
curPos[0] = screen_width - pointerRect.width();
if (curPos[0] < 0)
curPos[0] = 0;
if (curPos[1] > (screen_height - pointerRect.height()))
curPos[1] = screen_height - pointerRect.height();
if (curPos[1] < 0)
curPos[1] = 0;
translationMatrix.setTranslate(curPos[0], curPos[1]);
setImageMatrix(translationMatrix);
}
private void displayPointerImageAction(int resId)
{
setPointerImage(resId);
uiHandler.sendEmptyMessageDelayed(0, DEFAULT_TOUCH_POINTER_RESTORE_DELAY);
}
private void setPointerImage(int resId)
{
setImageResource(resId);
}
// returns the pointer area with the current translation matrix applied
private RectF getCurrentPointerArea(int area)
{
RectF transRect = new RectF(pointerAreaRects[area]);
translationMatrix.mapRect(transRect);
return transRect;
}
private boolean pointerAreaTouched(MotionEvent event, int area)
{
RectF transRect = new RectF(pointerAreaRects[area]);
translationMatrix.mapRect(transRect);
return transRect.contains(event.getX(), event.getY());
}
private boolean pointerTouched(MotionEvent event)
{
RectF transRect = new RectF(pointerRect);
translationMatrix.mapRect(transRect);
return transRect.contains(event.getX(), event.getY());
}
@Override public boolean onTouchEvent(MotionEvent event)
{
// check if pointer is being moved or if we are in scroll mode or if the pointer is touched
if (!pointerMoving && !pointerScrolling && !pointerTouched(event))
return false;
return gestureDetector.onTouchEvent(event);
}
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
// ensure touch pointer is visible
if (changed)
ensureVisibility(right - left, bottom - top);
}
// touch pointer listener - is triggered if an action field is
public interface TouchPointerListener {
void onTouchPointerClose();
void onTouchPointerLeftClick(int x, int y, boolean down);
void onTouchPointerRightClick(int x, int y, boolean down);
void onTouchPointerMove(int x, int y);
void onTouchPointerScroll(boolean down);
void onTouchPointerToggleKeyboard();
void onTouchPointerToggleExtKeyboard();
void onTouchPointerResetScrollZoom();
}
private class UIHandler extends Handler
{
UIHandler()
{
super();
}
@Override public void handleMessage(Message msg)
{
setPointerImage(R.drawable.touch_pointer_default);
}
}
private class TouchPointerGestureListener extends GestureDetector.SimpleOnGestureListener
{
private MotionEvent prevEvent = null;
public boolean onDown(MotionEvent e)
{
if (pointerAreaTouched(e, POINTER_ACTION_MOVE))
{
prevEvent = MotionEvent.obtain(e);
pointerMoving = true;
}
else if (pointerAreaTouched(e, POINTER_ACTION_SCROLL))
{
prevEvent = MotionEvent.obtain(e);
pointerScrolling = true;
setPointerImage(R.drawable.touch_pointer_scroll);
}
return true;
}
public boolean onUp(MotionEvent e)
{
if (prevEvent != null)
{
prevEvent.recycle();
prevEvent = null;
}
if (pointerScrolling)
setPointerImage(R.drawable.touch_pointer_default);
pointerMoving = false;
pointerScrolling = false;
return true;
}
public void onLongPress(MotionEvent e)
{
if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
{
setPointerImage(R.drawable.touch_pointer_active);
pointerMoving = true;
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
}
}
public void onLongPressUp(MotionEvent e)
{
if (pointerMoving)
{
setPointerImage(R.drawable.touch_pointer_default);
pointerMoving = false;
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
}
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
{
if (pointerMoving)
{
// move pointer graphics
movePointer((int)(e2.getX() - prevEvent.getX()),
(int)(e2.getY() - prevEvent.getY()));
prevEvent.recycle();
prevEvent = MotionEvent.obtain(e2);
// send move notification
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerMove((int)rect.centerX(), (int)rect.centerY());
return true;
}
else if (pointerScrolling)
{
// calc if user scrolled up or down (or if any scrolling happened at all)
float deltaY = e2.getY() - prevEvent.getY();
if (deltaY > SCROLL_DELTA)
{
listener.onTouchPointerScroll(true);
prevEvent.recycle();
prevEvent = MotionEvent.obtain(e2);
}
else if (deltaY < -SCROLL_DELTA)
{
listener.onTouchPointerScroll(false);
prevEvent.recycle();
prevEvent = MotionEvent.obtain(e2);
}
return true;
}
return false;
}
public boolean onSingleTapUp(MotionEvent e)
{
// look what area got touched and fire actions accordingly
if (pointerAreaTouched(e, POINTER_ACTION_CLOSE))
listener.onTouchPointerClose();
else if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
{
displayPointerImageAction(R.drawable.touch_pointer_lclick);
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
}
else if (pointerAreaTouched(e, POINTER_ACTION_RCLICK))
{
displayPointerImageAction(R.drawable.touch_pointer_rclick);
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerRightClick((int)rect.centerX(), (int)rect.centerY(), true);
listener.onTouchPointerRightClick((int)rect.centerX(), (int)rect.centerY(), false);
}
else if (pointerAreaTouched(e, POINTER_ACTION_KEYBOARD))
{
displayPointerImageAction(R.drawable.touch_pointer_keyboard);
listener.onTouchPointerToggleKeyboard();
}
else if (pointerAreaTouched(e, POINTER_ACTION_EXTKEYBOARD))
{
displayPointerImageAction(R.drawable.touch_pointer_extkeyboard);
listener.onTouchPointerToggleExtKeyboard();
}
else if (pointerAreaTouched(e, POINTER_ACTION_RESET))
{
displayPointerImageAction(R.drawable.touch_pointer_reset);
listener.onTouchPointerResetScrollZoom();
}
return true;
}
public boolean onDoubleTap(MotionEvent e)
{
// issue a double click notification if performed in center quadrant
if (pointerAreaTouched(e, POINTER_ACTION_LCLICK))
{
RectF rect = getCurrentPointerArea(POINTER_ACTION_CURSOR);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), true);
listener.onTouchPointerLeftClick((int)rect.centerX(), (int)rect.centerY(), false);
}
return true;
}
}
}

View File

@@ -0,0 +1,610 @@
/*
Helper class to access bookmark database
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import java.util.ArrayList;
public abstract class BookmarkBaseGateway
{
private final static String TAG = "BookmarkBaseGateway";
private final SQLiteOpenHelper bookmarkDB;
private static final String JOIN_PREFIX = "join_";
private static final String KEY_BOOKMARK_ID = "bookmarkId";
private static final String KEY_SCREEN_COLORS = "screenColors";
private static final String KEY_SCREEN_COLORS_3G = "screenColors3G";
private static final String KEY_SCREEN_RESOLUTION = "screenResolution";
private static final String KEY_SCREEN_RESOLUTION_3G = "screenResolution3G";
private static final String KEY_SCREEN_WIDTH = "screenWidth";
private static final String KEY_SCREEN_WIDTH_3G = "screenWidth3G";
private static final String KEY_SCREEN_HEIGHT = "screenHeight";
private static final String KEY_SCREEN_HEIGHT_3G = "screenHeight3G";
private static final String KEY_PERFORMANCE_RFX = "performanceRemoteFX";
private static final String KEY_PERFORMANCE_RFX_3G = "performanceRemoteFX3G";
private static final String KEY_PERFORMANCE_GFX = "performanceGfx";
private static final String KEY_PERFORMANCE_GFX_3G = "performanceGfx3G";
private static final String KEY_PERFORMANCE_H264 = "performanceGfxH264";
private static final String KEY_PERFORMANCE_H264_3G = "performanceGfxH2643G";
private static final String KEY_PERFORMANCE_WALLPAPER = "performanceWallpaper";
private static final String KEY_PERFORMANCE_WALLPAPER_3G = "performanceWallpaper3G";
private static final String KEY_PERFORMANCE_THEME = "performanceTheming";
private static final String KEY_PERFORMANCE_THEME_3G = "performanceTheming3G";
private static final String KEY_PERFORMANCE_DRAG = "performanceFullWindowDrag";
private static final String KEY_PERFORMANCE_DRAG_3G = "performanceFullWindowDrag3G";
private static final String KEY_PERFORMANCE_MENU_ANIMATIONS = "performanceMenuAnimations";
private static final String KEY_PERFORMANCE_MENU_ANIMATIONS_3G = "performanceMenuAnimations3G";
private static final String KEY_PERFORMANCE_FONTS = "performanceFontSmoothing";
private static final String KEY_PERFORMANCE_FONTS_3G = "performanceFontSmoothing3G";
private static final String KEY_PERFORMANCE_COMPOSITION = "performanceDesktopComposition";
private static final String KEY_PERFORMANCE_COMPOSITION_3G = "performanceDesktopComposition3G";
public BookmarkBaseGateway(SQLiteOpenHelper bookmarkDB)
{
this.bookmarkDB = bookmarkDB;
}
protected abstract BookmarkBase createBookmark();
protected abstract String getBookmarkTableName();
protected abstract void addBookmarkSpecificColumns(ArrayList<String> columns);
protected abstract void addBookmarkSpecificColumns(BookmarkBase bookmark,
ContentValues columns);
protected abstract void readBookmarkSpecificColumns(BookmarkBase bookmark, Cursor cursor);
public void insert(BookmarkBase bookmark)
{
// begin transaction
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
long rowid;
ContentValues values = new ContentValues();
values.put(BookmarkDB.DB_KEY_BOOKMARK_LABEL, bookmark.getLabel());
values.put(BookmarkDB.DB_KEY_BOOKMARK_USERNAME, bookmark.getUsername());
values.put(BookmarkDB.DB_KEY_BOOKMARK_PASSWORD, bookmark.getPassword());
values.put(BookmarkDB.DB_KEY_BOOKMARK_DOMAIN, bookmark.getDomain());
// insert screen and performance settings
rowid = insertScreenSettings(db, bookmark.getScreenSettings());
values.put(BookmarkDB.DB_KEY_SCREEN_SETTINGS, rowid);
rowid = insertPerformanceFlags(db, bookmark.getPerformanceFlags());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_FLAGS, rowid);
// advanced settings
values.put(BookmarkDB.DB_KEY_BOOKMARK_3G_ENABLE,
bookmark.getAdvancedSettings().getEnable3GSettings());
// insert 3G screen and 3G performance settings
rowid = insertScreenSettings(db, bookmark.getAdvancedSettings().getScreen3G());
values.put(BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G, rowid);
rowid = insertPerformanceFlags(db, bookmark.getAdvancedSettings().getPerformance3G());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G, rowid);
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SDCARD,
bookmark.getAdvancedSettings().getRedirectSDCard());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SOUND,
bookmark.getAdvancedSettings().getRedirectSound());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_MICROPHONE,
bookmark.getAdvancedSettings().getRedirectMicrophone());
values.put(BookmarkDB.DB_KEY_BOOKMARK_SECURITY,
bookmark.getAdvancedSettings().getSecurity());
values.put(BookmarkDB.DB_KEY_BOOKMARK_CONSOLE_MODE,
bookmark.getAdvancedSettings().getConsoleMode());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REMOTE_PROGRAM,
bookmark.getAdvancedSettings().getRemoteProgram());
values.put(BookmarkDB.DB_KEY_BOOKMARK_WORK_DIR,
bookmark.getAdvancedSettings().getWorkDir());
values.put(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_CHANNEL,
bookmark.getDebugSettings().getAsyncChannel());
values.put(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_UPDATE,
bookmark.getDebugSettings().getAsyncUpdate());
values.put(BookmarkDB.DB_KEY_BOOKMARK_DEBUG_LEVEL,
bookmark.getDebugSettings().getDebugLevel());
// add any special columns
addBookmarkSpecificColumns(bookmark, values);
// insert bookmark and end transaction
db.insertOrThrow(getBookmarkTableName(), null, values);
db.setTransactionSuccessful();
db.endTransaction();
}
public boolean update(BookmarkBase bookmark)
{
// start a transaction
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
// bookmark settings
ContentValues values = new ContentValues();
values.put(BookmarkDB.DB_KEY_BOOKMARK_LABEL, bookmark.getLabel());
values.put(BookmarkDB.DB_KEY_BOOKMARK_USERNAME, bookmark.getUsername());
values.put(BookmarkDB.DB_KEY_BOOKMARK_PASSWORD, bookmark.getPassword());
values.put(BookmarkDB.DB_KEY_BOOKMARK_DOMAIN, bookmark.getDomain());
// update screen and performance settings settings
updateScreenSettings(db, bookmark);
updatePerformanceFlags(db, bookmark);
// advanced settings
values.put(BookmarkDB.DB_KEY_BOOKMARK_3G_ENABLE,
bookmark.getAdvancedSettings().getEnable3GSettings());
// update 3G screen and 3G performance settings settings
updateScreenSettings3G(db, bookmark);
updatePerformanceFlags3G(db, bookmark);
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SDCARD,
bookmark.getAdvancedSettings().getRedirectSDCard());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SOUND,
bookmark.getAdvancedSettings().getRedirectSound());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_MICROPHONE,
bookmark.getAdvancedSettings().getRedirectMicrophone());
values.put(BookmarkDB.DB_KEY_BOOKMARK_SECURITY,
bookmark.getAdvancedSettings().getSecurity());
values.put(BookmarkDB.DB_KEY_BOOKMARK_CONSOLE_MODE,
bookmark.getAdvancedSettings().getConsoleMode());
values.put(BookmarkDB.DB_KEY_BOOKMARK_REMOTE_PROGRAM,
bookmark.getAdvancedSettings().getRemoteProgram());
values.put(BookmarkDB.DB_KEY_BOOKMARK_WORK_DIR,
bookmark.getAdvancedSettings().getWorkDir());
values.put(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_CHANNEL,
bookmark.getDebugSettings().getAsyncChannel());
values.put(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_UPDATE,
bookmark.getDebugSettings().getAsyncUpdate());
values.put(BookmarkDB.DB_KEY_BOOKMARK_DEBUG_LEVEL,
bookmark.getDebugSettings().getDebugLevel());
addBookmarkSpecificColumns(bookmark, values);
// update bookmark
boolean res = (db.update(getBookmarkTableName(), values,
BookmarkDB.ID + " = " + bookmark.getId(), null) == 1);
// commit
db.setTransactionSuccessful();
db.endTransaction();
return res;
}
public void delete(long id)
{
SQLiteDatabase db = getWritableDatabase();
db.delete(getBookmarkTableName(), BookmarkDB.ID + " = " + id, null);
}
public BookmarkBase findById(long id)
{
Cursor cursor =
queryBookmarks(getBookmarkTableName() + "." + BookmarkDB.ID + " = " + id, null);
if (cursor.getCount() == 0)
{
cursor.close();
return null;
}
cursor.moveToFirst();
BookmarkBase bookmark = getBookmarkFromCursor(cursor);
cursor.close();
return bookmark;
}
public BookmarkBase findByLabel(String label)
{
Cursor cursor = queryBookmarks(BookmarkDB.DB_KEY_BOOKMARK_LABEL + " = '" + label + "'",
BookmarkDB.DB_KEY_BOOKMARK_LABEL);
if (cursor.getCount() > 1)
Log.e(TAG, "More than one bookmark with the same label found!");
BookmarkBase bookmark = null;
if (cursor.moveToFirst() && (cursor.getCount() > 0))
bookmark = getBookmarkFromCursor(cursor);
cursor.close();
return bookmark;
}
public ArrayList<BookmarkBase> findByLabelLike(String pattern)
{
Cursor cursor =
queryBookmarks(BookmarkDB.DB_KEY_BOOKMARK_LABEL + " LIKE '%" + pattern + "%'",
BookmarkDB.DB_KEY_BOOKMARK_LABEL);
ArrayList<BookmarkBase> bookmarks = new ArrayList<>(cursor.getCount());
if (cursor.moveToFirst() && (cursor.getCount() > 0))
{
do
{
bookmarks.add(getBookmarkFromCursor(cursor));
} while (cursor.moveToNext());
}
cursor.close();
return bookmarks;
}
public ArrayList<BookmarkBase> findAll()
{
Cursor cursor = queryBookmarks(null, BookmarkDB.DB_KEY_BOOKMARK_LABEL);
final int count = cursor.getCount();
ArrayList<BookmarkBase> bookmarks = new ArrayList<>(count);
if (cursor.moveToFirst() && (count > 0))
{
do
{
bookmarks.add(getBookmarkFromCursor(cursor));
} while (cursor.moveToNext());
}
cursor.close();
return bookmarks;
}
protected Cursor queryBookmarks(String whereClause, String orderBy)
{
// create tables string
final String ID = BookmarkDB.ID;
final String tables =
BookmarkDB.DB_TABLE_BOOKMARK + " INNER JOIN " + BookmarkDB.DB_TABLE_SCREEN + " AS " +
JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS + " ON " + JOIN_PREFIX +
BookmarkDB.DB_KEY_SCREEN_SETTINGS + "." + ID + " = " + BookmarkDB.DB_TABLE_BOOKMARK +
"." + BookmarkDB.DB_KEY_SCREEN_SETTINGS + " INNER JOIN " +
BookmarkDB.DB_TABLE_PERFORMANCE + " AS " + JOIN_PREFIX +
BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + " ON " + JOIN_PREFIX +
BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." + ID + " = " + BookmarkDB.DB_TABLE_BOOKMARK +
"." + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + " INNER JOIN " +
BookmarkDB.DB_TABLE_SCREEN + " AS " + JOIN_PREFIX +
BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + " ON " + JOIN_PREFIX +
BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + "." + ID + " = " + BookmarkDB.DB_TABLE_BOOKMARK +
"." + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + " INNER JOIN " +
BookmarkDB.DB_TABLE_PERFORMANCE + " AS " + JOIN_PREFIX +
BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + " ON " + JOIN_PREFIX +
BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." + ID + " = " +
BookmarkDB.DB_TABLE_BOOKMARK + "." + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G;
// create columns list
ArrayList<String> columns = new ArrayList<>();
addBookmarkColumns(columns);
addScreenSettingsColumns(columns);
addPerformanceFlagsColumns(columns);
addScreenSettings3GColumns(columns);
addPerformanceFlags3GColumns(columns);
String[] cols = new String[columns.size()];
columns.toArray(cols);
SQLiteDatabase db = getReadableDatabase();
final String query = SQLiteQueryBuilder.buildQueryString(false, tables, cols, whereClause,
null, null, orderBy, null);
return db.rawQuery(query, null);
}
private void addBookmarkColumns(ArrayList<String> columns)
{
columns.add(getBookmarkTableName() + "." + BookmarkDB.ID + " " + KEY_BOOKMARK_ID);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_LABEL);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_USERNAME);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_PASSWORD);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_DOMAIN);
// advanced settings
columns.add(BookmarkDB.DB_KEY_BOOKMARK_3G_ENABLE);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SDCARD);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SOUND);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_MICROPHONE);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_SECURITY);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_CONSOLE_MODE);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_REMOTE_PROGRAM);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_WORK_DIR);
// debug settings
columns.add(BookmarkDB.DB_KEY_BOOKMARK_DEBUG_LEVEL);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_CHANNEL);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_UPDATE);
addBookmarkSpecificColumns(columns);
}
private void addScreenSettingsColumns(ArrayList<String> columns)
{
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS + "." +
BookmarkDB.DB_KEY_SCREEN_COLORS + " as " + KEY_SCREEN_COLORS);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS + "." +
BookmarkDB.DB_KEY_SCREEN_RESOLUTION + " as " + KEY_SCREEN_RESOLUTION);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS + "." +
BookmarkDB.DB_KEY_SCREEN_WIDTH + " as " + KEY_SCREEN_WIDTH);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS + "." +
BookmarkDB.DB_KEY_SCREEN_HEIGHT + " as " + KEY_SCREEN_HEIGHT);
}
private void addPerformanceFlagsColumns(ArrayList<String> columns)
{
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_RFX + " as " + KEY_PERFORMANCE_RFX);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_GFX + " as " + KEY_PERFORMANCE_GFX);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_H264 + " as " + KEY_PERFORMANCE_H264);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_WALLPAPER + " as " + KEY_PERFORMANCE_WALLPAPER);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_THEME + " as " + KEY_PERFORMANCE_THEME);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_DRAG + " as " + KEY_PERFORMANCE_DRAG);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_MENU_ANIMATIONS + " as " +
KEY_PERFORMANCE_MENU_ANIMATIONS);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_FONTS + " as " + KEY_PERFORMANCE_FONTS);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + "." +
BookmarkDB.DB_KEY_PERFORMANCE_COMPOSITION + " " + KEY_PERFORMANCE_COMPOSITION);
}
private void addScreenSettings3GColumns(ArrayList<String> columns)
{
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + "." +
BookmarkDB.DB_KEY_SCREEN_COLORS + " as " + KEY_SCREEN_COLORS_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + "." +
BookmarkDB.DB_KEY_SCREEN_RESOLUTION + " as " + KEY_SCREEN_RESOLUTION_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + "." +
BookmarkDB.DB_KEY_SCREEN_WIDTH + " as " + KEY_SCREEN_WIDTH_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + "." +
BookmarkDB.DB_KEY_SCREEN_HEIGHT + " as " + KEY_SCREEN_HEIGHT_3G);
}
private void addPerformanceFlags3GColumns(ArrayList<String> columns)
{
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_RFX + " as " + KEY_PERFORMANCE_RFX_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_GFX + " as " + KEY_PERFORMANCE_GFX_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_H264 + " as " + KEY_PERFORMANCE_H264_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_WALLPAPER + " as " +
KEY_PERFORMANCE_WALLPAPER_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_THEME + " as " + KEY_PERFORMANCE_THEME_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_DRAG + " as " + KEY_PERFORMANCE_DRAG_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_MENU_ANIMATIONS + " as " +
KEY_PERFORMANCE_MENU_ANIMATIONS_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_FONTS + " as " + KEY_PERFORMANCE_FONTS_3G);
columns.add(JOIN_PREFIX + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + "." +
BookmarkDB.DB_KEY_PERFORMANCE_COMPOSITION + " " +
KEY_PERFORMANCE_COMPOSITION_3G);
}
protected BookmarkBase getBookmarkFromCursor(Cursor cursor)
{
BookmarkBase bookmark = createBookmark();
bookmark.setId(cursor.getLong(cursor.getColumnIndex(KEY_BOOKMARK_ID)));
bookmark.setLabel(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_LABEL)));
bookmark.setUsername(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_USERNAME)));
bookmark.setPassword(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_PASSWORD)));
bookmark.setDomain(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_DOMAIN)));
readScreenSettings(bookmark, cursor);
readPerformanceFlags(bookmark, cursor);
// advanced settings
bookmark.getAdvancedSettings().setEnable3GSettings(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_3G_ENABLE)) != 0);
readScreenSettings3G(bookmark, cursor);
readPerformanceFlags3G(bookmark, cursor);
bookmark.getAdvancedSettings().setRedirectSDCard(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SDCARD)) != 0);
bookmark.getAdvancedSettings().setRedirectSound(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_SOUND)));
bookmark.getAdvancedSettings().setRedirectMicrophone(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_REDIRECT_MICROPHONE)) !=
0);
bookmark.getAdvancedSettings().setSecurity(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_SECURITY)));
bookmark.getAdvancedSettings().setConsoleMode(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_CONSOLE_MODE)) != 0);
bookmark.getAdvancedSettings().setRemoteProgram(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_REMOTE_PROGRAM)));
bookmark.getAdvancedSettings().setWorkDir(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_WORK_DIR)));
bookmark.getDebugSettings().setAsyncChannel(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_CHANNEL)) == 1);
bookmark.getDebugSettings().setAsyncUpdate(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_ASYNC_UPDATE)) == 1);
bookmark.getDebugSettings().setDebugLevel(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_DEBUG_LEVEL)));
readBookmarkSpecificColumns(bookmark, cursor);
return bookmark;
}
private void readScreenSettings(BookmarkBase bookmark, Cursor cursor)
{
BookmarkBase.ScreenSettings screenSettings = bookmark.getScreenSettings();
screenSettings.setColors(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_COLORS)));
screenSettings.setResolution(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_RESOLUTION)));
screenSettings.setWidth(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_WIDTH)));
screenSettings.setHeight(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_HEIGHT)));
}
private void readPerformanceFlags(BookmarkBase bookmark, Cursor cursor)
{
BookmarkBase.PerformanceFlags perfFlags = bookmark.getPerformanceFlags();
perfFlags.setRemoteFX(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_RFX)) != 0);
perfFlags.setGfx(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_GFX)) != 0);
perfFlags.setH264(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_H264)) != 0);
perfFlags.setWallpaper(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_WALLPAPER)) !=
0);
perfFlags.setTheming(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_THEME)) != 0);
perfFlags.setFullWindowDrag(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_DRAG)) !=
0);
perfFlags.setMenuAnimations(
cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_MENU_ANIMATIONS)) != 0);
perfFlags.setFontSmoothing(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_FONTS)) !=
0);
perfFlags.setDesktopComposition(
cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_COMPOSITION)) != 0);
}
private void readScreenSettings3G(BookmarkBase bookmark, Cursor cursor)
{
BookmarkBase.ScreenSettings screenSettings = bookmark.getAdvancedSettings().getScreen3G();
screenSettings.setColors(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_COLORS_3G)));
screenSettings.setResolution(
cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_RESOLUTION_3G)));
screenSettings.setWidth(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_WIDTH_3G)));
screenSettings.setHeight(cursor.getInt(cursor.getColumnIndex(KEY_SCREEN_HEIGHT_3G)));
}
private void readPerformanceFlags3G(BookmarkBase bookmark, Cursor cursor)
{
BookmarkBase.PerformanceFlags perfFlags = bookmark.getAdvancedSettings().getPerformance3G();
perfFlags.setRemoteFX(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_RFX_3G)) != 0);
perfFlags.setGfx(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_GFX_3G)) != 0);
perfFlags.setH264(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_H264_3G)) != 0);
perfFlags.setWallpaper(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_WALLPAPER_3G)) !=
0);
perfFlags.setTheming(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_THEME_3G)) != 0);
perfFlags.setFullWindowDrag(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_DRAG_3G)) !=
0);
perfFlags.setMenuAnimations(
cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_MENU_ANIMATIONS_3G)) != 0);
perfFlags.setFontSmoothing(cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_FONTS_3G)) !=
0);
perfFlags.setDesktopComposition(
cursor.getInt(cursor.getColumnIndex(KEY_PERFORMANCE_COMPOSITION_3G)) != 0);
}
private void fillScreenSettingsContentValues(BookmarkBase.ScreenSettings settings,
ContentValues values)
{
values.put(BookmarkDB.DB_KEY_SCREEN_COLORS, settings.getColors());
values.put(BookmarkDB.DB_KEY_SCREEN_RESOLUTION, settings.getResolution());
values.put(BookmarkDB.DB_KEY_SCREEN_WIDTH, settings.getWidth());
values.put(BookmarkDB.DB_KEY_SCREEN_HEIGHT, settings.getHeight());
}
private void fillPerformanceFlagsContentValues(BookmarkBase.PerformanceFlags perfFlags,
ContentValues values)
{
values.put(BookmarkDB.DB_KEY_PERFORMANCE_RFX, perfFlags.getRemoteFX());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_GFX, perfFlags.getGfx());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_H264, perfFlags.getH264());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_WALLPAPER, perfFlags.getWallpaper());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_THEME, perfFlags.getTheming());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_DRAG, perfFlags.getFullWindowDrag());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_MENU_ANIMATIONS, perfFlags.getMenuAnimations());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_FONTS, perfFlags.getFontSmoothing());
values.put(BookmarkDB.DB_KEY_PERFORMANCE_COMPOSITION, perfFlags.getDesktopComposition());
}
private long insertScreenSettings(SQLiteDatabase db, BookmarkBase.ScreenSettings settings)
{
ContentValues values = new ContentValues();
fillScreenSettingsContentValues(settings, values);
return db.insertOrThrow(BookmarkDB.DB_TABLE_SCREEN, null, values);
}
private boolean updateScreenSettings(SQLiteDatabase db, BookmarkBase bookmark)
{
ContentValues values = new ContentValues();
fillScreenSettingsContentValues(bookmark.getScreenSettings(), values);
String whereClause = BookmarkDB.ID + " IN "
+ "(SELECT " + BookmarkDB.DB_KEY_SCREEN_SETTINGS + " FROM " +
getBookmarkTableName() + " WHERE " + BookmarkDB.ID + " = " +
bookmark.getId() + ");";
return (db.update(BookmarkDB.DB_TABLE_SCREEN, values, whereClause, null) == 1);
}
private boolean updateScreenSettings3G(SQLiteDatabase db, BookmarkBase bookmark)
{
ContentValues values = new ContentValues();
fillScreenSettingsContentValues(bookmark.getAdvancedSettings().getScreen3G(), values);
String whereClause = BookmarkDB.ID + " IN "
+ "(SELECT " + BookmarkDB.DB_KEY_SCREEN_SETTINGS_3G + " FROM " +
getBookmarkTableName() + " WHERE " + BookmarkDB.ID + " = " +
bookmark.getId() + ");";
return (db.update(BookmarkDB.DB_TABLE_SCREEN, values, whereClause, null) == 1);
}
private long insertPerformanceFlags(SQLiteDatabase db, BookmarkBase.PerformanceFlags perfFlags)
{
ContentValues values = new ContentValues();
fillPerformanceFlagsContentValues(perfFlags, values);
return db.insertOrThrow(BookmarkDB.DB_TABLE_PERFORMANCE, null, values);
}
private boolean updatePerformanceFlags(SQLiteDatabase db, BookmarkBase bookmark)
{
ContentValues values = new ContentValues();
fillPerformanceFlagsContentValues(bookmark.getPerformanceFlags(), values);
String whereClause = BookmarkDB.ID + " IN "
+ "(SELECT " + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS + " FROM " +
getBookmarkTableName() + " WHERE " + BookmarkDB.ID + " = " +
bookmark.getId() + ");";
return (db.update(BookmarkDB.DB_TABLE_PERFORMANCE, values, whereClause, null) == 1);
}
private boolean updatePerformanceFlags3G(SQLiteDatabase db, BookmarkBase bookmark)
{
ContentValues values = new ContentValues();
fillPerformanceFlagsContentValues(bookmark.getAdvancedSettings().getPerformance3G(),
values);
String whereClause = BookmarkDB.ID + " IN "
+ "(SELECT " + BookmarkDB.DB_KEY_PERFORMANCE_FLAGS_3G + " FROM " +
getBookmarkTableName() + " WHERE " + BookmarkDB.ID + " = " +
bookmark.getId() + ");";
return (db.update(BookmarkDB.DB_TABLE_PERFORMANCE, values, whereClause, null) == 1);
}
// safety wrappers
// in case of getReadableDatabase it could happen that upgradeDB gets called which is
// a problem if the DB is only readable
private SQLiteDatabase getWritableDatabase()
{
return bookmarkDB.getWritableDatabase();
}
private SQLiteDatabase getReadableDatabase()
{
SQLiteDatabase db;
try
{
db = bookmarkDB.getReadableDatabase();
}
catch (SQLiteException e)
{
db = bookmarkDB.getWritableDatabase();
}
return db;
}
}

View File

@@ -0,0 +1,412 @@
/*
Android Bookmark Database
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class BookmarkDB extends SQLiteOpenHelper
{
public static final String ID = BaseColumns._ID;
private static final int DB_VERSION = 10;
private static final String DB_BACKUP_PREFIX = "temp_";
private static final String DB_NAME = "bookmarks.db";
static final String DB_TABLE_BOOKMARK = "tbl_manual_bookmarks";
static final String DB_TABLE_SCREEN = "tbl_screen_settings";
static final String DB_TABLE_PERFORMANCE = "tbl_performance_flags";
private static final String[] DB_TABLES = { DB_TABLE_BOOKMARK, DB_TABLE_SCREEN,
DB_TABLE_PERFORMANCE };
static final String DB_KEY_SCREEN_COLORS = "colors";
static final String DB_KEY_SCREEN_RESOLUTION = "resolution";
static final String DB_KEY_SCREEN_WIDTH = "width";
static final String DB_KEY_SCREEN_HEIGHT = "height";
static final String DB_KEY_SCREEN_SETTINGS = "screen_settings";
static final String DB_KEY_SCREEN_SETTINGS_3G = "screen_3g";
static final String DB_KEY_PERFORMANCE_FLAGS = "performance_flags";
static final String DB_KEY_PERFORMANCE_FLAGS_3G = "performance_3g";
static final String DB_KEY_PERFORMANCE_RFX = "perf_remotefx";
static final String DB_KEY_PERFORMANCE_GFX = "perf_gfx";
static final String DB_KEY_PERFORMANCE_H264 = "perf_gfx_h264";
static final String DB_KEY_PERFORMANCE_WALLPAPER = "perf_wallpaper";
static final String DB_KEY_PERFORMANCE_THEME = "perf_theming";
static final String DB_KEY_PERFORMANCE_DRAG = "perf_full_window_drag";
static final String DB_KEY_PERFORMANCE_MENU_ANIMATIONS = "perf_menu_animations";
static final String DB_KEY_PERFORMANCE_FONTS = "perf_font_smoothing";
static final String DB_KEY_PERFORMANCE_COMPOSITION = "perf_desktop_composition";
static final String DB_KEY_BOOKMARK_LABEL = "label";
static final String DB_KEY_BOOKMARK_HOSTNAME = "hostname";
static final String DB_KEY_BOOKMARK_USERNAME = "username";
static final String DB_KEY_BOOKMARK_PASSWORD = "password";
static final String DB_KEY_BOOKMARK_DOMAIN = "domain";
static final String DB_KEY_BOOKMARK_PORT = "port";
static final String DB_KEY_BOOKMARK_REDIRECT_SDCARD = "redirect_sdcard";
static final String DB_KEY_BOOKMARK_REDIRECT_SOUND = "redirect_sound";
static final String DB_KEY_BOOKMARK_REDIRECT_MICROPHONE = "redirect_microphone";
static final String DB_KEY_BOOKMARK_SECURITY = "security";
static final String DB_KEY_BOOKMARK_REMOTE_PROGRAM = "remote_program";
static final String DB_KEY_BOOKMARK_WORK_DIR = "work_dir";
static final String DB_KEY_BOOKMARK_ASYNC_CHANNEL = "async_channel";
static final String DB_KEY_BOOKMARK_ASYNC_UPDATE = "async_update";
static final String DB_KEY_BOOKMARK_CONSOLE_MODE = "console_mode";
static final String DB_KEY_BOOKMARK_DEBUG_LEVEL = "debug_level";
static final String DB_KEY_BOOKMARK_GW_ENABLE = "enable_gateway_settings";
static final String DB_KEY_BOOKMARK_GW_HOSTNAME = "gateway_hostname";
static final String DB_KEY_BOOKMARK_GW_PORT = "gateway_port";
static final String DB_KEY_BOOKMARK_GW_USERNAME = "gateway_username";
static final String DB_KEY_BOOKMARK_GW_PASSWORD = "gateway_password";
static final String DB_KEY_BOOKMARK_GW_DOMAIN = "gateway_domain";
static final String DB_KEY_BOOKMARK_3G_ENABLE = "enable_3g_settings";
public BookmarkDB(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
private static List<String> GetColumns(SQLiteDatabase db, String tableName)
{
List<String> ar = null;
try (Cursor c = db.rawQuery("SELECT * FROM " + tableName + " LIMIT 1", null))
{
if (c != null)
{
ar = new ArrayList<>(Arrays.asList(c.getColumnNames()));
}
}
catch (Exception e)
{
Log.v(tableName, e.getMessage(), e);
e.printStackTrace();
}
return ar;
}
private static String joinStrings(List<String> list, String delim)
{
StringBuilder buf = new StringBuilder();
int num = list.size();
for (int i = 0; i < num; i++)
{
if (i != 0)
buf.append(delim);
buf.append(list.get(i));
}
return buf.toString();
}
private void backupTables(SQLiteDatabase db)
{
for (String table : DB_TABLES)
{
final String tmpTable = DB_BACKUP_PREFIX + table;
final String query = "ALTER TABLE '" + table + "' RENAME TO '" + tmpTable + "'";
try
{
db.execSQL(query);
}
catch (Exception e)
{
/* Ignore errors if table does not exist. */
}
}
}
private void dropOldTables(SQLiteDatabase db)
{
for (String table : DB_TABLES)
{
final String tmpTable = DB_BACKUP_PREFIX + table;
final String query = "DROP TABLE IF EXISTS '" + tmpTable + "'";
db.execSQL(query);
}
}
private void createDB(SQLiteDatabase db)
{
final String sqlScreenSettings =
"CREATE TABLE IF NOT EXISTS " + DB_TABLE_SCREEN + " (" + ID + " INTEGER PRIMARY KEY, " +
DB_KEY_SCREEN_COLORS + " INTEGER DEFAULT 16, " + DB_KEY_SCREEN_RESOLUTION +
" INTEGER DEFAULT 0, " + DB_KEY_SCREEN_WIDTH + ", " + DB_KEY_SCREEN_HEIGHT + ");";
db.execSQL(sqlScreenSettings);
final String sqlPerformanceFlags =
"CREATE TABLE IF NOT EXISTS " + DB_TABLE_PERFORMANCE + " (" + ID +
" INTEGER PRIMARY KEY, " + DB_KEY_PERFORMANCE_RFX + " INTEGER, " +
DB_KEY_PERFORMANCE_GFX + " INTEGER, " + DB_KEY_PERFORMANCE_H264 + " INTEGER, " +
DB_KEY_PERFORMANCE_WALLPAPER + " INTEGER, " + DB_KEY_PERFORMANCE_THEME + " INTEGER, " +
DB_KEY_PERFORMANCE_DRAG + " INTEGER, " + DB_KEY_PERFORMANCE_MENU_ANIMATIONS +
" INTEGER, " + DB_KEY_PERFORMANCE_FONTS + " INTEGER, " +
DB_KEY_PERFORMANCE_COMPOSITION + " INTEGER);";
db.execSQL(sqlPerformanceFlags);
final String sqlManualBookmarks = getManualBookmarksCreationString();
db.execSQL(sqlManualBookmarks);
}
private void upgradeTables(SQLiteDatabase db)
{
for (String table : DB_TABLES)
{
final String tmpTable = DB_BACKUP_PREFIX + table;
final List<String> newColumns = GetColumns(db, table);
List<String> columns = GetColumns(db, tmpTable);
if (columns != null)
{
columns.retainAll(newColumns);
// restore data
final String cols = joinStrings(columns, ",");
final String query = String.format("INSERT INTO %s (%s) SELECT %s from '%s'", table,
cols, cols, tmpTable);
db.execSQL(query);
}
}
}
private void downgradeTables(SQLiteDatabase db)
{
for (String table : DB_TABLES)
{
final String tmpTable = DB_BACKUP_PREFIX + table;
List<String> oldColumns = GetColumns(db, table);
final List<String> columns = GetColumns(db, tmpTable);
if (oldColumns != null)
{
oldColumns.retainAll(columns);
// restore data
final String cols = joinStrings(oldColumns, ",");
final String query = String.format("INSERT INTO %s (%s) SELECT %s from '%s'", table,
cols, cols, tmpTable);
db.execSQL(query);
}
}
}
private List<String> getTableNames(SQLiteDatabase db)
{
final String query = "SELECT name FROM sqlite_master WHERE type='table'";
Cursor cursor = db.rawQuery(query, null);
List<String> list = new ArrayList<>();
try
{
if (cursor.moveToFirst() && (cursor.getCount() > 0))
{
while (!cursor.isAfterLast())
{
final String name = cursor.getString(cursor.getColumnIndex("name"));
list.add(name);
cursor.moveToNext();
}
}
}
finally
{
cursor.close();
}
return list;
}
private void insertDefault(SQLiteDatabase db)
{
ContentValues screenValues = new ContentValues();
screenValues.put(DB_KEY_SCREEN_COLORS, 32);
screenValues.put(DB_KEY_SCREEN_RESOLUTION, 1);
screenValues.put(DB_KEY_SCREEN_WIDTH, 1024);
screenValues.put(DB_KEY_SCREEN_HEIGHT, 768);
final long idScreen = db.insert(DB_TABLE_SCREEN, null, screenValues);
final long idScreen3g = db.insert(DB_TABLE_SCREEN, null, screenValues);
ContentValues performanceValues = new ContentValues();
performanceValues.put(DB_KEY_PERFORMANCE_RFX, 1);
performanceValues.put(DB_KEY_PERFORMANCE_GFX, 1);
performanceValues.put(DB_KEY_PERFORMANCE_H264, 0);
performanceValues.put(DB_KEY_PERFORMANCE_WALLPAPER, 0);
performanceValues.put(DB_KEY_PERFORMANCE_THEME, 0);
performanceValues.put(DB_KEY_PERFORMANCE_DRAG, 0);
performanceValues.put(DB_KEY_PERFORMANCE_MENU_ANIMATIONS, 0);
performanceValues.put(DB_KEY_PERFORMANCE_FONTS, 0);
performanceValues.put(DB_KEY_PERFORMANCE_COMPOSITION, 0);
final long idPerformance = db.insert(DB_TABLE_PERFORMANCE, null, performanceValues);
final long idPerformance3g = db.insert(DB_TABLE_PERFORMANCE, null, performanceValues);
ContentValues bookmarkValues = new ContentValues();
bookmarkValues.put(DB_KEY_BOOKMARK_LABEL, "Test Server");
bookmarkValues.put(DB_KEY_BOOKMARK_HOSTNAME, "testservice.afreerdp.com");
bookmarkValues.put(DB_KEY_BOOKMARK_USERNAME, "");
bookmarkValues.put(DB_KEY_BOOKMARK_PASSWORD, "");
bookmarkValues.put(DB_KEY_BOOKMARK_DOMAIN, "");
bookmarkValues.put(DB_KEY_BOOKMARK_PORT, "3389");
bookmarkValues.put(DB_KEY_SCREEN_SETTINGS, idScreen);
bookmarkValues.put(DB_KEY_SCREEN_SETTINGS_3G, idScreen3g);
bookmarkValues.put(DB_KEY_PERFORMANCE_FLAGS, idPerformance);
bookmarkValues.put(DB_KEY_PERFORMANCE_FLAGS_3G, idPerformance3g);
bookmarkValues.put(DB_KEY_BOOKMARK_REDIRECT_SDCARD, 0);
bookmarkValues.put(DB_KEY_BOOKMARK_REDIRECT_SOUND, 0);
bookmarkValues.put(DB_KEY_BOOKMARK_REDIRECT_MICROPHONE, 0);
bookmarkValues.put(DB_KEY_BOOKMARK_SECURITY, 0);
bookmarkValues.put(DB_KEY_BOOKMARK_REMOTE_PROGRAM, "");
bookmarkValues.put(DB_KEY_BOOKMARK_WORK_DIR, "");
bookmarkValues.put(DB_KEY_BOOKMARK_ASYNC_CHANNEL, 1);
bookmarkValues.put(DB_KEY_BOOKMARK_ASYNC_UPDATE, 1);
bookmarkValues.put(DB_KEY_BOOKMARK_CONSOLE_MODE, 0);
bookmarkValues.put(DB_KEY_BOOKMARK_DEBUG_LEVEL, "INFO");
db.insert(DB_TABLE_BOOKMARK, null, bookmarkValues);
}
@Override public void onCreate(SQLiteDatabase db)
{
createDB(db);
insertDefault(db);
}
private String getManualBookmarksCreationString()
{
return ("CREATE TABLE IF NOT EXISTS " + DB_TABLE_BOOKMARK + " (" + ID +
" INTEGER PRIMARY KEY, " + DB_KEY_BOOKMARK_LABEL + " TEXT NOT NULL, " +
DB_KEY_BOOKMARK_HOSTNAME + " TEXT NOT NULL, " + DB_KEY_BOOKMARK_USERNAME +
" TEXT NOT NULL, " + DB_KEY_BOOKMARK_PASSWORD + " TEXT, " + DB_KEY_BOOKMARK_DOMAIN +
" TEXT, " + DB_KEY_BOOKMARK_PORT + " TEXT, " + DB_KEY_SCREEN_SETTINGS +
" INTEGER NOT NULL, " + DB_KEY_PERFORMANCE_FLAGS + " INTEGER NOT NULL, "
+ DB_KEY_BOOKMARK_GW_ENABLE + " INTEGER DEFAULT 0, " + DB_KEY_BOOKMARK_GW_HOSTNAME +
" TEXT, " + DB_KEY_BOOKMARK_GW_PORT + " INTEGER DEFAULT 443, " +
DB_KEY_BOOKMARK_GW_USERNAME + " TEXT, " + DB_KEY_BOOKMARK_GW_PASSWORD + " TEXT, " +
DB_KEY_BOOKMARK_GW_DOMAIN + " TEXT, "
+ DB_KEY_BOOKMARK_3G_ENABLE + " INTEGER DEFAULT 0, " + DB_KEY_SCREEN_SETTINGS_3G +
" INTEGER NOT NULL, " + DB_KEY_PERFORMANCE_FLAGS_3G + " INTEGER NOT NULL, " +
DB_KEY_BOOKMARK_REDIRECT_SDCARD + " INTEGER DEFAULT 0, " +
DB_KEY_BOOKMARK_REDIRECT_SOUND + " INTEGER DEFAULT 0, " +
DB_KEY_BOOKMARK_REDIRECT_MICROPHONE + " INTEGER DEFAULT 0, " +
DB_KEY_BOOKMARK_SECURITY + " INTEGER, " + DB_KEY_BOOKMARK_REMOTE_PROGRAM +
" TEXT, " + DB_KEY_BOOKMARK_WORK_DIR + " TEXT, " + DB_KEY_BOOKMARK_ASYNC_CHANNEL +
" INTEGER DEFAULT 0, " + DB_KEY_BOOKMARK_ASYNC_UPDATE + " INTEGER DEFAULT 0, " +
DB_KEY_BOOKMARK_CONSOLE_MODE + " INTEGER, " + DB_KEY_BOOKMARK_DEBUG_LEVEL +
" TEXT DEFAULT 'INFO', "
+ "FOREIGN KEY(" + DB_KEY_SCREEN_SETTINGS + ") REFERENCES " + DB_TABLE_SCREEN +
"(" + ID + "), "
+ "FOREIGN KEY(" + DB_KEY_PERFORMANCE_FLAGS + ") REFERENCES " +
DB_TABLE_PERFORMANCE + "(" + ID + "), "
+ "FOREIGN KEY(" + DB_KEY_SCREEN_SETTINGS_3G + ") REFERENCES " + DB_TABLE_SCREEN +
"(" + ID + "), "
+ "FOREIGN KEY(" + DB_KEY_PERFORMANCE_FLAGS_3G + ") REFERENCES " +
DB_TABLE_PERFORMANCE + "(" + ID + ") "
+ ");");
}
private void recreateDB(SQLiteDatabase db)
{
for (String table : DB_TABLES)
{
final String query = "DROP TABLE IF EXISTS '" + table + "'";
db.execSQL(query);
}
onCreate(db);
}
private void upgradeDB(SQLiteDatabase db)
{
db.beginTransaction();
try
{
/* Back up old tables. */
dropOldTables(db);
backupTables(db);
createDB(db);
upgradeTables(db);
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
dropOldTables(db);
}
}
private void downgradeDB(SQLiteDatabase db)
{
db.beginTransaction();
try
{
/* Back up old tables. */
dropOldTables(db);
backupTables(db);
createDB(db);
downgradeTables(db);
db.setTransactionSuccessful();
}
finally
{
db.endTransaction();
dropOldTables(db);
}
}
// from
// http://stackoverflow.com/questions/3424156/upgrade-sqlite-database-from-one-version-to-another
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
switch (oldVersion)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
upgradeDB(db);
break;
default:
recreateDB(db);
break;
}
}
@Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
downgradeDB(db);
}
}

View File

@@ -0,0 +1,134 @@
/*
Suggestion Provider for RDP bookmarks
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import com.freerdp.freerdpcore.R;
import com.freerdp.freerdpcore.application.GlobalApp;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.ConnectionReference;
import com.freerdp.freerdpcore.domain.ManualBookmark;
import java.util.ArrayList;
public class FreeRDPSuggestionProvider extends ContentProvider
{
public static final Uri CONTENT_URI =
Uri.parse("content://com.freerdp.afreerdp.services.freerdpsuggestionprovider");
@Override public int delete(Uri uri, String selection, String[] selectionArgs)
{
// TODO Auto-generated method stub
return 0;
}
@Override public String getType(Uri uri)
{
return "vnd.android.cursor.item/vnd.freerdp.remote";
}
@Override public Uri insert(Uri uri, ContentValues values)
{
// TODO Auto-generated method stub
return null;
}
@Override public boolean onCreate()
{
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder)
{
String query = (selectionArgs != null && selectionArgs.length > 0) ? selectionArgs[0] : "";
// search history
ArrayList<BookmarkBase> history =
GlobalApp.getQuickConnectHistoryGateway().findHistory(query);
// search bookmarks
ArrayList<BookmarkBase> manualBookmarks;
if (query.length() > 0)
manualBookmarks = GlobalApp.getManualBookmarkGateway().findByLabelOrHostnameLike(query);
else
manualBookmarks = GlobalApp.getManualBookmarkGateway().findAll();
return createResultCursor(history, manualBookmarks);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
// TODO Auto-generated method stub
return 0;
}
private void addBookmarksToCursor(ArrayList<BookmarkBase> bookmarks, MatrixCursor resultCursor)
{
Object[] row = new Object[5];
for (BookmarkBase bookmark : bookmarks)
{
row[0] = bookmark.getId();
row[1] = bookmark.getLabel();
row[2] = bookmark.<ManualBookmark>get().getHostname();
row[3] = ConnectionReference.getManualBookmarkReference(bookmark.getId());
row[4] = "android.resource://" + getContext().getPackageName() + "/" +
R.drawable.icon_star_on;
resultCursor.addRow(row);
}
}
private void addHistoryToCursor(ArrayList<BookmarkBase> history, MatrixCursor resultCursor)
{
Object[] row = new Object[5];
for (BookmarkBase bookmark : history)
{
row[0] = 1;
row[1] = bookmark.getLabel();
row[2] = bookmark.getLabel();
row[3] = ConnectionReference.getHostnameReference(bookmark.getLabel());
row[4] = "android.resource://" + getContext().getPackageName() + "/" +
R.drawable.icon_star_off;
resultCursor.addRow(row);
}
}
private Cursor createResultCursor(ArrayList<BookmarkBase> history,
ArrayList<BookmarkBase> manualBookmarks)
{
// create result matrix cursor
int totalCount = history.size() + manualBookmarks.size();
String[] columns = { android.provider.BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_ICON_2 };
MatrixCursor matrixCursor = new MatrixCursor(columns, totalCount);
// populate result matrix
if (totalCount > 0)
{
addHistoryToCursor(history, matrixCursor);
addBookmarksToCursor(manualBookmarks, matrixCursor);
}
return matrixCursor;
}
}

View File

@@ -0,0 +1,46 @@
/*
Quick Connect History Database
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class HistoryDB extends SQLiteOpenHelper
{
public static final String QUICK_CONNECT_TABLE_NAME = "quick_connect_history";
public static final String QUICK_CONNECT_TABLE_COL_ITEM = "item";
public static final String QUICK_CONNECT_TABLE_COL_TIMESTAMP = "timestamp";
private static final int DB_VERSION = 1;
private static final String DB_NAME = "history.db";
public HistoryDB(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
@Override public void onCreate(SQLiteDatabase db)
{
String sqlQuickConnectHistory = "CREATE TABLE " + QUICK_CONNECT_TABLE_NAME + " (" +
QUICK_CONNECT_TABLE_COL_ITEM + " TEXT PRIMARY KEY, " +
QUICK_CONNECT_TABLE_COL_TIMESTAMP + " INTEGER"
+ ");";
db.execSQL(sqlQuickConnectHistory);
}
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
// TODO Auto-generated method stub
}
}

View File

@@ -0,0 +1,681 @@
/*
Android FreeRDP JNI Wrapper
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.util.Log;
import androidx.collection.LongSparseArray;
import com.freerdp.freerdpcore.application.GlobalApp;
import com.freerdp.freerdpcore.application.SessionState;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.ManualBookmark;
import com.freerdp.freerdpcore.presentation.ApplicationSettingsActivity;
import java.util.ArrayList;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LibFreeRDP
{
private static final String TAG = "LibFreeRDP";
private static EventListener listener;
private static boolean mHasH264 = false;
private static final LongSparseArray<Boolean> mInstanceState = new LongSparseArray<>();
public static final long VERIFY_CERT_FLAG_NONE = 0x00;
public static final long VERIFY_CERT_FLAG_LEGACY = 0x02;
public static final long VERIFY_CERT_FLAG_REDIRECT = 0x10;
public static final long VERIFY_CERT_FLAG_GATEWAY = 0x20;
public static final long VERIFY_CERT_FLAG_CHANGED = 0x40;
public static final long VERIFY_CERT_FLAG_MISMATCH = 0x80;
public static final long VERIFY_CERT_FLAG_MATCH_LEGACY_SHA1 = 0x100;
public static final long VERIFY_CERT_FLAG_FP_IS_PEM = 0x200;
private static boolean tryLoad(String[] libraries)
{
boolean success = false;
final String LD_PATH = System.getProperty("java.library.path");
for (String lib : libraries)
{
try
{
Log.v(TAG, "Trying to load library " + lib + " from LD_PATH: " + LD_PATH);
System.loadLibrary(lib);
success = true;
}
catch (UnsatisfiedLinkError e)
{
Log.e(TAG, "Failed to load library " + lib + ": " + e);
success = false;
break;
}
}
return success;
}
private static boolean tryLoad(String library)
{
return tryLoad(new String[] { library });
}
static
{
try
{
System.loadLibrary("freerdp-android");
/* Load dependent libraries too to trigger JNI_OnLoad calls */
String version = freerdp_get_jni_version();
String[] versions = version.split("[\\.-]");
if (versions.length > 0)
{
System.loadLibrary("freerdp-client" + versions[0]);
System.loadLibrary("freerdp" + versions[0]);
System.loadLibrary("winpr" + versions[0]);
}
Pattern pattern = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+).*");
Matcher matcher = pattern.matcher(version);
if (!matcher.matches() || (matcher.groupCount() < 3))
throw new RuntimeException("APK broken: native library version " + version +
" does not meet requirements!");
int major = Integer.parseInt(Objects.requireNonNull(matcher.group(1)));
int minor = Integer.parseInt(Objects.requireNonNull(matcher.group(2)));
int patch = Integer.parseInt(Objects.requireNonNull(matcher.group(3)));
if (major > 2)
mHasH264 = freerdp_has_h264();
else if (minor > 5)
mHasH264 = freerdp_has_h264();
else if ((minor == 5) && (patch >= 1))
mHasH264 = freerdp_has_h264();
else
throw new RuntimeException("APK broken: native library version " + version +
" does not meet requirements!");
Log.i(TAG, "Successfully loaded native library. H264 is " +
(mHasH264 ? "supported" : "not available"));
}
catch (UnsatisfiedLinkError e)
{
Log.e(TAG, "Failed to load library: " + e);
throw e;
}
}
public static boolean hasH264Support()
{
return mHasH264;
}
private static native boolean freerdp_has_h264();
private static native String freerdp_get_jni_version();
private static native String freerdp_get_version();
private static native String freerdp_get_build_revision();
private static native String freerdp_get_build_config();
private static native long freerdp_new(Context context);
private static native void freerdp_free(long inst);
private static native boolean freerdp_parse_arguments(long inst, String[] args);
private static native boolean freerdp_connect(long inst);
private static native boolean freerdp_disconnect(long inst);
private static native boolean freerdp_update_graphics(long inst, Bitmap bitmap, int x, int y,
int width, int height);
private static native boolean freerdp_send_cursor_event(long inst, int x, int y, int flags);
private static native boolean freerdp_send_key_event(long inst, int keycode, boolean down);
private static native boolean freerdp_send_unicodekey_event(long inst, int keycode,
boolean down);
private static native boolean freerdp_send_clipboard_data(long inst, String data);
private static native String freerdp_get_last_error_string(long inst);
public static void setEventListener(EventListener l)
{
listener = l;
}
public static long newInstance(Context context)
{
return freerdp_new(context);
}
public static void freeInstance(long inst)
{
synchronized (mInstanceState)
{
if (mInstanceState.get(inst, false))
{
freerdp_disconnect(inst);
}
while (mInstanceState.get(inst, false))
{
try
{
mInstanceState.wait();
}
catch (InterruptedException e)
{
throw new RuntimeException();
}
}
}
freerdp_free(inst);
}
public static boolean connect(long inst)
{
synchronized (mInstanceState)
{
if (mInstanceState.get(inst, false))
{
throw new RuntimeException("instance already connected");
}
}
return freerdp_connect(inst);
}
public static boolean disconnect(long inst)
{
synchronized (mInstanceState)
{
if (mInstanceState.get(inst, false))
{
return freerdp_disconnect(inst);
}
return true;
}
}
public static boolean cancelConnection(long inst)
{
synchronized (mInstanceState)
{
if (mInstanceState.get(inst, false))
{
return freerdp_disconnect(inst);
}
return true;
}
}
private static String addFlag(String name, boolean enabled)
{
if (enabled)
{
return "+" + name;
}
return "-" + name;
}
public static boolean setConnectionInfo(Context context, long inst, BookmarkBase bookmark)
{
BookmarkBase.ScreenSettings screenSettings = bookmark.getActiveScreenSettings();
BookmarkBase.AdvancedSettings advanced = bookmark.getAdvancedSettings();
BookmarkBase.DebugSettings debug = bookmark.getDebugSettings();
String arg;
ArrayList<String> args = new ArrayList<>();
args.add(TAG);
args.add("/gdi:sw");
final String clientName = ApplicationSettingsActivity.getClientName(context);
if (!clientName.isEmpty())
{
args.add("/client-hostname:" + clientName);
}
String certName = "";
if (bookmark.getType() != BookmarkBase.TYPE_MANUAL)
{
return false;
}
int port = bookmark.<ManualBookmark>get().getPort();
String hostname = bookmark.<ManualBookmark>get().getHostname();
args.add("/v:" + hostname);
args.add("/port:" + port);
arg = bookmark.getUsername();
if (!arg.isEmpty())
{
args.add("/u:" + arg);
}
arg = bookmark.getDomain();
if (!arg.isEmpty())
{
args.add("/d:" + arg);
}
arg = bookmark.getPassword();
if (!arg.isEmpty())
{
args.add("/p:" + arg);
}
args.add(
String.format("/size:%dx%d", screenSettings.getWidth(), screenSettings.getHeight()));
args.add("/bpp:" + screenSettings.getColors());
if (advanced.getConsoleMode())
{
args.add("/admin");
}
switch (advanced.getSecurity())
{
case 3: // NLA
args.add("/sec:nla");
break;
case 2: // TLS
args.add("/sec:tls");
break;
case 1: // RDP
args.add("/sec:rdp");
break;
default:
break;
}
if (!certName.isEmpty())
{
args.add("/cert-name:" + certName);
}
BookmarkBase.PerformanceFlags flags = bookmark.getActivePerformanceFlags();
if (flags.getRemoteFX())
{
args.add("/rfx");
args.add("/network:auto");
}
if (flags.getGfx())
{
args.add("/gfx");
args.add("/network:auto");
}
if (flags.getH264() && mHasH264)
{
args.add("/gfx:AVC444");
args.add("/network:auto");
}
args.add(addFlag("wallpaper", flags.getWallpaper()));
args.add(addFlag("window-drag", flags.getFullWindowDrag()));
args.add(addFlag("menu-anims", flags.getMenuAnimations()));
args.add(addFlag("themes", flags.getTheming()));
args.add(addFlag("fonts", flags.getFontSmoothing()));
args.add(addFlag("aero", flags.getDesktopComposition()));
if (!advanced.getRemoteProgram().isEmpty())
{
args.add("/shell:" + advanced.getRemoteProgram());
}
if (!advanced.getWorkDir().isEmpty())
{
args.add("/shell-dir:" + advanced.getWorkDir());
}
args.add(addFlag("async-channels", debug.getAsyncChannel()));
args.add(addFlag("async-update", debug.getAsyncUpdate()));
if (advanced.getRedirectSDCard())
{
String path = android.os.Environment.getExternalStorageDirectory().getPath();
args.add("/drive:sdcard," + path);
}
args.add("/clipboard");
// Gateway enabled?
if (bookmark.getType() == BookmarkBase.TYPE_MANUAL &&
bookmark.<ManualBookmark>get().getEnableGatewaySettings())
{
ManualBookmark.GatewaySettings gateway =
bookmark.<ManualBookmark>get().getGatewaySettings();
StringBuilder carg = new StringBuilder();
carg.append(
String.format("/gateway:g:%s:%d", gateway.getHostname(), gateway.getPort()));
arg = gateway.getUsername();
if (!arg.isEmpty())
{
carg.append(",u:" + arg);
}
arg = gateway.getDomain();
if (!arg.isEmpty())
{
carg.append(",d:" + arg);
}
arg = gateway.getPassword();
if (!arg.isEmpty())
{
carg.append(",p:" + arg);
}
args.add(carg.toString());
}
/* 0 ... local
1 ... remote
2 ... disable */
args.add("/audio-mode:" + advanced.getRedirectSound());
if (advanced.getRedirectSound() == 0)
{
args.add("/sound");
}
if (advanced.getRedirectMicrophone())
{
args.add("/microphone");
}
args.add("/kbd:unicode:on");
args.add("/cert:ignore");
args.add("/log-level:" + debug.getDebugLevel());
String[] arrayArgs = args.toArray(new String[0]);
return freerdp_parse_arguments(inst, arrayArgs);
}
public static boolean setConnectionInfo(Context context, long inst, Uri openUri)
{
ArrayList<String> args = new ArrayList<>();
// Parse URI from query string. Same key overwrite previous one
// freerdp://user@ip:port/connect?sound=&rfx=&p=password&clipboard=%2b&themes=-
// Now we only support Software GDI
args.add(TAG);
args.add("/gdi:sw");
final String clientName = ApplicationSettingsActivity.getClientName(context);
if (!clientName.isEmpty())
{
args.add("/client-hostname:" + clientName);
}
// Parse hostname and port. Set to 'v' argument
String hostname = openUri.getHost();
int port = openUri.getPort();
if (hostname != null)
{
hostname = hostname + ((port == -1) ? "" : (":" + port));
args.add("/v:" + hostname);
}
String user = openUri.getUserInfo();
if (user != null)
{
args.add("/u:" + user);
}
for (String key : openUri.getQueryParameterNames())
{
String value = openUri.getQueryParameter(key);
if (value.isEmpty())
{
// Query: key=
// To freerdp argument: /key
args.add("/" + key);
}
else if (value.equals("-") || value.equals("+"))
{
// Query: key=- or key=+
// To freerdp argument: -key or +key
args.add(value + key);
}
else
{
// Query: key=value
// To freerdp argument: /key:value
if (key.equals("drive") && value.equals("sdcard"))
{
// Special for sdcard redirect
String path = android.os.Environment.getExternalStorageDirectory().getPath();
value = "sdcard," + path;
}
args.add("/" + key + ":" + value);
}
}
String[] arrayArgs = args.toArray(new String[0]);
return freerdp_parse_arguments(inst, arrayArgs);
}
public static boolean updateGraphics(long inst, Bitmap bitmap, int x, int y, int width,
int height)
{
return freerdp_update_graphics(inst, bitmap, x, y, width, height);
}
public static boolean sendCursorEvent(long inst, int x, int y, int flags)
{
return freerdp_send_cursor_event(inst, x, y, flags);
}
public static boolean sendKeyEvent(long inst, int keycode, boolean down)
{
return freerdp_send_key_event(inst, keycode, down);
}
public static boolean sendUnicodeKeyEvent(long inst, int keycode, boolean down)
{
return freerdp_send_unicodekey_event(inst, keycode, down);
}
public static boolean sendClipboardData(long inst, String data)
{
return freerdp_send_clipboard_data(inst, data);
}
private static void OnConnectionSuccess(long inst)
{
if (listener != null)
listener.OnConnectionSuccess(inst);
synchronized (mInstanceState)
{
mInstanceState.append(inst, true);
mInstanceState.notifyAll();
}
}
private static void OnConnectionFailure(long inst)
{
if (listener != null)
listener.OnConnectionFailure(inst);
synchronized (mInstanceState)
{
mInstanceState.remove(inst);
mInstanceState.notifyAll();
}
}
private static void OnPreConnect(long inst)
{
if (listener != null)
listener.OnPreConnect(inst);
}
private static void OnDisconnecting(long inst)
{
if (listener != null)
listener.OnDisconnecting(inst);
}
private static void OnDisconnected(long inst)
{
if (listener != null)
listener.OnDisconnected(inst);
synchronized (mInstanceState)
{
mInstanceState.remove(inst);
mInstanceState.notifyAll();
}
}
private static void OnSettingsChanged(long inst, int width, int height, int bpp)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
uiEventListener.OnSettingsChanged(width, height, bpp);
}
private static boolean OnAuthenticate(long inst, StringBuilder username, StringBuilder domain,
StringBuilder password)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return false;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
return uiEventListener.OnAuthenticate(username, domain, password);
return false;
}
private static boolean OnGatewayAuthenticate(long inst, StringBuilder username,
StringBuilder domain, StringBuilder password)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return false;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
return uiEventListener.OnGatewayAuthenticate(username, domain, password);
return false;
}
private static int OnVerifyCertificateEx(long inst, String host, long port, String commonName,
String subject, String issuer, String fingerprint,
long flags)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return 0;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
return uiEventListener.OnVerifiyCertificateEx(host, port, commonName, subject, issuer,
fingerprint, flags);
return 0;
}
private static int OnVerifyChangedCertificateEx(long inst, String host, long port,
String commonName, String subject,
String issuer, String fingerprint,
String oldSubject, String oldIssuer,
String oldFingerprint, long flags)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return 0;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
return uiEventListener.OnVerifyChangedCertificateEx(host, port, commonName, subject,
issuer, fingerprint, oldSubject,
oldIssuer, oldFingerprint, flags);
return 0;
}
private static void OnGraphicsUpdate(long inst, int x, int y, int width, int height)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
uiEventListener.OnGraphicsUpdate(x, y, width, height);
}
private static void OnGraphicsResize(long inst, int width, int height, int bpp)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
uiEventListener.OnGraphicsResize(width, height, bpp);
}
private static void OnRemoteClipboardChanged(long inst, String data)
{
SessionState s = GlobalApp.getSession(inst);
if (s == null)
return;
UIEventListener uiEventListener = s.getUIEventListener();
if (uiEventListener != null)
uiEventListener.OnRemoteClipboardChanged(data);
}
public static String getVersion()
{
return freerdp_get_version();
}
public interface EventListener
{
void OnPreConnect(long instance);
void OnConnectionSuccess(long instance);
void OnConnectionFailure(long instance);
void OnDisconnecting(long instance);
void OnDisconnected(long instance);
}
public interface UIEventListener
{
void OnSettingsChanged(int width, int height, int bpp);
boolean OnAuthenticate(StringBuilder username, StringBuilder domain,
StringBuilder password);
boolean OnGatewayAuthenticate(StringBuilder username, StringBuilder domain,
StringBuilder password);
int OnVerifiyCertificateEx(String host, long port, String commonName, String subject, String issuer,
String fingerprint, long flags);
int OnVerifyChangedCertificateEx(String host, long port, String commonName, String subject, String issuer,
String fingerprint, String oldSubject, String oldIssuer,
String oldFingerprint, long flags);
void OnGraphicsUpdate(int x, int y, int width, int height);
void OnGraphicsResize(int width, int height, int bpp);
void OnRemoteClipboardChanged(String data);
}
}

View File

@@ -0,0 +1,131 @@
/*
Manual bookmarks database gateway
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteOpenHelper;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.ManualBookmark;
import java.util.ArrayList;
public class ManualBookmarkGateway extends BookmarkBaseGateway
{
public ManualBookmarkGateway(SQLiteOpenHelper bookmarkDB)
{
super(bookmarkDB);
}
@Override protected BookmarkBase createBookmark()
{
return new ManualBookmark();
}
@Override protected String getBookmarkTableName()
{
return BookmarkDB.DB_TABLE_BOOKMARK;
}
@Override
protected void addBookmarkSpecificColumns(BookmarkBase bookmark, ContentValues columns)
{
ManualBookmark bm = (ManualBookmark)bookmark;
columns.put(BookmarkDB.DB_KEY_BOOKMARK_HOSTNAME, bm.getHostname());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_PORT, bm.getPort());
// gateway settings
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_ENABLE, bm.getEnableGatewaySettings());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_HOSTNAME, bm.getGatewaySettings().getHostname());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_PORT, bm.getGatewaySettings().getPort());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_USERNAME, bm.getGatewaySettings().getUsername());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_PASSWORD, bm.getGatewaySettings().getPassword());
columns.put(BookmarkDB.DB_KEY_BOOKMARK_GW_DOMAIN, bm.getGatewaySettings().getDomain());
}
@Override protected void addBookmarkSpecificColumns(ArrayList<String> columns)
{
columns.add(BookmarkDB.DB_KEY_BOOKMARK_HOSTNAME);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_PORT);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_ENABLE);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_HOSTNAME);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_PORT);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_USERNAME);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_PASSWORD);
columns.add(BookmarkDB.DB_KEY_BOOKMARK_GW_DOMAIN);
}
@Override protected void readBookmarkSpecificColumns(BookmarkBase bookmark, Cursor cursor)
{
ManualBookmark bm = (ManualBookmark)bookmark;
bm.setHostname(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_HOSTNAME)));
bm.setPort(cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_PORT)));
bm.setEnableGatewaySettings(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_ENABLE)) != 0);
readGatewaySettings(bm, cursor);
}
public BookmarkBase findByLabelOrHostname(String pattern)
{
if (pattern.length() == 0)
return null;
Cursor cursor =
queryBookmarks(BookmarkDB.DB_KEY_BOOKMARK_LABEL + " = '" + pattern + "' OR " +
BookmarkDB.DB_KEY_BOOKMARK_HOSTNAME + " = '" + pattern + "'",
BookmarkDB.DB_KEY_BOOKMARK_LABEL);
BookmarkBase bookmark = null;
if (cursor.moveToFirst() && (cursor.getCount() > 0))
bookmark = getBookmarkFromCursor(cursor);
cursor.close();
return bookmark;
}
public ArrayList<BookmarkBase> findByLabelOrHostnameLike(String pattern)
{
Cursor cursor =
queryBookmarks(BookmarkDB.DB_KEY_BOOKMARK_LABEL + " LIKE '%" + pattern + "%' OR " +
BookmarkDB.DB_KEY_BOOKMARK_HOSTNAME + " LIKE '%" + pattern + "%'",
BookmarkDB.DB_KEY_BOOKMARK_LABEL);
ArrayList<BookmarkBase> bookmarks = new ArrayList<>(cursor.getCount());
if (cursor.moveToFirst() && (cursor.getCount() > 0))
{
do
{
bookmarks.add(getBookmarkFromCursor(cursor));
} while (cursor.moveToNext());
}
cursor.close();
return bookmarks;
}
private void readGatewaySettings(ManualBookmark bookmark, Cursor cursor)
{
ManualBookmark.GatewaySettings gatewaySettings = bookmark.getGatewaySettings();
gatewaySettings.setHostname(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_HOSTNAME)));
gatewaySettings.setPort(
cursor.getInt(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_PORT)));
gatewaySettings.setUsername(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_USERNAME)));
gatewaySettings.setPassword(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_PASSWORD)));
gatewaySettings.setDomain(
cursor.getString(cursor.getColumnIndex(BookmarkDB.DB_KEY_BOOKMARK_GW_DOMAIN)));
}
}

View File

@@ -0,0 +1,121 @@
/*
Quick connect history gateway
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file, You can obtain one at
http://mozilla.org/MPL/2.0/.
*/
package com.freerdp.freerdpcore.services;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.freerdp.freerdpcore.domain.BookmarkBase;
import com.freerdp.freerdpcore.domain.QuickConnectBookmark;
import java.util.ArrayList;
public class QuickConnectHistoryGateway
{
private final static String TAG = "QuickConnectHistoryGateway";
private final SQLiteOpenHelper historyDB;
public QuickConnectHistoryGateway(SQLiteOpenHelper historyDB)
{
this.historyDB = historyDB;
}
public ArrayList<BookmarkBase> findHistory(String filter)
{
String[] column = { HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM };
SQLiteDatabase db = getReadableDatabase();
String selection =
(filter.length() > 0)
? (HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM + " LIKE '%" + filter + "%'")
: null;
Cursor cursor = db.query(HistoryDB.QUICK_CONNECT_TABLE_NAME, column, selection, null, null,
null, HistoryDB.QUICK_CONNECT_TABLE_COL_TIMESTAMP);
ArrayList<BookmarkBase> result = new ArrayList<>(cursor.getCount());
if (cursor.moveToFirst())
{
do
{
String hostname =
cursor.getString(cursor.getColumnIndex(HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM));
QuickConnectBookmark bookmark = new QuickConnectBookmark();
bookmark.setLabel(hostname);
bookmark.setHostname(hostname);
result.add(bookmark);
} while (cursor.moveToNext());
}
cursor.close();
return result;
}
public void addHistoryItem(String item)
{
String insertHistoryItem = "INSERT OR REPLACE INTO " + HistoryDB.QUICK_CONNECT_TABLE_NAME +
" (" + HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM + ", " +
HistoryDB.QUICK_CONNECT_TABLE_COL_TIMESTAMP + ") VALUES('" +
item + "', datetime('now'))";
SQLiteDatabase db = getWritableDatabase();
try
{
db.execSQL(insertHistoryItem);
}
catch (SQLException e)
{
Log.v(TAG, e.toString());
}
}
public boolean historyItemExists(String item)
{
String[] column = { HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM };
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(HistoryDB.QUICK_CONNECT_TABLE_NAME, column,
HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM + " = '" + item + "'", null,
null, null, null);
boolean exists = (cursor.getCount() == 1);
cursor.close();
return exists;
}
public void removeHistoryItem(String hostname)
{
SQLiteDatabase db = getWritableDatabase();
db.delete(HistoryDB.QUICK_CONNECT_TABLE_NAME,
HistoryDB.QUICK_CONNECT_TABLE_COL_ITEM + " = '" + hostname + "'", null);
}
// safety wrappers
// in case of getReadableDatabase it could happen that upgradeDB gets called which is
// a problem if the DB is only readable
private SQLiteDatabase getWritableDatabase()
{
return historyDB.getWritableDatabase();
}
private SQLiteDatabase getReadableDatabase()
{
SQLiteDatabase db;
try
{
db = historyDB.getReadableDatabase();
}
catch (SQLiteException e)
{
db = historyDB.getWritableDatabase();
}
return db;
}
}

Some files were not shown because too many files have changed in this diff Show More