[Libreoffice-commits] .: android/experimental
Iain Billett
iainb at kemper.freedesktop.org
Fri Jun 22 09:34:10 PDT 2012
android/experimental/LibreOffice4Android/AndroidManifest.xml | 40
android/experimental/LibreOffice4Android/Makefile | 272 +++
android/experimental/LibreOffice4Android/build.xml | 114 +
android/experimental/LibreOffice4Android/fonts.conf | 154 ++
android/experimental/LibreOffice4Android/jni/Android.mk | 8
android/experimental/LibreOffice4Android/jni/Application.mk | 3
android/experimental/LibreOffice4Android/project.properties | 14
android/experimental/LibreOffice4Android/res/drawable-hdpi/action_search.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/base.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/calc.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/draw.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/dummy_page.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/folder.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/ic_launcher.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/impress.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/light_sort_by_size.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_grid.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_list.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/lo_icon.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/main.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/math.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/startcenter.png |binary
android/experimental/LibreOffice4Android/res/drawable-hdpi/writer.png |binary
android/experimental/LibreOffice4Android/res/drawable-ldpi/dummy_page.png |binary
android/experimental/LibreOffice4Android/res/drawable-ldpi/ic_launcher.png |binary
android/experimental/LibreOffice4Android/res/drawable-ldpi/lo_icon.png |binary
android/experimental/LibreOffice4Android/res/drawable-mdpi/ic_launcher.png |binary
android/experimental/LibreOffice4Android/res/drawable-mdpi/lo_icon.png |binary
android/experimental/LibreOffice4Android/res/layout/file_explorer_grid_item.xml | 29
android/experimental/LibreOffice4Android/res/layout/file_grid.xml | 20
android/experimental/LibreOffice4Android/res/layout/file_list.xml | 13
android/experimental/LibreOffice4Android/res/layout/file_list_item.xml | 41
android/experimental/LibreOffice4Android/res/layout/main.xml | 15
android/experimental/LibreOffice4Android/res/menu/view_menu.xml | 20
android/experimental/LibreOffice4Android/res/values/arrays.xml | 56
android/experimental/LibreOffice4Android/res/values/strings.xml | 16
android/experimental/LibreOffice4Android/res/xml/libreoffice_preferences.xml | 21
android/experimental/LibreOffice4Android/src/com/polites/android/Animation.java | 32
android/experimental/LibreOffice4Android/src/com/polites/android/Animator.java | 96 +
android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimation.java | 74 +
android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimationListener.java | 29
android/experimental/LibreOffice4Android/src/com/polites/android/FlingListener.java | 45
android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageView.java | 712 ++++++++++
android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewListener.java | 30
android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewTouchListener.java | 540 +++++++
android/experimental/LibreOffice4Android/src/com/polites/android/MathUtils.java | 76 +
android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimation.java | 107 +
android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimationListener.java | 27
android/experimental/LibreOffice4Android/src/com/polites/android/VectorF.java | 63
android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimation.java | 167 ++
android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimationListener.java | 26
android/experimental/LibreOffice4Android/src/org/libreoffice/android/examples/DocumentLoader.java | 596 ++++++++
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/FileUtilities.java | 159 ++
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/GridItemAdapter.java | 95 +
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/LibreOfficeUIActivity.java | 548 +++++++
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/ListItemAdapter.java | 159 ++
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/PageView.java | 63
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/PreferenceEditor.java | 18
android/experimental/LibreOffice4Android/src/org/libreoffice/ui/WriterViewerActivity.java | 37
59 files changed, 4535 insertions(+)
New commits:
commit d3949d453d06bbdd88d33dd0d6e660f67fad546a
Author: Iain Billett <iainbillett at gmail.com>
Date: Fri Jun 22 17:32:08 2012 +0100
An new project to combine DocumentLoader with the Android UI. (Not building - see manifest)
diff --git a/android/experimental/LibreOffice4Android/AndroidManifest.xml b/android/experimental/LibreOffice4Android/AndroidManifest.xml
new file mode 100644
index 0000000..f521536
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.libreoffice"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="11"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+
+ <application
+ android:debuggable="true"
+ android:icon="@drawable/lo_icon"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Holo.Light">
+
+ <!-- Original Document Loader activity - file Viewer -->
+ <activity android:name=".android.examples.DocumentLoader"
+ android:label="LO DocumentLoader"
+ android:configChanges="orientation|keyboardHidden">
+ <!-- <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>-->
+ </activity>
+ <!-- File Explorer Activities taken from eclipse workspace -->
+ <activity
+ android:name=".ui.LibreOfficeUIActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:theme="@android:style/Theme.Holo.Light" android:name=".ui.WriterViewerActivity">
+ <intent-filter android:label="writer_viewer"></intent-filter>
+ </activity>
+ <activity android:name=".ui.PreferenceEditor"></activity>
+
+ </application>
+</manifest>
diff --git a/android/experimental/LibreOffice4Android/Makefile b/android/experimental/LibreOffice4Android/Makefile
new file mode 100644
index 0000000..9423336
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/Makefile
@@ -0,0 +1,272 @@
+include ../../../config_host.mk
+
+# The package of this app
+APP_PACKAGE=org.libreoffice.android.examples
+
+# We can't keep assuming APP_DATA_PATH like this, surely this can vary with
+# Android versions and whatnot, this is temporary and works at least with the
+# SDK 16 emulator...
+
+# Probably would be best to just stop fooling around with the possibilities to
+# set various stuff with the -env command line parameters (and environment
+# variables?) and in a plethora of rc files, and hardcode construction of
+# *all* required pathnames based on the app installation location for Android
+# (and iOS), etc. We don't really win anything by having so many layers of
+# configurability on platforms like Android and iOS where apps based on LO
+# code are very much self-contained pre-packaged thingies.
+APP_DATA_PATH=/data/data/$(APP_PACKAGE)
+
+SODEST=libs/armeabi-v7a
+OBJLOCAL=obj/local/armeabi-v7a
+
+define COPYSO
+cp $(1) $(SODEST)$(if $(2),/$(2)) && $(STRIP) --strip-debug $(SODEST)$(if $(2),/$(2),/$(notdir $(1))) && \
+cp $(1) $(OBJLOCAL)$(if $(2),/$(2))
+endef
+
+define COPYJAR
+cp $(1) libs
+endef
+
+# The default target just builds.
+
+all: build-ant
+
+properties:
+ echo sdk.dir=$(ANDROID_SDK_HOME) >local.properties
+ echo sdk.dir=$(ANDROID_SDK_HOME) >../../Bootstrap/local.properties
+
+copy-stuff:
+# First always clean
+ rm -rf libs $(OBJLOCAL)
+ mkdir -p $(SODEST) $(OBJLOCAL)
+#
+# Copy shared libraries (including UNO components) we need to
+# libs/armeabi-v7a so that ant will include them in the .apk.
+#
+# Copy them to obj/local/armeabi-v7a, too, where gdb will look for
+# them.
+#
+ for F in $(strip \
+ analysislo \
+ basebmplo \
+ basegfxlo \
+ bootstrap.uno \
+ comphelpgcc3 \
+ ctllo \
+ datelo \
+ dbaxmllo \
+ dbtoolslo \
+ evtattlo \
+ expwrap.uno \
+ fastsax.uno \
+ fileacc \
+ forlo \
+ foruilo \
+ frmlo \
+ fsstorage.uno \
+ gcc3_uno \
+ hwplo \
+ i18nisolang1gcc3 \
+ i18npool.uno \
+ i18nutilgcc3 \
+ icudatalo \
+ icui18nlo \
+ iculelo \
+ icuuclo \
+ introspection.uno \
+ java_uno \
+ juh \
+ juhx \
+ jvmaccessgcc3 \
+ jvmfwk \
+ libotouchlo \
+ lo-bootstrap \
+ localebe1.uno \
+ localedata_en \
+ localedata_others \
+ lwpftlo \
+ mergedlo \
+ msfilterlo \
+ mswordlo \
+ ooxlo \
+ reflection.uno \
+ reg \
+ saxlo \
+ sclo \
+ scdlo \
+ scfiltlo \
+ sddlo \
+ smdlo \
+ sotlo \
+ stocservices.uno \
+ store \
+ svgfilterlo \
+ svllo \
+ swdlo \
+ swlo \
+ t602filterlo \
+ textinstream.uno \
+ tllo \
+ ucbhelper4gcc3 \
+ ucppkg1 \
+ uno_cppu \
+ uno_cppuhelpergcc3 \
+ uno_sal \
+ uno_salhelpergcc3 \
+ uno_cppuhelpergcc3 \
+ unordflo \
+ unoxmllo \
+ utllo \
+ vbahelperlo \
+ vbaswobj.uno \
+ wpftdrawlo \
+ wpftwriterlo \
+ vcllo \
+ xml2 \
+ xmlfdlo \
+ xmlreader \
+ xmlsecurity \
+ xslt \
+ xstor \
+ ); do \
+ $(call COPYSO,$(OUTDIR)/lib/lib$${F}.so); \
+ done
+#
+# Then the shared GNU C++ library
+ $(call COPYSO,$(ANDROID_NDK_HOME)/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/libgnustl_shared.so)
+#
+# Then other "assets". Let the directory structure under assets mimic
+# that under solver for now.
+#
+# Please note that I have no idea what all of this is really necessary and for
+# much of this stuff being copied, no idea whether it makes any sense at all.
+# Much of this is copy-pasted from android/qa/sc/Makefile (where a couple of
+# unit tests for sc are built, and those do seem to mostly work) and
+# android/qa/desktop/Makefile (mmeeks's desktop demo, also works to some
+# extent).
+#
+ mkdir -p assets/bin/ure assets/lib assets/program assets/xml/ure assets/ComponentTarget/i18npool/util
+ cp $(OUTDIR)/bin/udkapi.rdb assets/bin
+ cp $(OUTDIR)/bin/types.rdb assets/bin
+ cp $(OUTDIR)/bin/ure/types.rdb assets/bin/ure
+# For some reason the vnd.sun.star.expand:$LO_LIB_DIR doesn't seem to work, it expands to empty!?
+# So just hardcode the known APP_DATA_PATH for now...
+ for F in xml/services xml/ure/services; do \
+ sed -e 's!uri="vnd.sun.star.expand:$$LO_LIB_DIR/!uri="file://$(APP_DATA_PATH)/lib/!g' <$(OUTDIR)/$$F.rdb >assets/$$F.rdb; \
+ done
+ cp $(SRC_ROOT)/odk/examples/java/DocumentHandling/test/test1.odt \
+ $(SRC_ROOT)/sc/qa/unit/data/xls/border.xls \
+ $(SRC_ROOT)/sw/qa/core/data/odt/test.odt \
+ $(SRC_ROOT)/sw/qa/core/data/doc/testVba.doc \
+ assets
+ cp $(WORKDIR)/ComponentTarget/i18npool/util/i18npool.component assets/ComponentTarget/i18npool/util
+#
+ mkdir -p assets/ure/share/misc assets/share/registry/res assets/share/config/soffice.cfg
+ cp -R $(OUTDIR)/xml/*.xcd assets/share/registry
+ mv assets/share/registry/fcfg_langpack_en-US.xcd assets/share/registry/res
+ cp -R $(OUTDIR)/xml/uiconfig/* assets/share/config/soffice.cfg
+ cp -R $(OUTDIR)/xml/registry/* assets/share/registry
+#
+# Set up rc, the "inifile". See BootstrapMap::getBaseIni(). As this app
+# doesn't use soffice_main() (at least I think it shouldn't), the
+# rtl::Bootstrap::setIniFilename() call there that hardcodes
+# /assets/program/lofficerc isn't executed. Instead the hardcoding of
+# /assets/rc in BootstrapMap::getBaseIni() gets used.
+ echo '[Bootstrap]' > assets/rc
+ echo 'Logo=1' >> assets/rc
+ echo 'NativeProgress=1' >> assets/rc
+ echo 'URE_BOOTSTRAP=file:///assets/program/fundamentalrc' >> assets/rc
+# echo 'RTL_LOGFILE=file:///dev/log/main' >> assets/rc
+ echo "HOME=$(APP_DATA_PATH)/cache" >> assets/rc
+ echo "OSL_SOCKET_PATH=$(APP_DATA_PATH)/cache" >> assets/rc
+#
+# Set up fundamentalrc
+ echo '[Bootstrap]' > assets/program/fundamentalrc
+ echo "LO_LIB_DIR=file:$(APP_DATA_PATH)/lib/" >> assets/program/fundamentalrc
+ echo "URE_LIB_DIR=file://$(APP_DATA_PATH)/lib/" >> assets/program/fundamentalrc # checkme - is this used to find configs ?
+ echo 'BRAND_BASE_DIR=file:///assets' >> assets/program/fundamentalrc
+ echo 'CONFIGURATION_LAYERS=xcsxcu:$${BRAND_BASE_DIR}/share/registry module:$${BRAND_BASE_DIR}/share/registry/modules res:$${BRAND_BASE_DIR}/share/registry' >> assets/program/fundamentalrc
+ echo 'URE_BIN_DIR=file:///assets/ure/bin/dir/not-here/can-we/exec-anyway' >> assets/program/fundamentalrc
+ echo 'URE_MORE_TYPES=file:///assets/bin/ure/types.rdb file:///assets/bin/types.rdb' >> assets/program/fundamentalrc
+ echo 'URE_MORE_SERVICES=file:///assets/xml/services.rdb <$$BRAND_BASE_DIR/program/services>*' >> assets/program/fundamentalrc
+#
+# Set up unorc
+ echo '[Bootstrap]' > assets/program/unorc
+ echo "URE_INTERNAL_LIB_DIR=file://$(APP_DATA_PATH)/lib/" >> assets/program/unorc
+ echo 'UNO_TYPES=file:///assets/bin/ure/types.rdb file:///assets/bin/types.rdb $${URE_MORE_TYPES}' >> assets/program/unorc
+ echo 'UNO_SERVICES=file:///assets/xml/ure/services.rdb $${URE_MORE_SERVICES}' >> assets/program/unorc
+#
+# Set up bootstraprc
+ echo '[Bootstrap]' > assets/program/bootstraprc
+ echo 'InstallMode=<installmode>' >> assets/program/bootstraprc
+ echo 'ProductKey=LibreOffice 3.6' >> assets/program/bootstraprc
+ echo "UserInstallation=file://$(APP_DATA_PATH)" >> assets/program/bootstraprc
+#
+# Set up versionrc
+ echo '[Version]' > assets/program/versionrc
+ echo 'AllLanguages=en-US' >> assets/program/versionrc
+ echo 'BuildVersion=' >> assets/program/versionrc
+ echo 'buildid=dead-beef' >> assets/program/versionrc
+ echo 'ProductBuildid=3' >> assets/program/versionrc
+ echo 'ProductMajor=360' >> assets/program/versionrc
+ echo 'ProductMinor=1' >> assets/program/versionrc
+ echo 'ProductSource=OOO350' >> assets/program/versionrc
+ echo 'ReferenceOOoMajorMinor=3.6' >> assets/program/versionrc
+#
+# .res files
+ mkdir -p assets/program/resource
+ cp $(OUTDIR)/bin/*en-US.res assets/program/resource
+#
+# Assets that are unpacked at run-time into the app's data directory. These
+# are files read by non-LO code, fontconfig and freetype for now, that doesn't
+# understand "/assets" paths.
+ mkdir -p assets/unpack/etc/fonts
+ cp fonts.conf assets/unpack/etc/fonts
+ mkdir -p assets/unpack/user/fonts
+# $UserInstallation/user/fonts is added to the fontconfig path in
+# vcl/generic/fontmanager/helper.cxx: psp::getFontPath(). UserInstallation is
+# set to the app's data dir above.
+ cp $(OUTDIR)/pck/Liberation*.ttf assets/unpack/user/fonts
+ cp $(OUTDIR)/pck/Gen*.ttf assets/unpack/user/fonts
+ cp $(OUTDIR)/pck/opens___.ttf assets/unpack/user/fonts
+#
+# Then gdbserver and gdb.setup so that we can debug with ndk-gdb.
+#
+ cp $(ANDROID_NDK_HOME)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/gdbserver $(SODEST)
+ echo set solib-search-path ./obj/local/armeabi-v7a >$(SODEST)/gdb.setup
+
+build-ant: copy-stuff properties
+#
+# Copy jar files we need, and even construct one.
+#
+ for F in $(strip \
+ java_uno \
+ juh \
+ jurt \
+ ridl \
+ unoil \
+ unoloader \
+ ); do \
+ $(call COPYJAR,$(OUTDIR)/bin/$${F}.jar); \
+ done
+#
+ unset JAVA_HOME && $(ANT) debug
+
+install: build-ant
+ unset JAVA_HOME && $(ANT) debug install
+ @echo
+ @echo 'Run it with something like what "make run" does (see Makefile)'
+ @echo
+
+uninstall:
+ $(ANDROID_SDK_HOME)/platform-tools/adb uninstall $(APP_PACKAGE)
+
+run:
+# /data/local/tmp/sample-document.odt
+ adb shell am start -n org.libreoffice.android.examples/.DocumentLoader -e input /assets/test1.odt
+
+
+clean: properties
+ $(ANT) clean
+ rm -rf assets libs $(SODEST) $(OBJLOCAL)
diff --git a/android/experimental/LibreOffice4Android/build.xml b/android/experimental/LibreOffice4Android/build.xml
new file mode 100644
index 0000000..5d96019
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/build.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="LibreOfficeDocumentLoader" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+ <!-- Re-define the "-package-resources" target to not compress resources -->
+
+ <target name="-package-resources" depends="-crunch">
+ <!-- only package resources if *not* a library project -->
+ <do-only-if-not-library elseText="Library project: do not package resources..." >
+ <aapt executable="${aapt}"
+ command="package"
+ versioncode="${version.code}"
+ versionname="${version.name}"
+ debug="${build.is.packaging.debug}"
+ manifest="AndroidManifest.xml"
+ assets="${asset.absolute.dir}"
+ androidjar="${android.jar}"
+ apkfolder="${out.absolute.dir}"
+ nocrunch="${build.packaging.nocrunch}"
+ resourcefilename="${resource.package.file.name}"
+ resourcefilter="${aapt.resource.filter}"
+ projectLibrariesResName="project.libraries.res"
+ projectLibrariesPackageName="project.libraries.package"
+ previousBuildType="${build.last.target}"
+ buildType="${build.target}">
+ <res path="${out.res.absolute.dir}" />
+ <res path="${resource.absolute.dir}" />
+ <nocompress /> <!-- forces no compression on any files in assets or res/raw -->
+ <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
+ </aapt>
+ </do-only-if-not-library>
+ </target>
+
+</project>
diff --git a/android/experimental/LibreOffice4Android/fonts.conf b/android/experimental/LibreOffice4Android/fonts.conf
new file mode 100644
index 0000000..699e9d1
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/fonts.conf
@@ -0,0 +1,154 @@
+<?xml version="1.0"?>
+<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
+<!-- /etc/fonts/fonts.conf file to configure system font access -->
+<fontconfig>
+
+<!-- Font directory list -->
+
+ <dir>/system/fonts</dir>
+
+ <alias>
+ <family>serif</family>
+ <prefer>
+ <family>Droid Serif</family>
+ </prefer>
+ </alias>
+ <alias>
+ <family>sans-serif</family>
+ <prefer>
+ <family>Roboto</family>
+ <family>Droid Sans Fallback</family>
+ </prefer>
+ </alias>
+ <alias>
+ <family>monospace</family>
+ <prefer>
+ <family>Droid Sans Mono</family>
+ </prefer>
+ </alias>
+
+<!--
+ Accept deprecated 'mono' alias, replacing it with 'monospace'
+-->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>mono</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>monospace</string>
+ </edit>
+ </match>
+
+<!--
+ Accept alternate 'sans serif' spelling, replacing it with 'sans-serif'
+-->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>sans serif</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>sans-serif</string>
+ </edit>
+ </match>
+
+<!--
+ Accept deprecated 'sans' alias, replacing it with 'sans-serif'
+-->
+ <match target="pattern">
+ <test qual="any" name="family">
+ <string>sans</string>
+ </test>
+ <edit name="family" mode="assign">
+ <string>sans-serif</string>
+ </edit>
+ </match>
+
+<!--
+ Load local system customization file
+-->
+ <include ignore_missing="yes">conf.d</include>
+
+<!-- Font cache directory list -->
+
+ <!-- Yeah this hardcoding is wrong of course, will have to fix
+ later to patch in proper code in fontonfig on Android to
+ find out a good place.
+ -->
+ <cachedir>/data/data/org.libreoffice.android.examples/fontconfig</cachedir>
+
+ <config>
+<!--
+ These are the default Unicode chars that are expected to be blank
+ in fonts. All other blank chars are assumed to be broken and
+ won't appear in the resulting charsets
+ -->
+ <blank>
+ <int>0x0020</int> <!-- SPACE -->
+ <int>0x00A0</int> <!-- NO-BREAK SPACE -->
+ <int>0x00AD</int> <!-- SOFT HYPHEN -->
+ <int>0x034F</int> <!-- COMBINING GRAPHEME JOINER -->
+ <int>0x0600</int> <!-- ARABIC NUMBER SIGN -->
+ <int>0x0601</int> <!-- ARABIC SIGN SANAH -->
+ <int>0x0602</int> <!-- ARABIC FOOTNOTE MARKER -->
+ <int>0x0603</int> <!-- ARABIC SIGN SAFHA -->
+ <int>0x06DD</int> <!-- ARABIC END OF AYAH -->
+ <int>0x070F</int> <!-- SYRIAC ABBREVIATION MARK -->
+ <int>0x115F</int> <!-- HANGUL CHOSEONG FILLER -->
+ <int>0x1160</int> <!-- HANGUL JUNGSEONG FILLER -->
+ <int>0x1680</int> <!-- OGHAM SPACE MARK -->
+ <int>0x17B4</int> <!-- KHMER VOWEL INHERENT AQ -->
+ <int>0x17B5</int> <!-- KHMER VOWEL INHERENT AA -->
+ <int>0x180E</int> <!-- MONGOLIAN VOWEL SEPARATOR -->
+ <int>0x2000</int> <!-- EN QUAD -->
+ <int>0x2001</int> <!-- EM QUAD -->
+ <int>0x2002</int> <!-- EN SPACE -->
+ <int>0x2003</int> <!-- EM SPACE -->
+ <int>0x2004</int> <!-- THREE-PER-EM SPACE -->
+ <int>0x2005</int> <!-- FOUR-PER-EM SPACE -->
+ <int>0x2006</int> <!-- SIX-PER-EM SPACE -->
+ <int>0x2007</int> <!-- FIGURE SPACE -->
+ <int>0x2008</int> <!-- PUNCTUATION SPACE -->
+ <int>0x2009</int> <!-- THIN SPACE -->
+ <int>0x200A</int> <!-- HAIR SPACE -->
+ <int>0x200B</int> <!-- ZERO WIDTH SPACE -->
+ <int>0x200C</int> <!-- ZERO WIDTH NON-JOINER -->
+ <int>0x200D</int> <!-- ZERO WIDTH JOINER -->
+ <int>0x200E</int> <!-- LEFT-TO-RIGHT MARK -->
+ <int>0x200F</int> <!-- RIGHT-TO-LEFT MARK -->
+ <int>0x2028</int> <!-- LINE SEPARATOR -->
+ <int>0x2029</int> <!-- PARAGRAPH SEPARATOR -->
+ <int>0x202A</int> <!-- LEFT-TO-RIGHT EMBEDDING -->
+ <int>0x202B</int> <!-- RIGHT-TO-LEFT EMBEDDING -->
+ <int>0x202C</int> <!-- POP DIRECTIONAL FORMATTING -->
+ <int>0x202D</int> <!-- LEFT-TO-RIGHT OVERRIDE -->
+ <int>0x202E</int> <!-- RIGHT-TO-LEFT OVERRIDE -->
+ <int>0x202F</int> <!-- NARROW NO-BREAK SPACE -->
+ <int>0x205F</int> <!-- MEDIUM MATHEMATICAL SPACE -->
+ <int>0x2060</int> <!-- WORD JOINER -->
+ <int>0x2061</int> <!-- FUNCTION APPLICATION -->
+ <int>0x2062</int> <!-- INVISIBLE TIMES -->
+ <int>0x2063</int> <!-- INVISIBLE SEPARATOR -->
+ <int>0x206A</int> <!-- INHIBIT SYMMETRIC SWAPPING -->
+ <int>0x206B</int> <!-- ACTIVATE SYMMETRIC SWAPPING -->
+ <int>0x206C</int> <!-- INHIBIT ARABIC FORM SHAPING -->
+ <int>0x206D</int> <!-- ACTIVATE ARABIC FORM SHAPING -->
+ <int>0x206E</int> <!-- NATIONAL DIGIT SHAPES -->
+ <int>0x206F</int> <!-- NOMINAL DIGIT SHAPES -->
+ <int>0x2800</int> <!-- BRAILLE PATTERN BLANK -->
+ <int>0x3000</int> <!-- IDEOGRAPHIC SPACE -->
+ <int>0x3164</int> <!-- HANGUL FILLER -->
+ <int>0xFEFF</int> <!-- ZERO WIDTH NO-BREAK SPACE -->
+ <int>0xFFA0</int> <!-- HALFWIDTH HANGUL FILLER -->
+ <int>0xFFF9</int> <!-- INTERLINEAR ANNOTATION ANCHOR -->
+ <int>0xFFFA</int> <!-- INTERLINEAR ANNOTATION SEPARATOR -->
+ <int>0xFFFB</int> <!-- INTERLINEAR ANNOTATION TERMINATOR -->
+ </blank>
+<!--
+ Rescan configuration every 3600 seconds when FcFontSetList is called
+ -->
+ <rescan>
+ <int>3600</int>
+ </rescan>
+ </config>
+
+</fontconfig>
diff --git a/android/experimental/LibreOffice4Android/jni/Android.mk b/android/experimental/LibreOffice4Android/jni/Android.mk
new file mode 100644
index 0000000..939a1ea
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/jni/Android.mk
@@ -0,0 +1,8 @@
+# Needed just to satisfy ndk-gdb for now, but maybe later we will actually add
+# some JNI code here
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/experimental/LibreOffice4Android/jni/Application.mk b/android/experimental/LibreOffice4Android/jni/Application.mk
new file mode 100644
index 0000000..f326d1a
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/jni/Application.mk
@@ -0,0 +1,3 @@
+# File needed by ndk-gdb
+APP_ABI := armeabi-v7a
+APP_PLATFORM := android-14
diff --git a/android/experimental/LibreOffice4Android/project.properties b/android/experimental/LibreOffice4Android/project.properties
new file mode 100644
index 0000000..06b2d88
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-14
+
+# Use the Bootstrap class
+android.library.reference.1=../../Bootstrap
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/action_search.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/action_search.png
new file mode 100644
index 0000000..e6b7045
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/action_search.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/base.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/base.png
new file mode 100644
index 0000000..729dbcd
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/base.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/calc.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/calc.png
new file mode 100644
index 0000000..a3f5fd4
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/calc.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/draw.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/draw.png
new file mode 100644
index 0000000..b3ee114
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/draw.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/dummy_page.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/dummy_page.png
new file mode 100644
index 0000000..c58d276
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/dummy_page.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/folder.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/folder.png
new file mode 100644
index 0000000..9c9b42c
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/folder.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/ic_launcher.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..8074c4c
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/ic_launcher.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/impress.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/impress.png
new file mode 100644
index 0000000..5909f05
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/impress.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_sort_by_size.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_sort_by_size.png
new file mode 100644
index 0000000..3b34aaf
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_sort_by_size.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_grid.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_grid.png
new file mode 100644
index 0000000..ae138ed
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_grid.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_list.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_list.png
new file mode 100644
index 0000000..c5f6c97
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/light_view_as_list.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/lo_icon.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/lo_icon.png
new file mode 100644
index 0000000..2ef8641
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/lo_icon.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/main.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/main.png
new file mode 100644
index 0000000..7e8e2a0
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/main.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/math.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/math.png
new file mode 100644
index 0000000..50b8dc8
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/math.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/startcenter.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/startcenter.png
new file mode 100644
index 0000000..7e8e2a0
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/startcenter.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-hdpi/writer.png b/android/experimental/LibreOffice4Android/res/drawable-hdpi/writer.png
new file mode 100644
index 0000000..2f4abcb
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-hdpi/writer.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-ldpi/dummy_page.png b/android/experimental/LibreOffice4Android/res/drawable-ldpi/dummy_page.png
new file mode 100644
index 0000000..c58d276
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-ldpi/dummy_page.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-ldpi/ic_launcher.png b/android/experimental/LibreOffice4Android/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..1095584
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-ldpi/ic_launcher.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-ldpi/lo_icon.png b/android/experimental/LibreOffice4Android/res/drawable-ldpi/lo_icon.png
new file mode 100644
index 0000000..95b3113
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-ldpi/lo_icon.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-mdpi/ic_launcher.png b/android/experimental/LibreOffice4Android/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-mdpi/ic_launcher.png differ
diff --git a/android/experimental/LibreOffice4Android/res/drawable-mdpi/lo_icon.png b/android/experimental/LibreOffice4Android/res/drawable-mdpi/lo_icon.png
new file mode 100644
index 0000000..4f3f89b
Binary files /dev/null and b/android/experimental/LibreOffice4Android/res/drawable-mdpi/lo_icon.png differ
diff --git a/android/experimental/LibreOffice4Android/res/layout/file_explorer_grid_item.xml b/android/experimental/LibreOffice4Android/res/layout/file_explorer_grid_item.xml
new file mode 100644
index 0000000..ce42e57
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/layout/file_explorer_grid_item.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <ImageView
+ android:id="@+id/grid_item_image"
+ android:layout_width="50dp"
+ android:layout_height="75dp"
+ android:paddingTop="15dp"
+ android:paddingBottom="10dp"
+ android:layout_gravity="center" >
+ </ImageView>
+
+ <TextView
+ android:id="@+id/grid_item_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@+id/label"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:layout_gravity="center"
+ android:textSize="15dp"
+ android:textStyle="bold"
+ android:maxLines="2">
+ </TextView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/layout/file_grid.xml b/android/experimental/LibreOffice4Android/res/layout/file_grid.xml
new file mode 100644
index 0000000..1e241c0
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/layout/file_grid.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <GridView
+ android:id="@+id/file_explorer_grid_view"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:columnWidth="120dp"
+ android:numColumns="auto_fit"
+ android:verticalSpacing="10dp"
+ android:horizontalSpacing="10dp"
+ android:stretchMode="columnWidth"
+ android:gravity="center">
+ </GridView>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/layout/file_list.xml b/android/experimental/LibreOffice4Android/res/layout/file_list.xml
new file mode 100644
index 0000000..6ef0255
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/layout/file_list.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <ListView
+ android:id="@+id/file_explorer_list_view"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ ></ListView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/layout/file_list_item.xml b/android/experimental/LibreOffice4Android/res/layout/file_list_item.xml
new file mode 100644
index 0000000..0bff445
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/layout/file_list_item.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="horizontal" >
+ <ImageView
+ android:id="@+id/file_list_item_icon"
+ android:layout_height="match_parent"
+ android:layout_width="32dp"
+ android:layout_margin="8dp"
+ android:layout_gravity="center"/>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/file_list_item_name"
+ android:layout_height="48dp"
+ android:layout_width="0dp"
+ android:textSize="15dp"
+ android:textStyle="bold"
+ android:layout_weight="2"
+ android:gravity="center"/>
+ <TextView
+ android:id="@+id/file_list_item_size"
+ android:layout_height="48dp"
+ android:layout_width="0dp"
+ android:textSize="15dp"
+ android:textStyle="bold"
+ android:layout_weight="1"
+ android:gravity="center"/>
+ <TextView
+ android:id="@+id/file_list_item_date"
+ android:layout_height="48dp"
+ android:layout_width="0dp"
+ android:textSize="15dp"
+ android:textStyle="bold"
+ android:layout_weight="2"
+ android:gravity="center"/>
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/layout/main.xml b/android/experimental/LibreOffice4Android/res/layout/main.xml
new file mode 100644
index 0000000..6b97fe1
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/layout/main.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#aaa"
+ android:orientation="vertical"
+ >
+
+ <org.libreoffice.ui.PageView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/menu/view_menu.xml b/android/experimental/LibreOffice4Android/res/menu/view_menu.xml
new file mode 100644
index 0000000..87270d3
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/menu/view_menu.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/menu_search"
+ android:icon="@drawable/action_search"
+ android:showAsAction="always" />
+ <item android:id="@+id/menu_view_toggle"
+ android:title="@string/grid_view"/>
+ <item android:id="@+id/menu_sort_size"
+ android:title="@string/menu_sort_size"
+ android:onClick="sortFiles"/>
+ <item android:id="@+id/menu_sort_az"
+ android:title="@string/menu_sort_az"
+ android:onClick="sortFiles"/>
+ <item android:id="@+id/menu_sort_modified"
+ android:title="@string/menu_sort_modified"
+ android:onClick="sortFiles"/>
+ <item android:id="@+id/menu_preferences"
+ android:title="@string/menu_preferences"
+ android:onClick="editPreferences"/>
+</menu>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/values/arrays.xml b/android/experimental/LibreOffice4Android/res/values/arrays.xml
new file mode 100644
index 0000000..67a157d
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/values/arrays.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer-array name="FilterTypeValues">
+ <item >-1</item>
+ <item >0</item>
+ <item >1</item>
+ <item >2</item>
+ </integer-array>
+ <string-array name="FilterTypeStringValues">
+ <item >-1</item>
+ <item >0</item>
+ <item >1</item>
+ <item >2</item>
+ </string-array>
+ <string-array name="SortModeStringValues">
+ <item >0</item>
+ <item >1</item>
+ <item >2</item>
+ <item >3</item>
+ <item >4</item>
+ <item >5</item>
+ </string-array>
+ <!-- View Mode names,values -->
+ <string-array name="ViewModeNames">
+ <item >Grid</item>
+ <item >List</item>
+ </string-array>
+ <string-array name="ViewModeStringValues">
+ <item >0</item>
+ <item >1</item>
+ </string-array>
+
+ <!-- Preference Name Arrays -->
+ <string-array name="file_view_modes">
+ <item >EVERYTHING</item>
+ <item >DOCUMENTS</item>
+ <item >SPREADSHEETS</item>
+ <item >PRESENTATIONS</item>
+ </string-array>
+ <string-array name="FilterTypeNames">
+ <item >Everything</item>
+ <item >Documents</item>
+ <item >Spreadsheets</item>
+ <item >Presentations</item>
+ </string-array>
+ <string-array name="SortModeNames">
+ <item >A-Z</item>
+ <item >Z-A</item>
+ <item >Oldest First</item>
+ <item >Newest First</item>
+ <item >Largest First</item>
+ <item >Smallest First</item>
+ </string-array>
+
+
+</resources>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/values/strings.xml b/android/experimental/LibreOffice4Android/res/values/strings.xml
new file mode 100644
index 0000000..8951dd4
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">LibreOfficeUI</string>
+ <string name="menu_search">Search</string>
+ <string name="list_view">List</string>
+ <string name="grid_view">Grid</string>
+ <string name="menu_sort_size">Sort By Size</string>
+ <string name="menu_sort_az">Sort A-Z</string>
+ <string name="menu_sort_modified">Sort by Date</string>
+ <string name="menu_preferences">Preferences</string>
+ <!-- Pref keys as resources ; Not currently used -->
+ <string name="EXPLORER_VIEW_TYPE_KEY">EXPLORER_VIEW_TYPE</string>
+ <string name="CURRENT_DIRECTORY_KEY">CURRENT_DIRECTORY</string>
+
+
+</resources>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/res/xml/libreoffice_preferences.xml b/android/experimental/LibreOffice4Android/res/xml/libreoffice_preferences.xml
new file mode 100644
index 0000000..d19d9e6
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/res/xml/libreoffice_preferences.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+ <ListPreference
+ android:title="Default File Filter"
+ android:summary="Set which files to show by default"
+ android:entries="@array/FilterTypeNames"
+ android:entryValues="@array/FilterTypeStringValues"
+ android:key="FILTER_MODE"/>
+ <ListPreference
+ android:summary="Select how to order files; A-Z, by size, etc."
+ android:key="SORT_MODE"
+ android:title="File Order" android:entries="@array/SortModeNames" android:entryValues="@array/SortModeStringValues"/>
+ <ListPreference
+ android:entries="@array/ViewModeNames"
+ android:entryValues="@array/ViewModeStringValues"
+ android:title="Default File Explorer View"
+ android:key="EXPLORER_VIEW_TYPE"
+ android:summary="View files as a grid or in a list. #not functional, yet."/>
+
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/Animation.java b/android/experimental/LibreOffice4Android/src/com/polites/android/Animation.java
new file mode 100644
index 0000000..9936208
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/Animation.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface Animation {
+
+ /**
+ * Transforms the view.
+ * @param view
+ * @param diffTime
+ * @return true if this animation should remain active. False otherwise.
+ */
+ public boolean update(GestureImageView view, long time);
+
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/Animator.java b/android/experimental/LibreOffice4Android/src/com/polites/android/Animator.java
new file mode 100644
index 0000000..fb0728b
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/Animator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class Animator extends Thread {
+
+ private GestureImageView view;
+ private Animation animation;
+ private boolean running = false;
+ private boolean active = false;
+ private long lastTime = -1L;
+
+ public Animator(GestureImageView view, String threadName) {
+ super(threadName);
+ this.view = view;
+ }
+
+ @Override
+ public void run() {
+
+ running = true;
+
+ while(running) {
+
+ while(active && animation != null) {
+ long time = System.currentTimeMillis();
+ active = animation.update(view, time - lastTime);
+ view.redraw();
+ lastTime = time;
+
+ while(active) {
+ try {
+ if(view.waitForDraw(32)) { // 30Htz
+ break;
+ }
+ }
+ catch (InterruptedException ignore) {
+ active = false;
+ }
+ }
+ }
+
+ synchronized(this) {
+ if(running) {
+ try {
+ wait();
+ }
+ catch (InterruptedException ignore) {}
+ }
+ }
+ }
+ }
+
+ public synchronized void finish() {
+ running = false;
+ active = false;
+ notifyAll();
+ }
+
+ public void play(Animation transformer) {
+ if(active) {
+ cancel();
+ }
+ this.animation = transformer;
+
+ activate();
+ }
+
+ public synchronized void activate() {
+ lastTime = System.currentTimeMillis();
+ active = true;
+ notifyAll();
+ }
+
+ public void cancel() {
+ active = false;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimation.java b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimation.java
new file mode 100644
index 0000000..3124b62
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimation.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class FlingAnimation implements Animation {
+
+ private float velocityX;
+ private float velocityY;
+
+ private float factor = 0.85f;
+
+ private float threshold = 10;
+
+ private FlingAnimationListener listener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Transformer#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ float seconds = (float) time / 1000.0f;
+
+ float dx = velocityX * seconds;
+ float dy = velocityY * seconds;
+
+ velocityX *= factor;
+ velocityY *= factor;
+
+ boolean active = (Math.abs(velocityX) > threshold && Math.abs(velocityY) > threshold);
+
+ if(listener != null) {
+ listener.onMove(dx, dy);
+
+ if(!active) {
+ listener.onComplete();
+ }
+ }
+
+ return active;
+ }
+
+ public void setVelocityX(float velocityX) {
+ this.velocityX = velocityX;
+ }
+
+ public void setVelocityY(float velocityY) {
+ this.velocityY = velocityY;
+ }
+
+ public void setFactor(float factor) {
+ this.factor = factor;
+ }
+
+ public void setListener(FlingAnimationListener listener) {
+ this.listener = listener;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimationListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimationListener.java
new file mode 100644
index 0000000..b9611d5
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingAnimationListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface FlingAnimationListener {
+
+ public void onMove(float x, float y);
+
+ public void onComplete();
+
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/FlingListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingListener.java
new file mode 100644
index 0000000..ab3007a
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/FlingListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class FlingListener extends SimpleOnGestureListener {
+
+ private float velocityX;
+ private float velocityY;
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ this.velocityX = velocityX;
+ this.velocityY = velocityY;
+ return true;
+ }
+
+ public float getVelocityX() {
+ return velocityX;
+ }
+
+ public float getVelocityY() {
+ return velocityY;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageView.java b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageView.java
new file mode 100644
index 0000000..1cde6e4
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageView.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import java.io.InputStream;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ImageView;
+
+public class GestureImageView extends ImageView {
+
+ public static final String GLOBAL_NS = "http://schemas.android.com/apk/res/android";
+ public static final String LOCAL_NS = "http://schemas.polites.com/android";
+
+ private final Semaphore drawLock = new Semaphore(0);
+ private Animator animator;
+
+ private Drawable drawable;
+
+ private float x = 0, y = 0;
+
+ private boolean layout = false;
+
+ private float scaleAdjust = 1.0f;
+ private float startingScale = -1.0f;
+
+ private float scale = 1.0f;
+ private float maxScale = 5.0f;
+ private float minScale = 0.75f;
+ private float fitScaleHorizontal = 1.0f;
+ private float fitScaleVertical = 1.0f;
+ private float rotation = 0.0f;
+
+ private float centerX;
+ private float centerY;
+
+ private Float startX, startY;
+
+ private int hWidth;
+ private int hHeight;
+
+ private int resId = -1;
+ private boolean recycle = false;
+ private boolean strict = false;
+
+ private int displayHeight;
+ private int displayWidth;
+
+ private int alpha = 255;
+ private ColorFilter colorFilter;
+
+ private int deviceOrientation = -1;
+ private int imageOrientation;
+
+ private GestureImageViewListener gestureImageViewListener;
+ private GestureImageViewTouchListener gestureImageViewTouchListener;
+
+ private OnTouchListener customOnTouchListener;
+ private OnClickListener onClickListener;
+
+ public GestureImageView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs);
+ }
+
+ public GestureImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ String scaleType = attrs.getAttributeValue(GLOBAL_NS, "scaleType");
+
+ if(scaleType == null || scaleType.trim().length() == 0) {
+ setScaleType(ScaleType.CENTER_INSIDE);
+ }
+
+ String strStartX = attrs.getAttributeValue(LOCAL_NS, "start-x");
+ String strStartY = attrs.getAttributeValue(LOCAL_NS, "start-y");
+
+ if(strStartX != null && strStartX.trim().length() > 0) {
+ startX = Float.parseFloat(strStartX);
+ }
+
+ if(strStartY != null && strStartY.trim().length() > 0) {
+ startY = Float.parseFloat(strStartY);
+ }
+
+ setStartingScale(attrs.getAttributeFloatValue(LOCAL_NS, "start-scale", startingScale));
+ setMinScale(attrs.getAttributeFloatValue(LOCAL_NS, "min-scale", minScale));
+ setMaxScale(attrs.getAttributeFloatValue(LOCAL_NS, "max-scale", maxScale));
+ setStrict(attrs.getAttributeBooleanValue(LOCAL_NS, "strict", strict));
+ setRecycle(attrs.getAttributeBooleanValue(LOCAL_NS, "recycle", recycle));
+
+ initImage();
+ }
+
+ public GestureImageView(Context context) {
+ super(context);
+ setScaleType(ScaleType.CENTER_INSIDE);
+ initImage();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ if(drawable != null) {
+ int orientation = getResources().getConfiguration().orientation;
+ if(orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ if(getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
+ float ratio = (float) getImageWidth() / (float) getImageHeight();
+ displayWidth = Math.round( (float) displayHeight * ratio) ;
+ }
+ else {
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ }
+ }
+ else {
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ if(getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
+ float ratio = (float) getImageHeight() / (float) getImageWidth();
+ displayHeight = Math.round( (float) displayWidth * ratio) ;
+ }
+ else {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+ }
+ }
+ }
+ else {
+ displayHeight = MeasureSpec.getSize(heightMeasureSpec);
+ displayWidth = MeasureSpec.getSize(widthMeasureSpec);
+ }
+
+ setMeasuredDimension(displayWidth, displayHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if(changed || !layout) {
+ setupCanvas(displayWidth, displayHeight, getResources().getConfiguration().orientation);
+ }
+ }
+
+ protected void setupCanvas(int measuredWidth, int measuredHeight, int orientation) {
+
+ if(deviceOrientation != orientation) {
+ layout = false;
+ deviceOrientation = orientation;
+ }
+
+ if(drawable != null && !layout) {
+ int imageWidth = getImageWidth();
+ int imageHeight = getImageHeight();
+
+ hWidth = Math.round(((float)imageWidth / 2.0f));
+ hHeight = Math.round(((float)imageHeight / 2.0f));
+
+ measuredWidth -= (getPaddingLeft() + getPaddingRight());
+ measuredHeight -= (getPaddingTop() + getPaddingBottom());
+
+ computeCropScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
+
+ if(startingScale <= 0.0f) {
+ computeStartingScale(imageWidth, imageHeight, measuredWidth, measuredHeight);
+ }
+
+ scaleAdjust = startingScale;
+
+ this.centerX = (float) measuredWidth / 2.0f;
+ this.centerY = (float) measuredHeight / 2.0f;
+
+ if(startX == null) {
+ x = centerX;
+ }
+ else {
+ x = startX;
+ }
+
+ if(startY == null) {
+ y = centerY;
+ }
+ else {
+ y = startY;
+ }
+
+ gestureImageViewTouchListener = new GestureImageViewTouchListener(this, measuredWidth, measuredHeight);
+
+ if(isLandscape()) {
+ gestureImageViewTouchListener.setMinScale(minScale * fitScaleHorizontal);
+ }
+ else {
+ gestureImageViewTouchListener.setMinScale(minScale * fitScaleVertical);
+ }
+
+
+ gestureImageViewTouchListener.setMaxScale(maxScale * startingScale);
+
+ gestureImageViewTouchListener.setFitScaleHorizontal(fitScaleHorizontal);
+ gestureImageViewTouchListener.setFitScaleVertical(fitScaleVertical);
+ gestureImageViewTouchListener.setCanvasWidth(measuredWidth);
+ gestureImageViewTouchListener.setCanvasHeight(measuredHeight);
+ gestureImageViewTouchListener.setOnClickListener(onClickListener);
+
+ drawable.setBounds(-hWidth,-hHeight,hWidth,hHeight);
+
+ super.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if(customOnTouchListener != null) {
+ customOnTouchListener.onTouch(v, event);
+ }
+ return gestureImageViewTouchListener.onTouch(v, event);
+ }
+ });
+
+ layout = true;
+ }
+ }
+
+ protected void computeCropScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
+ fitScaleHorizontal = (float) measuredWidth / (float) imageWidth;
+ fitScaleVertical = (float) measuredHeight / (float) imageHeight;
+ }
+
+ protected void computeStartingScale(int imageWidth, int imageHeight, int measuredWidth, int measuredHeight) {
+ switch(getScaleType()) {
+ case CENTER:
+ // Center the image in the view, but perform no scaling.
+ startingScale = 1.0f;
+ break;
+
+ case CENTER_CROP:
+ startingScale = Math.max((float) measuredHeight / (float) imageHeight, (float) measuredWidth/ (float) imageWidth);
+ break;
+
+ case CENTER_INSIDE:
+ if(isLandscape()) {
+ startingScale = fitScaleHorizontal;
+ }
+ else {
+ startingScale = fitScaleVertical;
+ }
+ break;
+ }
+ }
+
+ protected boolean isRecycled() {
+ if(drawable != null && drawable instanceof BitmapDrawable) {
+ Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
+ if(bitmap != null) {
+ return bitmap.isRecycled();
+ }
+ }
+ return false;
+ }
+
+ protected void recycle() {
+ if(recycle && drawable != null && drawable instanceof BitmapDrawable) {
+ Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
+ if(bitmap != null) {
+ bitmap.recycle();
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if(layout) {
+ if(drawable != null && !isRecycled()) {
+ canvas.save();
+
+ float adjustedScale = scale * scaleAdjust;
+
+ canvas.translate(x, y);
+
+ if(rotation != 0.0f) {
+ canvas.rotate(rotation);
+ }
+
+ if(adjustedScale != 1.0f) {
+ canvas.scale(adjustedScale, adjustedScale);
+ }
+
+ drawable.draw(canvas);
+
+ canvas.restore();
+ }
+
+ if(drawLock.availablePermits() <= 0) {
+ drawLock.release();
+ }
+ }
+ }
+
+ /**
+ * Waits for a draw
+ * @param max time to wait for draw (ms)
+ * @throws InterruptedException
+ */
+ public boolean waitForDraw(long timeout) throws InterruptedException {
+ return drawLock.tryAcquire(timeout, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ animator = new Animator(this, "GestureImageViewAnimator");
+ animator.start();
+
+ if(resId >= 0 && drawable == null) {
+ setImageResource(resId);
+ }
+
+ super.onAttachedToWindow();
+ }
+
+ public void animationStart(Animation animation) {
+ if(animator != null) {
+ animator.play(animation);
+ }
+ }
+
+ public void animationStop() {
+ if(animator != null) {
+ animator.cancel();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if(animator != null) {
+ animator.finish();
+ }
+ if(recycle && drawable != null && !isRecycled()) {
+ recycle();
+ drawable = null;
+ }
+ super.onDetachedFromWindow();
+ }
+
+ protected void initImage() {
+ if(this.drawable != null) {
+ this.drawable.setAlpha(alpha);
+ this.drawable.setFilterBitmap(true);
+ if(colorFilter != null) {
+ this.drawable.setColorFilter(colorFilter);
+ }
+ }
+
+ if(!layout) {
+ requestLayout();
+ redraw();
+ }
+ }
+
+ public void setImageBitmap(Bitmap image) {
+ this.drawable = new BitmapDrawable(getResources(), image);
+ initImage();
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ this.drawable = drawable;
+ initImage();
+ }
+
+ public void setImageResource(int id) {
+ if(this.drawable != null) {
+ this.recycle();
+ }
+ if(id >= 0) {
+ this.resId = id;
+ setImageDrawable(getContext().getResources().getDrawable(id));
+ }
+ }
+
+ public int getScaledWidth() {
+ return Math.round(getImageWidth() * getScale());
+ }
+
+ public int getScaledHeight() {
+ return Math.round(getImageHeight() * getScale());
+ }
+
+ public int getImageWidth() {
+ if(drawable != null) {
+ return drawable.getIntrinsicWidth();
+ }
+ return 0;
+ }
+
+ public int getImageHeight() {
+ if(drawable != null) {
+ return drawable.getIntrinsicHeight();
+ }
+ return 0;
+ }
+
+ public void moveBy(float x, float y) {
+ this.x += x;
+ this.y += y;
+ }
+
+ public void setPosition(float x, float y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public void redraw() {
+ postInvalidate();
+ }
+
+ public void setMinScale(float min) {
+ this.minScale = min;
+ if(gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setMinScale(min * fitScaleHorizontal);
+ }
+ }
+
+ public void setMaxScale(float max) {
+ this.maxScale = max;
+ if(gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setMaxScale(max * startingScale);
+ }
+ }
+
+ public void setScale(float scale) {
+ scaleAdjust = scale;
+ }
+
+ public float getScale() {
+ return scaleAdjust;
+ }
+
+ public float getImageX() {
+ return x;
+ }
+
+ public float getImageY() {
+ return y;
+ }
+
+ public boolean isStrict() {
+ return strict;
+ }
+
+ public void setStrict(boolean strict) {
+ this.strict = strict;
+ }
+
+ public boolean isRecycle() {
+ return recycle;
+ }
+
+ public void setRecycle(boolean recycle) {
+ this.recycle = recycle;
+ }
+
+ public void reset() {
+ x = centerX;
+ y = centerY;
+ scaleAdjust = startingScale;
+ redraw();
+ }
+
+ public void setRotation(float rotation) {
+ this.rotation = rotation;
+ }
+
+ public void setGestureImageViewListener(GestureImageViewListener pinchImageViewListener) {
+ this.gestureImageViewListener = pinchImageViewListener;
+ }
+
+ public GestureImageViewListener getGestureImageViewListener() {
+ return gestureImageViewListener;
+ }
+
+ @Override
+ public Drawable getDrawable() {
+ return drawable;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ this.alpha = alpha;
+ if(drawable != null) {
+ drawable.setAlpha(alpha);
+ }
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ this.colorFilter = cf;
+ if(drawable != null) {
+ drawable.setColorFilter(cf);
+ }
+ }
+
+ @Override
+ public void setImageURI(Uri mUri) {
+ if ("content".equals(mUri.getScheme())) {
+ try {
+ String[] orientationColumn = {MediaStore.Images.Media.ORIENTATION};
+
+ Cursor cur = getContext().getContentResolver().query(mUri, orientationColumn, null, null, null);
+
+ if (cur != null && cur.moveToFirst()) {
+ imageOrientation = cur.getInt(cur.getColumnIndex(orientationColumn[0]));
+ }
+
+ InputStream in = null;
+
+ try {
+ in = getContext().getContentResolver().openInputStream(mUri);
+ Bitmap bmp = BitmapFactory.decodeStream(in);
+
+ if(imageOrientation != 0) {
+ Matrix m = new Matrix();
+ m.postRotate(imageOrientation);
+ Bitmap rotated = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
+ bmp.recycle();
+ setImageDrawable(new BitmapDrawable(getResources(), rotated));
+ }
+ else {
+ setImageDrawable(new BitmapDrawable(getResources(), bmp));
+ }
+ }
+ finally {
+ if(in != null) {
+ in.close();
+ }
+
+ if(cur != null) {
+ cur.close();
+ }
+ }
+ }
+ catch (Exception e) {
+ Log.w("GestureImageView", "Unable to open content: " + mUri, e);
+ }
+ }
+ else {
+ setImageDrawable(Drawable.createFromPath(mUri.toString()));
+ }
+
+ if (drawable == null) {
+ Log.e("GestureImageView", "resolveUri failed on bad bitmap uri: " + mUri);
+ // Don't try again.
+ mUri = null;
+ }
+ }
+
+ @Override
+ public Matrix getImageMatrix() {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ return super.getImageMatrix();
+ }
+
+ @Override
+ public void setScaleType(ScaleType scaleType) {
+ if(scaleType == ScaleType.CENTER ||
+ scaleType == ScaleType.CENTER_CROP ||
+ scaleType == ScaleType.CENTER_INSIDE) {
+
+ super.setScaleType(scaleType);
+ }
+ else if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void invalidateDrawable(Drawable dr) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.invalidateDrawable(dr);
+ }
+
+ @Override
+ public int[] onCreateDrawableState(int extraSpace) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ return super.onCreateDrawableState(extraSpace);
+ }
+
+ @Override
+ public void setAdjustViewBounds(boolean adjustViewBounds) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setAdjustViewBounds(adjustViewBounds);
+ }
+
+ @Override
+ public void setImageLevel(int level) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setImageLevel(level);
+ }
+
+ @Override
+ public void setImageMatrix(Matrix matrix) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void setImageState(int[] state, boolean merge) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ }
+
+ @Override
+ public void setSelected(boolean selected) {
+ if(strict) {
+ throw new UnsupportedOperationException("Not supported");
+ }
+ super.setSelected(selected);
+ }
+
+ @Override
+ public void setOnTouchListener(OnTouchListener l) {
+ this.customOnTouchListener = l;
+ }
+
+ public float getCenterX() {
+ return centerX;
+ }
+
+ public float getCenterY() {
+ return centerY;
+ }
+
+ public boolean isLandscape() {
+ return getImageWidth() >= getImageHeight();
+ }
+
+ public boolean isPortrait() {
+ return getImageWidth() <= getImageHeight();
+ }
+
+ public void setStartingScale(float startingScale) {
+ this.startingScale = startingScale;
+ }
+
+ public void setStartingPosition(float x, float y) {
+ this.startX = x;
+ this.startY = y;
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener l) {
+ this.onClickListener = l;
+
+ if(gestureImageViewTouchListener != null) {
+ gestureImageViewTouchListener.setOnClickListener(l);
+ }
+ }
+
+ /**
+ * Returns true if the image dimensions are aligned with the orientation of the device.
+ * @return
+ */
+ public boolean isOrientationAligned() {
+ if(deviceOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ return isLandscape();
+ }
+ else if(deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ return isPortrait();
+ }
+ return true;
+ }
+
+ public int getDeviceOrientation() {
+ return deviceOrientation;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewListener.java
new file mode 100644
index 0000000..4a52358
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+/**
+ * @author jasonpolites
+ *
+ */
+public interface GestureImageViewListener {
+
+ public void onTouch(float x, float y);
+
+ public void onScale(float scale);
+
+ public void onPosition(float x, float y);
+
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewTouchListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewTouchListener.java
new file mode 100644
index 0000000..76751d1
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/GestureImageViewTouchListener.java
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import android.content.res.Configuration;
+import android.graphics.PointF;
+import android.view.GestureDetector;
+import android.view.GestureDetector.SimpleOnGestureListener;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+
+public class GestureImageViewTouchListener implements OnTouchListener {
+
+ private GestureImageView image;
+ private OnClickListener onClickListener;
+
+ private final PointF current = new PointF();
+ private final PointF last = new PointF();
+ private final PointF next = new PointF();
+ private final PointF midpoint = new PointF();
+
+ private final VectorF scaleVector = new VectorF();
+ private final VectorF pinchVector = new VectorF();
+
+ private boolean touched = false;
+ private boolean inZoom = false;
+
+ private float initialDistance;
+ private float lastScale = 1.0f;
+ private float currentScale = 1.0f;
+
+ private float boundaryLeft = 0;
+ private float boundaryTop = 0;
+ private float boundaryRight = 0;
+ private float boundaryBottom = 0;
+
+ private float maxScale = 5.0f;
+ private float minScale = 0.25f;
+ private float fitScaleHorizontal = 1.0f;
+ private float fitScaleVertical = 1.0f;
+
+ private int canvasWidth = 0;
+ private int canvasHeight = 0;
+
+ private float centerX = 0;
+ private float centerY = 0;
+
+ private float startingScale = 0;
+
+ private boolean canDragX = false;
+ private boolean canDragY = false;
+
+ private boolean multiTouch = false;
+
+ private int displayWidth;
+ private int displayHeight;
+
+ private int imageWidth;
+ private int imageHeight;
+
+ private FlingListener flingListener;
+ private FlingAnimation flingAnimation;
+ private ZoomAnimation zoomAnimation;
+ private MoveAnimation moveAnimation;
+ private GestureDetector tapDetector;
+ private GestureDetector flingDetector;
+ private GestureImageViewListener imageListener;
+
+ public GestureImageViewTouchListener(final GestureImageView image, int displayWidth, int displayHeight) {
+ super();
+
+ this.image = image;
+
+ this.displayWidth = displayWidth;
+ this.displayHeight = displayHeight;
+
+ this.centerX = (float) displayWidth / 2.0f;
+ this.centerY = (float) displayHeight / 2.0f;
+
+ this.imageWidth = image.getImageWidth();
+ this.imageHeight = image.getImageHeight();
+
+ startingScale = image.getScale();
+
+ currentScale = startingScale;
+ lastScale = startingScale;
+
+ boundaryRight = displayWidth;
+ boundaryBottom = displayHeight;
+ boundaryLeft = 0;
+ boundaryTop = 0;
+
+ next.x = image.getImageX();
+ next.y = image.getImageY();
+
+ flingListener = new FlingListener();
+ flingAnimation = new FlingAnimation();
+ zoomAnimation = new ZoomAnimation();
+ moveAnimation = new MoveAnimation();
+
+ flingAnimation.setListener(new FlingAnimationListener() {
+ @Override
+ public void onMove(float x, float y) {
+ handleDrag(current.x + x, current.y + y);
+ }
+
+ @Override
+ public void onComplete() {}
+ });
+
+ zoomAnimation.setZoom(2.0f);
+ zoomAnimation.setZoomAnimationListener(new ZoomAnimationListener() {
+ @Override
+ public void onZoom(float scale, float x, float y) {
+ if(scale <= maxScale && scale >= minScale) {
+ handleScale(scale, x, y);
+ }
+ }
+
+ @Override
+ public void onComplete() {
+ inZoom = false;
+ handleUp();
+ }
+ });
+
+ moveAnimation.setMoveAnimationListener(new MoveAnimationListener() {
+
+ @Override
+ public void onMove(float x, float y) {
+ image.setPosition(x, y);
+ image.redraw();
+ }
+ });
+
+ tapDetector = new GestureDetector(image.getContext(), new SimpleOnGestureListener() {
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ startZoom(e);
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if(!inZoom) {
+ if(onClickListener != null) {
+ onClickListener.onClick(image);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ });
+
+ flingDetector = new GestureDetector(image.getContext(), flingListener);
+ imageListener = image.getGestureImageViewListener();
+
+ calculateBoundaries();
+ }
+
+ private void startFling() {
+ flingAnimation.setVelocityX(flingListener.getVelocityX());
+ flingAnimation.setVelocityY(flingListener.getVelocityY());
+ image.animationStart(flingAnimation);
+ }
+
+ private void startZoom(MotionEvent e) {
+ inZoom = true;
+ zoomAnimation.reset();
+
+ float zoomTo = 1.0f;
+
+ if(image.isLandscape()) {
+ if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
+ int scaledHeight = image.getScaledHeight();
+
+ if(scaledHeight < canvasHeight) {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ else {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ else {
+ int scaledWidth = image.getScaledWidth();
+
+ if(scaledWidth == canvasWidth) {
+ zoomTo = currentScale*4.0f;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else if(scaledWidth < canvasWidth) {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ }
+ else {
+ if(image.getDeviceOrientation() == Configuration.ORIENTATION_PORTRAIT) {
+
+ int scaledHeight = image.getScaledHeight();
+
+ if(scaledHeight == canvasHeight) {
+ zoomTo = currentScale*4.0f;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else if(scaledHeight < canvasHeight) {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(e.getX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ else {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ else {
+ int scaledWidth = image.getScaledWidth();
+
+ if(scaledWidth < canvasWidth) {
+ zoomTo = fitScaleHorizontal / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(e.getY());
+ }
+ else {
+ zoomTo = fitScaleVertical / currentScale;
+ zoomAnimation.setTouchX(image.getCenterX());
+ zoomAnimation.setTouchY(image.getCenterY());
+ }
+ }
+ }
+
+ zoomAnimation.setZoom(zoomTo);
+ image.animationStart(zoomAnimation);
+ }
+
+
+ private void stopAnimations() {
+ image.animationStop();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ if(!inZoom) {
+
+ if(!tapDetector.onTouchEvent(event)) {
+ if(event.getPointerCount() == 1 && flingDetector.onTouchEvent(event)) {
+ startFling();
+ }
+
+ if(event.getAction() == MotionEvent.ACTION_UP) {
+ handleUp();
+ }
+ else if(event.getAction() == MotionEvent.ACTION_DOWN) {
+ stopAnimations();
+
+ last.x = event.getX();
+ last.y = event.getY();
+
+ if(imageListener != null) {
+ imageListener.onTouch(last.x, last.y);
+ }
+
+ touched = true;
+ }
+ else if(event.getAction() == MotionEvent.ACTION_MOVE) {
+ if(event.getPointerCount() > 1) {
+ multiTouch = true;
+ if(initialDistance > 0) {
+
+ pinchVector.set(event);
+ pinchVector.calculateLength();
+
+ float distance = pinchVector.length;
+
+ if(initialDistance != distance) {
+
+ float newScale = (distance / initialDistance) * lastScale;
+
+ if(newScale <= maxScale) {
+ scaleVector.length *= newScale;
+
+ scaleVector.calculateEndPoint();
+
+ scaleVector.length /= newScale;
+
+ float newX = scaleVector.end.x;
+ float newY = scaleVector.end.y;
+
+ handleScale(newScale, newX, newY);
+ }
+ }
+ }
+ else {
+ initialDistance = MathUtils.distance(event);
+
+ MathUtils.midpoint(event, midpoint);
+
+ scaleVector.setStart(midpoint);
+ scaleVector.setEnd(next);
+
+ scaleVector.calculateLength();
+ scaleVector.calculateAngle();
+
+ scaleVector.length /= lastScale;
+ }
+ }
+ else {
+ if(!touched) {
+ touched = true;
+ last.x = event.getX();
+ last.y = event.getY();
+ next.x = image.getImageX();
+ next.y = image.getImageY();
+ }
+ else if(!multiTouch) {
+ if(handleDrag(event.getX(), event.getY())) {
+ image.redraw();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ protected void handleUp() {
+
+ multiTouch = false;
+
+ initialDistance = 0;
+ lastScale = currentScale;
+
+ if(!canDragX) {
+ next.x = centerX;
+ }
+
+ if(!canDragY) {
+ next.y = centerY;
+ }
+
+ boundCoordinates();
+
+ if(!canDragX && !canDragY) {
+
+ if(image.isLandscape()) {
+ currentScale = fitScaleHorizontal;
+ lastScale = fitScaleHorizontal;
+ }
+ else {
+ currentScale = fitScaleVertical;
+ lastScale = fitScaleVertical;
+ }
+ }
+
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onScale(currentScale);
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ image.redraw();
+ }
+
+ protected void handleScale(float scale, float x, float y) {
+
+ currentScale = scale;
+
+ if(currentScale > maxScale) {
+ currentScale = maxScale;
+ }
+ else if (currentScale < minScale) {
+ currentScale = minScale;
+ }
+ else {
+ next.x = x;
+ next.y = y;
+ }
+
+ calculateBoundaries();
+
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onScale(currentScale);
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ image.redraw();
+ }
+
+ protected boolean handleDrag(float x, float y) {
+ current.x = x;
+ current.y = y;
+
+ float diffX = (current.x - last.x);
+ float diffY = (current.y - last.y);
+
+ if(diffX != 0 || diffY != 0) {
+
+ if(canDragX) next.x += diffX;
+ if(canDragY) next.y += diffY;
+
+ boundCoordinates();
+
+ last.x = current.x;
+ last.y = current.y;
+
+ if(canDragX || canDragY) {
+ image.setPosition(next.x, next.y);
+
+ if(imageListener != null) {
+ imageListener.onPosition(next.x, next.y);
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void reset() {
+ currentScale = startingScale;
+ next.x = centerX;
+ next.y = centerY;
+ calculateBoundaries();
+ image.setScale(currentScale);
+ image.setPosition(next.x, next.y);
+ image.redraw();
+ }
+
+
+ public float getMaxScale() {
+ return maxScale;
+ }
+
+ public void setMaxScale(float maxScale) {
+ this.maxScale = maxScale;
+ }
+
+ public float getMinScale() {
+ return minScale;
+ }
+
+ public void setMinScale(float minScale) {
+ this.minScale = minScale;
+ }
+
+ public void setOnClickListener(OnClickListener onClickListener) {
+ this.onClickListener = onClickListener;
+ }
+
+ protected void setCanvasWidth(int canvasWidth) {
+ this.canvasWidth = canvasWidth;
+ }
+
+ protected void setCanvasHeight(int canvasHeight) {
+ this.canvasHeight = canvasHeight;
+ }
+
+ protected void setFitScaleHorizontal(float fitScale) {
+ this.fitScaleHorizontal = fitScale;
+ }
+
+ protected void setFitScaleVertical(float fitScaleVertical) {
+ this.fitScaleVertical = fitScaleVertical;
+ }
+
+ protected void boundCoordinates() {
+ if(next.x < boundaryLeft) {
+ next.x = boundaryLeft;
+ }
+ else if(next.x > boundaryRight) {
+ next.x = boundaryRight;
+ }
+
+ if(next.y < boundaryTop) {
+ next.y = boundaryTop;
+ }
+ else if(next.y > boundaryBottom) {
+ next.y = boundaryBottom;
+ }
+ }
+
+ protected void calculateBoundaries() {
+
+ int effectiveWidth = Math.round( (float) imageWidth * currentScale );
+ int effectiveHeight = Math.round( (float) imageHeight * currentScale );
+
+ canDragX = effectiveWidth > displayWidth;
+ canDragY = effectiveHeight > displayHeight;
+
+ if(canDragX) {
+ float diff = (float)(effectiveWidth - displayWidth) / 2.0f;
+ boundaryLeft = centerX - diff;
+ boundaryRight = centerX + diff;
+ }
+
+ if(canDragY) {
+ float diff = (float)(effectiveHeight - displayHeight) / 2.0f;
+ boundaryTop = centerY - diff;
+ boundaryBottom = centerY + diff;
+ }
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/MathUtils.java b/android/experimental/LibreOffice4Android/src/com/polites/android/MathUtils.java
new file mode 100644
index 0000000..df7f30d
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/MathUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class MathUtils {
+
+ public static float distance(MotionEvent event) {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(PointF p1, PointF p2) {
+ float x = p1.x - p2.x;
+ float y = p1.y - p2.y;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static float distance(float x1, float y1, float x2, float y2) {
+ float x = x1 - x2;
+ float y = y1 - y2;
+ return FloatMath.sqrt(x * x + y * y);
+ }
+
+ public static void midpoint(MotionEvent event, PointF point) {
+ float x1 = event.getX(0);
+ float y1 = event.getY(0);
+ float x2 = event.getX(1);
+ float y2 = event.getY(1);
+ midpoint(x1, y1, x2, y2, point);
+ }
+
+ public static void midpoint(float x1, float y1, float x2, float y2, PointF point) {
+ point.x = (x1 + x2) / 2.0f;
+ point.y = (y1 + y2) / 2.0f;
+ }
+ /**
+ * Rotates p1 around p2 by angle degrees.
+ * @param p1
+ * @param p2
+ * @param angle
+ */
+ public void rotate(PointF p1, PointF p2, float angle) {
+ float px = p1.x;
+ float py = p1.y;
+ float ox = p2.x;
+ float oy = p2.y;
+ p1.x = (FloatMath.cos(angle) * (px-ox) - FloatMath.sin(angle) * (py-oy) + ox);
+ p1.y = (FloatMath.sin(angle) * (px-ox) + FloatMath.cos(angle) * (py-oy) + oy);
+ }
+
+ public static float angle(PointF p1, PointF p2) {
+ return angle(p1.x, p1.y, p2.x, p2.y);
+ }
+
+ public static float angle(float x1, float y1, float x2, float y2) {
+ return (float) Math.atan2(y2 - y1, x2 - x1);
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimation.java b/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimation.java
new file mode 100644
index 0000000..5303d64
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimation.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class MoveAnimation implements Animation {
+
+ private boolean firstFrame = true;
+
+ private float startX;
+ private float startY;
+
+ private float targetX;
+ private float targetY;
+ private long animationTimeMS = 100;
+ private long totalTime = 0;
+
+ private MoveAnimationListener moveAnimationListener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ totalTime += time;
+
+ if(firstFrame) {
+ firstFrame = false;
+ startX = view.getImageX();
+ startY = view.getImageY();
+ }
+
+ if(totalTime < animationTimeMS) {
+
+ float ratio = (float) totalTime / animationTimeMS;
+
+ float newX = ((targetX - startX) * ratio) + startX;
+ float newY = ((targetY - startY) * ratio) + startY;
+
+ if(moveAnimationListener != null) {
+ moveAnimationListener.onMove(newX, newY);
+ }
+
+ return true;
+ }
+ else {
+ if(moveAnimationListener != null) {
+ moveAnimationListener.onMove(targetX, targetY);
+ }
+ }
+
+ return false;
+ }
+
+ public void reset() {
+ firstFrame = true;
+ totalTime = 0;
+ }
+
+
+ public float getTargetX() {
+ return targetX;
+ }
+
+
+ public void setTargetX(float targetX) {
+ this.targetX = targetX;
+ }
+
+
+ public float getTargetY() {
+ return targetY;
+ }
+
+ public void setTargetY(float targetY) {
+ this.targetY = targetY;
+ }
+
+ public long getAnimationTimeMS() {
+ return animationTimeMS;
+ }
+
+ public void setAnimationTimeMS(long animationTimeMS) {
+ this.animationTimeMS = animationTimeMS;
+ }
+
+ public void setMoveAnimationListener(MoveAnimationListener moveAnimationListener) {
+ this.moveAnimationListener = moveAnimationListener;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimationListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimationListener.java
new file mode 100644
index 0000000..a19a265
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/MoveAnimationListener.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface MoveAnimationListener {
+
+ public void onMove(float x, float y);
+
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/VectorF.java b/android/experimental/LibreOffice4Android/src/com/polites/android/VectorF.java
new file mode 100644
index 0000000..1ff4b19
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/VectorF.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import android.graphics.PointF;
+import android.util.FloatMath;
+import android.view.MotionEvent;
+
+public class VectorF {
+
+ public float angle;
+ public float length;
+
+ public final PointF start = new PointF();
+ public final PointF end = new PointF();
+
+ public void calculateEndPoint() {
+ end.x = FloatMath.cos(angle) * length + start.x;
+ end.y = FloatMath.sin(angle) * length + start.y;
+ }
+
+ public void setStart(PointF p) {
+ this.start.x = p.x;
+ this.start.y = p.y;
+ }
+
+ public void setEnd(PointF p) {
+ this.end.x = p.x;
+ this.end.y = p.y;
+ }
+
+ public void set(MotionEvent event) {
+ this.start.x = event.getX(0);
+ this.start.y = event.getY(0);
+ this.end.x = event.getX(1);
+ this.end.y = event.getY(1);
+ }
+
+ public float calculateLength() {
+ length = MathUtils.distance(start, end);
+ return length;
+ }
+
+ public float calculateAngle() {
+ angle = MathUtils.angle(start, end);
+ return angle;
+ }
+
+
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimation.java b/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimation.java
new file mode 100644
index 0000000..673b7f9
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimation.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+import android.graphics.PointF;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public class ZoomAnimation implements Animation {
+
+ private boolean firstFrame = true;
+
+ private float touchX;
+ private float touchY;
+
+ private float zoom;
+
+ private float startX;
+ private float startY;
+ private float startScale;
+
+ private float xDiff;
+ private float yDiff;
+ private float scaleDiff;
+
+ private long animationLengthMS = 200;
+ private long totalTime = 0;
+
+ private ZoomAnimationListener zoomAnimationListener;
+
+ /* (non-Javadoc)
+ * @see com.polites.android.Animation#update(com.polites.android.GestureImageView, long)
+ */
+ @Override
+ public boolean update(GestureImageView view, long time) {
+ if(firstFrame) {
+ firstFrame = false;
+
+ startX = view.getImageX();
+ startY = view.getImageY();
+ startScale = view.getScale();
+ scaleDiff = (zoom * startScale) - startScale;
+
+ if(scaleDiff > 0) {
+ // Calculate destination for midpoint
+ VectorF vector = new VectorF();
+
+ // Set the touch point as start because we want to move the end
+ vector.setStart(new PointF(touchX, touchY));
+ vector.setEnd(new PointF(startX, startY));
+
+ vector.calculateAngle();
+
+ // Get the current length
+ float length = vector.calculateLength();
+
+ // Multiply length by zoom to get the new length
+ vector.length = length*zoom;
+
+ // Now deduce the new endpoint
+ vector.calculateEndPoint();
+
+ xDiff = vector.end.x - startX;
+ yDiff = vector.end.y - startY;
+ }
+ else {
+ // Zoom out to center
+ xDiff = view.getCenterX() - startX;
+ yDiff = view.getCenterY() - startY;
+ }
+ }
+
+ totalTime += time;
+
+ float ratio = (float) totalTime / (float) animationLengthMS;
+
+ if(ratio < 1) {
+
+ if(ratio > 0) {
+ // we still have time left
+ float newScale = (ratio * scaleDiff) + startScale;
+ float newX = (ratio * xDiff) + startX;
+ float newY = (ratio * yDiff) + startY;
+
+ if(zoomAnimationListener != null) {
+ zoomAnimationListener.onZoom(newScale, newX, newY);
+ }
+ }
+
+ return true;
+ }
+ else {
+
+ float newScale = scaleDiff + startScale;
+ float newX = xDiff + startX;
+ float newY = yDiff + startY;
+
+ if(zoomAnimationListener != null) {
+ zoomAnimationListener.onZoom(newScale, newX, newY);
+ zoomAnimationListener.onComplete();
+ }
+
+ return false;
+ }
+ }
+
+ public void reset() {
+ firstFrame = true;
+ totalTime = 0;
+ }
+
+ public float getZoom() {
+ return zoom;
+ }
+
+ public void setZoom(float zoom) {
+ this.zoom = zoom;
+ }
+
+ public float getTouchX() {
+ return touchX;
+ }
+
+ public void setTouchX(float touchX) {
+ this.touchX = touchX;
+ }
+
+ public float getTouchY() {
+ return touchY;
+ }
+
+ public void setTouchY(float touchY) {
+ this.touchY = touchY;
+ }
+
+ public long getAnimationLengthMS() {
+ return animationLengthMS;
+ }
+
+ public void setAnimationLengthMS(long animationLengthMS) {
+ this.animationLengthMS = animationLengthMS;
+ }
+
+ public ZoomAnimationListener getZoomAnimationListener() {
+ return zoomAnimationListener;
+ }
+
+ public void setZoomAnimationListener(ZoomAnimationListener zoomAnimationListener) {
+ this.zoomAnimationListener = zoomAnimationListener;
+ }
+}
diff --git a/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimationListener.java b/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimationListener.java
new file mode 100644
index 0000000..8df4bf6
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/com/polites/android/ZoomAnimationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Jason Polites
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.polites.android;
+
+
+/**
+ * @author Jason Polites
+ *
+ */
+public interface ZoomAnimationListener {
+ public void onZoom(float scale, float x, float y);
+ public void onComplete();
+}
diff --git a/android/experimental/LibreOffice4Android/src/org/libreoffice/android/examples/DocumentLoader.java b/android/experimental/LibreOffice4Android/src/org/libreoffice/android/examples/DocumentLoader.java
new file mode 100644
index 0000000..cf042a4
--- /dev/null
+++ b/android/experimental/LibreOffice4Android/src/org/libreoffice/android/examples/DocumentLoader.java
@@ -0,0 +1,596 @@
+// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+//
+// This file is part of the LibreOffice project.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// This is just a testbed for ideas and implementations. (Still, it might turn
+// out to be somewhat useful as such while waiting for "real" apps.)
+
+// Important points:
+
+// Everything that might take a long time should be done asynchronously:
+// - loading the document (loadComponentFromURL())
+// - counting number of pages (getRendererCount())
+// - rendering a page (render())
+
+// Unclear whether pages can be rendered in parallel. Probably best to
+// serialize all the above in the same worker thread, for instance using
+// AsyncTask.SERIAL_EXECUTOR.
+
+// While a page is loading ideally should display some animated spinner (but
+// for now just a static "please wait" text).
+
+// Just three views are used for the pages: For the current page being viewed,
+// the previous, and the next. This could be bumped higher, need to make the
+// "3" into a parameter below.
+
+package org.libreoffice.android.examples;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.TranslateAnimation;
... etc. - the rest is truncated
More information about the Libreoffice-commits
mailing list