--- /dev/null
+<?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>
--- /dev/null
+# 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
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+-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 *;
+}
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<set xmlns:android="http://schemas.android.com/apk/res/android">\r
+ <scale\r
+ android:fromXScale="0.3" android:toXScale="1.0"\r
+ android:fromYScale="0.3" android:toYScale="1.0"\r
+ android:pivotX="100%" android:pivotY="50%"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+ <alpha\r
+ android:interpolator="@android:anim/decelerate_interpolator"\r
+ android:fromAlpha="0.0" android:toAlpha="1.0"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+</set>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<set xmlns:android="http://schemas.android.com/apk/res/android">\r
+ <scale\r
+ android:fromXScale="0.3" android:toXScale="1.0"\r
+ android:fromYScale="0.3" android:toYScale="1.0"\r
+ android:pivotX="100%" android:pivotY="0%"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+ <alpha\r
+ android:interpolator="@android:anim/decelerate_interpolator"\r
+ android:fromAlpha="0.0" android:toAlpha="1.0"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+</set>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<set xmlns:android="http://schemas.android.com/apk/res/android">\r
+ <scale\r
+ android:fromXScale="1.0" android:toXScale="0.3"\r
+ android:fromYScale="1.0" android:toYScale="0.3"\r
+ android:pivotX="100%" android:pivotY="0%"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+ <alpha\r
+ android:interpolator="@android:anim/accelerate_interpolator"\r
+ android:fromAlpha="1.0" android:toAlpha="0.0"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+</set>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<set xmlns:android="http://schemas.android.com/apk/res/android">\r
+ <scale\r
+ android:fromXScale="1.0" android:toXScale="0.3"\r
+ android:fromYScale="1.0" android:toYScale="0.3"\r
+ android:pivotX="100%" android:pivotY="100%"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+ <alpha\r
+ android:interpolator="@android:anim/accelerate_interpolator"\r
+ android:fromAlpha="1.0" android:toAlpha="0.0"\r
+ android:duration="@android:integer/config_shortAnimTime"\r
+ />\r
+</set>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<selector xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:dither="true">\r
+\r
+ <item \r
+ android:state_window_focused="false"\r
+ android:drawable="@android:color/transparent" />\r
+ <item \r
+ android:state_pressed="true"\r
+ android:drawable="@drawable/action_item_selected"/>\r
+ <item \r
+ android:state_focused="true"\r
+ android:drawable="@drawable/action_item_selected"/>\r
+ <item\r
+ android:drawable="@android:color/transparent"/>\r
+ \r
+</selector>
\ No newline at end of file
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<LinearLayout\r
+ xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:orientation="horizontal"\r
+ android:layout_width="fill_parent"\r
+ android:layout_height="wrap_content" \r
+ android:clickable="true"\r
+ android:focusable="true"\r
+ android:background="@drawable/action_item_btn">\r
+ \r
+ <ImageView\r
+ android:id="@+id/icon" \r
+ android:layout_width="wrap_content" \r
+ android:layout_height="wrap_content"/>\r
+\r
+ <TextView \r
+ android:id="@+id/title"\r
+ android:layout_width="fill_parent" \r
+ android:layout_height="fill_parent" \r
+ android:gravity="center_vertical"\r
+ android:paddingLeft="5dip"\r
+ android:paddingRight="10dip"\r
+ android:text="Chart"\r
+ android:textColor="#fff"/>\r
+ \r
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<RelativeLayout \r
+ xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ >\r
+ \r
+ <ScrollView \r
+ android:id="@+id/scroller"\r
+ android:layout_marginTop="16dip"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:background="@drawable/popup"\r
+ android:fadingEdgeLength="5dip"\r
+ android:scrollbars="none">\r
+ \r
+ <LinearLayout\r
+ android:id="@+id/tracks"\r
+ android:orientation="vertical"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_weight="1"\r
+ android:padding="10dip"/>\r
+ \r
+ </ScrollView >\r
+ \r
+ <ImageView\r
+ android:id="@+id/arrow_up"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:src="@drawable/arrow_up" />\r
+ \r
+ <ImageView\r
+ android:id="@+id/arrow_down"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_marginTop="-4dip"\r
+ android:src="@drawable/arrow_down" android:layout_below="@+id/scroller"/>\r
+\r
+</RelativeLayout>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
\ No newline at end of file
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <EditTextPreference></EditTextPreference>
+ <EditTextPreference></EditTextPreference>
+</PreferenceScreen>
--- /dev/null
+<?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"
+/>
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+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) {
+ }
+ }
+}
--- /dev/null
+/* 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");
+
+ }
+}
--- /dev/null
+/* 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");
+ }
+ }
+
+}
--- /dev/null
+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
+
+ }
+}
--- /dev/null
+/* 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();
+ }
+
+
+
+}
\ No newline at end of file
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+
+ }
+
+ }
+
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+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();
+ }
+
+}
--- /dev/null
+package eu.alefzero.owncloud;\r
+\r
+import android.content.Context;\r
+\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.Drawable;\r
+\r
+import android.widget.ImageView;\r
+import android.widget.TextView;\r
+import android.widget.LinearLayout;\r
+import android.widget.ScrollView;\r
+\r
+import android.view.Gravity;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+import android.view.ViewGroup.LayoutParams;\r
+import android.view.ViewGroup;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Popup window, shows action list as icon and text like the one in Gallery3D app. \r
+ * \r
+ * @author Lorensius. W. T\r
+ */\r
+public class QuickAction extends CustomPopup {\r
+ private final View root;\r
+ private final ImageView mArrowUp;\r
+ private final ImageView mArrowDown;\r
+ private final LayoutInflater inflater;\r
+ private final Context context;\r
+ \r
+ protected static final int ANIM_GROW_FROM_LEFT = 1;\r
+ protected static final int ANIM_GROW_FROM_RIGHT = 2;\r
+ protected static final int ANIM_GROW_FROM_CENTER = 3;\r
+ protected static final int ANIM_REFLECT = 4;\r
+ protected static final int ANIM_AUTO = 5;\r
+\r
+ private int animStyle;\r
+ private ViewGroup mTrack;\r
+ private ScrollView scroller;\r
+ private ArrayList<ActionItem> actionList;\r
+ \r
+ /**\r
+ * Constructor\r
+ * \r
+ * @param anchor {@link View} on where the popup window should be displayed\r
+ */\r
+ public QuickAction(View anchor) {\r
+ super(anchor);\r
+ \r
+ actionList = new ArrayList<ActionItem>();\r
+ context = anchor.getContext();\r
+ inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+ \r
+ root = (ViewGroup) inflater.inflate(R.layout.popup, null);\r
+ \r
+ mArrowDown = (ImageView) root.findViewById(R.id.arrow_down);\r
+ mArrowUp = (ImageView) root.findViewById(R.id.arrow_up);\r
+ \r
+ setContentView(root);\r
+ \r
+ mTrack = (ViewGroup) root.findViewById(R.id.tracks);\r
+ scroller = (ScrollView) root.findViewById(R.id.scroller);\r
+ animStyle = ANIM_AUTO;\r
+ }\r
+\r
+ /**\r
+ * Set animation style\r
+ * \r
+ * @param animStyle animation style, default is set to ANIM_AUTO\r
+ */\r
+ public void setAnimStyle(int animStyle) {\r
+ this.animStyle = animStyle;\r
+ }\r
+\r
+ /**\r
+ * Add action item\r
+ * \r
+ * @param action {@link ActionItem} object\r
+ */\r
+ public void addActionItem(ActionItem action) {\r
+ actionList.add(action); \r
+ }\r
+ \r
+ /**\r
+ * Show popup window. Popup is automatically positioned, on top or bottom of anchor view.\r
+ * \r
+ */\r
+ public void show () {\r
+ preShow();\r
+ \r
+ int xPos, yPos;\r
+ \r
+ int[] location = new int[2];\r
+ \r
+ mAnchor.getLocationOnScreen(location);\r
+\r
+ Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] \r
+ + mAnchor.getHeight());\r
+\r
+ createActionList();\r
+ \r
+ root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));\r
+ root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
+ \r
+ int rootHeight = root.getMeasuredHeight();\r
+ int rootWidth = root.getMeasuredWidth();\r
+ \r
+ int screenWidth = mWManager.getDefaultDisplay().getWidth();\r
+ int screenHeight = mWManager.getDefaultDisplay().getHeight();\r
+ \r
+ //automatically get X coord of popup (top left)\r
+ if ((anchorRect.left + rootWidth) > screenWidth) {\r
+ xPos = anchorRect.left - (rootWidth-mAnchor.getWidth());\r
+ } else {\r
+ if (mAnchor.getWidth() > rootWidth) {\r
+ xPos = anchorRect.centerX() - (rootWidth/2);\r
+ } else {\r
+ xPos = anchorRect.left;\r
+ }\r
+ }\r
+ \r
+ int dyTop = anchorRect.top;\r
+ int dyBottom = screenHeight - anchorRect.bottom;\r
+\r
+ boolean onTop = (dyTop > dyBottom) ? true : false;\r
+\r
+ if (onTop) {\r
+ if (rootHeight > dyTop) {\r
+ yPos = 15;\r
+ LayoutParams l = scroller.getLayoutParams();\r
+ l.height = dyTop - mAnchor.getHeight();\r
+ } else {\r
+ yPos = anchorRect.top - rootHeight;\r
+ }\r
+ } else {\r
+ yPos = anchorRect.bottom;\r
+ \r
+ if (rootHeight > dyBottom) { \r
+ LayoutParams l = scroller.getLayoutParams();\r
+ l.height = dyBottom;\r
+ }\r
+ }\r
+ \r
+ showArrow(((onTop) ? R.id.arrow_down : R.id.arrow_up), anchorRect.centerX()-xPos);\r
+ \r
+ setAnimationStyle(screenWidth, anchorRect.centerX(), onTop);\r
+ \r
+ mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xPos, yPos);\r
+ }\r
+ \r
+ /**\r
+ * Set animation style\r
+ * \r
+ * @param screenWidth screen width\r
+ * @param requestedX distance from left edge\r
+ * @param onTop flag to indicate where the popup should be displayed. Set TRUE if displayed on top of anchor view\r
+ * and vice versa\r
+ */\r
+ private void setAnimationStyle(int screenWidth, int requestedX, boolean onTop) {\r
+ int arrowPos = requestedX - mArrowUp.getMeasuredWidth()/2;\r
+\r
+ switch (animStyle) {\r
+ case ANIM_GROW_FROM_LEFT:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);\r
+ break;\r
+ \r
+ case ANIM_GROW_FROM_RIGHT:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);\r
+ break;\r
+ \r
+ case ANIM_GROW_FROM_CENTER:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);\r
+ break;\r
+ \r
+ case ANIM_REFLECT:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect : R.style.Animations_PopDownMenu_Reflect);\r
+ break;\r
+ \r
+ case ANIM_AUTO:\r
+ if (arrowPos <= screenWidth/4) {\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Left : R.style.Animations_PopDownMenu_Left);\r
+ } else if (arrowPos > screenWidth/4 && arrowPos < 3 * (screenWidth/4)) {\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center : R.style.Animations_PopDownMenu_Center);\r
+ } else {\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right : R.style.Animations_PopDownMenu_Right);\r
+ }\r
+ \r
+ break;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Create action list\r
+ */\r
+ private void createActionList() {\r
+ View view;\r
+ String title;\r
+ Drawable icon;\r
+ OnClickListener listener;\r
+ \r
+ for (int i = 0; i < actionList.size(); i++) {\r
+ title = actionList.get(i).getTitle();\r
+ icon = actionList.get(i).getIcon();\r
+ listener = actionList.get(i).getOnClickListerner();\r
+ \r
+ view = getActionItem(title, icon, listener);\r
+ \r
+ view.setFocusable(true);\r
+ view.setClickable(true);\r
+ \r
+ mTrack.addView(view);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Get action item {@link View}\r
+ * \r
+ * @param title action item title\r
+ * @param icon {@link Drawable} action item icon\r
+ * @param listener {@link View.OnClickListener} action item listener\r
+ * @return action item {@link View}\r
+ */\r
+ private View getActionItem(String title, Drawable icon, OnClickListener listener) {\r
+ LinearLayout container = (LinearLayout) inflater.inflate(R.layout.action_item, null);\r
+ \r
+ ImageView img = (ImageView) container.findViewById(R.id.icon);\r
+ TextView text = (TextView) container.findViewById(R.id.title);\r
+ \r
+ if (icon != null) {\r
+ img.setImageDrawable(icon);\r
+ }\r
+ \r
+ if (title != null) { \r
+ text.setText(title);\r
+ }\r
+ \r
+ if (listener != null) {\r
+ container.setOnClickListener(listener);\r
+ }\r
+\r
+ return container;\r
+ }\r
+ \r
+ /**\r
+ * Show arrow\r
+ * \r
+ * @param whichArrow arrow type resource id\r
+ * @param requestedX distance from left screen\r
+ */\r
+ private void showArrow(int whichArrow, int requestedX) {\r
+ final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;\r
+ final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;\r
+\r
+ final int arrowWidth = mArrowUp.getMeasuredWidth();\r
+\r
+ showArrow.setVisibility(View.VISIBLE);\r
+ \r
+ ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams)showArrow.getLayoutParams();\r
+ \r
+ param.leftMargin = requestedX - arrowWidth / 2;\r
+ \r
+ hideArrow.setVisibility(View.INVISIBLE);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+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;
+
+ }
+}
--- /dev/null
+/* 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();
+ }
+
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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) {
+
+ }
+
+ }
+
+}
--- /dev/null
+/* 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";
+
+ }
+}
--- /dev/null
+/* 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());
+ }
+ }
+ }
+}
--- /dev/null
+
+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();
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+package eu.alefzero.webdav;
+
+import java.net.URI;
+
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+
+public class HttpPropPatch extends HttpEntityEnclosingRequestBase {
+
+ public static final String METHOD_NAME = "PROPPATCH";
+
+ public HttpPropPatch(URI uri) {
+ super();
+ setURI(uri);
+ }
+
+ public HttpPropPatch(final String uri) {
+ super();
+ setURI(URI.create(uri));
+ }
+
+ @Override
+ public String getMethod() {
+ return METHOD_NAME;
+ }
+
+}
--- /dev/null
+/* 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.webdav;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map.Entry;
+
+import org.w3c.dom.Document;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+public class TreeNode {
+ public enum NodeProperty {
+ NAME,
+ PARENT,
+ PATH,
+ RESOURCE_TYPE,
+ CREATE_DATE,
+ LAST_MODIFIED_DATE,
+ CONTENT_LENGTH
+ }
+
+ private LinkedList<TreeNode> mChildren;
+
+ public TreeNode() {
+ propertyMap_ = new HashMap<NodeProperty, String>();
+ mChildren = new LinkedList<TreeNode>();
+ }
+
+ public void setProperty(NodeProperty propertyName, String propertyValue) {
+ propertyMap_.put(propertyName, propertyValue);
+ }
+
+ public String getProperty(NodeProperty propertyName) {
+ return propertyMap_.get(propertyName);
+ }
+
+ void refreshData(Document document) {
+ throw new RuntimeException("Unimplemented refreshData");
+ }
+
+ public String toString() {
+ String str = "TreeNode {";
+ for (Entry<NodeProperty, String> e : propertyMap_.entrySet()) {
+ str += e.getKey() + ": " + e.getValue() + ",";
+ }
+ str += "}";
+ return str;
+ }
+
+ private HashMap<NodeProperty, String> propertyMap_;
+
+ public String stripPathFromFilename(String oc_path) {
+ if (propertyMap_.containsKey(NodeProperty.NAME)) {
+ String name = propertyMap_.get(NodeProperty.NAME);
+ name = name.replace(oc_path, "");
+ String path = "";
+ if (name.endsWith("/")) {
+ name = name.substring(0, name.length()-1);
+ }
+ path = name.substring(0, name.lastIndexOf('/')+1);
+ name = name.substring(name.lastIndexOf('/')+1);
+ name = name.replace("%20", " ");
+ if (TextUtils.isEmpty(name)) {
+ name = "/";
+ }
+
+ propertyMap_.remove(NodeProperty.NAME);
+ propertyMap_.put(NodeProperty.NAME, name);
+ propertyMap_.remove(NodeProperty.PATH);
+ propertyMap_.put(NodeProperty.PATH, path);
+ Log.i("TreeNode", toString());
+ return path;
+ }
+ return null;
+ }
+
+ public LinkedList<TreeNode> getChildList() {
+ return mChildren;
+ }
+
+ public String[] getChildrenNames() {
+ String[] names = new String[mChildren.size()];
+ for (int i = 0; i < mChildren.size(); ++i) {
+ names[i] = mChildren.get(i).getProperty(NodeProperty.NAME);
+ }
+ return names;
+ }
+
+ public boolean hasChildren() {
+ return !mChildren.isEmpty();
+ }
+}
--- /dev/null
+package eu.alefzero.webdav;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import org.w3c.dom.Document;
+
+import android.util.Xml;
+
+public class TreeNodeContainer extends TreeNode {
+
+ @Override
+ void refreshData(Document document) {
+ ListIterator<TreeNode> iterator = children_.listIterator();
+ while (iterator.hasNext()) {
+ iterator.next().refreshData(document);
+ }
+ }
+
+ private List<TreeNode> children_;
+}
--- /dev/null
+/* 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.webdav;
+
+import java.sql.Date;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import eu.alefzero.webdav.TreeNode.NodeProperty;
+
+import android.util.Xml;
+
+public class TreeNodeFile extends TreeNode {
+
+ public TreeNodeFile() {
+ is_pinned_ = false;
+ pathToDownloadedFile_ = "";
+ lastUpdateDate_ = new Date(1970, 1, 1);
+ }
+
+ @Override
+ void refreshData(Document document) {
+ /*if (is_pinned_) {
+ String fullPath = getProperty(NodeProperty.PATH);
+ if (document.hasChildNodes()) {
+ Node child = document.getFirstChild();
+ do {
+
+ } while ((child = child.getNextSibling()) != null);
+ }
+
+ //TODO: update file
+ }*/
+ }
+
+ private boolean is_pinned_;
+ private String pathToDownloadedFile_;
+ private Date lastUpdateDate_;
+}
--- /dev/null
+/* 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.webdav;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import eu.alefzero.webdav.TreeNode.NodeProperty;
+
+import android.text.Html;
+import android.util.Log;
+
+public class WebdavUtils {
+
+ public static final String RESPONSE = "response";
+ public static final String HREF = "href";
+ public static final String IS_HIDDEN = "ishidden";
+ public static final String RESOURCE_TYPE = "resourcetype";
+ public static final String CONTENT_TYPE = "getcontenttype";
+ public static final String CONTENT_LENGTH = "getcontentlength";
+ public static final String LAST_MODIFIED = "getlastmodified";
+ public static final String LAST_ACCESS = "lastaccessed";
+ public static final String CREATE_DATE = "creationdate";
+
+ public static final String PROPSTAT = "propstat";
+ public static final String STATUS = "status";
+ public static final String PROP = "prop";
+
+ private static final String DAV_NAMESPACE_PREFIX = "DAV:";
+
+ public static final SimpleDateFormat DISPLAY_DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy hh:mm");
+ private static final SimpleDateFormat DATETIME_FORMATS[] = {
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US),
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
+ new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
+ new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)};
+
+ public static String prepareXmlForPropFind() {
+ String ret = "<?xml version=\"1.0\" ?><D:propfind xmlns:D=\"DAV:\"><D:allprop/></D:propfind>";
+ return ret;
+ }
+
+ public static String prepareXmlForPatch() {
+ return "<?xml version=\"1.0\" ?><D:propertyupdate xmlns:D=\"DAV:\"></D:propertyupdate>";
+ }
+
+ private static Date parseResponseDate(String date) {
+ Date returnDate = null;
+ for (int i = 0; i < DATETIME_FORMATS.length; ++i) {
+ try {
+ returnDate = DATETIME_FORMATS[i].parse(date);
+ return returnDate;
+ } catch (ParseException e) {}
+ }
+ return null;
+ }
+
+ private static String determineDAVPrefix(Element e) {
+ for (int i = 0; i < e.getAttributes().getLength(); ++i) {
+ String attrName = e.getAttributes().item(i).getNodeName();
+ if (e.getAttribute(attrName).equals(DAV_NAMESPACE_PREFIX)) {
+ return attrName.substring(attrName.lastIndexOf(':')+1) + ":";
+ }
+ }
+ return null;
+ }
+
+ public static List<TreeNode> parseResponseToNodes(InputStream response) {
+ LinkedList<TreeNode> rList = new LinkedList<TreeNode>();
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder;
+ try {
+ builder = factory.newDocumentBuilder();
+ Document document = builder.parse(response);
+ String davPrefix = determineDAVPrefix(document.getDocumentElement());
+
+ NodeList nodes = document.getElementsByTagName(davPrefix + RESPONSE);
+ Log.i("WebdavUtils", "Parsing " + nodes.getLength() + " response nodes");
+
+ for (int i = 0; i < nodes.getLength(); ++i) {
+ Node currentNode = nodes.item(i);
+ TreeNode resultNode = new TreeNode();
+ parseResourceType(currentNode, resultNode, davPrefix);
+ parseResourceDates(currentNode, resultNode, davPrefix);
+ parseDisplayName(currentNode, resultNode, davPrefix);
+ rList.add(resultNode);
+ }
+
+
+ } catch (ParserConfigurationException e) {
+ e.printStackTrace();
+ } catch (SAXException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return rList;
+ }
+
+ private static void parseDisplayName(Node currentNode, TreeNode resultNode,
+ String davPrefix) {
+ Element currentElement = (Element) currentNode;
+ if (currentElement.getElementsByTagName(davPrefix + HREF).getLength() != 0) {
+ String filepath = currentElement.getElementsByTagName(davPrefix + HREF).item(0).getFirstChild().getNodeValue();
+ resultNode.setProperty(NodeProperty.NAME, filepath);
+ }
+ }
+
+ private static void parseResourceDates(Node currentNode, TreeNode resultNode, String davPrefix) {
+ Element currentElement = (Element)currentNode;
+ if (currentElement.getElementsByTagName(davPrefix + LAST_MODIFIED).getLength() != 0) {
+ Date date = parseResponseDate(
+ currentElement.getElementsByTagName(davPrefix + LAST_MODIFIED).item(0).getFirstChild().getNodeValue());
+ resultNode.setProperty(NodeProperty.LAST_MODIFIED_DATE, DISPLAY_DATE_FORMAT.format(date));
+ }
+ if (currentElement.getElementsByTagName(davPrefix + CREATE_DATE).getLength() != 0) {
+ Date date = parseResponseDate(
+ currentElement.getElementsByTagName(davPrefix + CREATE_DATE).item(0).getFirstChild().getNodeValue());
+ resultNode.setProperty(NodeProperty.CREATE_DATE, DISPLAY_DATE_FORMAT.format(date));
+ }
+ }
+
+ private static void parseResourceType(Node currentNode, TreeNode resultNode, String davPrefix) {
+ Element currentElement = (Element)currentNode;
+ if (currentElement.getElementsByTagName(davPrefix + RESOURCE_TYPE).getLength() != 0 &&
+ currentElement.getElementsByTagName(davPrefix + RESOURCE_TYPE).item(0).hasChildNodes()) {
+ resultNode.setProperty(NodeProperty.RESOURCE_TYPE, "DIR");
+ } else {
+ if (currentElement.getElementsByTagName(davPrefix + CONTENT_TYPE).getLength() != 0) {
+ resultNode.setProperty(NodeProperty.RESOURCE_TYPE,
+ currentElement.getElementsByTagName(davPrefix + CONTENT_TYPE).item(0).getFirstChild().getNodeValue());
+ }
+ if (currentElement.getElementsByTagName(davPrefix + CONTENT_LENGTH).getLength() != 0) {
+ resultNode.setProperty(NodeProperty.CONTENT_LENGTH,
+ currentElement.getElementsByTagName(davPrefix + CONTENT_LENGTH).item(0).getFirstChild().getNodeValue());
+ }
+ }
+ }
+}