diff options
Diffstat (limited to 'tests/auto/android')
-rw-r--r-- | tests/auto/android/AndroidManifest.xml | 37 | ||||
-rw-r--r-- | tests/auto/android/res/layout/main.xml | 12 | ||||
-rw-r--r-- | tests/auto/android/res/values/libs.xml | 19 | ||||
-rw-r--r-- | tests/auto/android/res/values/strings.xml | 4 | ||||
-rwxr-xr-x | tests/auto/android/runtests.pl | 317 | ||||
-rw-r--r-- | tests/auto/android/src/org/qtproject/qt5/android/QtActivity.java | 327 | ||||
-rw-r--r-- | tests/auto/android/src/org/qtproject/qt5/android/QtInputConnection.java | 209 | ||||
-rw-r--r-- | tests/auto/android/src/org/qtproject/qt5/android/QtNative.java | 421 | ||||
-rw-r--r-- | tests/auto/android/src/org/qtproject/qt5/android/QtSurface.java | 163 |
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; + } +} |