summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@digia.com>2013-03-04 10:16:42 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-05 08:31:23 +0100
commit97fcf3bc987a18f32233fea550eb4a5a22e2b822 (patch)
tree31d75557fdc4a525af8f5053db514066c63bd1bd /tests
parent1b582d64eb6d13e60a02ebc40956302a4864eb6c (diff)
Introducing the Qt Android port
Based on the Necessitas project by Bogdan Vatra. Contributors to the Qt5 project: BogDan Vatra <bogdan@kde.org> Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com> hjk <hjk121@nokiamail.com> Oswald Buddenhagen <oswald.buddenhagen@digia.com> Paul Olav Tvete <paul.tvete@digia.com> Robin Burchell <robin+qt@viroteck.net> Samuel Rødal <samuel.rodal@digia.com> Yoann Lopes <yoann.lopes@digia.com> The full history of the Qt5 port can be found in refs/old-heads/android, SHA-1 249ca9ca2c7d876b91b31df9434dde47f9065d0d Change-Id: Iff1a7b2dbb707c986f2639e65e39ed8f22430120 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com> Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/android/AndroidManifest.xml37
-rw-r--r--tests/auto/android/res/layout/main.xml12
-rw-r--r--tests/auto/android/res/values/libs.xml19
-rw-r--r--tests/auto/android/res/values/strings.xml4
-rwxr-xr-xtests/auto/android/runtests.pl317
-rw-r--r--tests/auto/android/src/org/qtproject/qt5/android/QtActivity.java327
-rw-r--r--tests/auto/android/src/org/qtproject/qt5/android/QtInputConnection.java209
-rw-r--r--tests/auto/android/src/org/qtproject/qt5/android/QtNative.java421
-rw-r--r--tests/auto/android/src/org/qtproject/qt5/android/QtSurface.java163
9 files changed, 1509 insertions, 0 deletions
diff --git a/tests/auto/android/AndroidManifest.xml b/tests/auto/android/AndroidManifest.xml
new file mode 100644
index 0000000000..32e2b88d66
--- /dev/null
+++ b/tests/auto/android/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.qt5.android.tests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="4" />
+ <application android:icon="@drawable/icon" android:label="@string/app_name" android:name="org.qtproject.qt5.android.QtNative">
+ <activity android:label="@string/app_name" android:name="org.qtproject.qt5.android.QtActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+ <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.BLUETOOTH"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+ <uses-permission android:name="android.permission.CAMERA"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
+ <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <uses-permission android:name="android.permission.NFC"/>
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.SET_ORIENTATION"/>
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+</manifest>
diff --git a/tests/auto/android/res/layout/main.xml b/tests/auto/android/res/layout/main.xml
new file mode 100644
index 0000000000..7fe6bbac67
--- /dev/null
+++ b/tests/auto/android/res/layout/main.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/textView"
+ />
+</LinearLayout>
diff --git a/tests/auto/android/res/values/libs.xml b/tests/auto/android/res/values/libs.xml
new file mode 100644
index 0000000000..7f2858e554
--- /dev/null
+++ b/tests/auto/android/res/values/libs.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+ <array name="qt_libs">
+ <item>QtCore</item>
+ <item>QtGui</item>
+ <item>QtTest</item>
+ <item>QtOpenGL</item>
+ <item>QtNetwork</item>
+ <item>QtScript</item>
+ <item>QtSql</item>
+ <item>QtXml</item>
+ <item>QtScriptTools</item>
+ <item>QtSvg</item>
+ <item>QtXmlPatterns</item>
+ <item>QtDeclarative</item>
+ <item>QtWebKit</item>
+ </array>
+ <array name="bundled_libs"/>
+</resources>
diff --git a/tests/auto/android/res/values/strings.xml b/tests/auto/android/res/values/strings.xml
new file mode 100644
index 0000000000..faf61040b5
--- /dev/null
+++ b/tests/auto/android/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Quadruplor</string>
+</resources>
diff --git a/tests/auto/android/runtests.pl b/tests/auto/android/runtests.pl
new file mode 100755
index 0000000000..006e865ff2
--- /dev/null
+++ b/tests/auto/android/runtests.pl
@@ -0,0 +1,317 @@
+#!/usr/bin/perl -w
+
+use Cwd;
+use Cwd 'abs_path';
+use File::Basename;
+use File::Temp 'tempdir';
+use File::Path 'remove_tree';
+use Getopt::Long;
+use Pod::Usage;
+
+### default options
+my @stack = cwd;
+my $device_serial=""; # "-s device_serial";
+my $packageName="org.qtproject.qt5.android.tests";
+my $intentName="$packageName/org.qtproject.qt5.android.QtActivity";
+my $jobs = 4;
+my $testsubset = "";
+my $man = 0;
+my $help = 0;
+my $make_clean = 0;
+my $deploy_qt = 0;
+my $time_out=400;
+my $android_sdk_dir = "$ENV{'HOME'}/NecessitasQtSDK/android-sdk";
+my $ant_tool = `which ant`;
+chomp $ant_tool;
+my $strip_tool="";
+my $readelf_tool="";
+GetOptions('h|help' => \$help
+ , man => \$man
+ , 's|serial=s' => \$device_serial
+ , 't|test=s' => \$testsubset
+ , 'c|clean' => \$make_clean
+ , 'd|deploy' => \$deploy_qt
+ , 'j|jobs=i' => \$jobs
+ , 'sdk=s' => \$android_sdk_dir
+ , 'ant=s' => \$ant_tool
+ , 'strip=s' => \$strip_tool
+ , 'readelf=s' => \$readelf_tool
+ ) or pod2usage(2);
+pod2usage(1) if $help;
+pod2usage(-verbose => 2) if $man;
+
+my $adb_tool="$android_sdk_dir/platform-tools/adb";
+system("$adb_tool devices") == 0 or die "No device found, please plug/start at least one device/emulator\n"; # make sure we have at least on device attached
+
+$device_serial = "-s $device_serial" if ($device_serial);
+$testsubset="/$testsubset" if ($testsubset);
+
+$strip_tool="$ENV{'HOME'}/NecessitasQtSDK/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-strip" unless($strip_tool);
+$readelf_tool="$ENV{'HOME'}/NecessitasQtSDK/android-ndk/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-readelf" unless($readelf_tool);
+$readelf_tool="$readelf_tool -d -w ";
+
+sub dir
+{
+# print "@stack\n";
+}
+
+sub pushd ($)
+{
+ unless ( chdir $_[0] )
+ {
+ warn "Error: $!\n";
+ return;
+ }
+ unshift @stack, cwd;
+ dir;
+}
+
+sub popd ()
+{
+ @stack > 1 and shift @stack;
+ chdir $stack[0];
+ dir;
+}
+
+
+sub waitForProcess
+{
+ my $process=shift;
+ my $action=shift;
+ my $timeout=shift;
+ my $sleepPeriod=shift;
+ $sleepPeriod=1 if !defined($sleepPeriod);
+ print "Waiting for $process ".$timeout*$sleepPeriod." seconds to";
+ print $action?" start...\n":" die...\n";
+ while ($timeout--)
+ {
+ my $output = `$adb_tool $device_serial shell ps 2>&1`; # get current processes
+ #FIXME check why $output is not matching m/.*S $process\n/ or m/.*S $process$/ (eol)
+ my $res=($output =~ m/.*S $process/)?1:0; # check the procress
+ if ($action == $res)
+ {
+ print "... succeed\n";
+ return 1;
+ }
+ sleep($sleepPeriod);
+ print "timeount in ".$timeout*$sleepPeriod." seconds\n"
+ }
+ print "... failed\n";
+ return 0;
+}
+
+my $src_dir_qt=abs_path(dirname($0)."/..");
+my $quadruplor_dir="$src_dir_qt/tests/auto/android";
+my $qmake_path="$src_dir_qt/bin/qmake";
+my $tests_dir="$src_dir_qt/tests$testsubset";
+my $temp_dir=tempdir(CLEANUP => 1);
+my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+my $output_dir=$stack[0]."/".(1900+$year)."-$mon-$mday-$hour:$min";
+mkdir($output_dir);
+my $sdk_api=0;
+my $output = `$adb_tool $device_serial shell getprop`; # get device properties
+if ($output =~ m/.*\[ro.build.version.sdk\]: \[(\d+)\]/)
+{
+ $sdk_api=int($1);
+ $sdk_api=5 if ($sdk_api>5 && $sdk_api<8);
+ $sdk_api=9 if ($sdk_api>9);
+}
+
+sub reinstallQuadruplor
+{
+ pushd($quadruplor_dir);
+ system("$android_sdk_dir/tools/android update project -p . -t android-4")==0 or die "Can't update project ...\n";
+ system("$ant_tool uninstall clean debug install")==0 or die "Can't install Quadruplor\n";
+ system("$adb_tool $device_serial shell am start -n $intentName"); # create application folders
+ waitForProcess($packageName,1,10);
+ waitForProcess($packageName,0,20);
+ popd();
+}
+sub killProcess
+{
+ reinstallQuadruplor;
+# #### it seems I'm too idiot to use perl regexp
+# my $process=shift;
+# my $output = `$adb_tool $device_serial shell ps 2>&1`; # get current processes
+# $output =~ s/\r//g; # replace all "\r" with ""
+# chomp($output);
+# print $output;
+# if ($output =~ m/^.*_\d+\s+(\d+).*S $process/) # check the procress
+# {
+# print("Killing $process PID:$1\n");
+# system("$adb_tool $device_serial shell kill $1");
+# waitForProcess($process,0,20);
+# }
+# else
+# {
+# print("Can't kill the process $process\n");
+# }
+}
+
+
+sub startTest
+{
+ my $libs = shift;
+ my $mainLib = shift;
+ my $openGL = ((shift)?"true":"false");
+ system("$adb_tool $device_serial shell am start -n $intentName --ez needsOpenGl $openGL --es extra_libs \"$libs\" --es lib_name \"$mainLib\""); # start intent
+ #wait to start
+ return 0 unless(waitForProcess($packageName,1,10));
+ #wait to stop
+ unless(waitForProcess($packageName,0,$time_out,5))
+ {
+ killProcess($packageName);
+ return 1;
+ }
+ my $output_file = shift;
+ system("$adb_tool $device_serial pull /data/data/$packageName/app_files/output.xml $output_dir/$output_file");
+ return 1;
+}
+
+sub needsOpenGl
+{
+ my $app=$readelf_tool.shift.' |grep -e "^.*(NEEDED).*Shared library: \[libQtOpenGL\.so\]$"';
+ my $res=`$app`;
+ chomp $res;
+ return $res;
+}
+
+########### delpoy qt libs ###########
+if ($deploy_qt)
+{
+
+ pushd($src_dir_qt);
+ mkdir("$temp_dir/lib");
+ my @libs=`find lib -name *.so`; # libs must be handled diferently
+ foreach (@libs)
+ {
+ chomp;
+ print ("cp -L $_ $temp_dir/lib\n");
+ system("cp -L $_ $temp_dir/lib");
+ }
+ system("cp -a plugins $temp_dir");
+ system("cp -a imports $temp_dir");
+ pushd($temp_dir);
+ system("find -name *.so | xargs $strip_tool --strip-unneeded");
+ popd;
+ system("$adb_tool $device_serial shell rm -r /data/local/qt"); # remove old qt libs
+ system("$adb_tool $device_serial push $temp_dir /data/local/qt"); # copy newer qt libs
+ popd;
+}
+
+########### build & install quadruplor ###########
+reinstallQuadruplor;
+
+########### build qt tests and benchmarks ###########
+pushd($tests_dir);
+system("make distclean") if ($make_clean);
+system("$qmake_path CONFIG-=QTDIR_build -r") == 0 or die "Can't run qmake\n"; #exec qmake
+system("make -j$jobs") == 0 or warn "Can't build all tests\n"; #exec make
+my $testsFiles=`find . -name libtst_*.so`; # only tests
+foreach (split("\n",$testsFiles))
+{
+ chomp; #remove white spaces
+ pushd(abs_path(dirname($_))); # cd to application dir
+ system("make INSTALL_ROOT=$temp_dir install"); # install the application to temp dir
+ system("$adb_tool $device_serial shell rm -r /data/data/$packageName/app_files/*"); # remove old data
+ system("$adb_tool $device_serial push $temp_dir /data/data/$packageName/app_files"); # copy
+ my $application=basename(cwd);
+ my $output_name=dirname($_);
+ $output_name =~ s/\.//; # remove first "." character
+ $output_name =~ s/\///; # remove first "/" character
+ $output_name =~ s/\//_/g; # replace all "/" with "_"
+ $output_name=$application unless($output_name);
+ $time_out=5*60/5; # 5 minutes time out for a normal test
+ if (-e "$temp_dir/libtst_bench_$application.so")
+ {
+ $time_out=5*60/5; # 10 minutes for a benchmark
+ $application = "bench_$application";
+ }
+
+ if (-e "$temp_dir/libtst_$application.so")
+ {
+ if (needsOpenGl("$temp_dir/libtst_$application.so"))
+ {
+ startTest("/data/local/qt/plugins/platforms/android/libandroidGL-$sdk_api.so", "/data/data/$packageName/app_files/libtst_$application.so", 1
+ , "$output_name.xml") or warn "Can't run $application ...\n";
+ }
+ else
+ {
+ startTest("/data/local/qt/plugins/platforms/android/libandroid-$sdk_api.so", "/data/data/$packageName/app_files/libtst_$application.so", 0
+ , "$output_name.xml") or warn "Can't run $application stopping tests ...\n";
+ }
+ }
+ else
+ { #ups this test application doesn't respect name convention
+ warn "$application test application doesn't respect name convention please fix it !\n";
+ }
+ popd();
+ remove_tree( $temp_dir, {keep_root => 1} );
+}
+popd();
+
+__END__
+
+=head1 NAME
+
+Script to run all qt tests/benchmarks to an android device/emulator
+
+=head1 SYNOPSIS
+
+runtests.pl [options]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-s --serial = serial>
+
+Device serial number. May be empty if only one device is attached.
+
+=item B<-t --test = test_subset>
+
+Tests subset (e.g. benchmarks, auto, auto/qbuffer, etc.).
+
+=item B<-d --deploy>
+
+Deploy current qt libs.
+
+=item B<-c --clean>
+
+Clean tests before building them.
+
+=item B<-j --jobs = number>
+
+Make jobs when building tests.
+
+=item B<--sdk = sdk_path>
+
+Android SDK path.
+
+=item B<--ant = ant_tool_path>
+
+Ant tool path.
+
+=item B<--strip = strip_tool_path>
+
+Android strip tool path, used to deploy qt libs.
+
+=item B<--readelf = readelf_tool_path>
+
+Android readelf tool path, used to check if a test application uses qt OpenGL.
+
+=item B<-h --help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<This program> will run all qt tests/benchmarks to an android device/emulator.
+
+=cut
diff --git a/tests/auto/android/src/org/qtproject/qt5/android/QtActivity.java b/tests/auto/android/src/org/qtproject/qt5/android/QtActivity.java
new file mode 100644
index 0000000000..ae4ca3c30a
--- /dev/null
+++ b/tests/auto/android/src/org/qtproject/qt5/android/QtActivity.java
@@ -0,0 +1,327 @@
+/*
+ Copyright (c) 2012, BogDan Vatra <bogdan@kde.org>
+ Contact: http://www.qt-project.org/legal
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+*/
+
+package org.qtproject.qt5.android;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.qtproject.qt5.android.tests.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.text.method.MetaKeyKeyListener;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+public class QtActivity extends Activity {
+ private int m_id =- 1;
+ private boolean softwareKeyboardIsVisible = false;
+ private long m_metaState;
+ private int m_lastChar = 0;
+ private boolean m_fullScreen = false;
+ private boolean m_started = false;
+ private QtSurface m_surface = null;
+ private boolean m_usesGL = false;
+ private void loadQtLibs(String[] libs, String environment, String params, String mainLib, String nativeLibDir) throws Exception
+ {
+ QtNative.loadQtLibraries(libs);
+ // start application
+
+ final String envPaths = "NECESSITAS_API_LEVEL=2\tHOME=" + getDir("files", MODE_WORLD_WRITEABLE).getAbsolutePath() +
+ "\tTMPDIR=" + getDir("files", MODE_WORLD_WRITEABLE).getAbsolutePath() +
+ "\tCACHE_PATH=" + getDir("files", MODE_WORLD_WRITEABLE).getAbsolutePath();
+ if (environment != null && environment.length() > 0)
+ environment = envPaths + "\t" + environment;
+ else
+ environment = envPaths;
+
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ QtNative.startApplication(params, environment, mainLib, nativeLibDir);
+ m_surface.applicationStarted(m_usesGL);
+ m_started = true;
+ }
+
+ private boolean m_quitApp = true;
+ private Process m_debuggerProcess = null; // debugger process
+
+ private void startApp(final boolean firstStart)
+ {
+ try {
+ String qtLibs[] = getResources().getStringArray(R.array.qt_libs);
+ ArrayList<String> libraryList = new ArrayList<String>();
+ for (int i = 0; i < qtLibs.length; i++)
+ libraryList.add("/data/local/qt/lib/lib" + qtLibs[i] + ".so");
+
+ String mainLib = null;
+ String nativeLibDir = null;
+ if (getIntent().getExtras() != null) {
+ if (getIntent().getExtras().containsKey("extra_libs")) {
+ String extra_libs = getIntent().getExtras().getString("extra_libs");
+ for (String lib : extra_libs.split(":"))
+ libraryList.add(lib);
+ }
+ if (getIntent().getExtras().containsKey("lib_name")) {
+ mainLib = getIntent().getExtras().getString("lib_name");
+ int slash = mainLib.lastIndexOf("/");
+ if (slash >= 0) {
+ nativeLibDir = mainLib.substring(0, slash+1);
+ mainLib = mainLib.substring(slash+1+3, mainLib.length()-3); //remove lib and .so
+ } else {
+ nativeLibDir = "";
+ }
+ }
+
+ if (getIntent().getExtras().containsKey("needsOpenGl"))
+ m_usesGL = getIntent().getExtras().getBoolean("needsOpenGl");
+ } else {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ System.exit(0);
+ }
+ String[] libs = new String[libraryList.size()];
+ libs = libraryList.toArray(libs);
+ loadQtLibs(libs, "QML_IMPORT_PATH=/data/local/qt/imports\tQT_PLUGIN_PATH=/data/local/qt/plugins",
+ "-xml\t-silent\t-o\toutput.xml", mainLib, nativeLibDir);
+ } catch (Exception e) {
+ Log.e(QtNative.QtTAG, "Can't create main activity", e);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ getDir("files", MODE_WORLD_WRITEABLE);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ m_quitApp = true;
+ QtNative.setMainActivity(this);
+ if (null == getLastNonConfigurationInstance()) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ QtNative.setApplicationDisplayMetrics(metrics.widthPixels, metrics.heightPixels,
+ metrics.widthPixels, metrics.heightPixels,
+ metrics.xdpi, metrics.ydpi);
+ }
+ m_surface = new QtSurface(this, m_id);
+ setContentView(m_surface);
+ if (null == getLastNonConfigurationInstance())
+ startApp(true);
+ }
+
+ public QtSurface getQtSurface()
+ {
+ return m_surface;
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance()
+ {
+ super.onRetainNonConfigurationInstance();
+ m_quitApp = false;
+ return true;
+ }
+
+ @Override
+ protected void onDestroy()
+ {
+ QtNative.setMainActivity(null);
+ super.onDestroy();
+ if (m_quitApp) {
+ Log.i(QtNative.QtTAG, "onDestroy");
+ if (m_debuggerProcess != null)
+ m_debuggerProcess.destroy();
+ System.exit(0);// FIXME remove it or find a better way
+ }
+ QtNative.setMainActivity(null);
+ }
+
+ @Override
+ protected void onResume()
+ {
+ // fire all lostActions
+ synchronized (QtNative.m_mainActivityMutex) {
+ Iterator<Runnable> itr = QtNative.getLostActions().iterator();
+ while (itr.hasNext())
+ runOnUiThread(itr.next());
+ if (m_started) {
+ QtNative.clearLostActions();
+ QtNative.updateWindow();
+ }
+ }
+ super.onResume();
+ }
+
+ public void redrawWindow(int left, int top, int right, int bottom)
+ {
+ m_surface.drawBitmap(new Rect(left, top, right, bottom));
+ }
+
+ public void setFullScreen(boolean enterFullScreen)
+ {
+ if (m_fullScreen == enterFullScreen)
+ return;
+ if (m_fullScreen = enterFullScreen)
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ else
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean("FullScreen", m_fullScreen);
+ outState.putBoolean("Started", m_started);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState)
+ {
+ super.onRestoreInstanceState(savedInstanceState);
+ setFullScreen(savedInstanceState.getBoolean("FullScreen"));
+ m_started = savedInstanceState.getBoolean("Started");
+ if (m_started)
+ m_surface.applicationStarted(true);
+ }
+
+ public void showSoftwareKeyboard()
+ {
+ softwareKeyboardIsVisible = true;
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
+ }
+
+ public void resetSoftwareKeyboard()
+ {
+ }
+
+ public void hideSoftwareKeyboard()
+ {
+ if (softwareKeyboardIsVisible) {
+ InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.toggleSoftInput(0, 0);
+ }
+ softwareKeyboardIsVisible = false;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event)
+ {
+ if (m_started && event.getAction() == KeyEvent.ACTION_MULTIPLE &&
+ event.getCharacters() != null &&
+ event.getCharacters().length() == 1 &&
+ event.getKeyCode() == 0) {
+ Log.i(QtNative.QtTAG, "dispatchKeyEvent at MULTIPLE with one character: " + event.getCharacters());
+ QtNative.keyDown(0, event.getCharacters().charAt(0), event.getMetaState());
+ QtNative.keyUp(0, event.getCharacters().charAt(0), event.getMetaState());
+ }
+
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ if (!m_started)
+ return false;
+ m_metaState = MetaKeyKeyListener.handleKeyDown(m_metaState, keyCode, event);
+ int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(m_metaState));
+ int lc = c;
+ m_metaState = MetaKeyKeyListener.adjustMetaAfterKeypress(m_metaState);
+
+ if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ c = c & KeyCharacterMap.COMBINING_ACCENT_MASK;
+ int composed = KeyEvent.getDeadChar(m_lastChar, c);
+ c = composed;
+ }
+ m_lastChar = lc;
+ if (keyCode != KeyEvent.KEYCODE_BACK)
+ QtNative.keyDown(keyCode, c, event.getMetaState());
+ return true;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event)
+ {
+ if (!m_started)
+ return false;
+ m_metaState = MetaKeyKeyListener.handleKeyUp(m_metaState, keyCode, event);
+ QtNative.keyUp(keyCode, event.getUnicodeChar(), event.getMetaState());
+ return true;
+ }
+
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
+
+/* public boolean onCreateOptionsMenu(Menu menu)
+ {
+ QtNative.createOptionsMenu(menu);
+ try {
+ return onPrepareOptionsMenu(menu);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ QtNative.prepareOptionsMenu(menu);
+ try {
+ return (Boolean) onPrepareOptionsMenu(menu);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ return QtNative.optionsItemSelected(item.getGroupId(), item.getItemId());
+ }*/
+}
diff --git a/tests/auto/android/src/org/qtproject/qt5/android/QtInputConnection.java b/tests/auto/android/src/org/qtproject/qt5/android/QtInputConnection.java
new file mode 100644
index 0000000000..e69a03061b
--- /dev/null
+++ b/tests/auto/android/src/org/qtproject/qt5/android/QtInputConnection.java
@@ -0,0 +1,209 @@
+/*
+ Copyright (c) 2012, BogDan Vatra <bogdan@kde.org>
+ Contact: http://www.qt-project.org/legal
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+*/
+
+package org.qtproject.qt5.android;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputMethodManager;
+
+class QtExtractedText
+{
+ public int partialEndOffset;
+ public int partialStartOffset;
+ public int selectionEnd;
+ public int selectionStart;
+ public int startOffset;
+ public String text;
+}
+
+class QtNativeInputConnection
+{
+ static native boolean commitText(String text, int newCursorPosition);
+ static native boolean commitCompletion(String text, int position);
+ static native boolean deleteSurroundingText(int leftLength, int rightLength);
+ static native boolean finishComposingText();
+ static native int getCursorCapsMode(int reqModes);
+ static native QtExtractedText getExtractedText(int hintMaxChars, int hintMaxLines, int flags);
+ static native String getSelectedText(int flags);
+ static native String getTextAfterCursor(int length, int flags);
+ static native String getTextBeforeCursor(int length, int flags);
+ static native boolean setComposingText(String text, int newCursorPosition);
+ static native boolean setSelection(int start, int end);
+ static native boolean selectAll();
+ static native boolean cut();
+ static native boolean copy();
+ static native boolean copyURL();
+ static native boolean paste();
+}
+
+public class QtInputConnection extends BaseInputConnection
+{
+ private static final int ID_SELECT_ALL = android.R.id.selectAll;
+ private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
+ private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText;
+ private static final int ID_CUT = android.R.id.cut;
+ private static final int ID_COPY = android.R.id.copy;
+ private static final int ID_PASTE = android.R.id.paste;
+ private static final int ID_COPY_URL = android.R.id.copyUrl;
+ private static final int ID_SWITCH_INPUT_METHOD = android.R.id.switchInputMethod;
+ private static final int ID_ADD_TO_DICTIONARY = android.R.id.addToDictionary;
+ View m_view;
+
+ public QtInputConnection(View targetView)
+ {
+ super(targetView, true);
+ m_view = targetView;
+ }
+
+ @Override
+ public boolean beginBatchEdit()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean endBatchEdit()
+ {
+ return true;
+ }
+
+ @Override
+ public boolean commitCompletion(CompletionInfo text)
+ {
+ return QtNativeInputConnection.commitCompletion(text.getText().toString(), text.getPosition());
+ }
+
+ @Override
+ public boolean commitText(CharSequence text, int newCursorPosition)
+ {
+ return QtNativeInputConnection.commitText(text.toString(), newCursorPosition);
+ }
+
+ @Override
+ public boolean deleteSurroundingText(int leftLength, int rightLength)
+ {
+ return QtNativeInputConnection.deleteSurroundingText(leftLength, rightLength);
+ }
+
+ @Override
+ public boolean finishComposingText()
+ {
+ return QtNativeInputConnection.finishComposingText();
+ }
+
+ @Override
+ public int getCursorCapsMode(int reqModes)
+ {
+ return QtNativeInputConnection.getCursorCapsMode(reqModes);
+ }
+
+ @Override
+ public ExtractedText getExtractedText(ExtractedTextRequest request, int flags)
+ {
+ QtExtractedText qExtractedText = QtNativeInputConnection.getExtractedText(request.hintMaxChars, request.hintMaxLines, flags);
+ ExtractedText extractedText = new ExtractedText();
+ extractedText.partialEndOffset = qExtractedText.partialEndOffset;
+ extractedText.partialStartOffset = qExtractedText.partialStartOffset;
+ extractedText.selectionEnd = qExtractedText.selectionEnd;
+ extractedText.selectionStart = qExtractedText.selectionStart;
+ extractedText.startOffset = qExtractedText.startOffset;
+ extractedText.text = qExtractedText.text;
+ return extractedText;
+ }
+
+ public CharSequence getSelectedText(int flags)
+ {
+ return QtNativeInputConnection.getSelectedText(flags);
+ }
+
+ @Override
+ public CharSequence getTextAfterCursor(int length, int flags)
+ {
+ return QtNativeInputConnection.getTextAfterCursor(length, flags);
+ }
+
+ @Override
+ public CharSequence getTextBeforeCursor(int length, int flags)
+ {
+ return QtNativeInputConnection.getTextBeforeCursor(length, flags);
+ }
+
+ @Override
+ public boolean performContextMenuAction(int id)
+ {
+ switch (id) {
+ case ID_SELECT_ALL:
+ return QtNativeInputConnection.selectAll();
+ case ID_COPY:
+ return QtNativeInputConnection.copy();
+ case ID_COPY_URL:
+ return QtNativeInputConnection.copyURL();
+ case ID_CUT:
+ return QtNativeInputConnection.cut();
+ case ID_PASTE:
+ return QtNativeInputConnection.paste();
+
+ case ID_SWITCH_INPUT_METHOD:
+ InputMethodManager imm = (InputMethodManager)m_view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showInputMethodPicker();
+ }
+ return true;
+
+ case ID_ADD_TO_DICTIONARY:
+// TODO
+// String word = m_editable.subSequence(0, m_editable.length()).toString();
+// if (word != null) {
+// Intent i = new Intent("com.android.settings.USER_DICTIONARY_INSERT");
+// i.putExtra("word", word);
+// i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+// m_view.getContext().startActivity(i);
+// }
+ return true;
+ }
+ return super.performContextMenuAction(id);
+ }
+
+ @Override
+ public boolean setComposingText(CharSequence text, int newCursorPosition) {
+ return QtNativeInputConnection.setComposingText(text.toString(), newCursorPosition);
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ return QtNativeInputConnection.setSelection(start, end);
+ }
+}
diff --git a/tests/auto/android/src/org/qtproject/qt5/android/QtNative.java b/tests/auto/android/src/org/qtproject/qt5/android/QtNative.java
new file mode 100644
index 0000000000..8faeabe5b0
--- /dev/null
+++ b/tests/auto/android/src/org/qtproject/qt5/android/QtNative.java
@@ -0,0 +1,421 @@
+/*
+ Copyright (c) 2012, BogDan Vatra <bogdan@kde.org>
+ Contact: http://www.qt-project.org/legal
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+*/
+
+package org.qtproject.qt5.android;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MotionEvent;
+
+public class QtNative extends Application
+{
+ private static QtActivity m_mainActivity = null;
+ private static QtSurface m_mainView = null;
+ public static Object m_mainActivityMutex = new Object(); // mutex used to synchronize runnable operations
+
+ public static final String QtTAG = "Qt JAVA"; // string used for Log.x
+ private static ArrayList<Runnable> m_lostActions = new ArrayList<Runnable>(); // a list containing all actions which could not be performed (e.g. the main activity is destroyed, etc.)
+ private static boolean m_started = false;
+ private static int m_displayMetricsScreenWidthPixels = 0;
+ private static int m_displayMetricsScreenHeightPixels = 0;
+ private static int m_displayMetricsDesktopWidthPixels = 0;
+ private static int m_displayMetricsDesktopHeightPixels = 0;
+ private static double m_displayMetricsXDpi = .0;
+ private static double m_displayMetricsYDpi = .0;
+ private static int m_oldx, m_oldy;
+ private static final int m_moveThreshold = 0;
+
+ public static ClassLoader classLoader()
+ {
+ return m_mainActivity.getClassLoader();
+ }
+
+ public static Activity activity()
+ {
+ return m_mainActivity;
+ }
+
+ public static QtSurface mainView()
+ {
+ return m_mainView;
+ }
+
+ public static void openURL(String url)
+ {
+ Uri uri = Uri.parse(url);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ activity().startActivity(intent);
+ }
+
+ // this method loads full path libs
+ public static void loadQtLibraries(String[] libraries)
+ {
+ if (libraries == null)
+ return;
+
+ for (int i = 0; i < libraries.length; i++) {
+ try {
+ File f = new File(libraries[i]);
+ if (f.exists())
+ System.load(libraries[i]);
+ } catch (SecurityException e) {
+ Log.i(QtTAG, "Can't load '" + libraries[i] + "'", e);
+ } catch (Exception e) {
+ Log.i(QtTAG, "Can't load '" + libraries[i] + "'", e);
+ }
+ }
+ }
+
+ // this method loads bundled libs by name.
+ public static void loadBundledLibraries(String[] libraries)
+ {
+ for (int i = 0; i < libraries.length; i++) {
+ try {
+ System.loadLibrary(libraries[i]);
+ } catch (UnsatisfiedLinkError e) {
+ Log.i(QtTAG, "Can't load '" + libraries[i] + "'", e);
+ } catch (SecurityException e) {
+ Log.i(QtTAG, "Can't load '" + libraries[i] + "'", e);
+ } catch (Exception e) {
+ Log.i(QtTAG, "Can't load '" + libraries[i] + "'", e);
+ }
+ }
+ }
+
+ public static void setMainActivity(QtActivity qtMainActivity)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_mainActivity = qtMainActivity;
+ }
+ }
+ public static void setMainView(QtSurface qtSurface)
+ {
+ synchronized (m_mainActivityMutex) {
+ m_mainView = qtSurface;
+ }
+ }
+
+ static public ArrayList<Runnable> getLostActions()
+ {
+ return m_lostActions;
+ }
+
+ static public void clearLostActions()
+ {
+ m_lostActions.clear();
+ }
+
+ private static boolean runAction(Runnable action)
+ {
+ synchronized (m_mainActivityMutex) {
+ if (m_mainActivity == null)
+ m_lostActions.add(action);
+ else
+ m_mainActivity.runOnUiThread(action);
+ return m_mainActivity != null;
+ }
+ }
+
+ public static boolean startApplication(String params, String environment, String mainLibrary, String nativeLibraryDir) throws Exception
+ {
+ File f = new File(nativeLibraryDir+"lib"+mainLibrary+".so");
+ if (!f.exists())
+ throw new Exception("Can't find main library '" + mainLibrary + "'");
+
+ if (params == null)
+ params = "-platform\tandroid";
+
+ boolean res = false;
+ synchronized (m_mainActivityMutex) {
+ res = startQtAndroidPlugin();
+ setDisplayMetrics(m_displayMetricsScreenWidthPixels,
+ m_displayMetricsScreenHeightPixels,
+ m_displayMetricsDesktopWidthPixels,
+ m_displayMetricsDesktopHeightPixels,
+ m_displayMetricsXDpi,
+ m_displayMetricsYDpi);
+ startQtApplication(f.getAbsolutePath()+"\t"+params, environment);
+ m_started = true;
+ }
+ return res;
+ }
+
+ public static void setApplicationDisplayMetrics(int screenWidthPixels,
+ int screenHeightPixels, int desktopWidthPixels,
+ int desktopHeightPixels, double XDpi, double YDpi)
+ {
+ /* Fix buggy dpi report */
+ if (XDpi < android.util.DisplayMetrics.DENSITY_LOW)
+ XDpi = android.util.DisplayMetrics.DENSITY_LOW;
+ if (YDpi < android.util.DisplayMetrics.DENSITY_LOW)
+ YDpi = android.util.DisplayMetrics.DENSITY_LOW;
+
+ synchronized (m_mainActivityMutex) {
+ if (m_started) {
+ setDisplayMetrics(screenWidthPixels, screenHeightPixels, desktopWidthPixels, desktopHeightPixels, XDpi, YDpi);
+ } else {
+ m_displayMetricsScreenWidthPixels = screenWidthPixels;
+ m_displayMetricsScreenHeightPixels = screenHeightPixels;
+ m_displayMetricsDesktopWidthPixels = desktopWidthPixels;
+ m_displayMetricsDesktopHeightPixels = desktopHeightPixels;
+ m_displayMetricsXDpi = XDpi;
+ m_displayMetricsYDpi = YDpi;
+ }
+ }
+ }
+
+ public static void pauseApplication()
+ {
+ synchronized (m_mainActivityMutex) {
+ if (m_started)
+ pauseQtApp();
+ }
+ }
+
+ public static void resumeApplication()
+ {
+ synchronized (m_mainActivityMutex) {
+ if (m_started) {
+ resumeQtApp();
+ updateWindow();
+ }
+ }
+ }
+ // application methods
+ public static native void startQtApplication(String params, String env);
+ public static native void pauseQtApp();
+ public static native void resumeQtApp();
+ public static native boolean startQtAndroidPlugin();
+ public static native void quitQtAndroidPlugin();
+ public static native void terminateQt();
+ // application methods
+
+ private static void quitApp()
+ {
+ m_mainActivity.finish();
+ }
+
+ private static void redrawSurface(final int left, final int top, final int right, final int bottom )
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ m_mainActivity.redrawWindow(left, top, right, bottom);
+ }
+ });
+ }
+
+ @Override
+ public void onTerminate()
+ {
+ if (m_started)
+ terminateQt();
+ super.onTerminate();
+ }
+
+
+ static public void sendTouchEvent(MotionEvent event, int id)
+ {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ mouseUp(id,(int) event.getX(), (int) event.getY());
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ mouseDown(id,(int) event.getX(), (int) event.getY());
+ m_oldx = (int) event.getX();
+ m_oldy = (int) event.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ int dx = (int) (event.getX() - m_oldx);
+ int dy = (int) (event.getY() - m_oldy);
+ if (Math.abs(dx) > m_moveThreshold || Math.abs(dy) > m_moveThreshold) {
+ mouseMove(id,(int) event.getX(), (int) event.getY());
+ m_oldx = (int) event.getX();
+ m_oldy = (int) event.getY();
+ }
+ break;
+ }
+ }
+
+ static public void sendTrackballEvent(MotionEvent event, int id)
+ {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_UP:
+ mouseUp(id, (int) event.getX(), (int) event.getY());
+ break;
+
+ case MotionEvent.ACTION_DOWN:
+ mouseDown(id, (int) event.getX(), (int) event.getY());
+ m_oldx = (int) event.getX();
+ m_oldy = (int) event.getY();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ int dx = (int) (event.getX() - m_oldx);
+ int dy = (int) (event.getY() - m_oldy);
+ if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
+ mouseMove(id, (int) event.getX(), (int) event.getY());
+ m_oldx = (int) event.getX();
+ m_oldy = (int) event.getY();
+ }
+ break;
+ }
+ }
+
+
+ private static void showSoftwareKeyboard(final int x, final int y
+ , final int width, final int height
+ , final int inputHints )
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ m_mainActivity.showSoftwareKeyboard();
+ }
+ });
+ }
+
+ private static void resetSoftwareKeyboard()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ m_mainActivity.resetSoftwareKeyboard();
+ }
+ });
+ }
+
+ private static void hideSoftwareKeyboard()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ m_mainActivity.hideSoftwareKeyboard();
+ }
+ });
+ }
+
+ private static void setFullScreen(final boolean fullScreen)
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ m_mainActivity.setFullScreen(fullScreen);
+ updateWindow();
+ }
+ });
+ }
+
+ // screen methods
+ public static native void setDisplayMetrics(int screenWidthPixels,
+ int screenHeightPixels, int desktopWidthPixels,
+ int desktopHeightPixels, double XDpi, double YDpi);
+ // screen methods
+
+ private static void showOptionsMenu()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ if (m_mainActivity != null)
+ m_mainActivity.openOptionsMenu();
+ }
+ });
+ }
+
+ private static void hideOptionsMenu()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ if (m_mainActivity != null)
+ m_mainActivity.closeOptionsMenu();
+ }
+ });
+ }
+
+ private static void showContextMenu()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ if (m_mainActivity != null)
+ m_mainActivity.openContextMenu(m_mainView);
+ }
+ });
+ }
+
+ private static void hideContextMenu()
+ {
+ runAction(new Runnable() {
+ @Override
+ public void run() {
+ if (m_mainActivity != null)
+ m_mainActivity.closeContextMenu();
+ }
+ });
+ }
+
+ // pointer methods
+ public static native void mouseDown(int winId, int x, int y);
+ public static native void mouseUp(int winId, int x, int y);
+ public static native void mouseMove(int winId, int x, int y);
+ public static native void touchBegin(int winId);
+ public static native void touchAdd(int winId, int pointerId, int action, boolean primary, int x, int y, float size, float pressure);
+ public static native void touchEnd(int winId, int action);
+ // pointer methods
+
+ // keyboard methods
+ public static native void keyDown(int key, int unicode, int modifier);
+ public static native void keyUp(int key, int unicode, int modifier);
+ // keyboard methods
+
+ // surface methods
+ public static native void destroySurface();
+ public static native void setSurface(Object surface);
+ public static native void lockSurface();
+ public static native void unlockSurface();
+ // surface methods
+
+ // window methods
+ public static native void updateWindow();
+ // window methods
+
+ // options menu methods
+ public static native void createOptionsMenu(Menu menu);
+ public static native boolean prepareOptionsMenu(Menu menu);
+ public static native boolean optionsItemSelected(int groupId, int itemId);
+ // options menu methods
+}
diff --git a/tests/auto/android/src/org/qtproject/qt5/android/QtSurface.java b/tests/auto/android/src/org/qtproject/qt5/android/QtSurface.java
new file mode 100644
index 0000000000..7e7db031ec
--- /dev/null
+++ b/tests/auto/android/src/org/qtproject/qt5/android/QtSurface.java
@@ -0,0 +1,163 @@
+/*
+ Copyright (c) 2012, BogDan Vatra <bogdan@kde.org>
+ Contact: http://www.qt-project.org/legal
+
+ 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.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+*/
+
+package org.qtproject.qt5.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+public class QtSurface extends SurfaceView implements SurfaceHolder.Callback
+{
+ private Bitmap m_bitmap=null;
+ private boolean m_started = false;
+ private boolean m_usesGL = false;
+ public QtSurface(Context context, int id)
+ {
+ super(context);
+ setFocusable(true);
+ getHolder().addCallback(this);
+ getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
+ setId(id);
+ }
+
+ public void applicationStarted(boolean usesGL)
+ {
+ m_started = true;
+ m_usesGL = usesGL;
+ if (getWidth() < 1 || getHeight() < 1)
+ return;
+ if (m_usesGL) {
+ QtNative.setSurface(getHolder().getSurface());
+ } else {
+ QtNative.lockSurface();
+ QtNative.setSurface(null);
+ m_bitmap=Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.RGB_565);
+ QtNative.setSurface(m_bitmap);
+ QtNative.unlockSurface();
+ }
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder)
+ {
+ DisplayMetrics metrics = new DisplayMetrics();
+ ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ QtNative.setApplicationDisplayMetrics(metrics.widthPixels,
+ metrics.heightPixels, getWidth(), getHeight(), metrics.xdpi, metrics.ydpi);
+
+ if (m_usesGL)
+ holder.setFormat(PixelFormat.RGBA_8888);
+ else
+ holder.setFormat(PixelFormat.RGB_565);
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
+ {
+ Log.i(QtNative.QtTAG,"surfaceChanged: "+width+","+height);
+ if (width < 1 || height < 1)
+ return;
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ QtNative.setApplicationDisplayMetrics(metrics.widthPixels,
+ metrics.heightPixels, width, height, metrics.xdpi, metrics.ydpi);
+
+ if (!m_started)
+ return;
+
+ if (m_usesGL) {
+ QtNative.setSurface(holder.getSurface());
+ } else {
+ QtNative.lockSurface();
+ QtNative.setSurface(null);
+ m_bitmap=Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ QtNative.setSurface(m_bitmap);
+ QtNative.unlockSurface();
+ QtNative.updateWindow();
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder)
+ {
+ Log.i(QtNative.QtTAG,"surfaceDestroyed ");
+ if (m_usesGL) {
+ QtNative.destroySurface();
+ } else {
+ if (!m_started)
+ return;
+
+ QtNative.lockSurface();
+ QtNative.setSurface(null);
+ QtNative.unlockSurface();
+ }
+ }
+
+ public void drawBitmap(Rect rect)
+ {
+ if (!m_started)
+ return;
+ QtNative.lockSurface();
+ if (null != m_bitmap) {
+ try {
+ Canvas cv=getHolder().lockCanvas(rect);
+ cv.drawBitmap(m_bitmap, rect, rect, null);
+ getHolder().unlockCanvasAndPost(cv);
+ } catch (Exception e) {
+ Log.e(QtNative.QtTAG, "Can't create main activity", e);
+ }
+ }
+ QtNative.unlockSurface();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ if (!m_started)
+ return false;
+ QtNative.sendTouchEvent(event, getId());
+ return true;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event)
+ {
+ if (!m_started)
+ return false;
+ QtNative.sendTrackballEvent(event, getId());
+ return true;
+ }
+}