Bartek Przybylski пре 13 година
комит
154bb85cf1
100 измењених фајлова са 4023 додато и 0 уклоњено
  1. 15 0
      .classpath
  2. 19 0
      .gitignore
  3. 33 0
      .project
  4. 79 0
      AndroidManifest.xml
  5. 11 0
      default.properties
  6. BIN
      lib/commons-codec-1.4.jar
  7. BIN
      lib/commons-httpclient-3.0.1.jar
  8. BIN
      lib/commons-io-2.0.1.jar
  9. BIN
      lib/commons-logging-1.1.1.jar
  10. BIN
      lib/httpclient-4.1.1.jar
  11. BIN
      lib/httpclient-cache-4.1.1.jar
  12. BIN
      lib/httpcore-4.1.jar
  13. BIN
      lib/httpmime-4.1.1.jar
  14. 36 0
      proguard.cfg
  15. 9 0
      res/anim/disappear.xml
  16. 14 0
      res/anim/grow_from_bottom.xml
  17. 14 0
      res/anim/grow_from_bottomleft_to_topright.xml
  18. 14 0
      res/anim/grow_from_bottomright_to_topleft.xml
  19. 14 0
      res/anim/grow_from_top.xml
  20. 14 0
      res/anim/grow_from_topleft_to_bottomright.xml
  21. 14 0
      res/anim/grow_from_topright_to_bottomleft.xml
  22. 14 0
      res/anim/pump_bottom.xml
  23. 14 0
      res/anim/pump_top.xml
  24. 20 0
      res/anim/push_left_in.xml
  25. 20 0
      res/anim/push_left_out.xml
  26. 14 0
      res/anim/shrink_from_bottom.xml
  27. 14 0
      res/anim/shrink_from_bottomleft_to_topright.xml
  28. 14 0
      res/anim/shrink_from_bottomright_to_topleft.xml
  29. 14 0
      res/anim/shrink_from_top.xml
  30. 14 0
      res/anim/shrink_from_topleft_to_bottomright.xml
  31. 14 0
      res/anim/shrink_from_topright_to_bottomleft.xml
  32. BIN
      res/drawable-hdpi/ic_menu_archive.png
  33. BIN
      res/drawable-hdpi/icon.png
  34. BIN
      res/drawable-hdpi/owncloud_logo.png
  35. BIN
      res/drawable-ldpi/ic_menu_archive.png
  36. BIN
      res/drawable-ldpi/icon.png
  37. BIN
      res/drawable-ldpi/owncloud_logo.png
  38. BIN
      res/drawable-mdpi/ic_menu_archive.png
  39. BIN
      res/drawable-mdpi/icon.png
  40. BIN
      res/drawable-mdpi/owncloud_logo.png
  41. 17 0
      res/drawable/action_item_btn.xml
  42. BIN
      res/drawable/action_item_selected.9.png
  43. BIN
      res/drawable/arrow_down.png
  44. BIN
      res/drawable/arrow_left.png
  45. BIN
      res/drawable/arrow_right.png
  46. BIN
      res/drawable/arrow_up.png
  47. 8 0
      res/drawable/btn.xml
  48. 8 0
      res/drawable/btn_round.xml
  49. 8 0
      res/drawable/btn_round_pressed.xml
  50. BIN
      res/drawable/dashboard.png
  51. BIN
      res/drawable/download.png
  52. BIN
      res/drawable/file.png
  53. BIN
      res/drawable/folder.png
  54. BIN
      res/drawable/header.png
  55. BIN
      res/drawable/ic_menu_archive.png
  56. BIN
      res/drawable/owncloud_logo_small_white.png
  57. BIN
      res/drawable/popup.9.png
  58. BIN
      res/drawable/share.png
  59. 5 0
      res/drawable/uploader_list_separator.xml
  60. 29 0
      res/layout/account_setup.xml
  61. 25 0
      res/layout/action_item.xml
  62. 37 0
      res/layout/file_display.xml
  63. 10 0
      res/layout/file_display_action_list_element.xml
  64. 17 0
      res/layout/list_layout.xml
  65. 27 0
      res/layout/main.xml
  66. 40 0
      res/layout/popup.xml
  67. 22 0
      res/layout/uploader_layout.xml
  68. 9 0
      res/layout/uploader_list_item_layout.xml
  69. 6 0
      res/menu/menu.xml
  70. 5 0
      res/menu/prefs_menu.xml
  71. 6 0
      res/menu/session_context_menu.xml
  72. 39 0
      res/values/strings.xml
  73. 52 0
      res/values/styles.xml
  74. 7 0
      res/xml/authenticator.xml
  75. 30 0
      res/xml/contacts.xml
  76. 4 0
      res/xml/preferences.xml
  77. 6 0
      res/xml/preferences_new_session.xml
  78. 27 0
      res/xml/syncadapter.xml
  79. 37 0
      src/eu/alefzero/owncloud/ActionItem.java
  80. 126 0
      src/eu/alefzero/owncloud/CustomPopup.java
  81. 79 0
      src/eu/alefzero/owncloud/DbHandler.java
  82. 60 0
      src/eu/alefzero/owncloud/DisplayUtils.java
  83. 158 0
      src/eu/alefzero/owncloud/FileListActionListAdapter.java
  84. 103 0
      src/eu/alefzero/owncloud/FileListListAdapter.java
  85. 281 0
      src/eu/alefzero/owncloud/OwnCloudMainScreen.java
  86. 33 0
      src/eu/alefzero/owncloud/OwnCloudSession.java
  87. 495 0
      src/eu/alefzero/owncloud/OwnCloudUploader.java
  88. 139 0
      src/eu/alefzero/owncloud/Preferences.java
  89. 143 0
      src/eu/alefzero/owncloud/PreferencesNewSession.java
  90. 267 0
      src/eu/alefzero/owncloud/QuickAction.java
  91. 238 0
      src/eu/alefzero/owncloud/authenticator/AccountAuthenticator.java
  92. 41 0
      src/eu/alefzero/owncloud/authenticator/AccountAuthenticatorService.java
  93. 189 0
      src/eu/alefzero/owncloud/authenticator/AuthUtils.java
  94. 176 0
      src/eu/alefzero/owncloud/authenticator/AuthenticatorActivity.java
  95. 210 0
      src/eu/alefzero/owncloud/cp.java
  96. 59 0
      src/eu/alefzero/owncloud/db/ProviderMeta.java
  97. 225 0
      src/eu/alefzero/owncloud/syncadapter/SyncAdapter.java
  98. 31 0
      src/eu/alefzero/owncloud/syncadapter/SyncService.java
  99. 19 0
      src/eu/alefzero/webdav/HttpMkCol.java
  100. 32 0
      src/eu/alefzero/webdav/HttpPropFind.java

+ 15 - 0
.classpath

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="lib" path="lib/commons-codec-1.4.jar"/>
+	<classpathentry kind="lib" path="lib/commons-logging-1.1.1.jar"/>
+	<classpathentry kind="lib" path="lib/httpclient-4.1.1.jar"/>
+	<classpathentry kind="lib" path="lib/httpclient-cache-4.1.1.jar"/>
+	<classpathentry kind="lib" path="lib/httpcore-4.1.jar"/>
+	<classpathentry kind="lib" path="lib/httpmime-4.1.1.jar"/>
+	<classpathentry kind="lib" path="lib/commons-httpclient-3.0.1.jar"/>
+	<classpathentry kind="lib" path="lib/commons-io-2.0.1.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>

+ 19 - 0
.gitignore

@@ -0,0 +1,19 @@
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Mac .DS_Store files
+.DS_Store

+ 33 - 0
.project

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ownCloud</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 79 - 0
AndroidManifest.xml

@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="eu.alefzero.owncloud"
+      android:versionCode="1"
+      android:versionName="1.0">
+<uses-permission
+        android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission
+        android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission
+        android:name="android.permission.MANAGE_ACCOUNTS" />
+    <uses-permission
+        android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+    <uses-permission
+        android:name="android.permission.INTERNET" />
+    <uses-permission
+        android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission
+        android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission
+        android:name="android.permission.READ_CONTACTS" />
+    <uses-permission
+        android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission
+        android:name="android.permission.READ_SYNC_STATS" />
+    <uses-permission
+        android:name="android.permission.READ_SYNC_SETTINGS" />
+    <uses-permission
+        android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <uses-sdk android:minSdkVersion="7"></uses-sdk>
+    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
+
+    <application android:icon="@drawable/icon" android:label="@string/app_name">
+        <activity android:name=".OwnCloudMainScreen"
+                  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:name="OwnCloudUploader">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND"></action>
+                <category android:name="android.intent.category.DEFAULT"></category>
+                <data android:mimeType="*/*"></data>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SEND_MULTIPLE"></action>
+                <category android:name="android.intent.category.DEFAULT"></category>
+                <data android:mimeType="*/*"></data>
+            </intent-filter>
+        </activity>
+        <activity android:name="Preferences"></activity>
+        <activity android:name="PreferencesNewSession">
+        </activity>
+                <service
+            android:exported="true" android:name=".authenticator.AccountAuthenticatorService">
+            <intent-filter>
+                <action
+                    android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+            <meta-data
+                android:name="android.accounts.AccountAuthenticator"
+                android:resource="@xml/authenticator" />
+        </service>
+         <service
+            android:exported="true" android:name=".syncadapter.SyncService">
+            <intent-filter>
+                <action
+                    android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data
+                android:name="android.content.SyncAdapter"
+                android:resource="@xml/syncadapter" />
+        </service>
+         <provider android:name="cp" android:authorities="org.owncloud" android:enabled="true" android:syncable="true" android:exported="false" android:label="@string/sync_string"></provider>
+         <activity android:name=".authenticator.AuthenticatorActivity"></activity>
+    </application>
+</manifest>

+ 11 - 0
default.properties

@@ -0,0 +1,11 @@
+# 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,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-7

BIN
lib/commons-codec-1.4.jar


BIN
lib/commons-httpclient-3.0.1.jar


BIN
lib/commons-io-2.0.1.jar


BIN
lib/commons-logging-1.1.1.jar


BIN
lib/httpclient-4.1.1.jar


BIN
lib/httpclient-cache-4.1.1.jar


BIN
lib/httpcore-4.1.jar


BIN
lib/httpmime-4.1.1.jar


+ 36 - 0
proguard.cfg

@@ -0,0 +1,36 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}

+ 9 - 0
res/anim/disappear.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="400"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_bottom.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="50%" android:pivotY="100%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_bottomleft_to_topright.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="0%" android:pivotY="50%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_bottomright_to_topleft.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="100%" android:pivotY="50%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_top.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="50%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_topleft_to_bottomright.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="0%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/grow_from_topright_to_bottomleft.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="0.3" android:toXScale="1.0"
+		android:fromYScale="0.3" android:toYScale="1.0"
+		android:pivotX="100%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/pump_bottom.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.1" android:toXScale="1.0"
+		android:fromYScale="1.1" android:toYScale="1.0"
+		android:pivotX="50%" android:pivotY="100%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/pump_top.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.1" android:toXScale="1.0"
+		android:fromYScale="1.1" android:toYScale="1.0"
+		android:pivotX="50%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/decelerate_interpolator"
+		android:fromAlpha="0.0" android:toAlpha="1.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 20 - 0
res/anim/push_left_in.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="300"/>
+	<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="300" />
+</set>

+ 20 - 0
res/anim/push_left_out.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="300"/>
+	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="300" />
+</set>

+ 14 - 0
res/anim/shrink_from_bottom.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="50%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/shrink_from_bottomleft_to_topright.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="100%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/shrink_from_bottomright_to_topleft.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="0%" android:pivotY="0%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/shrink_from_top.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="50%" android:pivotY="100%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/shrink_from_topleft_to_bottomright.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="100%" android:pivotY="100%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

+ 14 - 0
res/anim/shrink_from_topright_to_bottomleft.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+	<scale
+		android:fromXScale="1.0" android:toXScale="0.3"
+		android:fromYScale="1.0" android:toYScale="0.3"
+		android:pivotX="0%" android:pivotY="100%"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+	<alpha
+		android:interpolator="@android:anim/accelerate_interpolator"
+		android:fromAlpha="1.0" android:toAlpha="0.0"
+		android:duration="@android:integer/config_shortAnimTime"
+	/>
+</set>

BIN
res/drawable-hdpi/ic_menu_archive.png


BIN
res/drawable-hdpi/icon.png


BIN
res/drawable-hdpi/owncloud_logo.png


BIN
res/drawable-ldpi/ic_menu_archive.png


BIN
res/drawable-ldpi/icon.png


BIN
res/drawable-ldpi/owncloud_logo.png


BIN
res/drawable-mdpi/ic_menu_archive.png


BIN
res/drawable-mdpi/icon.png


BIN
res/drawable-mdpi/owncloud_logo.png


+ 17 - 0
res/drawable/action_item_btn.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:dither="true">
+
+    <item 
+    	android:state_window_focused="false"
+        android:drawable="@android:color/transparent" />
+    <item 
+    	android:state_pressed="true"
+        android:drawable="@drawable/action_item_selected"/>
+    <item 
+    	android:state_focused="true"
+        android:drawable="@drawable/action_item_selected"/>
+    <item
+        android:drawable="@android:color/transparent"/>
+        
+</selector>

BIN
res/drawable/action_item_selected.9.png


BIN
res/drawable/arrow_down.png


BIN
res/drawable/arrow_left.png


BIN
res/drawable/arrow_right.png


BIN
res/drawable/arrow_up.png


+ 8 - 0
res/drawable/btn.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/btn_round_pressed" />
+  <item android:state_focused="true" android:state_pressed="false" android:drawable="@drawable/btn_round_pressed" />
+  <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/btn_round_pressed" />
+  <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/btn_round_pressed" />
+  <item android:drawable="@drawable/btn_round"/>
+</selector>

+ 8 - 0
res/drawable/btn_round.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?> 
+<shape xmlns:android="http://schemas.android.com/apk/res/android" 
+        android:shape="rectangle"> 
+        <corners android:radius="5dip" />
+        <solid android:color="#aac4d2" /> 
+        <padding android:left="15dp" android:top="5dp" android:right="15dp" android:bottom="5dp" />
+        <stroke android:width="2dip" android:color="#ffffff" />
+</shape>

+ 8 - 0
res/drawable/btn_round_pressed.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?> 
+<shape xmlns:android="http://schemas.android.com/apk/res/android" 
+        android:shape="rectangle"> 
+        <corners android:radius="5dip" />
+        <solid android:color="#aac4d2" /> 
+        <padding android:left="15dp" android:top="5dp" android:right="15dp" android:bottom="5dp" />
+        <stroke android:width="5dip" android:color="#ff8000" /> 
+</shape>

BIN
res/drawable/dashboard.png


BIN
res/drawable/download.png


BIN
res/drawable/file.png


BIN
res/drawable/folder.png


BIN
res/drawable/header.png


BIN
res/drawable/ic_menu_archive.png


BIN
res/drawable/owncloud_logo_small_white.png


BIN
res/drawable/popup.9.png


BIN
res/drawable/share.png


+ 5 - 0
res/drawable/uploader_list_separator.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<shape
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient android:startColor="#fefefe" android:centerColor="#cccccc" android:endColor="#fefefe" android:angle="0"/>
+</shape>

+ 29 - 0
res/layout/account_setup.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="fill_parent" android:layout_height="fill_parent"
+	android:orientation="vertical" android:background="#F7F7F7" android:focusable="true">
+    <LinearLayout android:id="@+id/linearLayout7" android:paddingBottom="2pt" android:gravity="center_vertical|top" android:layout_gravity="top" android:layout_height="wrap_content" android:paddingTop="2pt" android:orientation="vertical" android:layout_width="fill_parent" android:background="#1D2D44">
+        <ImageView android:id="@+id/main_header_small" android:src="@drawable/owncloud_logo_small_white" android:layout_gravity="center|center_vertical|center_horizontal" android:layout_height="wrap_content" android:layout_width="wrap_content" android:focusable="true"></ImageView>
+    </LinearLayout>
+	<LinearLayout android:id="@+id/linearLayout2" android:layout_width="fill_parent" android:orientation="vertical" android:gravity="center_vertical" android:layout_gravity="center|center_vertical" android:layout_height="fill_parent">
+		<TableLayout android:id="@+id/tableLayout1" android:layout_height="wrap_content" android:gravity="center_horizontal" android:layout_width="fill_parent">
+			<TableRow android:layout_height="wrap_content" android:id="@+id/tableRow1" android:layout_width="wrap_content" android:gravity="center_horizontal">
+				<TextView android:id="@+id/textView1" android:text="@string/setup_title" android:layout_marginBottom="15dip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textStyle="bold" android:layout_marginTop="15dip" android:textSize="7pt"></TextView>
+			</TableRow>
+			<TableRow android:layout_height="wrap_content" android:weightSum="1.0" android:id="@+id/tableRow2" android:layout_width="wrap_content" android:gravity="center_horizontal">
+				<EditText android:id="@+id/host_URL" android:layout_weight="0.75" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/setup_hint_address">
+					<requestFocus></requestFocus>
+				</EditText>
+			</TableRow>
+			<TableRow android:layout_height="wrap_content" android:weightSum="1.0" android:id="@+id/tableRow3" android:layout_width="wrap_content" android:gravity="center_horizontal">
+				<EditText android:id="@+id/account_username" android:layout_weight=".75" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textColor="@android:color/black" android:hint="@string/setup_hint_username"></EditText>
+			</TableRow>
+			<TableRow android:layout_height="wrap_content" android:weightSum="1.0" android:id="@+id/tableRow4" android:layout_width="fill_parent" android:gravity="center_horizontal">
+				<EditText android:id="@+id/account_password" android:layout_weight=".75" android:layout_width="fill_parent" android:inputType="textPassword" android:layout_height="wrap_content" android:textColor="@android:color/black" android:hint="@string/setup_hint_password"></EditText>
+			</TableRow>
+		</TableLayout>
+		<LinearLayout android:layout_height="wrap_content" android:weightSum="1.0" android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:gravity="center_horizontal" android:orientation="horizontal">
+			<Button android:id="@+id/buttonOK" android:text="@string/setup_btn_connect" android:layout_width="wrap_content" android:layout_weight=".50" android:layout_height="wrap_content" android:onClick="onOkClick"></Button>
+		</LinearLayout>
+	</LinearLayout>
+</LinearLayout>

+ 25 - 0
res/layout/action_item.xml

@@ -0,0 +1,25 @@
+<LinearLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:orientation="horizontal"
+	android:layout_width="fill_parent"
+	android:layout_height="wrap_content" 
+	android:clickable="true"
+	android:focusable="true"
+	android:background="@drawable/action_item_btn">
+        	
+	<ImageView
+		android:id="@+id/icon" 
+		android:layout_width="wrap_content" 
+		android:layout_height="wrap_content"/>
+
+	<TextView 
+		android:id="@+id/title"
+		android:layout_width="fill_parent" 
+		android:layout_height="fill_parent" 
+		android:gravity="center_vertical"
+		android:paddingLeft="5dip"
+		android:paddingRight="10dip"
+		android:text="Chart"
+		android:textColor="#fff"/>
+									         
+</LinearLayout>         

+ 37 - 0
res/layout/file_display.xml

@@ -0,0 +1,37 @@
+<?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:orientation="vertical" android:background="#F7F7F7">
+	<LinearLayout android:id="@+id/linearLayout7" android:paddingBottom="2pt" android:gravity="center_vertical|top" android:layout_gravity="top" android:layout_height="wrap_content" android:paddingTop="2pt" android:orientation="vertical" android:layout_width="fill_parent" android:background="#1D2D44">
+        <ImageView android:id="@+id/main_header_small" android:src="@drawable/owncloud_logo_small_white" android:layout_gravity="center|center_vertical|center_horizontal" android:layout_height="wrap_content" android:layout_width="wrap_content" android:focusable="true"></ImageView>
+    </LinearLayout>
+	<LinearLayout android:layout_width="fill_parent"
+		android:id="@+id/linearLayout1" android:layout_height="wrap_content">
+		<LinearLayout android:layout_width="wrap_content"
+			android:id="@+id/linearLayout2" android:layout_height="fill_parent">
+			<ImageView android:layout_width="wrap_content"
+				android:layout_height="wrap_content" android:src="@drawable/icon"
+				android:id="@+id/imageView1"></ImageView>
+		</LinearLayout>
+		<LinearLayout android:layout_width="wrap_content"
+			android:id="@+id/linearLayout3" android:layout_height="fill_parent"
+			android:orientation="vertical">
+			<TextView android:id="@+id/textView1" android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:text="TextView"></TextView>
+			<TextView android:id="@+id/textView2" android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:text="TextView"></TextView>
+			<TextView android:id="@+id/textView3" android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:text="TextView"></TextView>
+			<TextView android:id="@+id/textView4" android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:text="TextView"></TextView>
+			<TextView android:id="@+id/textView5" android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:text="TextView"></TextView>
+		</LinearLayout>
+	</LinearLayout>
+	<LinearLayout android:layout_width="fill_parent"
+		android:id="@+id/linearLayout4" android:layout_height="fill_parent">
+		<ListView android:layout_width="fill_parent" android:id="@android:id/list"
+			android:layout_height="fill_parent" android:divider="@drawable/uploader_list_separator"
+			android:dividerHeight="1dip"></ListView>
+	</LinearLayout>
+</LinearLayout>

+ 10 - 0
res/layout/file_display_action_list_element.xml

@@ -0,0 +1,10 @@
+<?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:layout_gravity="center_horizontal" android:gravity="center_horizontal">
+	<ImageView android:layout_width="wrap_content"
+		android:layout_height="wrap_content" android:src="@drawable/icon"
+		android:id="@+id/imageView1" android:layout_gravity="center_vertical"></ImageView>
+	<TextView android:text="TextView" android:id="@+id/textView1"
+		android:layout_width="wrap_content" android:layout_height="wrap_content"
+		android:layout_gravity="center_vertical" android:textSize="11pt"></TextView>
+</LinearLayout>

+ 17 - 0
res/layout/list_layout.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	android:orientation="horizontal"
+	android:id="@+id/ListItemLayout" android:layout_width="fill_parent"
+	android:background="#F7F7F7" android:layout_height="wrap_content">
+	<ImageView android:layout_width="wrap_content" android:src="@drawable/ic_menu_archive"
+		android:id="@+id/imageView1" android:layout_height="wrap_content"
+		android:layout_marginLeft="15dip" android:focusable="false"
+		android:focusableInTouchMode="false"></ImageView>
+	<TextView android:layout_height="wrap_content"
+		android:textColor="#303030" android:layout_width="wrap_content"
+		android:text="TextView" android:layout_marginLeft="5dip"
+		android:layout_marginBottom="5dip" android:layout_marginRight="30dip"
+		android:id="@+id/Filename" android:focusable="false"
+    android:focusableInTouchMode="false" android:textSize="20dip">
+	</TextView>
+</LinearLayout>

+ 27 - 0
res/layout/main.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="fill_parent" android:background="#F7F7F7"
+	android:orientation="vertical" android:layout_height="fill_parent">
+	<LinearLayout android:layout_width="fill_parent"
+		android:id="@+id/linearLayout7" android:orientation="vertical"
+		android:background="#1D2D44" android:gravity="top"
+		android:layout_gravity="center_vertical" android:layout_height="wrap_content" android:paddingTop="2pt" android:paddingBottom="2pt">
+		<ImageView android:layout_height="wrap_content"
+			android:src="@drawable/owncloud_logo_small_white"
+			android:layout_width="wrap_content" android:layout_gravity="center_vertical|center_horizontal"
+			android:id="@+id/main_header_small"></ImageView>
+	</LinearLayout>
+	<LinearLayout android:layout_height="wrap_content"
+		android:id="@+id/linearLayout1" android:layout_width="fill_parent"
+		android:layout_gravity="center_horizontal" android:gravity="center_horizontal">
+		<TextView android:layout_width="wrap_content"
+			android:layout_height="wrap_content" android:id="@+id/directory_name"
+			android:layout_gravity="center" android:gravity="center"></TextView>
+	</LinearLayout>
+	<FrameLayout android:layout_height="fill_parent"
+    android:layout_width="fill_parent" android:id="@+id/frameLayout1">
+    <ListView android:id="@android:id/list" android:layout_width="fill_parent"
+      android:layout_height="fill_parent" android:divider="@drawable/uploader_list_separator"
+      android:dividerHeight="1dip" android:fadingEdge="none"></ListView>
+  </FrameLayout>
+</LinearLayout>

+ 40 - 0
res/layout/popup.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout 
+  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    >
+      
+  <ScrollView 
+    android:id="@+id/scroller"
+    android:layout_marginTop="16dip"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:background="@drawable/popup"
+      android:fadingEdgeLength="5dip"
+      android:scrollbars="none">
+        
+      <LinearLayout
+        android:id="@+id/tracks"
+        android:orientation="vertical"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:padding="10dip"/>
+       
+  </ScrollView >
+  
+  <ImageView
+        android:id="@+id/arrow_up"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:src="@drawable/arrow_up" />
+        
+  <ImageView
+        android:id="@+id/arrow_down"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="-4dip"
+        android:src="@drawable/arrow_down" android:layout_below="@+id/scroller"/>
+
+</RelativeLayout>

+ 22 - 0
res/layout/uploader_layout.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_height="wrap_content" android:orientation="vertical"
+	android:layout_width="wrap_content" android:background="#fefefe"
+	android:gravity="center">
+	<TextView android:layout_width="fill_parent" android:text="Choose upload directory"
+		android:layout_height="wrap_content" android:id="@+id/textView1" android:textColor="@android:color/black"
+		android:gravity="center_horizontal"></TextView>
+	<FrameLayout android:layout_height="fill_parent"
+		android:layout_width="fill_parent" android:id="@+id/frameLayout1"
+		android:layout_below="@+id/textView1" android:layout_above="@+id/linearLayout1">
+		<ListView android:id="@android:id/list" android:layout_width="fill_parent"
+			android:layout_height="fill_parent" android:divider="@drawable/uploader_list_separator"
+			android:dividerHeight="1dip"></ListView>
+	</FrameLayout>
+	<LinearLayout android:id="@+id/linearLayout1"
+		android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:orientation="vertical">
+		<Button android:layout_gravity="bottom" android:layout_height="wrap_content"
+			android:layout_width="fill_parent" android:id="@+id/uploader_choose_folder"
+			android:text="@string/uploader_btn_upload_text"></Button>
+	</LinearLayout>
+</RelativeLayout>

+ 9 - 0
res/layout/uploader_list_item_layout.xml

@@ -0,0 +1,9 @@
+<?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="#fefefe">
+    <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_menu_archive" android:id="@+id/imageView1"></ImageView>
+    <TextView android:text="TextView" android:layout_width="fill_parent" android:id="@+id/textView1" android:layout_height="wrap_content" android:textColor="@android:color/black" android:textSize="20dip"/>
+</LinearLayout>

+ 6 - 0
res/menu/menu.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:title="Settings" android:icon="@android:drawable/ic_menu_preferences" android:id="@+id/settingsItem"></item>
+    <item android:id="@+id/item1" android:title="Create Directory" android:icon="@android:drawable/ic_menu_add"></item>
+</menu>

+ 5 - 0
res/menu/prefs_menu.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/addSessionItem" android:icon="@+android:drawable/ic_menu_add" android:title="@string/prefs_add_session"></item>
+</menu>

+ 6 - 0
res/menu/session_context_menu.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/SessionContextEdit" android:title="Edit"></item>
+    <item android:id="@+id/SessionContextRemove" android:title="REmove"></item>
+</menu>

+ 39 - 0
res/values/strings.xml

@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="hello">Hello World, OwnCloudMainScreen!</string>
+    <string name="app_name">ownCloud</string>
+    <string name="main_password">Password:</string>
+    <string name="main_login">Username:</string>
+    <string name="main_button_login">Login</string>
+    <string name="prefs_general">General</string>
+    <string name="prefs_sessions">Stored sessions</string>
+    <string name="prefs_add_session">Add new session</string>
+    <string name="new_session_id">Session Name</string>
+    <string name="new_session_url">URL</string>
+    <string name="new_session_username">Username</string>
+    <string name="new_session_password">Password</string>
+    <string name="new_session_save">OK</string>
+    <string name="new_session_cancel">Cancel</string>
+    <string name="new_session_uri_error">Wrong URL given</string>
+    <string name="new_session_session_name_error">Wrong session name</string>
+    <string name="sync_string">filelist and pinned files</string>
+    <string name="uploader_no_file_selected">No file selected for upload</string>
+    <string name="setup_hint_username">Username</string>
+    <string name="setup_hint_password">Password</string>
+    <string name="setup_hint_address">Web address</string>
+    <string name="setup_title">Connect to your ownCloud</string>
+    <string name="setup_btn_connect">Connect</string>
+    <string name="uploader_btn_upload_text">Upload</string>
+    <string name="uploader_wrn_no_account_title">No account found</string>
+    <string name="uploader_wrn_no_account_text">No correct ownCloud account found on device. Please setup account first.</string>
+    <string name="uploader_wrn_no_account_setup_btn_text">Setup</string>
+    <string name="uploader_wrn_no_account_quit_btn_text">Quit</string>
+    <string name="uploader_info_uploading">Uploading</string>
+    <string name="uploader_btn_create_dir_text">Create dir for upload</string>
+    <string name="common_ok">OK</string>
+    <string name="common_cancel">Cancel</string>
+    <string name="uploader_info_dirname">Directory name</string>
+    <string name="uploader_upload_succeed">Uploading completed successfully</string>
+    <string name="uploader_upload_failed">Upload failed: </string>
+    <string name="common_choose_account">Choose account</string>
+</resources>

+ 52 - 0
res/values/styles.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<style name="Animations" />
+
+	<!-- PopDownMenu -->
+	<style name="Animations.PopDownMenu" />
+	
+	<style name="Animations.PopDownMenu.Center">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_top</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_bottom</item>
+	</style>
+	
+	<style name="Animations.PopDownMenu.Left">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_topleft_to_bottomright</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_bottomright_to_topleft</item>
+	</style>
+	
+	<style name="Animations.PopDownMenu.Right">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_topright_to_bottomleft</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_bottomleft_to_topright</item>
+	</style>
+	
+	<style name="Animations.PopDownMenu.Reflect">
+		<item name="@android:windowEnterAnimation">@anim/pump_top</item>
+		<item name="@android:windowExitAnimation">@anim/disappear</item>
+	</style>
+	
+	<!-- PopUpMenu -->
+	<style name="Animations.PopUpMenu" />
+	
+	<style name="Animations.PopUpMenu.Center">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_bottom</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_top</item>
+	</style>
+	
+	<style name="Animations.PopUpMenu.Left">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_bottomleft_to_topright</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_topright_to_bottomleft</item>
+	</style>
+	
+	<style name="Animations.PopUpMenu.Right">
+		<item name="@android:windowEnterAnimation">@anim/grow_from_bottomright_to_topleft</item>
+		<item name="@android:windowExitAnimation">@anim/shrink_from_topleft_to_bottomright</item>
+	</style>
+	
+	<style name="Animations.PopUpMenu.Reflect">
+		<item name="@android:windowEnterAnimation">@anim/pump_bottom</item>
+		<item name="@android:windowExitAnimation">@anim/disappear</item>
+	</style>
+	<color name="setup_text_hint">#777777</color>
+	<color name="setup_text_typed">#000000</color>
+</resources>

+ 7 - 0
res/xml/authenticator.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+                       android:accountType="owncloud"
+                       android:icon="@drawable/icon"
+                       android:label="@string/app_name"
+                       android:smallIcon="@drawable/icon">  
+</account-authenticator>

+ 30 - 0
res/xml/contacts.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * 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.
+ */
+-->
+
+<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <ContactsDataKind
+        android:mimeType="vnd.android.cursor.item/vnd.samplesyncadapter.profile"
+        android:icon="@drawable/icon"
+        android:summaryColumn="data2"
+        android:detailColumn="data3"
+        android:detailSocialSummary="true" 
+        android:syncable="true"/>
+
+</ContactsSource>

+ 4 - 0
res/xml/preferences.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+    <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+        <CheckBoxPreference android:title="Create images thumbnails" android:key="create_thumbnails"></CheckBoxPreference>
+    </PreferenceScreen>

+ 6 - 0
res/xml/preferences_new_session.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+  xmlns:android="http://schemas.android.com/apk/res/android">
+    <EditTextPreference></EditTextPreference>
+    <EditTextPreference></EditTextPreference>
+</PreferenceScreen>

+ 27 - 0
res/xml/syncadapter.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2010, The Android Open Source Project
+ *
+ * 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the SyncAdapter. -->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="org.owncloud"
+    android:accountType="owncloud"
+    android:supportsUploading="true"
+/>

+ 37 - 0
src/eu/alefzero/owncloud/ActionItem.java

@@ -0,0 +1,37 @@
+package eu.alefzero.owncloud;
+
+import android.graphics.drawable.Drawable;
+import android.view.View.OnClickListener;
+
+public class ActionItem {
+  private Drawable mIcon;
+  private String mTitle;
+  private OnClickListener mClickListener;
+  
+  public ActionItem() { }
+  
+  public void setTitle(String title) {
+    mTitle = title;
+  }
+  
+  public String getTitle() {
+    return mTitle;
+  }
+  
+  public void setIcon(Drawable icon) {
+    mIcon = icon;
+  }
+  
+  public Drawable getIcon() {
+    return mIcon;
+  }
+  
+  public void setOnClickListener(OnClickListener listener) {
+    mClickListener = listener;
+  }
+  
+  public OnClickListener getOnClickListerner() {
+    return mClickListener;
+  }
+  
+}

+ 126 - 0
src/eu/alefzero/owncloud/CustomPopup.java

@@ -0,0 +1,126 @@
+package eu.alefzero.owncloud;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.PopupWindow;
+
+public class CustomPopup {
+  protected final View mAnchor;
+  protected final PopupWindow mWindow;
+  private View root;
+  private Drawable background = null;
+  protected final WindowManager mWManager;
+  
+  public CustomPopup(View anchor) {
+    mAnchor = anchor;
+    mWindow = new PopupWindow(anchor.getContext());
+    
+    mWindow.setTouchInterceptor(new OnTouchListener() {
+      
+      public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+          CustomPopup.this.dismiss();
+          return true;
+        }
+        return false;
+      }
+    });
+    
+    mWManager = (WindowManager) anchor.getContext().getSystemService(Context.WINDOW_SERVICE);
+    onCreate();
+  }
+  
+  
+  public void onCreate() {}
+  public void onShow() {}
+  
+  public void preShow() {
+    if (root == null) {
+      throw new IllegalStateException("setContentView called with a view to display");
+    }
+    
+    onShow();
+    
+    if (background == null) {
+      mWindow.setBackgroundDrawable(new BitmapDrawable());
+    } else {
+      mWindow.setBackgroundDrawable(background);
+    }
+    
+    mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
+    mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
+    mWindow.setTouchable(true);
+    mWindow.setFocusable(true);
+    mWindow.setOutsideTouchable(true);
+    
+    mWindow.setContentView(root);
+  }
+  
+  public void setBackgroundDrawable(Drawable background) {
+    this.background = background;
+  }
+  
+  public void setContentView(View root) {
+    this.root = root;
+    mWindow.setContentView(root);
+  }
+  
+  public void setContentView(int layoutResId) {
+    LayoutInflater inflater = 
+      (LayoutInflater) mAnchor.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+    setContentView(inflater.inflate(layoutResId, null));
+  }
+  
+  public void showDropDown() {
+    showDropDown(0, 0);
+  }
+  
+  public void showDropDown(int x, int y) {
+    preShow();
+    mWindow.setAnimationStyle(android.R.style.Animation_Dialog);
+    mWindow.showAsDropDown(mAnchor, x, y);
+  }
+  
+  public void showLikeQuickAction() {
+    showLikeQuickAction(0, 0);
+  }
+  
+  public void showLikeQuickAction(int x, int y) {
+    preShow();
+    
+    mWindow.setAnimationStyle(android.R.style.Animation_Dialog);
+    int[] location = new int[2];
+    mAnchor.getLocationOnScreen(location);
+    
+    Rect anchorRect = 
+      new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight());
+    
+    root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+    root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    
+    int rootW = root.getWidth(), rootH = root.getHeight();
+    int screenW = mWManager.getDefaultDisplay().getWidth();
+    
+    int xpos = ((screenW-rootW)/2) + x;
+    int ypos = anchorRect.top - rootH + y;
+    
+    if (rootH > anchorRect.top) {
+      ypos = anchorRect.bottom + y;
+    }
+    mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xpos, ypos);
+  }
+  
+  public void dismiss() {
+    mWindow.dismiss();
+  }
+  
+}

+ 79 - 0
src/eu/alefzero/owncloud/DbHandler.java

@@ -0,0 +1,79 @@
+package eu.alefzero.owncloud;
+
+import java.util.Vector;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+public class DbHandler {
+  private SQLiteDatabase mDB;
+  private OpenerHepler mHelper;
+  private final String mDatabaseName = "ownCloud";
+  private final String TABLE_SESSIONS = "sessions";
+  private final int mDatabaseVersion = 1;
+  
+  public DbHandler(Context context) {
+    mHelper = new OpenerHepler(context);
+    mDB = mHelper.getWritableDatabase();
+  }
+  
+  public Vector<OwnCloudSession> getSessionList() {
+    Cursor c = mDB.query(TABLE_SESSIONS, null, null, null, null, null, null);
+    Vector<OwnCloudSession> v = new Vector<OwnCloudSession>();
+    if (!c.moveToFirst()) {
+      return v;
+    }
+    while (!c.isAfterLast()) {
+      v.add(new OwnCloudSession(c.getString(c.getColumnIndex("sessionName")),
+                                c.getString(c.getColumnIndex("sessionUrl")),
+                                c.getInt(c.getColumnIndex("_id"))));
+      c.moveToNext();
+    }
+    c.close();
+    return v;
+  }
+  
+  public void addSession(String sessionName, String uri) {
+    ContentValues cv = new ContentValues();
+    cv.put("sessionName", sessionName);
+    cv.put("sessionUrl", uri);
+    mDB.insert(TABLE_SESSIONS, null, cv);
+  }
+  
+  public void removeSessionWithId(int sessionId) {
+    mDB.delete(TABLE_SESSIONS, "_id = ?", new String[] {String.valueOf(sessionId)});
+  }
+
+  public void changeSessionFields(int id, String hostname, String uri) {
+    ContentValues cv = new ContentValues();
+    cv.put("sessionName", hostname);
+    cv.put("sessionUrl", uri);
+    mDB.update(TABLE_SESSIONS, cv, "_id = ?", new String[] {String.valueOf(id)});
+  }
+  
+  public void close() {
+    mDB.close();
+  }
+  
+  private class OpenerHepler extends SQLiteOpenHelper {
+    public OpenerHepler(Context context) {
+      super(context, mDatabaseName, null, mDatabaseVersion);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+      db.execSQL("CREATE TABLE " + TABLE_SESSIONS + " (" +
+                 " _id INTEGER PRIMARY KEY, " +
+                 " sessionName TEXT, " +
+                 " sessionUrl  TEXT);");
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+    }
+  }
+}

+ 60 - 0
src/eu/alefzero/owncloud/DisplayUtils.java

@@ -0,0 +1,60 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud;
+
+import java.util.HashMap;
+
+public class DisplayUtils {
+  public static String bitsToHumanReadable(long bitsLen) {
+    double result = bitsLen;
+    int attachedsuff = 0;
+    while (result > 1024 && attachedsuff < suffixes.length) {
+      result /= 1024.;
+      attachedsuff++;
+    }
+    result = ((int)(result * 100))/100.;
+    return result+suffixes[attachedsuff];
+  }
+  
+  public static String convertMIMEtoPrettyPrint(String mimetype) {
+    if (mimeType2HUmanReadable.containsKey(mimetype)) {
+      return mimeType2HUmanReadable.get(mimetype);
+    }
+    return mimetype.split("/")[1].toUpperCase() + " file";
+  }
+  
+  private static final String[] suffixes = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
+  
+  private static HashMap<String, String> mimeType2HUmanReadable;
+  static {
+    mimeType2HUmanReadable = new HashMap<String, String>();
+    // images
+    mimeType2HUmanReadable.put("image/jpeg", "JPEG image");
+    mimeType2HUmanReadable.put("image/jpg", "JPEG image");
+    mimeType2HUmanReadable.put("image/png", "PNG image");
+    mimeType2HUmanReadable.put("image/bmp", "Bitmap image");
+    mimeType2HUmanReadable.put("image/gif", "GIF image");
+    mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");
+    mimeType2HUmanReadable.put("image/tiff", "TIFF image");
+    // music
+    mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");
+    mimeType2HUmanReadable.put("application/ogg", "OGG music file");
+    
+  }
+}

+ 158 - 0
src/eu/alefzero/owncloud/FileListActionListAdapter.java

@@ -0,0 +1,158 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud;
+
+import java.io.File;
+
+import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
+import eu.alefzero.owncloud.db.ProviderMeta;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.net.Uri;
+import android.provider.MediaStore.Images.Media;
+import android.sax.StartElementListener;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+public class FileListActionListAdapter implements ListAdapter {
+
+  private Context mContext;
+  private Account mAccount;
+  private String mFilename, mFileType, mFilePath, mFileStoragePath, mItemId;
+  
+  private final int ITEM_DOWNLOAD = 0;
+  private final int ITEM_SHARE = 1;
+  
+  public FileListActionListAdapter(Cursor c, Context co, Account account) {
+    mContext = co;
+    mFilename = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_NAME));
+    mFileType = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE));
+    mFilePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH));
+    mFileStoragePath = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
+    mItemId = c.getString(c.getColumnIndex(ProviderTableMeta._ID));
+    mAccount = account;
+  }
+  
+  public boolean areAllItemsEnabled() {
+    // TODO Auto-generated method stub
+    return true;
+  }
+
+  public boolean isEnabled(int position) {
+    // TODO Auto-generated method stub
+    return true;
+  }
+
+  public int getCount() {
+    // TODO Auto-generated method stub
+    return 1;
+  }
+
+  public Object getItem(int position) {
+    if (position == 0) {
+      AccountManager accm = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
+      String ocurl = accm.getUserData(mAccount, AccountAuthenticator.KEY_OC_URL);
+      ocurl += mFilePath + mFilename;
+      Intent intent = new Intent(Intent.ACTION_VIEW);
+      intent.setDataAndType(Uri.fromFile(new File(mFileStoragePath)), mFileType);
+      return intent;
+    }
+    return null;
+  }
+
+  public long getItemId(int position) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  public int getItemViewType(int position) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  public View getView(int position, View convertView, ViewGroup parent) {
+    View v = convertView;
+    if (v == null) {
+      LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+      v = vi.inflate(R.layout.file_display_action_list_element, null);
+    }
+
+    TextView tv;
+    ImageView iv;
+    switch (position) {
+      case ITEM_DOWNLOAD :
+        tv = (TextView) v.findViewById(R.id.textView1);
+        if (mFileStoragePath == null) {
+          tv.setText("Download");
+        } else {
+          setActionName(tv);
+        }
+        iv = (ImageView) v.findViewById(R.id.imageView1);
+        iv.setImageResource(R.drawable.download);
+        break;
+    }
+    
+    return v;
+  }
+
+  public int getViewTypeCount() {
+    // TODO Auto-generated method stub
+    return 2;
+  }
+
+  public boolean hasStableIds() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  public boolean isEmpty() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  public void registerDataSetObserver(DataSetObserver observer) {
+    // TODO Auto-generated method stub
+    
+  }
+
+  public void unregisterDataSetObserver(DataSetObserver observer) {
+    // TODO Auto-generated method stub
+    
+  }
+
+  private void setActionName(TextView tv) {
+    if (mFileType.matches("image/.*")) {
+      tv.setText("View");
+    } else if (mFileType.matches("audio/.*") || mFileType.matches("video/.*")) {
+      tv.setText("Play");
+    } else {
+      tv.setText("Open");
+    }
+  }
+  
+}

+ 103 - 0
src/eu/alefzero/owncloud/FileListListAdapter.java

@@ -0,0 +1,103 @@
+package eu.alefzero.owncloud;
+
+import java.security.Provider;
+
+import eu.alefzero.owncloud.db.ProviderMeta;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnLongClickListener;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class FileListListAdapter implements ListAdapter {
+
+  private Cursor mCursor;
+  private Context mContext;
+  
+  public FileListListAdapter(Cursor c, Context context) {
+    mCursor = c;
+    mContext = context;
+  }
+  
+  public boolean areAllItemsEnabled() {
+    return true;
+  }
+
+  public boolean isEnabled(int position) {
+    // TODO Auto-generated method stub
+    return true;
+  }
+
+  public int getCount() {
+    // TODO Auto-generated method stub
+    return mCursor.getCount();
+  }
+
+  public Object getItem(int position) {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  public long getItemId(int position) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  public int getItemViewType(int position) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  public View getView(int position, View convertView, ViewGroup parent) {
+    View v = convertView;
+    if (v == null) {
+      LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+      v = vi.inflate(R.layout.list_layout, null);
+    }
+    if (mCursor.moveToPosition(position)) {
+      TextView tv = (TextView) v.findViewById(R.id.Filename);
+      tv.setText(mCursor.getString(mCursor.getColumnIndex(ProviderMeta.ProviderTableMeta.FILE_NAME)));
+      if (!mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)).equals("DIR")) {
+        ImageView iv = (ImageView) v.findViewById(R.id.imageView1);
+        iv.setImageResource(R.drawable.file);
+      }
+    }
+    
+    return v;
+  }
+
+  public int getViewTypeCount() {
+    // TODO Auto-generated method stub
+    return 4;
+  }
+
+  public boolean hasStableIds() {
+    // TODO Auto-generated method stub
+    return true;
+  }
+
+  public boolean isEmpty() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  public void registerDataSetObserver(DataSetObserver observer) {
+    // TODO Auto-generated method stub
+    
+  }
+
+  public void unregisterDataSetObserver(DataSetObserver observer) {
+    // TODO Auto-generated method stub
+    
+  }
+}

+ 281 - 0
src/eu/alefzero/owncloud/OwnCloudMainScreen.java

@@ -0,0 +1,281 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud;
+
+import java.util.Stack;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.DialogInterface.OnCancelListener;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+
+public class OwnCloudMainScreen extends ListActivity {
+  private DbHandler mDBHandler;
+  private Stack<String> mParents;
+  private Account mAccount;
+  private Cursor mCursor;
+  private boolean mIsDisplayingFile;
+
+  private static final int DIALOG_CHOOSE_ACCOUNT = 0;
+
+  @Override
+  public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    mParents = new Stack<String>();
+    mIsDisplayingFile = false;
+    mDBHandler = new DbHandler(getBaseContext());
+    requestWindowFeature(Window.FEATURE_NO_TITLE);
+    setContentView(R.layout.main);
+
+    AccountManager accMan = AccountManager.get(this);
+    Account[] accounts = accMan.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
+
+    if (accounts.length == 0) {
+      // using string value since in API7 this constatn is not defined
+      // in API7 < this constatant is defined in Settings.ADD_ACCOUNT_SETTINGS
+      // and Settings.EXTRA_AUTHORITIES
+      Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
+      intent.putExtra("authorities", new String[] {AccountAuthenticator.AUTH_TOKEN_TYPE});
+      startActivity(intent);
+    } else if (accounts.length > 1) {
+      showDialog(DIALOG_CHOOSE_ACCOUNT);
+    } else {
+      mAccount = accounts[0];
+      populateFileList();
+    }
+  }
+
+  @Override
+  public boolean onOptionsItemSelected(MenuItem item) {
+    switch (item.getItemId()) {
+      case R.id.settingsItem :
+        Intent i = new Intent(this, Preferences.class);
+        startActivity(i);
+        break;
+    }
+    return true;
+  }
+
+  @Override
+  protected Dialog onCreateDialog(int id) {
+    switch (id) {
+      case DIALOG_CHOOSE_ACCOUNT:
+        return createChooseAccountDialog();
+      default:
+        throw new IllegalArgumentException("Unknown dialog id: " + id);
+    }
+  }
+  
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu) {
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.menu, menu);
+    return true;
+  }
+  
+  @Override
+  protected void onDestroy() {
+    mDBHandler.close();
+    super.onDestroy();
+  }
+    
+  private Dialog createChooseAccountDialog() {
+    final AccountManager accMan = AccountManager.get(this);
+    CharSequence[] items = new CharSequence[accMan.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
+    int i = 0;
+    for (Account a : accMan.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)) {
+      items[i++] = a.name;
+    }
+    
+    AlertDialog.Builder builder = new AlertDialog.Builder(this);
+    builder.setTitle(R.string.common_choose_account);
+    builder.setCancelable(true);
+    builder.setItems(items, new DialogInterface.OnClickListener() {
+        public void onClick(DialogInterface dialog, int item) {
+            mAccount = accMan.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[item];
+            dialog.dismiss();
+            populateFileList();
+        }
+    });
+    builder.setOnCancelListener(new OnCancelListener() {
+      public void onCancel(DialogInterface dialog) {
+        OwnCloudMainScreen.this.finish();
+      }
+    });
+    AlertDialog alert = builder.create();
+    return alert;
+  }
+
+  @Override
+  public void onBackPressed() {
+    if (mIsDisplayingFile) {
+      mIsDisplayingFile = false;
+      setContentView(R.layout.main);
+      Uri uri;
+      if (mParents.empty()) {
+        uri = ProviderTableMeta.CONTENT_URI;
+      } else {
+        uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, mParents.peek());
+      }
+      mCursor = managedQuery(uri,
+                             null,
+                             ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+                             new String[]{mAccount.name}, null);
+  
+      if (mCursor.moveToFirst()) {
+        TextView tv = (TextView) findViewById(R.id.directory_name);
+        tv.setText(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH)));
+      }
+      getListView().setAdapter(new FileListListAdapter(mCursor, this));      
+      getListView().invalidate();
+      return;
+    }
+    if (mParents.size()==0) {
+      super.onBackPressed();
+      return;
+    } else if (mParents.size() == 1) {
+      mParents.pop();
+      mCursor = managedQuery(ProviderTableMeta.CONTENT_URI,
+          null,
+          ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+          new String[]{mAccount.name},
+          null);
+    } else {
+      mParents.pop();
+      mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, mParents.peek()),
+          null,
+          ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+          new String[]{mAccount.name},
+          null);
+    }
+    
+    setListAdapter(new FileListListAdapter(mCursor, this));
+    getListView().invalidate();
+
+    TextView tv = (TextView) findViewById(R.id.directory_name);
+    String s = tv.getText().toString();
+    if (s.endsWith("/")) {
+      s = s.substring(0, s.length() - 1);
+    }
+    s = s.substring(0, s.lastIndexOf('/') + 1);
+    tv.setText(s);
+  }
+
+  @Override
+  protected void onListItemClick(ListView l, View v, int position, long id) {
+    super.onListItemClick(l, v, position, id);
+    if (!mCursor.moveToPosition(position)) {
+      throw new IndexOutOfBoundsException("Incorrect item selected");
+    }
+    if (!mIsDisplayingFile) {
+      if (mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)).equals("DIR")) {
+        String id_ = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID));
+        String dirname = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME));
+        TextView tv = (TextView) findViewById(R.id.directory_name);
+        tv.setText(tv.getText().toString()+dirname+"/");
+        mParents.push(id_);
+        mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, id_),
+                               null,
+                               ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
+                               new String[]{mAccount.name}, null);
+        setListAdapter(new FileListListAdapter(mCursor, this));
+      } else {
+        mIsDisplayingFile = true;
+        setContentView(R.layout.file_display);
+        String id_ = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID));
+        mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, id_),
+                               null,
+                               null,
+                               null,
+                               null);
+        mCursor.moveToFirst();
+        // filename
+        TextView tv = (TextView) findViewById(R.id.textView1);
+        tv.setText(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)));
+        // filetype
+        tv = (TextView) findViewById(R.id.textView2);
+        tv.setText(DisplayUtils.convertMIMEtoPrettyPrint(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE))));
+        // size
+        tv = (TextView) findViewById(R.id.textView3);
+        tv.setText(DisplayUtils.bitsToHumanReadable(mCursor.getLong(mCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH))));
+        // modified
+        tv = (TextView) findViewById(R.id.textView4);
+        tv.setText(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
+        if (!TextUtils.isEmpty(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)))) {
+          ImageView iv = (ImageView) findViewById(R.id.imageView1);
+          Bitmap bmp = BitmapFactory.decodeFile(mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
+          Matrix m = new Matrix();
+          float scale;
+          if (bmp.getWidth() > bmp.getHeight()) {
+            scale = (float) (200./bmp.getWidth());
+          } else {
+            scale = (float) (200./bmp.getHeight());
+          }
+          m.postScale(scale, scale);
+          Bitmap newBmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
+          iv.setImageBitmap(newBmp);
+        }
+        setListAdapter(new FileListActionListAdapter(mCursor, this, mAccount));
+      }    
+      getListView().invalidate();
+    } else {
+      try {
+        Intent i = (Intent) getListAdapter().getItem(position);
+        startActivity(i);
+      } catch (ClassCastException e) {}
+    }
+  }
+  
+  private void populateFileList() {
+    TextView tv = (TextView) findViewById(R.id.directory_name);
+    tv.setText("/");
+    mCursor = getContentResolver().query(ProviderTableMeta.CONTENT_URI,
+                                         null,
+                                         ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+                                         new String[]{mAccount.name},
+                                         null);
+    setListAdapter(new FileListListAdapter(mCursor, this));
+    getListView().invalidate();
+  }
+  
+  
+
+}

+ 33 - 0
src/eu/alefzero/owncloud/OwnCloudSession.java

@@ -0,0 +1,33 @@
+package eu.alefzero.owncloud;
+
+public class OwnCloudSession {
+  private String mSessionName;
+  private String mSessionUrl;
+  private int mEntryId;
+  
+  public OwnCloudSession(String name, String url, int entryId) {
+    mSessionName = name;
+    mSessionUrl = url;
+    mEntryId = entryId;
+  }
+  
+  public void setName(String name) {
+    mSessionName = name;
+  }
+  
+  public String getName() {
+    return mSessionName;
+  }
+  
+  public void setUrl(String url) {
+    mSessionUrl = url;
+  }
+  
+  public String getUrl() {
+    return mSessionUrl;
+  }
+  
+  public int getEntryId() {
+    return mEntryId;
+  }
+}

+ 495 - 0
src/eu/alefzero/owncloud/OwnCloudUploader.java

@@ -0,0 +1,495 @@
+package eu.alefzero.owncloud;
+
+import java.io.File;
+import java.sql.Date;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Stack;
+
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.FileEntity;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.entity.mime.content.FileBody;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.app.AlertDialog.Builder;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.provider.MediaStore.Images.Media;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.Toast;
+import android.widget.AdapterView.OnItemClickListener;
+import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
+import eu.alefzero.owncloud.db.ProviderMeta;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+import eu.alefzero.webdav.HttpMkCol;
+import eu.alefzero.webdav.WebdavUtils;
+
+public class OwnCloudUploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
+  private static final String TAG = "ownCloudUploader";
+
+  private Account mAccount;
+  private AccountManager mAccountManager;
+  private String mUsername, mPassword;
+  private Cursor mCursor;
+  private Stack<String> mParents;
+  private Thread mUploadThread;
+  private Handler mHandler;
+  private ArrayList<Parcelable> mStreamsToUpload;
+
+  private final static int DIALOG_NO_ACCOUNT = 0;
+  private final static int DIALOG_WAITING = 1;
+  private final static int DIALOG_NO_STREAM = 2;
+  private final static int DIALOG_MULTIPLE_ACCOUNT = 3;
+  private final static int DIALOG_GET_DIRNAME = 4;
+
+  private final static int REQUEST_CODE_SETUP_ACCOUNT = 0;
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+    mParents = new Stack<String>();
+    mHandler = new Handler();
+    if (getIntent().hasExtra(Intent.EXTRA_STREAM)) {
+      prepareStreamsToUpload();
+      mAccountManager = (AccountManager)getSystemService(Context.ACCOUNT_SERVICE);
+      Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
+      if (accounts.length == 0) {
+        Log.i(TAG, "No ownCloud account is available");
+        showDialog(DIALOG_NO_ACCOUNT);
+      } else if (accounts.length > 1) {
+        Log.i(TAG, "More then one ownCloud is available");
+        showDialog(DIALOG_MULTIPLE_ACCOUNT);
+      } else {
+        mAccount = accounts[0];
+        setContentView(R.layout.uploader_layout);
+        populateDirectoryList();
+      }
+    } else {
+      showDialog(DIALOG_NO_STREAM);
+    }
+  }
+
+  @Override
+  protected Dialog onCreateDialog(final int id) {
+    final AlertDialog.Builder builder = new Builder(this);
+    switch (id) {
+      case DIALOG_WAITING:
+        ProgressDialog pDialog = new ProgressDialog(this);
+        pDialog.setIndeterminate(false);
+        pDialog.setCancelable(false);
+        pDialog.setMessage(getResources().getString(R.string.uploader_info_uploading));
+        return pDialog;
+      case DIALOG_NO_ACCOUNT:
+        builder.setIcon(android.R.drawable.ic_dialog_alert);
+        builder.setTitle(R.string.uploader_wrn_no_account_title);
+        builder.setMessage(R.string.uploader_wrn_no_account_text);
+        builder.setCancelable(false);
+        builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
+          public void onClick(DialogInterface dialog, int which) {
+            if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ECLAIR_MR1) {
+              // using string value since in API7 this constatn is not defined
+              // in API7 < this constatant is defined in Settings.ADD_ACCOUNT_SETTINGS
+              // and Settings.EXTRA_AUTHORITIES
+              Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
+              intent.putExtra("authorities", new String[] {AccountAuthenticator.AUTH_TOKEN_TYPE});
+              startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
+            } else {
+              // since in API7 there is no direct call for account setup, so we need to
+              // show our own AccountSetupAcricity, get desired results and setup
+              // everything for ourself
+              Intent intent = new Intent(getBaseContext(), AccountAuthenticator.class);
+              startActivityForResult(intent, REQUEST_CODE_SETUP_ACCOUNT);
+            }
+          }
+        });
+        builder.setNegativeButton(R.string.uploader_wrn_no_account_quit_btn_text, new OnClickListener() {
+          public void onClick(DialogInterface dialog, int which) {
+            finish();
+          }
+        });
+        return builder.create();
+      case DIALOG_GET_DIRNAME:
+        final EditText dirName = new EditText(getBaseContext());
+        builder.setView(dirName);
+        builder.setTitle(R.string.uploader_info_dirname);
+        String pathToUpload;
+        if (mParents.empty()) {
+          pathToUpload = "/";
+        } else {
+          mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), 
+                                 null,
+                                 null,
+                                 null,
+                                 null);
+          mCursor.moveToFirst();
+          pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH)) +
+                         mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20");
+        }
+        a a = new a(pathToUpload, dirName);
+        builder.setPositiveButton(R.string.common_ok, a);
+        builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
+          public void onClick(DialogInterface dialog, int which) {
+            dialog.cancel();
+          }
+        });
+        return builder.create();
+      case DIALOG_MULTIPLE_ACCOUNT:
+        CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE).length];
+        for (int i = 0;  i < ac.length; ++i) {
+          ac[i] = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[i].name;
+        }
+        builder.setTitle(R.string.common_choose_account);
+        builder.setItems(ac, new OnClickListener() {
+          public void onClick(DialogInterface dialog, int which) {
+            mAccount = mAccountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE)[which];
+            populateDirectoryList();
+          }
+        });
+        builder.setCancelable(true);
+        builder.setOnCancelListener(new OnCancelListener() {
+          public void onCancel(DialogInterface dialog) {
+            dialog.cancel();
+            finish();
+          }
+        });
+        return builder.create();
+      default:
+        throw new IllegalArgumentException("Unknown dialog id: " + id);
+    }
+  }
+
+  class a implements OnClickListener {
+    String mPath;
+    EditText mDirname;
+    public a(String path, EditText dirname) {
+      mPath = path; mDirname = dirname;
+    }
+    public void onClick(DialogInterface dialog, int which) {
+      showDialog(DIALOG_WAITING);
+      mUploadThread = new Thread(new BackgroundUploader(mPath+mDirname.getText().toString(), mStreamsToUpload, mHandler, true));
+      mUploadThread.start();
+    }
+  }
+  
+  @Override
+  public void onBackPressed() {
+    
+    if (mParents.size()==0) {
+      super.onBackPressed();
+      return;
+    } else if (mParents.size() == 1) {
+      mParents.pop();
+      mCursor = managedQuery(ProviderTableMeta.CONTENT_URI,
+          null,
+          ProviderTableMeta.FILE_CONTENT_TYPE+"=?",
+          new String[]{"DIR"},
+          null);
+    } else {
+      mParents.pop();
+      mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, mParents.peek()),
+          null,
+          ProviderTableMeta.FILE_CONTENT_TYPE+"=?",
+          new String[]{"DIR"},
+          null);
+    }
+    
+    SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.uploader_list_item_layout,
+                                   mCursor,
+                                   new String[]{ProviderTableMeta.FILE_NAME},
+                                   new int[]{R.id.textView1});
+    setListAdapter(sca);
+  }
+  
+  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+    if (!mCursor.moveToPosition(position)) {
+      throw new IndexOutOfBoundsException("Incorrect item selected");
+    }
+    String _id = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta._ID));
+    mParents.push(_id);
+    
+    mCursor.close();
+    mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, _id),
+                           null,
+                           ProviderTableMeta.FILE_CONTENT_TYPE+"=?",
+                           new String[]{"DIR"},
+                           null);
+    SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.uploader_list_item_layout,
+                                                      mCursor,
+                                                      new String[]{ProviderTableMeta.FILE_NAME},
+                                                      new int[]{R.id.textView1});
+    setListAdapter(sca);
+    getListView().invalidate();
+  }
+
+  public void onClick(View v) {
+    switch (v.getId()) {
+      case R.id.uploader_choose_folder:
+        String pathToUpload = null;
+        if (mParents.empty()) {
+          pathToUpload = "/";
+        } else {
+          mCursor = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()), 
+                                 null,
+                                 null,
+                                 null,
+                                 null);
+          mCursor.moveToFirst();
+          pathToUpload = mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_PATH)) +
+                         mCursor.getString(mCursor.getColumnIndex(ProviderTableMeta.FILE_NAME)).replace(" ", "%20");
+        }
+        
+        showDialog(DIALOG_WAITING);
+        mUploadThread = new Thread(new BackgroundUploader(pathToUpload, mStreamsToUpload, mHandler));
+        mUploadThread.start();
+        
+        break;
+      case android.R.id.button1: // dynamic action for create aditional dir
+        showDialog(DIALOG_GET_DIRNAME);
+        break;
+      default:
+        throw new IllegalArgumentException("Wrong element clicked");
+    }
+  }
+
+  public void onUploadComplete(boolean uploadSucc, String message) {
+    dismissDialog(DIALOG_WAITING);
+    Log.i(TAG, "UploadSucc: " + uploadSucc + " message: " + message);
+    if (uploadSucc) {
+      Toast.makeText(this, getResources().getString(R.string.uploader_upload_succeed), Toast.LENGTH_SHORT).show();
+    } else {
+      Toast.makeText(this, getResources().getString(R.string.uploader_upload_failed) + message, Toast.LENGTH_LONG).show();
+    }
+    finish();
+  }
+  
+  @Override
+  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    super.onActivityResult(requestCode, resultCode, data);
+    Log.i(TAG, "result received. req: " + requestCode + " res: " + resultCode);
+    if (requestCode == REQUEST_CODE_SETUP_ACCOUNT) {
+      dismissDialog(DIALOG_NO_ACCOUNT);
+      if (resultCode == RESULT_CANCELED) {
+        finish();
+      }
+      Account[] accounts = mAccountManager.getAccountsByType(AccountAuthenticator.AUTH_TOKEN_TYPE);
+      if (accounts.length == 0) {
+        showDialog(DIALOG_NO_ACCOUNT);
+      } else {
+        // there is no need for checking for is there more then one account at this point
+        // since account setup can set only one account at time
+        mAccount = accounts[0];
+        populateDirectoryList();
+      }
+    }
+  }
+  
+  private void populateDirectoryList() {
+    mUsername = mAccount.name.substring(0, mAccount.name.indexOf('@'));
+    mPassword = mAccountManager.getPassword(mAccount);
+    setContentView(R.layout.uploader_layout);
+    mCursor = managedQuery(ProviderMeta.ProviderTableMeta.CONTENT_URI,
+                           null,
+                           ProviderTableMeta.FILE_CONTENT_TYPE+"=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
+                           new String[]{"DIR", mAccount.name},
+                           null);
+
+    ListView lv = getListView();
+    lv.setOnItemClickListener(this);
+    SimpleCursorAdapter sca = new SimpleCursorAdapter(this,
+                                                      R.layout.uploader_list_item_layout,
+                                                      mCursor,
+                                                      new String[]{ProviderTableMeta.FILE_NAME},
+                                                      new int[]{R.id.textView1});
+    setListAdapter(sca);
+    Button btn = (Button) findViewById(R.id.uploader_choose_folder);
+    btn.setOnClickListener(this);
+    // insert create new directory for multiple items uploading
+    if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
+      Button createDirBtn = new Button(this);
+      createDirBtn.setId(android.R.id.button1);
+      createDirBtn.setText(R.string.uploader_btn_create_dir_text);
+      createDirBtn.setOnClickListener(this);
+      ((LinearLayout)findViewById(R.id.linearLayout1)).addView(createDirBtn, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+  }
+  
+  private void prepareStreamsToUpload() {
+    if (getIntent().getAction().equals(Intent.ACTION_SEND)) {
+      mStreamsToUpload = new ArrayList<Parcelable>();
+      mStreamsToUpload.add(getIntent().getParcelableExtra(Intent.EXTRA_STREAM));
+    } else if (getIntent().getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
+      mStreamsToUpload = getIntent().getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+    } else {
+      // unknow action inserted
+      throw new IllegalArgumentException("Unknown action given: " + getIntent().getAction());
+    }
+  }
+  
+  public void PartialupdateUpload(String fileLocalPath, String filename, String filepath, String contentType, String contentLength) {
+    ContentValues cv = new ContentValues();
+    cv.put(ProviderTableMeta.FILE_NAME, filename);
+    cv.put(ProviderTableMeta.FILE_PATH, filepath);
+    cv.put(ProviderTableMeta.FILE_STORAGE_PATH, fileLocalPath);
+    cv.put(ProviderTableMeta.FILE_MODIFIED, WebdavUtils.DISPLAY_DATE_FORMAT.format(new java.util.Date()));
+    cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, contentType);
+    cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, contentLength);
+    cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+    Log.d(TAG, filename+" ++ "+filepath+" ++ " + contentLength + " ++ " + contentType + " ++ " + fileLocalPath);
+    if (!mParents.empty()) {
+      Cursor c = managedQuery(Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, mParents.peek()),
+                              null,
+                              null,
+                              null,
+                              null);
+      c.moveToFirst();
+      cv.put(ProviderTableMeta.FILE_PARENT, c.getString(c.getColumnIndex(ProviderTableMeta._ID)));
+      c.close();
+    }
+    getContentResolver().insert(ProviderTableMeta.CONTENT_URI_FILE, cv);
+  }
+  
+  class BackgroundUploader implements Runnable {
+    private ArrayList<Parcelable> mUploadStreams;
+    private Handler mHandler;
+    private String mUploadPath;
+    private boolean mCreateDir;
+    
+    public BackgroundUploader(String pathToUpload, ArrayList<Parcelable> streamsToUpload,
+        Handler handler) {
+      mUploadStreams = streamsToUpload;
+      mHandler = handler;
+      mUploadPath = pathToUpload.replace(" ", "%20");
+      mCreateDir = false;
+    }
+
+    public BackgroundUploader(String pathToUpload, ArrayList<Parcelable> streamsToUpload,
+                              Handler handler, boolean createDir) {
+      mUploadStreams = streamsToUpload;
+      mHandler = handler;
+      mUploadPath = pathToUpload.replace(" ", "%20");
+      mCreateDir = createDir;
+    }
+
+    public void run() {
+      boolean any_failed = false;
+      DefaultHttpClient httpClient = new DefaultHttpClient();
+      Uri uri = Uri.parse(mAccountManager.getUserData(mAccount,
+          AccountAuthenticator.KEY_OC_URL));
+      httpClient.getCredentialsProvider().setCredentials(
+          new AuthScope(uri.getHost(), (uri.getPort() == -1) ? 80 : uri
+              .getPort()),
+          new UsernamePasswordCredentials(mUsername, mPassword));
+      BasicHttpContext httpContext = new BasicHttpContext();
+      BasicScheme basicAuth = new BasicScheme();
+      httpContext.setAttribute("preemptive-auth", basicAuth);
+      HttpHost targetHost = new HttpHost(uri.getHost(), (uri.getPort() == -1)
+          ? 80
+          : uri.getPort(), (uri.getScheme() == "https") ? "https" : "http");
+
+      // create last directory in path if nessesary
+      if (mCreateDir) {
+        HttpMkCol method = new HttpMkCol(uri.toString() + mUploadPath + "/");
+        method.setHeader("User-Agent", "Android-ownCloud");
+        try {
+          httpClient.execute(targetHost, method, httpContext);
+          Log.i(TAG, "Creating dir completed");
+        } catch (final Exception e) {
+          e.printStackTrace();
+          mHandler.post(new Runnable() {
+            public void run() {
+              OwnCloudUploader.this.onUploadComplete(false, e.getLocalizedMessage());
+            }
+          });
+          return;
+        }
+      }
+      
+      for (int i = 0; i < mUploadStreams.size(); ++i) {
+        final Cursor c = getContentResolver().query((Uri)mUploadStreams.get(i), null, null, null, null);
+        c.moveToFirst();
+
+        HttpPut method = new HttpPut(uri.toString() + mUploadPath + "/"
+            + c.getString(c.getColumnIndex(Media.DISPLAY_NAME)).replace(" ", "%20"));
+        method.setHeader("Content-type", c.getString(c.getColumnIndex(Media.MIME_TYPE)));
+        method.setHeader("User-Agent", "Android-ownCloud");
+
+        try {
+          FileBody fb = new FileBody(new File(c.getString(c.getColumnIndex(Media.DATA))), c.getString(c.getColumnIndex(Media.MIME_TYPE)));
+          MultipartEntity entity = new MultipartEntity();
+          final FileEntity fileEntity = new FileEntity(new File(c.getString(c.getColumnIndex(Media.DATA))),
+               c.getString(c.getColumnIndex(Media.MIME_TYPE)));
+
+          entity.addPart(c.getString(c.getColumnIndex(Media.DISPLAY_NAME)).replace(" ", "%20"), fb);
+
+          method.setEntity(fileEntity);
+          Log.i(TAG, "executing:" + method.getRequestLine().toString());
+
+          httpClient.execute(targetHost, method, httpContext);
+          mHandler.post(new Runnable() {
+            public void run() {
+              OwnCloudUploader.this.PartialupdateUpload(c.getString(c.getColumnIndex(Media.DATA)),
+                                                        c.getString(c.getColumnIndex(Media.DISPLAY_NAME)),
+                                                        mUploadPath + (mUploadPath.equals("/")?"":"/"),
+                                                        fileEntity.getContentType().getValue(),
+                                                        fileEntity.getContentLength()+"");
+            }
+          });
+          Log.i(TAG, "Uploading, done");
+
+        } catch (final Exception e) {
+          any_failed = true;
+          mHandler.post(new Runnable() {
+            public void run() {
+              OwnCloudUploader.this.onUploadComplete(false, c.getString(c.getColumnIndex(Media.DISPLAY_NAME))+ " " + e.getLocalizedMessage());
+            }
+          });
+        }
+      }
+      if (!any_failed) {
+        mHandler.post(new Runnable() {
+          public void run() {
+            OwnCloudUploader.this.onUploadComplete(true, "Success");
+          }
+        });
+      }
+      Bundle bundle = new Bundle();
+      bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+      //ContentResolver.requestSync(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE, bundle);
+
+    }
+
+  }
+  
+}

+ 139 - 0
src/eu/alefzero/owncloud/Preferences.java

@@ -0,0 +1,139 @@
+package eu.alefzero.owncloud;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Vector;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+public class Preferences extends PreferenceActivity {
+  private String TAG = "OwnCloudPreferences";
+  private final int mNewSession = 47;
+  private final int mEditSession = 48;
+  private DbHandler mDbHandler;
+  private Vector<OwnCloudSession> mSessions;
+  private int mSelectedMenuItem;
+  
+  @Override
+  public void onCreate(Bundle savedInstanceState){
+    super.onCreate(savedInstanceState);
+    mDbHandler = new DbHandler(getBaseContext());
+    mSessions = new Vector<OwnCloudSession>();
+    addPreferencesFromResource(R.xml.preferences);
+    registerForContextMenu(getListView());
+    //populateSessionList();
+  }
+  
+  private void populateSessionList() {
+    mSessions.clear();
+    mSessions = mDbHandler.getSessionList();
+    PreferenceScreen ps = getPreferenceScreen();
+    ps.removeAll();
+    addPreferencesFromResource(R.xml.preferences);
+    for (int i = 0; i < mSessions.size(); i++) {
+      Preference preference = new Preference(getBaseContext());
+      preference.setTitle(mSessions.get(i).getName());
+      URI uri;
+      try {
+        uri = new URI(mSessions.get(i).getUrl());
+      } catch (URISyntaxException e) {
+        e.printStackTrace(); // should never happend
+        continue;
+      }
+      preference.setSummary(uri.getScheme() + "://" + uri.getHost()+uri.getPath());
+      ps.addPreference(preference);
+    }
+  }
+
+  @Override
+  public boolean onCreateOptionsMenu(Menu menu) {
+    super.onCreateOptionsMenu(menu);
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.prefs_menu, menu);
+    return true;
+  }
+  
+  @Override
+  public boolean onMenuItemSelected(int featureId, MenuItem item) {
+    super.onMenuItemSelected(featureId, item);
+    Intent intent;
+    
+    switch (item.getItemId()) {
+      case R.id.addSessionItem:
+        intent = new Intent(this, PreferencesNewSession.class);
+        startActivityForResult(intent, mNewSession);
+        break;
+      case R.id.SessionContextEdit:
+        intent = new Intent(this, PreferencesNewSession.class);
+        intent.putExtra("sessionId", mSessions.get(mSelectedMenuItem).getEntryId());
+        intent.putExtra("sessionName", mSessions.get(mSelectedMenuItem).getName());
+        intent.putExtra("sessionURL", mSessions.get(mSelectedMenuItem).getUrl());
+        startActivityForResult(intent, mEditSession);
+        break;
+      case R.id.SessionContextRemove:
+        OwnCloudSession ocs = mSessions.get(mSelectedMenuItem);
+        mDbHandler.removeSessionWithId(ocs.getEntryId());
+        mSessions.remove(ocs);
+        getPreferenceScreen().removePreference(getPreferenceScreen().getPreference(mSelectedMenuItem+1));
+        break;
+      default:
+        Log.w(TAG, "Unknown menu item triggered");
+        return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    super.onActivityResult(requestCode, resultCode, data);
+    if (resultCode == Activity.RESULT_OK) {
+      switch (requestCode) {
+        case mNewSession:
+          mDbHandler.addSession(data.getStringExtra("sessionName"), 
+                                data.getStringExtra("sessionURL"));
+          getPreferenceScreen().removeAll();
+          addPreferencesFromResource(R.xml.preferences);
+          populateSessionList();
+          break;
+        case mEditSession:
+          mDbHandler.changeSessionFields(data.getIntExtra("sessionId", -1),
+                                         data.getStringExtra("sessionName"),
+                                         data.getStringExtra("sessionURL"));
+          populateSessionList();
+          break;
+      }
+    }
+  }
+
+  @Override
+  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+    super.onCreateContextMenu(menu, v, menuInfo);
+    AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
+    mSelectedMenuItem = info.position-1;
+    menu.setHeaderTitle(mSessions.get(mSelectedMenuItem).getName());
+    
+    MenuInflater inflater = getMenuInflater();
+    inflater.inflate(R.menu.session_context_menu, menu);
+    
+  }
+  
+  @Override
+  protected void onDestroy() {
+    mDbHandler.close();
+    super.onDestroy();
+  }
+  
+}

+ 143 - 0
src/eu/alefzero/owncloud/PreferencesNewSession.java

@@ -0,0 +1,143 @@
+package eu.alefzero.owncloud;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import eu.alefzero.owncloud.authenticator.AccountAuthenticatorService;
+
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorActivity;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class PreferencesNewSession extends AccountAuthenticatorActivity implements OnClickListener {
+  private Intent mReturnData;
+  private final String TAG = "OwnCloudPreferencesNewSession";
+  @Override
+  public void onCreate(Bundle savedInstanceState){
+    super.onCreate(savedInstanceState);
+    //setContentView(R.layout.add_new_session);
+    /*
+    EditText et;// = (EditText) findViewById(R.id.newSession_sessionName);
+    
+    et = (EditText) findViewById(R.id.newSession_URL);
+    if (getIntent().hasExtra("sessionURL")) {
+      try {
+        URI uri = new URI(getIntent().getStringExtra("sessionURL"));
+        String url = uri.getHost();
+        if (uri.getPort() != -1) {
+          url += ":" + String.valueOf(uri.getPort());
+        }
+        if (uri.getPath() != null) {
+          url += uri.getPath();
+        } else {
+          url += "/";
+        }
+        et.setText(url);
+        et = (EditText) findViewById(R.id.newSession_username);
+        if (uri.getAuthority() != null) {
+          if (uri.getUserInfo().indexOf(':') != -1) {
+            et.setText(uri.getUserInfo().substring(0, uri.getUserInfo().indexOf(':')));
+            et = (EditText) findViewById(R.id.newSession_password);
+            et.setText(uri.getUserInfo().substring(uri.getUserInfo().indexOf(':')+1));
+          } else {
+            et.setText(uri.getUserInfo());
+          }
+        }
+        
+      } catch (URISyntaxException e) {
+        Log.e(TAG, "Incorrect URI syntax " + e.getLocalizedMessage());
+      }
+    }
+    
+    mReturnData = new Intent();
+    setResult(Activity.RESULT_OK, mReturnData);
+    ((Button) findViewById(R.id.button1)).setOnClickListener(this);
+    ((Button) findViewById(R.id.button2)).setOnClickListener(this);*/
+  }
+  
+  @Override
+  protected void onResume() {
+    super.onResume();
+  }
+
+  public void onClick(View v) {
+   /* switch (v.getId()) {
+      case R.id.button1:
+        Intent intent = new Intent();
+        if (getIntent().hasExtra("sessionId")) {
+          intent.putExtra("sessionId", getIntent().getIntExtra("sessionId", -1));
+        }
+        //String sessionName = ((EditText) findViewById(R.id.newSession_sessionName)).getText().toString();
+      //  if (sessionName.trim().equals("") || !isNameValid(sessionName)) {
+     //    Toast.makeText(this, R.string.new_session_session_name_error, Toast.LENGTH_LONG).show();
+     //     break;
+       // }
+        URI uri = prepareURI();
+        if (uri != null) {
+          //intent.putExtra("sessionName", sessionName);
+          intent.putExtra("sessionURL", uri.toString());
+          setResult(Activity.RESULT_OK, intent);
+          AccountManager accMgr = AccountManager.get(this);
+          Account a = new Account("OwnCloud", AccountAuthenticatorService.ACCOUNT_TYPE);
+          accMgr.addAccountExplicitly(a, "asd", null);
+          finish();
+        }
+        break;
+      case R.id.button2:
+        setResult(Activity.RESULT_CANCELED);
+        finish();
+        break;
+    }*/
+  }
+  
+  private URI prepareURI() {
+    URI uri = null;
+   /* String url = "";
+    try {
+      String username = ((EditText) findViewById(R.id.newSession_username)).getText().toString().trim();
+      String password = ((EditText) findViewById(R.id.newSession_password)).getText().toString().trim();
+      String hostname = ((EditText) findViewById(R.id.newSession_URL)).getText().toString().trim();
+      String scheme;
+      if (hostname.matches("[A-Za-z]://")) {
+        scheme = hostname.substring(0, hostname.indexOf("://")+3);
+        hostname = hostname.substring(hostname.indexOf("://")+3);
+      } else {
+        scheme = "http://";
+      }
+      if (!username.equals("")) {
+        if (!password.equals("")) {
+          username += ":" + password + "@";
+        } else {
+          username += "@";
+        }
+      }
+      url = scheme + username + hostname;
+      Log.i(TAG, url);
+      uri = new URI(url);
+    } catch (URISyntaxException e) {
+      Log.e(TAG, "Incorrect URI syntax " + e.getLocalizedMessage());
+      Toast.makeText(this, R.string.new_session_uri_error, Toast.LENGTH_LONG).show();
+    }
+    */return uri;
+  }
+  
+  private boolean isNameValid(String string) {
+    return string.matches("[A-Za-z0-9 _-]*");
+  }
+  
+  @Override
+  public void onBackPressed() {
+    setResult(Activity.RESULT_CANCELED);
+    super.onBackPressed();
+  }
+  
+}

+ 267 - 0
src/eu/alefzero/owncloud/QuickAction.java

@@ -0,0 +1,267 @@
+package eu.alefzero.owncloud;
+
+import android.content.Context;
+
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+/**
+ * Popup window, shows action list as icon and text like the one in Gallery3D app. 
+ * 
+ * @author Lorensius. W. T
+ */
+public class QuickAction extends CustomPopup {
+	private final View root;
+	private final ImageView mArrowUp;
+	private final ImageView mArrowDown;
+	private final LayoutInflater inflater;
+	private final Context context;
+	
+	protected static final int ANIM_GROW_FROM_LEFT = 1;
+	protected static final int ANIM_GROW_FROM_RIGHT = 2;
+	protected static final int ANIM_GROW_FROM_CENTER = 3;
+	protected static final int ANIM_REFLECT = 4;
+	protected static final int ANIM_AUTO = 5;
+
+	private int animStyle;
+	private ViewGroup mTrack;
+	private ScrollView scroller;
+	private ArrayList<ActionItem> actionList;
+	
+	/**
+	 * Constructor
+	 * 
+	 * @param anchor {@link View} on where the popup window should be displayed
+	 */
+	public QuickAction(View anchor) {
+		super(anchor);
+		
+		actionList	= new ArrayList<ActionItem>();
+		context		= anchor.getContext();
+		inflater 	= (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+		
+		root		= (ViewGroup) inflater.inflate(R.layout.popup, null);
+		
+		mArrowDown 	= (ImageView) root.findViewById(R.id.arrow_down);
+		mArrowUp 	= (ImageView) root.findViewById(R.id.arrow_up);
+		
+		setContentView(root);
+	    
+		mTrack 			= (ViewGroup) root.findViewById(R.id.tracks);
+		scroller		= (ScrollView) root.findViewById(R.id.scroller);
+		animStyle		= ANIM_AUTO;
+	}
+
+	/**
+	 * Set animation style
+	 * 
+	 * @param animStyle animation style, default is set to ANIM_AUTO
+	 */
+	public void setAnimStyle(int animStyle) {
+		this.animStyle = animStyle;
+	}
+
+	/**
+	 * Add action item
+	 * 
+	 * @param action  {@link ActionItem} object
+	 */
+	public void addActionItem(ActionItem action) {
+		actionList.add(action); 
+	}
+	
+	/**
+	 * Show popup window. Popup is automatically positioned, on top or bottom of anchor view.
+	 * 
+	 */
+	public void show () {
+		preShow();
+		
+		int xPos, yPos;
+		
+		int[] location 		= new int[2];
+	
+		mAnchor.getLocationOnScreen(location);
+
+		Rect anchorRect 	= new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] 
+		                	+ mAnchor.getHeight());
+
+		createActionList();
+		
+		root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+		root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+	
+		int rootHeight 		= root.getMeasuredHeight();
+		int rootWidth		= root.getMeasuredWidth();
+		
+		int screenWidth 	= mWManager.getDefaultDisplay().getWidth();
+		int screenHeight	= mWManager.getDefaultDisplay().getHeight();
+		
+		//automatically get X coord of popup (top left)
+		if ((anchorRect.left + rootWidth) > screenWidth) {
+			xPos = anchorRect.left - (rootWidth-mAnchor.getWidth());
+		} else {
+			if (mAnchor.getWidth() > rootWidth) {
+				xPos = anchorRect.centerX() - (rootWidth/2);
+			} else {
+				xPos = anchorRect.left;
+			}
+		}
+		
+		int dyTop			= anchorRect.top;
+		int dyBottom		= screenHeight - anchorRect.bottom;
+
+		boolean onTop		= (dyTop > dyBottom) ? true : false;
+
+		if (onTop) {
+			if (rootHeight > dyTop) {
+				yPos 			= 15;
+				LayoutParams l 	= scroller.getLayoutParams();
+				l.height		= dyTop - mAnchor.getHeight();
+			} else {
+				yPos = anchorRect.top - rootHeight;
+			}
+		} else {
+			yPos = anchorRect.bottom;
+			
+			if (rootHeight > dyBottom) { 
+				LayoutParams l 	= scroller.getLayoutParams();
+				l.height		= dyBottom;
+			}
+		}
+		
+		showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), anchorRect.centerX()-xPos);
+		
+		setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);
+		
+		mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xPos, yPos);
+	}
+	
+	/**
+	 * Set animation style
+	 * 
+	 * @param screenWidth screen width
+	 * @param requestedX distance from left edge
+	 * @param onTop flag to indicate where the popup should be displayed. Set TRUE if displayed on top of anchor view
+	 * 		  and vice versa
+	 */
+	private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) {
+		int arrowPos = requestedX - mArrowUp.getMeasuredWidth()/2;
+
+		switch (animStyle) {
+		case ANIM_GROW_FROM_LEFT:
+			mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);
+			break;
+					
+		case ANIM_GROW_FROM_RIGHT:
+			mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);
+			break;
+					
+		case ANIM_GROW_FROM_CENTER:
+			mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);
+		break;
+			
+		case ANIM_REFLECT:
+			mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect : R.style.Animations_PopDownMenu_Reflect);
+		break;
+		
+		case ANIM_AUTO:
+			if (arrowPos <= screenWidth/4) {
+				mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);
+			} else if (arrowPos > screenWidth/4 && arrowPos < 3 * (screenWidth/4)) {
+				mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);
+			} else {
+				mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);
+			}
+					
+			break;
+		}
+	}
+	
+	/**
+	 * Create action list
+	 */
+	private void createActionList() {
+		View view;
+		String title;
+		Drawable icon;
+		OnClickListener listener;
+	
+		for (int i = 0; i < actionList.size(); i++) {
+			title 		= actionList.get(i).getTitle();
+			icon 		= actionList.get(i).getIcon();
+			listener	= actionList.get(i).getOnClickListerner();
+	
+			view 		= getActionItem(title, icon, listener);
+		
+			view.setFocusable(true);
+			view.setClickable(true);
+			 
+			mTrack.addView(view);
+		}
+	}
+	
+	/**
+	 * Get action item {@link View}
+	 * 
+	 * @param title action item title
+	 * @param icon {@link Drawable} action item icon
+	 * @param listener {@link View.OnClickListener} action item listener
+	 * @return action item {@link View}
+	 */
+	private View getActionItem(String title, Drawable icon, OnClickListener listener) {
+		LinearLayout container	= (LinearLayout) inflater.inflate(R.layout.action_item, null);
+		
+		ImageView img			= (ImageView) container.findViewById(R.id.icon);
+		TextView text			= (TextView) container.findViewById(R.id.title);
+		
+		if (icon != null) {
+			img.setImageDrawable(icon);
+		}
+		
+		if (title != null) {			
+			text.setText(title);
+		}
+		
+		if (listener != null) {
+			container.setOnClickListener(listener);
+		}
+
+		return container;
+	}
+	
+	/**
+	 * Show arrow
+	 * 
+	 * @param whichArrow arrow type resource id
+	 * @param requestedX distance from left screen
+	 */
+	private void showArrow(int whichArrow, int requestedX) {
+        final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;
+        final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;
+
+        final int arrowWidth = mArrowUp.getMeasuredWidth();
+
+        showArrow.setVisibility(View.VISIBLE);
+        
+        ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams)showArrow.getLayoutParams();
+       
+        param.leftMargin = requestedX - arrowWidth / 2;
+        
+        hideArrow.setVisibility(View.INVISIBLE);
+    }
+}

+ 238 - 0
src/eu/alefzero/owncloud/authenticator/AccountAuthenticator.java

@@ -0,0 +1,238 @@
+package eu.alefzero.owncloud.authenticator;
+
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class AccountAuthenticator extends AbstractAccountAuthenticator {
+  public static final String OPTIONS_USERNAME = "username";
+  public static final String OPTIONS_PASSWORD = "password";
+  public static final String OPTIONS_FILE_LIST_SYNC_ENABLED = "filelist";
+  public static final String OPTIONS_PINNED_FILE_SYNC_ENABLED = "pinned";
+  
+  public static final String ACCOUNT_TYPE = "owncloud";
+  public static final String AUTH_TOKEN_TYPE = "org.owncloud";
+  
+  public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";
+  public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";
+  public static final String KEY_LOGIN_OPTIONS = "loginOptions";
+  public static final String KEY_ACCOUNT = "account";
+  public static final String KEY_OC_URL = "oc_url";
+  
+  private Context mContext;
+  
+  public AccountAuthenticator(Context context) {
+    super(context);
+    mContext = context;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Bundle addAccount(AccountAuthenticatorResponse response,
+      String accountType, String authTokenType, String[] requiredFeatures,
+      Bundle options) throws NetworkErrorException {
+    Log.i(getClass().getName(), "Adding account with type " + accountType + 
+                                " and auth token " + authTokenType);
+    try {
+      validateAccountType(accountType);
+      //validateAuthTokenType(authTokenType);
+      validateRequiredFeatures(requiredFeatures);
+    } catch (AuthenticatorException e) {
+      e.printStackTrace();
+      return e.getFailureBundle();
+    }
+    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+    intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+    intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);
+    intent.putExtra(KEY_LOGIN_OPTIONS, options);
+    
+    setIntentFlags(intent);
+    Log.i(getClass().getName(), intent.toString());
+    final Bundle bundle = new Bundle();
+    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+    return bundle;
+  }
+   
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Bundle confirmCredentials(AccountAuthenticatorResponse response,
+      Account account, Bundle options) throws NetworkErrorException {
+    try {
+      validateAccountType(account.type);
+    } catch (AuthenticatorException e) {
+      return e.getFailureBundle();
+    }
+    Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+    intent.putExtra(KEY_ACCOUNT, account);
+    intent.putExtra(KEY_LOGIN_OPTIONS, options);
+    
+    setIntentFlags(intent);
+    
+    Bundle resultBundle = new Bundle();
+    resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);
+    return resultBundle;
+  }
+
+  @Override
+  public Bundle editProperties(AccountAuthenticatorResponse response,
+                               String accountType) {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  public Bundle getAuthToken(AccountAuthenticatorResponse response,
+      Account account, String authTokenType, Bundle options)
+      throws NetworkErrorException {
+    Log.i(getClass().getName(), "Getting authToken");
+    try {
+      validateAccountType(account.type);
+      validateAuthTokenType(authTokenType);
+    } catch (AuthenticatorException e) {
+      Log.w(getClass().getName(), "Validating failded in getAuthToken");
+      return e.getFailureBundle();
+    }
+    final AccountManager am = AccountManager.get(mContext);
+    final String password = am.getPassword(account);
+    if (password != null) {
+      final Bundle result = new Bundle();
+      result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+      result.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
+      result.putString(AccountManager.KEY_AUTHTOKEN, password);
+      return result;
+    }
+    
+    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+    intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+    intent.putExtra(KEY_LOGIN_OPTIONS, options);
+    intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name);
+    
+    final Bundle bundle = new Bundle();
+    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+    return bundle;
+  }
+
+  @Override
+  public String getAuthTokenLabel(String authTokenType) {
+    return null;
+  }
+
+  @Override
+  public Bundle hasFeatures(AccountAuthenticatorResponse response,
+      Account account, String[] features) throws NetworkErrorException {
+    final Bundle result = new Bundle();
+    result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+    return result;
+  }
+
+  @Override
+  public Bundle updateCredentials(AccountAuthenticatorResponse response,
+      Account account, String authTokenType, Bundle options)
+      throws NetworkErrorException {
+    final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+    intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+    intent.putExtra(KEY_ACCOUNT, account);
+    intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
+    intent.putExtra(KEY_LOGIN_OPTIONS, options);
+    setIntentFlags(intent);
+    
+    final Bundle bundle = new Bundle();
+    bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+    return bundle;
+  }
+
+  @Override
+  public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
+      Account account) throws NetworkErrorException {
+    return super.getAccountRemovalAllowed(response, account);
+  }
+  
+  private void setIntentFlags(Intent intent) {
+    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+    intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+    intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
+    intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+    intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
+  }
+  
+  private void validateAccountType(String type) throws UnsupportedAccountTypeException {
+     if (!type.equals(ACCOUNT_TYPE)) {
+       throw new UnsupportedAccountTypeException();
+     }
+  }  
+
+  private void validateAuthTokenType(String authTokenType) throws UnsupportedAuthTokenTypeException {
+    if (!authTokenType.equals(AUTH_TOKEN_TYPE)) {
+      throw new UnsupportedAuthTokenTypeException();
+    }
+  }
+
+  private void validateRequiredFeatures(String[] requiredFeatures) throws UnsupportedFeaturesException {
+    // TODO
+  }
+  
+  private void validateCreaditials(String username, String password, String path) throws AccessDeniedException {
+    
+  }
+  
+  public static class AuthenticatorException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private Bundle mFailureBundle;
+    public AuthenticatorException(int code, String errorMsg) {
+      mFailureBundle = new Bundle();
+      mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);
+      mFailureBundle.putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
+    }
+    
+    public Bundle getFailureBundle() {
+      return mFailureBundle;
+    }
+  }
+  
+  public static class UnsupportedAccountTypeException extends AuthenticatorException {
+    private static final long serialVersionUID = 1L;
+
+    public UnsupportedAccountTypeException() {
+      super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "Unsupported account type");
+    }
+  }
+ 
+  public static class UnsupportedAuthTokenTypeException extends AuthenticatorException {
+    private static final long serialVersionUID = 1L;
+
+    public UnsupportedAuthTokenTypeException() {
+      super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "Unsupported auth token type");
+    }
+  }
+  
+  public static class UnsupportedFeaturesException extends AuthenticatorException {
+    public static final long serialVersionUID = 1L;
+    
+    public UnsupportedFeaturesException() {
+      super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "Unsupported features");
+    }
+  }
+  
+  public static class AccessDeniedException extends AuthenticatorException {
+    public AccessDeniedException(int code, String errorMsg) {
+      super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");
+    }
+
+    private static final long serialVersionUID = 1L;
+    
+  }
+}

+ 41 - 0
src/eu/alefzero/owncloud/authenticator/AccountAuthenticatorService.java

@@ -0,0 +1,41 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud.authenticator;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class AccountAuthenticatorService extends Service {
+
+  private AccountAuthenticator mAuthenticator;
+  static final public String ACCOUNT_TYPE = "owncloud"; 
+  
+  @Override
+  public void onCreate() {
+    super.onCreate();
+    mAuthenticator = new AccountAuthenticator(this);
+  }
+  
+  @Override
+  public IBinder onBind(Intent intent) {
+    return mAuthenticator.getIBinder();
+  }
+
+}

+ 189 - 0
src/eu/alefzero/owncloud/authenticator/AuthUtils.java

@@ -0,0 +1,189 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud.authenticator;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.methods.HttpHead;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+
+public class AuthUtils {
+  public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";
+  public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";
+  
+  private static String mResultMsg = "";
+  
+  public static boolean authenticate(URL url, String username, String password,
+                                     Handler handler, Context context) {
+    String strippedPath = url.toString().endsWith("/") ?
+                          url.toString().substring(0, url.toString().length()-1) :
+                          url.toString();
+    String webdatPath = strippedPath + WEBDAV_PATH_2_0;
+    URL complete_url = null;
+    try {
+      complete_url = new URL(webdatPath);
+    } catch (MalformedURLException e) {
+      // should never happend
+      sendResult(false, handler, context, "URL error");
+      return false;
+    }
+    
+    // version 2.0 success
+    if (tryGetWebdav(complete_url, username, password, handler, context)) {
+      sendResult(true, handler, context, complete_url.toString());
+      return true;
+    }
+    
+    if (mResultMsg.equals("401")) {
+       sendResult(false, handler, context, "Invalid login or/and password");
+       return false;
+    }
+    
+    if (!mResultMsg.equals("404")) {
+      sendResult(false, handler, context, "Server error: " + mResultMsg);
+      return false;
+    }
+    
+    webdatPath = strippedPath + WEBDAV_PATH_1_2;
+    try {
+      complete_url = new URL(webdatPath);
+    } catch (MalformedURLException e) {
+      // should never happend
+      sendResult(false, handler, context, "URL error");
+      return false;
+    }
+    
+    // version 1.2 success
+    if (tryGetWebdav(complete_url, username, password, handler, context)) {
+      sendResult(true, handler, context, complete_url.toString());
+      return true;
+    }
+    
+    if (mResultMsg.equals("401")) {
+      sendResult(false, handler, context, "Invalid login or/and password");
+      return false;
+    }
+    
+    if (mResultMsg.equals("404")) {
+      sendResult(false, handler, context, "Wrong path given");
+      return false;
+    }
+    
+    sendResult(false, handler, context, "Server error: " + mResultMsg);
+    return false;
+  }
+
+  public static boolean tryGetWebdav(URL url, String username, String pwd,
+                                     Handler handler, Context context) {
+    DefaultHttpClient c = new DefaultHttpClient();
+    c.getCredentialsProvider().setCredentials(
+        new AuthScope(url.getHost(), (url.getPort() == -1)?80:url.getPort()), 
+        new UsernamePasswordCredentials(username, pwd));
+    
+    BasicHttpContext localcontext  = new BasicHttpContext();
+    BasicScheme basicAuth = new BasicScheme();
+
+    localcontext.setAttribute("preemptive-auth", basicAuth);
+    HttpHost targetHost = new HttpHost(url.getHost(), (url.getPort() == -1)
+        ? 80
+        : url.getPort(), (url.getProtocol() == "https") ? "https" : "http");
+    HttpHead httpget = new HttpHead(url.toString());
+    HttpResponse response = null;
+    try {
+      response = c.execute(targetHost, httpget, localcontext);
+    } catch (ClientProtocolException e1) {
+      sendResult(false, handler, context, "Protocol error: "
+          + e1.getLocalizedMessage());
+      return false;
+    } catch (UnknownHostException e1) {
+      mResultMsg = "Unknowh host: " + e1.getLocalizedMessage();
+      return false;
+    } catch (IOException e1) {
+      mResultMsg = "Error: " + e1.getLocalizedMessage();
+      return false;
+    }
+    String status = response.getStatusLine().toString();
+    status = status.split(" ")[1];
+    Log.i("AuthUtils", "Status returned: " + status);
+    if (status.equals("200")) {
+      return true;
+    } else if (status.equals("404")) {
+      mResultMsg = "404";
+      return false;
+    } else if (status.equals("401")) {
+      mResultMsg = "401";
+      return false;
+    }
+    mResultMsg = status;
+    return false;
+  }
+  
+  public static Thread performOnBackgroundThread(final Runnable r) {
+    final Thread t = new Thread() {
+      @Override
+      public void run() {
+        try {
+          r.run();
+        } finally {}
+      }
+    };
+    t.start();
+    return t;
+  }
+  
+  public static void sendResult(final Boolean result,
+                                final Handler handler,
+                                final Context context,
+                                final String message) {
+    if (handler == null || context == null) {
+      return;
+    }
+    handler.post(new Runnable() {
+      public void run() {
+        ((AuthenticatorActivity) context).onAuthenticationResult(result, message); 
+      }
+    });
+  }
+  
+  public static Thread attemptAuth(final URL url, final String username,
+                                   final String password, final Handler handler,
+                                   final Context context) {
+    final Runnable r = new Runnable() {
+      
+      public void run() {
+        authenticate(url, username, password, handler, context);
+      }
+    };
+    return performOnBackgroundThread(r);
+  }
+}

+ 176 - 0
src/eu/alefzero/owncloud/authenticator/AuthenticatorActivity.java

@@ -0,0 +1,176 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud.authenticator;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorActivity;
+import android.accounts.AccountManager;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+import android.widget.Toast;
+import eu.alefzero.owncloud.R;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+
+public class AuthenticatorActivity extends AccountAuthenticatorActivity {
+  private Thread mAuthThread;
+  private final Handler mHandler = new Handler();
+  
+  public static final String PARAM_USERNAME = "param_Username";
+  public static final String PARAM_HOSTNAME = "param_Hostname";
+  
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+    setContentView(R.layout.account_setup);
+    if (getIntent().hasExtra(PARAM_USERNAME)) {
+      String username =  getIntent().getStringExtra(PARAM_HOSTNAME);
+      TextView host_text, user_text;
+      host_text = (TextView) findViewById(R.id.host_URL);
+      user_text = (TextView) findViewById(R.id.account_username);
+      host_text.setText(host_text.getText() + username.substring(username.lastIndexOf('@')));
+      user_text.setText(user_text.getText() + username.substring(0, username.lastIndexOf('@')-1));
+    }
+  }
+
+  @Override
+  protected Dialog onCreateDialog(int id) {
+    final ProgressDialog dialog = new ProgressDialog(this);
+    dialog.setMessage("Trying to login");
+    dialog.setIndeterminate(true);
+    dialog.setCancelable(true);
+    dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+      public void onCancel(DialogInterface dialog) {
+        Log.i(getClass().getName(), "Login canceled");
+        if (mAuthThread != null) {
+          mAuthThread.interrupt();
+          finish();
+        }
+      }
+    });
+    return dialog;
+  }
+  
+  public void onAuthenticationResult(boolean result, String message) {
+    if (result) {
+      TextView username_text = (TextView) findViewById(R.id.account_username),
+               password_text = (TextView) findViewById(R.id.account_password);
+
+      URL url = null;
+      try {
+        url = new URL(message);
+      } catch (MalformedURLException e) {
+        // should never happend
+        Log.e(getClass().getName(), "Malformed URL: " + message);
+        return;
+      }
+      
+      Account account = new Account(username_text.getText().toString() + "@" + url.getHost(), AccountAuthenticator.ACCOUNT_TYPE);
+      AccountManager accManager = AccountManager.get(this);
+      accManager.addAccountExplicitly(account, password_text.getText().toString(),null);
+      
+      final Intent intent = new Intent();
+      intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);
+      intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
+      intent.putExtra(AccountManager.KEY_AUTHTOKEN, AccountAuthenticator.ACCOUNT_TYPE);
+      accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL, url.toString());
+      setAccountAuthenticatorResult(intent.getExtras());
+      setResult(RESULT_OK, intent);
+      Bundle bundle = new Bundle();
+      bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+      getContentResolver().startSync(ProviderTableMeta.CONTENT_URI, bundle);
+      
+      dismissDialog(0);
+      finish();
+    } else {
+      Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+      dismissDialog(0);
+    }
+  }
+  
+  public void onCancelClick(View view) {
+    Log.i(getClass().getName(), "Account creating canceled");
+    this.finish();
+  }
+  
+  public void onOkClick(View view) {
+    TextView url_text = (TextView) findViewById(R.id.host_URL);
+    TextView username_text = (TextView) findViewById(R.id.account_username);
+    TextView password_text = (TextView) findViewById(R.id.account_password);
+    Log.i(getClass().getName(), "OK clicked");
+    boolean hasErrors = false;
+    
+    URL uri = null;
+    if (url_text.getText().toString().trim().length() == 0) {
+      url_text.setTextColor(Color.RED);
+      hasErrors = true; 
+    } else {
+      url_text.setTextColor(Color.BLACK);
+    }
+    try {
+      String url_str = url_text.getText().toString();
+      if (!url_str.startsWith("http://") &&
+          !url_str.startsWith("https://")) {
+        url_str = "http://" + url_str;
+      }
+      uri = new URL(url_str);
+    } catch (MalformedURLException e) {
+      url_text.setTextColor(Color.RED);
+      e.printStackTrace();
+      hasErrors = true;
+    }
+    
+    if (username_text.getText().toString().contains(" ") ||
+        username_text.getText().toString().trim().length() == 0) {
+      username_text.setTextColor(Color.RED);
+      hasErrors = true;
+    } else {
+      username_text.setTextColor(Color.BLACK);
+    }
+    
+    if (password_text.getText().toString().trim().length() == 0) {
+      password_text.setTextColor(Color.RED);
+      hasErrors = true;
+    } else {
+      password_text.setTextColor(Color.BLACK);
+    }
+    if (hasErrors) {
+      return;
+    }
+    showDialog(0);
+    mAuthThread = AuthUtils.attemptAuth(uri,
+                                        username_text.getText().toString(),
+                                        password_text.getText().toString(),
+                                        mHandler,
+                                        AuthenticatorActivity.this);
+  }
+}

+ 210 - 0
src/eu/alefzero/owncloud/cp.java

@@ -0,0 +1,210 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud;
+
+import java.util.HashMap;
+
+import eu.alefzero.owncloud.db.ProviderMeta;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.text.TextUtils;
+
+public class cp extends ContentProvider {
+
+  private DataBaseHelper mDbHelper;
+  
+  private static HashMap<String, String> mProjectionMap;
+  static {
+    mProjectionMap = new HashMap<String, String>();
+    mProjectionMap.put(ProviderTableMeta._ID,
+                       ProviderTableMeta._ID);
+    mProjectionMap.put(ProviderTableMeta.FILE_PARENT,
+                       ProviderTableMeta.FILE_PARENT);
+    mProjectionMap.put(ProviderTableMeta.FILE_PATH,
+                       ProviderTableMeta.FILE_PATH);
+    mProjectionMap.put(ProviderTableMeta.FILE_NAME,
+                       ProviderTableMeta.FILE_NAME);
+    mProjectionMap.put(ProviderTableMeta.FILE_CREATION,
+                       ProviderTableMeta.FILE_CREATION);
+    mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,
+                       ProviderTableMeta.FILE_MODIFIED);
+    mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,
+                       ProviderTableMeta.FILE_CONTENT_LENGTH);
+    mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,
+                       ProviderTableMeta.FILE_CONTENT_TYPE);
+    mProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,
+                       ProviderTableMeta.FILE_STORAGE_PATH);
+  }
+  
+  private static final int SINGLE_FILE = 1;
+  private static final int DIRECTORY = 2;
+  private static final int ROOT_DIRECTORY = 3;
+  private static final UriMatcher mUriMatcher;
+  static {
+    mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+    mUriMatcher.addURI(ProviderMeta.AUTHORITY, "/", ROOT_DIRECTORY);
+    mUriMatcher.addURI(ProviderMeta.AUTHORITY, "file/", SINGLE_FILE);
+    mUriMatcher.addURI(ProviderMeta.AUTHORITY, "file/#", SINGLE_FILE);
+    mUriMatcher.addURI(ProviderMeta.AUTHORITY, "dir/#", DIRECTORY);
+  }
+  
+  private static final String TAG = "OC_ContentProvider";
+  
+  @Override
+  public int delete(Uri uri, String where, String[] whereArgs) {
+    SQLiteDatabase db = mDbHelper.getWritableDatabase();
+    int count = 0;
+    switch (mUriMatcher.match(uri)) {
+      case SINGLE_FILE:
+        count = db.delete(ProviderTableMeta.DB_NAME,
+                          ProviderTableMeta._ID + "=" + uri.getPathSegments().get(1)
+                          + (!TextUtils.isEmpty(where)?" AND (" + where +")" : ""),
+                          whereArgs);
+        break;
+      case ROOT_DIRECTORY:
+        count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
+        break;
+      default:
+        throw new IllegalArgumentException("Unknown uri: " + uri.toString());
+    }
+    getContext().getContentResolver().notifyChange(uri, null);
+    return count;
+  }
+
+  @Override
+  public String getType(Uri uri) {
+    switch (mUriMatcher.match(uri)) {
+      case ROOT_DIRECTORY:
+        return ProviderTableMeta.CONTENT_TYPE;
+      case SINGLE_FILE:
+        return ProviderTableMeta.CONTENT_TYPE_ITEM;
+      default:
+        throw new IllegalArgumentException("Unknown Uri id." + uri.toString());
+    }
+  }
+
+  @Override
+  public Uri insert(Uri uri, ContentValues values) {
+    if (mUriMatcher.match(uri) != SINGLE_FILE) {
+      throw new IllegalArgumentException("Unknown uri id: " + uri);
+    }
+    
+    SQLiteDatabase db = mDbHelper.getWritableDatabase();
+    long rowId = db.insert(ProviderTableMeta.DB_NAME, ProviderTableMeta.FILE_NAME, values);
+    if (rowId > 0) {
+      Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
+      getContext().getContentResolver().notifyChange(insertedFileUri, null);
+      return insertedFileUri;
+    }
+    throw new SQLException("ERROR " + uri);
+  }
+
+  @Override
+  public boolean onCreate() {
+    mDbHelper = new DataBaseHelper(getContext());
+    return true;
+  }
+
+  @Override
+  public Cursor query(Uri uri, String[] projection, String selection,
+      String[] selectionArgs, String sortOrder) {
+    SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
+        
+    sqlQuery.setTables(ProviderTableMeta.DB_NAME);
+    sqlQuery.setProjectionMap(mProjectionMap);
+    
+    switch (mUriMatcher.match(uri)) {
+      case ROOT_DIRECTORY:
+        sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + " is null");
+        break;
+      case DIRECTORY:
+        sqlQuery.appendWhere(ProviderTableMeta.FILE_PARENT + "="+uri.getPathSegments().get(1));
+        break;
+      case SINGLE_FILE:
+        if (uri.getPathSegments().size() > 1) {
+          sqlQuery.appendWhere(ProviderTableMeta._ID + "=" +
+                               uri.getPathSegments().get(1));
+        }
+        break;
+      default:
+        throw new IllegalArgumentException("Unknown uri id: " + uri);
+    }
+    
+    String order;
+    if (TextUtils.isEmpty(sortOrder)) {
+      order = ProviderTableMeta.DEFAULT_SORT_ORDER;
+    } else {
+      order = sortOrder;
+    }
+    
+    SQLiteDatabase db = mDbHelper.getReadableDatabase();
+    Cursor c = sqlQuery.query(db, projection, selection, selectionArgs, null, null, order);
+    
+    c.setNotificationUri(getContext().getContentResolver(), uri);
+
+    return c;
+  }
+
+  @Override
+  public int update(Uri uri, ContentValues values, String selection,
+      String[] selectionArgs) {
+    return mDbHelper.getWritableDatabase().update(ProviderTableMeta.DB_NAME, values, selection, selectionArgs);
+  }
+
+  class DataBaseHelper extends SQLiteOpenHelper {
+
+    public DataBaseHelper(Context context) {
+      super(context, ProviderMeta.DB_NAME, null, ProviderMeta.DB_VERSION);
+      
+    }
+    
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+      db.execSQL("CREATE TABLE " + ProviderTableMeta.DB_NAME + "(" +
+                 ProviderTableMeta._ID + " INTEGER PRIMARY KEY, " +
+                 ProviderTableMeta.FILE_NAME + " TEXT, " +
+                 ProviderTableMeta.FILE_PATH + " TEXT, " +
+                 ProviderTableMeta.FILE_PARENT + " INTEGER, " +
+                 ProviderTableMeta.FILE_CREATION + " INTEGER, " +
+                 ProviderTableMeta.FILE_MODIFIED + " INTEGER, " +
+                 ProviderTableMeta.FILE_CONTENT_TYPE + " TEXT, " +
+                 ProviderTableMeta.FILE_CONTENT_LENGTH + " INTEGER, " +
+                 ProviderTableMeta.FILE_STORAGE_PATH + " TEXT, " +
+                 ProviderTableMeta.FILE_ACCOUNT_OWNER + " TEXT);");
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+      
+    }
+    
+  }
+  
+}

+ 59 - 0
src/eu/alefzero/owncloud/db/ProviderMeta.java

@@ -0,0 +1,59 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+package eu.alefzero.owncloud.db;
+
+import android.net.Uri;
+import android.provider.BaseColumns;
+
+public class ProviderMeta {
+
+  public static final String AUTHORITY = "org.owncloud";
+  public static final String DB_FILE = "owncloud.db";
+  public static final String DB_NAME = "filelist";
+  public static final int DB_VERSION = 1;
+  
+  private ProviderMeta() { }
+  
+  static public class ProviderTableMeta implements BaseColumns {
+    public static final String DB_NAME = "filelist";
+    public static final Uri CONTENT_URI =  
+      Uri.parse("content://" + AUTHORITY + "/");
+    public static final Uri CONTENT_URI_FILE =
+      Uri.parse("content://" + AUTHORITY + "/file");
+    public static final Uri CONTENT_URI_DIR =
+      Uri.parse("content://" + AUTHORITY + "/dir");
+
+    public static final String CONTENT_TYPE =
+      "vnd.android.cursor.dir/vnd.owncloud.file";
+    public static final String CONTENT_TYPE_ITEM =
+      "vnd.android.cursor.item/vnd.owncloud.file";
+    
+    public static final String FILE_PARENT = "parent";
+    public static final String FILE_NAME = "filename";
+    public static final String FILE_CREATION = "created";
+    public static final String FILE_MODIFIED = "modified";
+    public static final String FILE_CONTENT_LENGTH = "content_length";
+    public static final String FILE_CONTENT_TYPE = "content_type";
+    public static final String FILE_STORAGE_PATH = "media_path";
+    public static final String FILE_PATH = "path";
+    public static final String FILE_ACCOUNT_OWNER = "file_owner";
+    
+    public static final String DEFAULT_SORT_ORDER = FILE_NAME + " asc";
+
+  }
+}

+ 225 - 0
src/eu/alefzero/owncloud/syncadapter/SyncAdapter.java

@@ -0,0 +1,225 @@
+/* ownCloud Android client application
+ *   Copyright (C) 2011  Bartek Przybylski
+ *
+ *   This program is free software: you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation, either version 3 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package eu.alefzero.owncloud.syncadapter;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.LinkedList;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.HttpContext;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import eu.alefzero.owncloud.authenticator.AccountAuthenticator;
+import eu.alefzero.owncloud.db.ProviderMeta.ProviderTableMeta;
+import eu.alefzero.webdav.HttpPropFind;
+import eu.alefzero.webdav.TreeNode;
+import eu.alefzero.webdav.WebdavUtils;
+import eu.alefzero.webdav.TreeNode.NodeProperty;
+
+/**
+ * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
+ * platform ContactOperations provider.
+ */
+public class SyncAdapter extends AbstractThreadedSyncAdapter {
+    private static final String TAG = "SyncAdapter";
+
+    private final AccountManager mAccountManager;
+    private Account mAccount;
+    private ContentProviderClient mContentProvider;
+    private final Context mContext;
+
+    private Date mLastUpdated;
+
+    public SyncAdapter(Context context, boolean autoInitialize) {
+        super(context, autoInitialize);
+        mContext = context;
+        mAccountManager = AccountManager.get(context);
+    }
+    
+    @Override
+    public synchronized void onPerformSync(Account account, Bundle extras, String authority,
+        ContentProviderClient provider, SyncResult syncResult) {
+      mAccount = account;
+      mContentProvider = provider;
+      try {
+        String username = account.name.split("@")[0];
+        String password = mAccountManager.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE, true);
+        if (mAccountManager.getUserData(account, AccountAuthenticator.KEY_OC_URL) == null) {
+          throw new UnknownHostException();
+        }
+        Uri uri = Uri.parse(mAccountManager.getUserData(account, AccountAuthenticator.KEY_OC_URL));
+        Log.i(TAG, "Syncing owncloud account: " + account.name + " on url: " + uri.toString());
+        
+        DefaultHttpClient client = new DefaultHttpClient();
+        client.getCredentialsProvider().setCredentials(
+            new AuthScope(uri.getHost(), (uri.getPort() == -1)?80:uri.getPort()),
+            new UsernamePasswordCredentials(username, password));
+        client.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
+          public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
+            // TODO: change keep alive straategy basing on response: ie forbidden/not found/etc
+            // should have keep alive 0
+            // default return: 5s
+            return 5 * 1000;
+          }
+        });
+        
+        BasicHttpContext httpContext = new BasicHttpContext();
+        BasicScheme basicAuth = new BasicScheme();
+        httpContext.setAttribute("preemptive-auth", basicAuth);
+        HttpHost targetHost = new HttpHost(uri.getHost(), (uri.getPort() == -1)
+            ? 80
+            : uri.getPort(), (uri.getScheme() == "https") ? "https" : "http");
+        
+        HttpPropFind query = new HttpPropFind(uri.toString());
+        query.setHeader("Content-type", "text/xml");
+        query.setHeader("User-Agent", "Android-ownCloud");
+        HttpEntity entity = new StringEntity(WebdavUtils.prepareXmlForPropFind());
+        query.setEntity(entity);
+        HttpResponse response = client.execute(targetHost, query, httpContext);
+        /*try {
+          mContentProvider.delete(ProviderTableMeta.CONTENT_URI,
+                                  ProviderTableMeta.FILE_NAME + " LIKE '%' AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER +"=?"
+                                  , new String[]{account.name});
+        } catch (RemoteException e) {
+          e.printStackTrace();
+          return;
+        }*/
+        TreeNode root = new TreeNode();
+        root.setProperty(TreeNode.NodeProperty.NAME, "/");
+        parseResponse(response, uri, client, targetHost, httpContext, root.getChildList());
+        
+        commitToDatabase(root, null);
+        
+      } catch (OperationCanceledException e) {
+        e.printStackTrace();
+      } catch (AuthenticatorException e) {
+        syncResult.stats.numAuthExceptions++;
+        e.printStackTrace();
+      } catch (IOException e) {
+        syncResult.stats.numIoExceptions++;
+        e.printStackTrace();
+      } catch (RemoteException e) {
+        e.printStackTrace();
+      }
+    }
+    
+    private void commitToDatabase(TreeNode root, String parentId) throws RemoteException {
+      for (TreeNode n : root.getChildList()) {
+        Log.d(TAG, n.toString());
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, n.getProperty(NodeProperty.CONTENT_LENGTH));
+        cv.put(ProviderTableMeta.FILE_MODIFIED, n.getProperty(NodeProperty.LAST_MODIFIED_DATE));
+        cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, n.getProperty(NodeProperty.RESOURCE_TYPE));
+        cv.put(ProviderTableMeta.FILE_PARENT, parentId);
+        
+        String name = n.getProperty(NodeProperty.NAME),
+               path = n.getProperty(NodeProperty.PATH);
+        Cursor c = mContentProvider.query(ProviderTableMeta.CONTENT_URI_FILE,
+                                          null,
+                                          ProviderTableMeta.FILE_NAME+"=? AND " + ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
+                                          new String[]{name, path, mAccount.name},
+                                          null);
+        if (c.moveToFirst()) {
+          mContentProvider.update(ProviderTableMeta.CONTENT_URI,
+                                  cv,
+                                  ProviderTableMeta._ID+"=?",
+                                  new String[]{c.getString(c.getColumnIndex(ProviderTableMeta._ID))});
+          Log.d(TAG, "ID of: "+name+":"+c.getString(c.getColumnIndex(ProviderTableMeta._ID)));
+        } else {
+          cv.put(ProviderTableMeta.FILE_NAME, n.getProperty(NodeProperty.NAME));
+          cv.put(ProviderTableMeta.FILE_PATH, n.getProperty(NodeProperty.PATH));
+          cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+          Uri entry = mContentProvider.insert(ProviderTableMeta.CONTENT_URI_FILE, cv);
+          Log.d(TAG, "Inserting new entry " + path + name);
+          c = mContentProvider.query(entry, null, null, null, null);
+          c.moveToFirst();
+        }
+        if (n.getProperty(NodeProperty.RESOURCE_TYPE).equals("DIR")) {
+          commitToDatabase(n, c.getString(c.getColumnIndex(ProviderTableMeta._ID)));
+        }
+      }
+      // clean removed files
+      String[] selection = new String[root.getChildList().size()+2];
+      selection[0] = mAccount.name;
+      selection[1] = parentId;
+      String qm = "";
+      for (int i = 2; i < selection.length-1; ++i) {
+        qm += "?,";
+        selection[i] = root.getChildList().get(i-2).getProperty(NodeProperty.NAME);
+      }
+      if (selection.length >= 3) {
+        selection[selection.length-1] = root.getChildrenNames()[selection.length-3];
+        qm += "?";
+      }
+      for (int i = 0; i < selection.length; ++i) {
+        Log.d(TAG,selection[i]+"");
+      }
+      Log.d(TAG,"Removing files "+ parentId);
+      mContentProvider.delete(ProviderTableMeta.CONTENT_URI,
+                              ProviderTableMeta.FILE_ACCOUNT_OWNER+"=? AND " + ProviderTableMeta.FILE_PARENT + (parentId==null?" IS ":"=")+"? AND " + ProviderTableMeta.FILE_NAME + " NOT IN ("+qm+")",
+                              selection);
+    }
+
+    private void parseResponse(HttpResponse resp, Uri uri, DefaultHttpClient client, HttpHost targetHost, BasicHttpContext httpContext, LinkedList<TreeNode> insertList) throws IOException {
+      boolean skipFirst = true;
+      for (TreeNode n :WebdavUtils.parseResponseToNodes(resp.getEntity().getContent())) {
+        String path = n.stripPathFromFilename(uri.getPath());
+        if (skipFirst) {
+          skipFirst = false;
+          continue;
+        }
+        insertList.add(n);
+        
+        if (!TextUtils.isEmpty(n.getProperty(NodeProperty.NAME)) &&
+            n.getProperty(NodeProperty.RESOURCE_TYPE).equals("DIR")) {
+          HttpPropFind method = new HttpPropFind(uri.getPath() + path + n.getProperty(NodeProperty.NAME).replace(" ", "%20") + "/");
+          Log.i(TAG, uri.getPath() + path + n.getProperty(NodeProperty.NAME).replace(" ", "%20") + "/");
+          Log.i(TAG, method.getRequestLine().toString());
+          HttpResponse response = client.execute(targetHost, method, httpContext);
+          parseResponse(response, uri, client, targetHost, httpContext, n.getChildList());
+        }
+      }
+    }
+}

+ 31 - 0
src/eu/alefzero/owncloud/syncadapter/SyncService.java

@@ -0,0 +1,31 @@
+
+package eu.alefzero.owncloud.syncadapter;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class SyncService extends Service {
+    private static final Object sSyncAdapterLock = new Object();
+    private static SyncAdapter sSyncAdapter = null;
+
+    /*
+     * {@inheritDoc}
+     */
+    @Override
+    public void onCreate() {
+        synchronized (sSyncAdapterLock) {
+            if (sSyncAdapter == null) {
+                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
+            }
+        }
+    }
+
+    /*
+     * {@inheritDoc}
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        return sSyncAdapter.getSyncAdapterBinder();
+    }
+}

+ 19 - 0
src/eu/alefzero/webdav/HttpMkCol.java

@@ -0,0 +1,19 @@
+package eu.alefzero.webdav;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+
+public class HttpMkCol extends HttpEntityEnclosingRequestBase {
+
+  public final static String METHOD_NAME = "MKCOL";
+  
+  public HttpMkCol(final String uri) {
+    setURI(URI.create(uri));
+  }
+
+  @Override
+  public String getMethod() {
+    return METHOD_NAME;
+  }
+}

+ 32 - 0
src/eu/alefzero/webdav/HttpPropFind.java

@@ -0,0 +1,32 @@
+package eu.alefzero.webdav;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.protocol.HTTP;
+
+public class HttpPropFind extends HttpEntityEnclosingRequestBase {
+
+  public final static String METHOD_NAME = "PROPFIND";
+  
+  public HttpPropFind(final URI uri) {
+    super();
+    setURI(uri);
+  }
+
+  public HttpPropFind(final String uri) {
+    this.setDepth("1");
+    setURI(URI.create(uri));
+    this.setHeader(HTTP.CONTENT_TYPE, "text/xml" + HTTP.CHARSET_PARAM + HTTP.UTF_8.toLowerCase());
+  }
+  
+  @Override
+  public String getMethod() {
+    return METHOD_NAME;
+  }
+  
+  public void setDepth(String depth) {
+    this.setHeader("Depth", depth);
+  }
+  
+}

Неке датотеке нису приказане због велике количине промена