You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
-<manifest package="de.mobilcom.debitel.cloud.android"
+<manifest package="com.owncloud.android"
android:versionCode="104006"
android:versionName="1.4.6" xmlns:android="http://schemas.android.com/apk/res/android">
<service android:name=".media.MediaService" />
<activity android:name=".ui.activity.PinCodeActivity" />
- <activity android:name=".extensions.ExtensionsAvailableActivity"></activity>
- <activity android:name=".extensions.ExtensionsListActivity"></activity>
+ <activity android:name="com.owncloud.android.extensions.ExtensionsAvailableActivity"></activity>
+ <activity android:name="com.owncloud.android.extensions.ExtensionsListActivity"></activity>
<activity android:name=".ui.activity.AccountSelectActivity" android:uiOptions="none" android:label="@string/prefs_accounts"></activity>
<activity android:name=".ui.activity.ConflictsResolveActivity"/>
<activity android:name=".ui.activity.GenericExplanationActivity"/>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="de.mobilcom.debitel.cloud.android.workaround.accounts"
+ package="com.owncloud.android.workaround.accounts"
android:versionCode="0100008"
android:versionName="1.0.8" >
--- /dev/null
+/* ownCloud Jelly Bean Workaround for lost credentials
+ * Copyright (C) 2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.workaround.accounts;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+//import android.util.Log;
+
+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();
+ }
+
+
+ public static class AccountAuthenticator extends AbstractAccountAuthenticator {
+
+ 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 AccountAuthenticator(Context context) {
+ super(context);
+ }
+
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response,
+ String accountType, String authTokenType,
+ String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException {
+ //Log.e("WORKAROUND", "Yes, WORKAROUND takes the control here");
+ final Intent intent = new Intent("com.owncloud.android.workaround.accounts.CREATE");
+ 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);
+
+ 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);
+
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+ return bundle;
+ //return getCommonResultBundle();
+ }
+
+
+ @Override
+ public Bundle confirmCredentials(AccountAuthenticatorResponse response,
+ Account account, Bundle options) throws NetworkErrorException {
+ return getCommonResultBundle();
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType) {
+ return getCommonResultBundle();
+ }
+
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options)
+ throws NetworkErrorException {
+ return getCommonResultBundle();
+ }
+
+ @Override
+ public String getAuthTokenLabel(String authTokenType) {
+ return "";
+ }
+
+ @Override
+ public Bundle hasFeatures(AccountAuthenticatorResponse response,
+ Account account, String[] features) throws NetworkErrorException {
+ return getCommonResultBundle();
+ }
+
+ @Override
+ public Bundle updateCredentials(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options)
+ throws NetworkErrorException {
+ return getCommonResultBundle();
+ }
+
+ @Override
+ public Bundle getAccountRemovalAllowed(
+ AccountAuthenticatorResponse response, Account account)
+ throws NetworkErrorException {
+ return super.getAccountRemovalAllowed(response, account);
+ }
+
+ private Bundle getCommonResultBundle() {
+ Bundle resultBundle = new Bundle();
+ resultBundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
+ resultBundle.putString(AccountManager.KEY_ERROR_MESSAGE, "This is just a workaround, not a real account authenticator");
+ return resultBundle;
+ }
+
+ }
+}
+++ /dev/null
-/* ownCloud Jelly Bean Workaround for lost credentials
- * Copyright (C) 2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.workaround.accounts;
-
-import android.accounts.AbstractAccountAuthenticator;
-import android.accounts.Account;
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.accounts.NetworkErrorException;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-//import android.util.Log;
-
-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();
- }
-
-
- public static class AccountAuthenticator extends AbstractAccountAuthenticator {
-
- 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 AccountAuthenticator(Context context) {
- super(context);
- }
-
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse response,
- String accountType, String authTokenType,
- String[] requiredFeatures, Bundle options)
- throws NetworkErrorException {
- //Log.e("WORKAROUND", "Yes, WORKAROUND takes the control here");
- final Intent intent = new Intent("de.mobilcom.debitel.cloud.android.workaround.accounts.CREATE");
- 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);
-
- 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);
-
- final Bundle bundle = new Bundle();
- bundle.putParcelable(AccountManager.KEY_INTENT, intent);
- return bundle;
- //return getCommonResultBundle();
- }
-
-
- @Override
- public Bundle confirmCredentials(AccountAuthenticatorResponse response,
- Account account, Bundle options) throws NetworkErrorException {
- return getCommonResultBundle();
- }
-
- @Override
- public Bundle editProperties(AccountAuthenticatorResponse response,
- String accountType) {
- return getCommonResultBundle();
- }
-
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse response,
- Account account, String authTokenType, Bundle options)
- throws NetworkErrorException {
- return getCommonResultBundle();
- }
-
- @Override
- public String getAuthTokenLabel(String authTokenType) {
- return "";
- }
-
- @Override
- public Bundle hasFeatures(AccountAuthenticatorResponse response,
- Account account, String[] features) throws NetworkErrorException {
- return getCommonResultBundle();
- }
-
- @Override
- public Bundle updateCredentials(AccountAuthenticatorResponse response,
- Account account, String authTokenType, Bundle options)
- throws NetworkErrorException {
- return getCommonResultBundle();
- }
-
- @Override
- public Bundle getAccountRemovalAllowed(
- AccountAuthenticatorResponse response, Account account)
- throws NetworkErrorException {
- return super.getAccountRemovalAllowed(response, account);
- }
-
- private Bundle getCommonResultBundle() {
- Bundle resultBundle = new Bundle();
- resultBundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
- resultBundle.putString(AccountManager.KEY_ERROR_MESSAGE, "This is just a workaround, not a real account authenticator");
- return resultBundle;
- }
-
- }
-}
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
- <groupId>de.mobilcom.debitel.cloud.android</groupId>
+ <groupId>com.owncloud.android</groupId>
<artifactId>owncloud</artifactId>
<version>1.3.21-SNAPSHOT</version>
<packaging>apk</packaging>
\r
</LinearLayout>\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+ <com.owncloud.android.ui.CustomButton\r
android:id="@id/buttonOK"\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
android:orientation="horizontal" >
<!-- 'OK' / 'CANCEL' BUTTONS CHANGE THEIR ORDER FROM ANDROID 4.0 ; THANKS, GOOGLE -->
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/common_cancel" />
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"\r
android:text="@string/auth_unauthorized" />\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+ <com.owncloud.android.ui.CustomButton\r
android:id="@+id/buttonOK"\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
android:layout_margin="5dp"
android:weightSum="1.0" >
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/buttonNo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/common_no"
android:layout_weight="0.5"/>
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/buttonYes"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/failed_upload_all_cb"\r
android:textSize="8sp" />\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+ <com.owncloud.android.ui.CustomButton\r
android:id="@+id/failed_upload_retry_all_btn"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
android:text="@string/failed_upload_headline_retryall_btn"\r
android:textSize="8sp" />\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+ <com.owncloud.android.ui.CustomButton\r
android:id="@+id/failed_upload_delete_all_btn"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
android:layout_height="wrap_content"\r
android:minWidth="100dp"/>\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+ <com.owncloud.android.ui.CustomButton\r
android:id="@+id/failed_uploadactivity_close_button"\r
android:layout_width="fill_parent"\r
android:layout_height="wrap_content"\r
</FrameLayout>
- <de.mobilcom.debitel.cloud.android.media.MediaControlView
+ <com.owncloud.android.media.MediaControlView
android:id="@id/media_controller"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/common_ok" />
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" >
- <de.mobilcom.debitel.cloud.android.ui.ExtendedListView
+ <com.owncloud.android.ui.ExtendedListView
android:id="@+id/list_root"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_height="wrap_content"
android:gravity="bottom">
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/deleteLogHistoryButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/main_wrn_accsetup"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/setup_account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/PassCodeStyle" />\r
</LinearLayout>\r
\r
- <de.mobilcom.debitel.cloud.android.ui.CustomButton android:layout_width="wrap_content"\r
+ <com.owncloud.android.ui.CustomButton android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
android:text="@string/common_cancel"\r
android:textColor="@android:color/black"\r
android:layout_height="wrap_content"
android:gravity="center" >
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/common_cancel" />
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/details_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/ssl_validator_btn_details_see" />
- <de.mobilcom.debitel.cloud.android.ui.CustomButton
+ <com.owncloud.android.ui.CustomButton
android:id="@+id/ok"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_height="wrap_content"
>
- <de.mobilcom.debitel.cloud.android.ui.dialog.SsoWebView
+ <com.owncloud.android.ui.dialog.SsoWebView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/sso_webview"
android:layout_width="match_parent"\r
android:layout_height="0dip"\r
android:layout_weight="1"\r
- class="de.mobilcom.debitel.cloud.android.ui.fragment.LocalFileListFragment" />\r
+ class="com.owncloud.android.ui.fragment.LocalFileListFragment" />\r
<LinearLayout\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
android:gravity="center"\r
android:orientation="horizontal" >\r
-\r <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+\r <com.owncloud.android.ui.CustomButton\r
android:id="@+id/upload_files_btn_cancel"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
android:layout_weight="1"\r
android:text="@string/common_cancel" />\r
-\r <de.mobilcom.debitel.cloud.android.ui.CustomButton\r
+\r <com.owncloud.android.ui.CustomButton\r
android:id="@+id/upload_files_btn_upload"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
</FrameLayout>
<LinearLayout android:id="@+id/linearLayout1"
android:layout_width="fill_parent" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:orientation="vertical">
- <de.mobilcom.debitel.cloud.android.ui.CustomButton android:layout_gravity="bottom" android:layout_height="wrap_content"
+ <com.owncloud.android.ui.CustomButton 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"/>
</LinearLayout>
In dieser Version von Android existiert ein Bug, der nach jedem Neustart eine erneute Eingabe der ownCloud Login-Informationen nötig macht. Um das zu umgehen installieren Sie bitte diese kostenlose Hilfs-App:
</p>
<p style="text-align:center">
- <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
+ <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
</p>
</body>
</html>
Para prevenir la pérdida de las credenciales de sus cuentas ownCloud en cada reinicio, por favor, instale esta app gratuita que evita el problema en Jelly Bean:
</p>
<p style="text-align:center">
- <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
+ <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
</p>
</body>
</html>
To prevent losing your ownCloud account credentials on every reboot, please, install this free helper app to work around the bug in Jelly Bean:
</p>
<p style="text-align:center">
- <a href="http://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
+ <a href="http://play.google.com/store/apps/details?id=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
</p>
</body>
</html>
<string name="oauth2_response_type">code</string> <!-- depends on oauth2_grant_type -->
<!-- values that should be pre-agreed between app and authorization server, but can be loaded without rebuilding the app -->
- <string name="oauth2_client_id">de.mobilcom.debitel.cloud.android</string> <!-- preferable that client decides this -->
+ <string name="oauth2_client_id">com.owncloud.android</string> <!-- preferable that client decides this -->
<string name="oauth2_client_secret"></string> <!-- preferable that client decides this -->
</resources>
<string name="url_imprint">"https://webapi.md.de/about/imprint"</string>
<string name="mail_recommend">"mailto:"</string>
<string name="mail_feedback">"mailto:appservice@cloud.md.de"</string>
- <!-- string name="url_app_download">"https://play.google.com/store/apps/details?id=de.mobilcom.debitel.cloud.android"</string -->
+ <!-- string name="url_app_download">"https://play.google.com/store/apps/details?id=com.owncloud.android"</string -->
</resources>
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android;\r
+\r
+import java.util.Arrays;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+/**\r
+ * A helper class for some string operations.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
+ */\r
+public class DisplayUtils {\r
+ \r
+ //private static String TAG = DisplayUtils.class.getSimpleName(); \r
+ \r
+ private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };\r
+\r
+ private static HashMap<String, String> mimeType2HUmanReadable;\r
+ static {\r
+ mimeType2HUmanReadable = new HashMap<String, String>();\r
+ // images\r
+ mimeType2HUmanReadable.put("image/jpeg", "JPEG image");\r
+ mimeType2HUmanReadable.put("image/jpg", "JPEG image");\r
+ mimeType2HUmanReadable.put("image/png", "PNG image");\r
+ mimeType2HUmanReadable.put("image/bmp", "Bitmap image");\r
+ mimeType2HUmanReadable.put("image/gif", "GIF image");\r
+ mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");\r
+ mimeType2HUmanReadable.put("image/tiff", "TIFF image");\r
+ // music\r
+ mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");\r
+ mimeType2HUmanReadable.put("application/ogg", "OGG music file");\r
+\r
+ }\r
+\r
+ private static final String TYPE_APPLICATION = "application";\r
+ private static final String TYPE_AUDIO = "audio";\r
+ private static final String TYPE_IMAGE = "image";\r
+ private static final String TYPE_TXT = "text";\r
+ private static final String TYPE_VIDEO = "video";\r
+ \r
+ private static final String SUBTYPE_PDF = "pdf";\r
+ private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", \r
+ "vnd.oasis.opendocument.presentation",\r
+ "vnd.oasis.opendocument.spreadsheet",\r
+ "vnd.oasis.opendocument.text"\r
+ };\r
+ private static Set<String> SUBTYPES_DOCUMENT_SET = new HashSet<String>(Arrays.asList(SUBTYPES_DOCUMENT));\r
+ private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"};\r
+ private static final Set<String> SUBTYPES_COMPRESSED_SET = new HashSet<String>(Arrays.asList(SUBTYPES_COMPRESSED));\r
+ \r
+ /**\r
+ * Converts the file size in bytes to human readable output.\r
+ * \r
+ * @param bytes Input file size\r
+ * @return Like something readable like "12 MB"\r
+ */\r
+ public static String bytesToHumanReadable(long bytes) {\r
+ double result = bytes;\r
+ int attachedsuff = 0;\r
+ while (result > 1024 && attachedsuff < sizeSuffixes.length) {\r
+ result /= 1024.;\r
+ attachedsuff++;\r
+ }\r
+ result = ((int) (result * 100)) / 100.;\r
+ return result + " " + sizeSuffixes[attachedsuff];\r
+ }\r
+\r
+ /**\r
+ * Removes special HTML entities from a string\r
+ * \r
+ * @param s Input string\r
+ * @return A cleaned version of the string\r
+ */\r
+ public static String HtmlDecode(String s) {\r
+ /*\r
+ * TODO: Perhaps we should use something more proven like:\r
+ * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29\r
+ */\r
+\r
+ String ret = "";\r
+ for (int i = 0; i < s.length(); ++i) {\r
+ if (s.charAt(i) == '%') {\r
+ ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);\r
+ i += 2;\r
+ } else {\r
+ ret += s.charAt(i);\r
+ }\r
+ }\r
+ return ret;\r
+ }\r
+\r
+ /**\r
+ * Converts MIME types like "image/jpg" to more end user friendly output\r
+ * like "JPG image".\r
+ * \r
+ * @param mimetype MIME type to convert\r
+ * @return A human friendly version of the MIME type\r
+ */\r
+ public static String convertMIMEtoPrettyPrint(String mimetype) {\r
+ if (mimeType2HUmanReadable.containsKey(mimetype)) {\r
+ return mimeType2HUmanReadable.get(mimetype);\r
+ }\r
+ if (mimetype.split("/").length >= 2)\r
+ return mimetype.split("/")[1].toUpperCase() + " file";\r
+ return "Unknown type";\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Returns the resource identifier of an image resource to use as icon associated to a \r
+ * known MIME type.\r
+ * \r
+ * @param mimetype MIME type string.\r
+ * @return Resource identifier of an image resource.\r
+ */\r
+ public static int getResourceId(String mimetype) {\r
+\r
+ if (mimetype == null || "DIR".equals(mimetype)) {\r
+ return R.drawable.ic_menu_archive;\r
+ \r
+ } else {\r
+ String [] parts = mimetype.split("/");\r
+ String type = parts[0];\r
+ String subtype = (parts.length > 1) ? parts[1] : "";\r
+ \r
+ if(TYPE_TXT.equals(type)) {\r
+ return R.drawable.file_doc;\r
+ \r
+ } else if(TYPE_IMAGE.equals(type)) {\r
+ return R.drawable.file_image;\r
+ \r
+ } else if(TYPE_VIDEO.equals(type)) {\r
+ return R.drawable.file_movie;\r
+ \r
+ } else if(TYPE_AUDIO.equals(type)) { \r
+ return R.drawable.file_sound;\r
+ \r
+ } else if(TYPE_APPLICATION.equals(type)) {\r
+ \r
+ if (SUBTYPE_PDF.equals(subtype)) {\r
+ return R.drawable.file_pdf;\r
+ \r
+ } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) {\r
+ return R.drawable.file_doc;\r
+\r
+ } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) {\r
+ return R.drawable.file_zip;\r
+ }\r
+ \r
+ }\r
+ // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed\r
+ }\r
+\r
+ // default icon\r
+ return R.drawable.file;\r
+ }\r
+\r
+ \r
+\r
+ /**\r
+ * Converts Unix time to human readable format\r
+ * @param miliseconds that have passed since 01/01/1970\r
+ * @return The human readable time for the users locale\r
+ */\r
+ public static String unixTimeToHumanReadable(long milliseconds) {\r
+ Date date = new Date(milliseconds);\r
+ return date.toLocaleString();\r
+ }\r
+}\r
--- /dev/null
+package com.owncloud.android;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import android.util.Log;
+
+
+
+public class Log_OC {
+
+
+ private static boolean isEnabled = false;
+ private static File logFile;
+ private static File folder;
+ private static BufferedWriter buf;
+
+ public static void i(String TAG, String message){
+ // Printing the message to LogCat console
+ Log.i(TAG, message);
+ // Write the log message to the file
+ appendLog(TAG+" : "+message);
+ }
+
+ public static void d(String TAG, String message){
+ Log.d(TAG, message);
+ appendLog(TAG + " : " + message);
+ }
+ public static void d(String TAG, String message, Exception e) {
+ Log.d(TAG, message, e);
+ appendLog(TAG + " : " + message + " Exception : "+ e.getStackTrace());
+ }
+ public static void e(String TAG, String message){
+ Log.e(TAG, message);
+ appendLog(TAG + " : " + message);
+ }
+
+ public static void e(String TAG, String message, Throwable e) {
+ Log.e(TAG, message, e);
+ appendLog(TAG+" : " + message +" Exception : " + e.getStackTrace());
+ }
+
+ public static void v(String TAG, String message){
+ Log.v(TAG, message);
+ appendLog(TAG+" : "+ message);
+ }
+
+ public static void w(String TAG, String message) {
+ Log.w(TAG,message);
+ appendLog(TAG+" : "+ message);
+ }
+
+ public static void wtf(String TAG, String message) {
+ Log.wtf(TAG,message);
+ appendLog(TAG+" : "+ message);
+ }
+
+ public static void startLogging(String logPath) {
+ folder = new File(logPath);
+ logFile = new File(folder + File.separator + "log.txt");
+
+ if (!folder.exists()) {
+ folder.mkdirs();
+ }
+ if (logFile.exists()) {
+ logFile.delete();
+ }
+ try {
+ logFile.createNewFile();
+ buf = new BufferedWriter(new FileWriter(logFile, true));
+ isEnabled = true;
+ appendPhoneInfo();
+ }catch (IOException e){
+ e.printStackTrace();
+ }
+ }
+
+ public static void stopLogging() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
+ String currentDateandTime = sdf.format(new Date());
+ if (logFile != null) {
+ logFile.renameTo(new File(folder + File.separator + MainApp.getLogName() + currentDateandTime+".log"));
+
+ isEnabled = false;
+ try {
+ buf.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+
+ private static void appendPhoneInfo() {
+ appendLog("Model : " + android.os.Build.MODEL);
+ appendLog("Brand : " + android.os.Build.BRAND);
+ appendLog("Product : " + android.os.Build.PRODUCT);
+ appendLog("Device : " + android.os.Build.DEVICE);
+ appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME);
+ appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE);
+ }
+
+ private static void appendLog(String text) {
+ if (isEnabled) {
+ try {
+ buf.append(text);
+ buf.newLine();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
+
+
+
+
+
+
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android;
+
+import android.app.Application;
+import android.content.Context;
+/**
+ * Main Application of the project
+ *
+ * Contains methods to build the "static" strings. These strings were before constants in different classes
+ *
+ * @author masensio
+ */
+public class MainApp extends Application {
+
+ private static Context mContext;
+
+ public void onCreate(){
+ super.onCreate();
+ MainApp.mContext = getApplicationContext();
+ }
+
+ public static Context getAppContext() {
+ return MainApp.mContext;
+ }
+
+ // Methods to obtain Strings referring app_name
+ // From AccountAuthenticator
+ // public static final String ACCOUNT_TYPE = "owncloud";
+ public static String getAccountType() {
+ return getAppContext().getResources().getString(R.string.account_type);
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTHORITY = "org.owncloud";
+ public static String getAuthority() {
+ return getAppContext().getResources().getString(R.string.authority);
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTH_TOKEN_TYPE = "org.owncloud";
+ public static String getAuthTokenType() {
+ return getAppContext().getResources().getString(R.string.authority);
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
+ public static String getAuthTokenTypePass() {
+ return getAppContext().getResources().getString(R.string.account_type) + ".password";
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
+ public static String getAuthTokenTypeAccessToken() {
+ return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.access_token";
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
+ public static String getAuthTokenTypeRefreshToken() {
+ return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.refresh_token";
+ }
+
+ // From AccountAuthenticator
+ // public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
+ public static String getAuthTokenTypeSamlSessionCookie() {
+ return getAppContext().getResources().getString(R.string.account_type) + ".saml.web_sso.session_cookie";
+ }
+
+ // From ProviderMeta
+ // public static final String DB_FILE = "owncloud.db";
+ public static String getDBFile() {
+ return getAppContext().getResources().getString(R.string.db_file);
+ }
+
+ // From ProviderMeta
+ // private final String mDatabaseName = "ownCloud";
+ public static String getDBName() {
+ return getAppContext().getResources().getString(R.string.db_name);
+ }
+
+ // data_folder
+ public static String getDataFolder() {
+ return getAppContext().getResources().getString(R.string.data_folder);
+ }
+
+ // log_name
+ public static String getLogName() {
+ return getAppContext().getResources().getString(R.string.log_name);
+ }
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android;\r
+\r
+/**\r
+ * Represents a session to an ownCloud instance\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class OwnCloudSession {\r
+ private String mSessionName;\r
+ private String mSessionUrl;\r
+ private int mEntryId;\r
+\r
+ public OwnCloudSession(String name, String url, int entryId) {\r
+ mSessionName = name;\r
+ mSessionUrl = url;\r
+ mEntryId = entryId;\r
+ }\r
+\r
+ public void setName(String name) {\r
+ mSessionName = name;\r
+ }\r
+\r
+ public String getName() {\r
+ return mSessionName;\r
+ }\r
+\r
+ public void setUrl(String url) {\r
+ mSessionUrl = url;\r
+ }\r
+\r
+ public String getUrl() {\r
+ return mSessionUrl;\r
+ }\r
+\r
+ public int getEntryId() {\r
+ return mEntryId;\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Stack;
+import java.util.Vector;
+
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.ui.CustomButton;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
+import android.view.View;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.EditText;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+
+
+/**
+ * This can be used to upload things to an ownCloud instance.
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
+ private static final String TAG = "ownCloudUploader";
+
+ private Account mAccount;
+ private AccountManager mAccountManager;
+ private Stack<String> mParents;
+ private ArrayList<Parcelable> mStreamsToUpload;
+ private boolean mCreateDir;
+ private String mUploadPath;
+ private DataStorageManager mStorageManager;
+ private OCFile mFile;
+
+ 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 REQUEST_CODE_SETUP_ACCOUNT = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ mParents = new Stack<String>();
+ mParents.add("");
+ if (prepareStreamsToUpload()) {
+ mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
+ Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
+ if (accounts.length == 0) {
+ Log_OC.i(TAG, "No ownCloud account is available");
+ showDialog(DIALOG_NO_ACCOUNT);
+ } else if (accounts.length > 1) {
+ Log_OC.i(TAG, "More then one ownCloud is available");
+ showDialog(DIALOG_MULTIPLE_ACCOUNT);
+ } else {
+ mAccount = accounts[0];
+ mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+ 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(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
+ builder.setCancelable(false);
+ builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
+ @Override
+ 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.provider.Settings.ACTION_ADD_ACCOUNT);
+ intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() });
+ 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() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ });
+ return builder.create();
+ case DIALOG_MULTIPLE_ACCOUNT:
+ CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
+ for (int i = 0; i < ac.length; ++i) {
+ ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name;
+ }
+ builder.setTitle(R.string.common_choose_account);
+ builder.setItems(ac, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which];
+ mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+ populateDirectoryList();
+ }
+ });
+ builder.setCancelable(true);
+ builder.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ dialog.cancel();
+ finish();
+ }
+ });
+ return builder.create();
+ case DIALOG_NO_STREAM:
+ builder.setIcon(android.R.drawable.ic_dialog_alert);
+ builder.setTitle(R.string.uploader_wrn_no_content_title);
+ builder.setMessage(R.string.uploader_wrn_no_content_text);
+ builder.setCancelable(false);
+ builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ 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;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
+ Uploader.this.mCreateDir = true;
+ uploadFiles();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+
+ if (mParents.size() <= 1) {
+ super.onBackPressed();
+ return;
+ } else {
+ mParents.pop();
+ populateDirectoryList();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // click on folder in the list
+ Log_OC.d(TAG, "on item click");
+ Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
+ if (tmpfiles.size() <= 0) return;
+ // filter on dirtype
+ Vector<OCFile> files = new Vector<OCFile>();
+ for (OCFile f : tmpfiles)
+ if (f.isDirectory())
+ files.add(f);
+ if (files.size() < position) {
+ throw new IndexOutOfBoundsException("Incorrect item selected");
+ }
+ mParents.push(files.get(position).getFileName());
+ populateDirectoryList();
+ }
+
+ @Override
+ public void onClick(View v) {
+ // click on button
+ switch (v.getId()) {
+ case R.id.uploader_choose_folder:
+ mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
+ for (String p : mParents)
+ mUploadPath += p + OCFile.PATH_SEPARATOR;
+ Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
+
+ uploadFiles();
+
+ break;
+ default:
+ throw new IllegalArgumentException("Wrong element clicked");
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ Log_OC.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(MainApp.getAuthTokenType());
+ 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() {
+ setContentView(R.layout.uploader_layout);
+
+ String full_path = "";
+ for (String a : mParents)
+ full_path += a + "/";
+
+ Log_OC.d(TAG, "Populating view with content of : " + full_path);
+
+ mFile = mStorageManager.getFileByPath(full_path);
+ if (mFile != null) {
+ Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
+ List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
+ for (OCFile f : files) {
+ HashMap<String, Object> h = new HashMap<String, Object>();
+ if (f.isDirectory()) {
+ h.put("dirname", f.getFileName());
+ data.add(h);
+ }
+ }
+ SimpleAdapter sa = new SimpleAdapter(this,
+ data,
+ R.layout.uploader_list_item_layout,
+ new String[] {"dirname"},
+ new int[] {R.id.textView1});
+ setListAdapter(sa);
+ CustomButton btn = (CustomButton) findViewById(R.id.uploader_choose_folder);
+ btn.setOnClickListener(this);
+ getListView().setOnItemClickListener(this);
+ }
+ }
+
+ private boolean 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);
+ }
+ return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
+ }
+
+ public void uploadFiles() {
+ try {
+ //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+
+ ArrayList<String> local = new ArrayList<String>();
+ ArrayList<String> remote = new ArrayList<String>();
+
+ /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed
+ WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+ // create last directory in path if necessary
+ if (mCreateDir) {
+ wdc.createDirectory(mUploadPath);
+ }
+ */
+
+ // this checks the mimeType
+ for (Parcelable mStream : mStreamsToUpload) {
+
+ Uri uri = (Uri) mStream;
+ if (uri !=null) {
+ if (uri.getScheme().equals("content")) {
+
+ String mimeType = getContentResolver().getType(uri);
+
+ if (mimeType.contains("image")) {
+ String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Images.Media.DATA);
+ String data = c.getString(index);
+ local.add(data);
+ remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
+
+ }
+ else if (mimeType.contains("video")) {
+ String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Video.Media.DATA);
+ String data = c.getString(index);
+ local.add(data);
+ remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
+
+ }
+ else if (mimeType.contains("audio")) {
+ String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
+ Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
+ c.moveToFirst();
+ int index = c.getColumnIndex(Audio.Media.DATA);
+ String data = c.getString(index);
+ local.add(data);
+ remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
+
+ }
+ else {
+ String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
+ // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
+ if (filePath.contains("mnt")) {
+ String splitedFilePath[] = filePath.split("/mnt");
+ filePath = splitedFilePath[1];
+ }
+ final File file = new File(filePath);
+ local.add(file.getAbsolutePath());
+ remote.add(mUploadPath + file.getName());
+ }
+
+ } else if (uri.getScheme().equals("file")) {
+ String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
+ if (filePath.contains("mnt")) {
+ String splitedFilePath[] = filePath.split("/mnt");
+ filePath = splitedFilePath[1];
+ }
+ final File file = new File(filePath);
+ local.add(file.getAbsolutePath());
+ remote.add(mUploadPath + file.getName());
+ }
+ else {
+ throw new SecurityException();
+ }
+ }
+ else {
+ throw new SecurityException();
+ }
+
+ Intent intent = new Intent(getApplicationContext(), FileUploader.class);
+ intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+ intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
+ intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
+ intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
+ startService(intent);
+ finish();
+ }
+
+ } catch (SecurityException e) {
+ String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.authentication;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+
+import android.accounts.*;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.widget.Toast;
+
+
+
+
+/**
+ * Authenticator for ownCloud accounts.
+ *
+ * Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system.
+ *
+ * TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
+ * TODO - review completeness
+ *
+ * @author David A. Velasco
+ */
+public class AccountAuthenticator extends AbstractAccountAuthenticator {
+
+ /**
+ * Is used by android system to assign accounts to authenticators. Should be
+ * used by application and all extensions.
+ */
+ /* These constants are now in MainApp
+ public static final String ACCOUNT_TYPE = "owncloud";
+ public static final String AUTHORITY = "org.owncloud";
+ public static final String AUTH_TOKEN_TYPE = "org.owncloud";
+ public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
+ public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
+ public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
+ public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
+ */
+ 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";
+
+ /**
+ * Value under this key should handle path to webdav php script. Will be
+ * removed and usage should be replaced by combining
+ * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
+ * {@link com.owncloud.android.utils.OwnCloudVersion}
+ *
+ * @deprecated
+ */
+ public static final String KEY_OC_URL = "oc_url";
+ /**
+ * Version should be 3 numbers separated by dot so it can be parsed by
+ * {@link com.owncloud.android.utils.OwnCloudVersion}
+ */
+ public static final String KEY_OC_VERSION = "oc_version";
+ /**
+ * Base url should point to owncloud installation without trailing / ie:
+ * http://server/path or https://owncloud.server
+ */
+ public static final String KEY_OC_BASE_URL = "oc_base_url";
+ /**
+ * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
+ */
+ public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
+ /**
+ * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
+ */
+ public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
+
+ private static final String TAG = AccountAuthenticator.class.getSimpleName();
+
+ private Context mContext;
+
+ private Handler mHandler;
+
+ public AccountAuthenticator(Context context) {
+ super(context);
+ mContext = context;
+ mHandler = new Handler();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Bundle addAccount(AccountAuthenticatorResponse response,
+ String accountType, String authTokenType,
+ String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException {
+ Log_OC.i(TAG, "Adding account with type " + accountType
+ + " and auth token " + authTokenType);
+
+ final Bundle bundle = new Bundle();
+
+ AccountManager accountManager = AccountManager.get(mContext);
+ Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
+
+ if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
+ try {
+ validateAccountType(accountType);
+ } catch (AuthenticatorException e) {
+ Log_OC.e(TAG, "Failed to validate account type " + accountType + ": "
+ + e.getMessage());
+ 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);
+ intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
+
+ setIntentFlags(intent);
+
+ bundle.putParcelable(AccountManager.KEY_INTENT, intent);
+
+ } else {
+
+ // Return an error
+ bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
+ final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name));
+ bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
+
+ mHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ }
+
+ return bundle;
+ }
+
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public Bundle confirmCredentials(AccountAuthenticatorResponse response,\r
+ Account account, Bundle options) throws NetworkErrorException {\r
+ try {\r
+ validateAccountType(account.type);\r
+ } catch (AuthenticatorException e) {\r
+ Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "\r
+ + e.getMessage());\r
+ e.printStackTrace();\r
+ return e.getFailureBundle();\r
+ }\r
+ Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,\r
+ response);\r
+ intent.putExtra(KEY_ACCOUNT, account);\r
+ intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
+\r
+ setIntentFlags(intent);\r
+\r
+ Bundle resultBundle = new Bundle();\r
+ resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
+ return resultBundle;\r
+ }\r
+\r
+ @Override\r
+ public Bundle editProperties(AccountAuthenticatorResponse response,\r
+ String accountType) {\r
+ return null;\r
+ }\r
+\r
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle options)
+ throws NetworkErrorException {
+ /// validate parameters
+ try {
+ validateAccountType(account.type);
+ validateAuthTokenType(authTokenType);
+ } catch (AuthenticatorException e) {
+ Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
+ + e.getMessage());
+ e.printStackTrace();
+ return e.getFailureBundle();
+ }
+
+ /// check if required token is stored
+ final AccountManager am = AccountManager.get(mContext);
+ String accessToken;
+ if (authTokenType.equals(MainApp.getAuthTokenTypePass())) {
+ accessToken = am.getPassword(account);
+ } else {
+ accessToken = am.peekAuthToken(account, authTokenType);
+ }
+ if (accessToken != null) {
+ final Bundle result = new Bundle();
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());
+ result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
+ return result;
+ }
+
+ /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
+ 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.EXTRA_ACCOUNT, account);
+ intent.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
+ intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+
+
+ 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, true);
+ 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_EXCLUDE_FROM_RECENTS);
+ intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ }
+
+ private void validateAccountType(String type)
+ throws UnsupportedAccountTypeException {
+ if (!type.equals(MainApp.getAccountType())) {
+ throw new UnsupportedAccountTypeException();
+ }
+ }
+
+ private void validateAuthTokenType(String authTokenType)\r
+ throws UnsupportedAuthTokenTypeException {\r
+ if (!authTokenType.equals(MainApp.getAuthTokenType()) &&\r
+ !authTokenType.equals(MainApp.getAuthTokenTypePass()) &&\r
+ !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) &&\r
+ !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) &&
+ !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) {\r
+ throw new UnsupportedAuthTokenTypeException();\r
+ }\r
+ }\r
+\r
+ public static class AuthenticatorException extends Exception {\r
+ private static final long serialVersionUID = 1L;\r
+ private Bundle mFailureBundle;\r
+\r
+ public AuthenticatorException(int code, String errorMsg) {\r
+ mFailureBundle = new Bundle();\r
+ mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);\r
+ mFailureBundle\r
+ .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);\r
+ }\r
+\r
+ public Bundle getFailureBundle() {\r
+ return mFailureBundle;\r
+ }\r
+ }\r
+\r
+ public static class UnsupportedAccountTypeException extends\r
+ AuthenticatorException {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public UnsupportedAccountTypeException() {\r
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+ "Unsupported account type");\r
+ }\r
+ }\r
+\r
+ public static class UnsupportedAuthTokenTypeException extends\r
+ AuthenticatorException {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public UnsupportedAuthTokenTypeException() {\r
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+ "Unsupported auth token type");\r
+ }\r
+ }\r
+\r
+ public static class UnsupportedFeaturesException extends\r
+ AuthenticatorException {\r
+ public static final long serialVersionUID = 1L;\r
+\r
+ public UnsupportedFeaturesException() {\r
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
+ "Unsupported features");\r
+ }\r
+ }\r
+\r
+ public static class AccessDeniedException extends AuthenticatorException {\r
+ public AccessDeniedException(int code, String errorMsg) {\r
+ super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");\r
+ }\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ }\r
+}\r
--- /dev/null
+/*
+ * Copyright (C) 2009 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.
+ */
+
+package com.owncloud.android.authentication;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.os.Bundle;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+
+/*
+ * Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator.
+ * If the AbstractAccountAuthenticator needs to use an activity to handle the request then it can have the activity extend
+ * AccountAuthenticatorActivity. The AbstractAccountAuthenticator passes in the response to the intent using the following:
+ * intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+ *
+ * The activity then sets the result that is to be handed to the response via setAccountAuthenticatorResult(android.os.Bundle).
+ * This result will be sent as the result of the request when the activity finishes. If this is never set or if it is set to null
+ * then error AccountManager.ERROR_CODE_CANCELED will be called on the response.
+ */
+
+public class AccountAuthenticatorActivity extends SherlockFragmentActivity {
+
+ private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
+ private Bundle mResultBundle = null;
+
+
+ /**
+ * Set the result that is to be sent as the result of the request that caused this Activity to be launched.
+ * If result is null or this method is never called then the request will be canceled.
+ *
+ * @param result this is returned as the result of the AbstractAccountAuthenticator request
+ */
+ public final void setAccountAuthenticatorResult(Bundle result) {
+ mResultBundle = result;
+ }
+
+ /**
+ * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
+ * icicle is non-zero.
+ * @param icicle the save instance data of this Activity, may be null
+ */
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mAccountAuthenticatorResponse =
+ getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+
+ if (mAccountAuthenticatorResponse != null) {
+ mAccountAuthenticatorResponse.onRequestContinued();
+ }
+ }
+
+ /**
+ * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+ */
+ public void finish() {
+ if (mAccountAuthenticatorResponse != null) {
+ // send the result bundle back if set, otherwise send an error.
+ if (mResultBundle != null) {
+ mAccountAuthenticatorResponse.onResult(mResultBundle);
+ } else {
+ mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED,
+ "canceled");
+ }
+ mAccountAuthenticatorResponse = null;
+ }
+ super.finish();
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.authentication;
+
+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\r
+ * Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.authentication;\r
+\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.utils.OwnCloudVersion;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.accounts.AccountsException;\r
+import android.content.Context;\r
+import android.content.SharedPreferences;\r
+import android.preference.PreferenceManager;\r
+\r
+public class AccountUtils {\r
+ public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
+ public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
+ public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
+ private static final String ODAV_PATH = "/remote.php/odav";\r
+ private static final String SAML_SSO_PATH = "/remote.php/webdav";\r
+ public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
+ public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
+ public static final String STATUS_PATH = "/status.php";\r
+\r
+ /**\r
+ * Can be used to get the currently selected ownCloud {@link Account} in the\r
+ * application preferences.\r
+ * \r
+ * @param context The current application {@link Context}\r
+ * @return The ownCloud {@link Account} currently saved in preferences, or the first \r
+ * {@link Account} available, if valid (still registered in the system as ownCloud \r
+ * account). If none is available and valid, returns null.\r
+ */\r
+ public static Account getCurrentOwnCloudAccount(Context context) {\r
+ Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+ MainApp.getAccountType());\r
+ Account defaultAccount = null;\r
+\r
+ SharedPreferences appPreferences = PreferenceManager\r
+ .getDefaultSharedPreferences(context);\r
+ String accountName = appPreferences\r
+ .getString("select_oc_account", null);\r
+\r
+ // account validation: the saved account MUST be in the list of ownCloud Accounts known by the AccountManager\r
+ if (accountName != null) {\r
+ for (Account account : ocAccounts) {\r
+ if (account.name.equals(accountName)) {\r
+ defaultAccount = account;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ if (defaultAccount == null && ocAccounts.length != 0) {\r
+ // take first account as fallback\r
+ defaultAccount = ocAccounts[0];\r
+ }\r
+\r
+ return defaultAccount;\r
+ }\r
+\r
+ \r
+ public static boolean exists(Account account, Context context) {\r
+ Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+ MainApp.getAccountType());\r
+\r
+ if (account != null && account.name != null) {\r
+ for (Account ac : ocAccounts) {\r
+ if (ac.name.equals(account.name)) {\r
+ return true;\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ \r
+\r
+ /**\r
+ * Checks, whether or not there are any ownCloud accounts setup.\r
+ * \r
+ * @return true, if there is at least one account.\r
+ */\r
+ public static boolean accountsAreSetup(Context context) {\r
+ AccountManager accMan = AccountManager.get(context);\r
+ Account[] accounts = accMan\r
+ .getAccountsByType(MainApp.getAccountType());\r
+ return accounts.length > 0;\r
+ }\r
+ \r
+ \r
+ public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {\r
+ boolean result = false;\r
+ if (accountName != null) {\r
+ Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
+ MainApp.getAccountType());\r
+ boolean found = false;\r
+ for (Account account : ocAccounts) {\r
+ found = (account.name.equals(accountName));\r
+ if (found) {\r
+ SharedPreferences.Editor appPrefs = PreferenceManager\r
+ .getDefaultSharedPreferences(context).edit();\r
+ appPrefs.putString("select_oc_account", accountName);\r
+ \r
+ appPrefs.commit();\r
+ result = true;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @param version version of owncloud\r
+ * @return webdav path for given OC version, null if OC version unknown\r
+ */\r
+ public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {\r
+ if (version != null) {\r
+ if (supportsOAuth) {\r
+ return ODAV_PATH;\r
+ }\r
+ if (supportsSamlSso) {\r
+ return SAML_SSO_PATH;\r
+ }\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
+ return WEBDAV_PATH_4_0;\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
+ || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
+ return WEBDAV_PATH_2_0;\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
+ return WEBDAV_PATH_1_2;\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ /**\r
+ * Returns the proper URL path to access the WebDAV interface of an ownCloud server,\r
+ * according to its version and the authorization method used.\r
+ * \r
+ * @param version Version of ownCloud server.\r
+ * @param authTokenType Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}. \r
+ * @return WebDAV path for given OC version and authorization method, null if OC version is unknown.\r
+ */\r
+ public static String getWebdavPath(OwnCloudVersion version, String authTokenType) {\r
+ if (version != null) {\r
+ if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) {\r
+ return ODAV_PATH;\r
+ }\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) {\r
+ return SAML_SSO_PATH;\r
+ }\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
+ return WEBDAV_PATH_4_0;\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
+ || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
+ return WEBDAV_PATH_2_0;\r
+ if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
+ return WEBDAV_PATH_1_2;\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ /**\r
+ * Constructs full url to host and webdav resource basing on host version\r
+ * @param context\r
+ * @param account\r
+ * @return url or null on failure\r
+ * @throws AccountNotFoundException When 'account' is unknown for the AccountManager\r
+ */\r
+ public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {\r
+ AccountManager ama = AccountManager.get(context);\r
+ String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
+ String strver = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
+ boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
+ boolean supportsSamlSso = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
+ OwnCloudVersion ver = new OwnCloudVersion(strver);\r
+ String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);\r
+\r
+ if (baseurl == null || webdavpath == null) \r
+ throw new AccountNotFoundException(account, "Account not found", null);\r
+ \r
+ return baseurl + webdavpath;\r
+ }\r
+ \r
+ \r
+ public static class AccountNotFoundException extends AccountsException {\r
+ \r
+ /** Generated - should be refreshed every time the class changes!! */\r
+ private static final long serialVersionUID = -9013287181793186830L;\r
+ \r
+ private Account mFailedAccount; \r
+ \r
+ public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {\r
+ super(message, cause);\r
+ mFailedAccount = failedAccount;\r
+ }\r
+ \r
+ public Account getFailedAccount() {\r
+ return mFailedAccount;\r
+ }\r
+ }\r
+\r
+}\r
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.authentication;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.app.AlertDialog;\r
+import android.app.Dialog;\r
+import android.app.ProgressDialog;\r
+import android.content.ContentResolver;\r
+import android.content.DialogInterface;\r
+import android.content.Intent;\r
+import android.content.SharedPreferences;\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.Drawable;\r
+import android.net.Uri;\r
+import android.os.Bundle;\r
+import android.os.Handler;\r
+import android.preference.PreferenceManager;\r
+import android.support.v4.app.Fragment;\r
+import android.text.Editable;\r
+import android.text.InputType;\r
+import android.text.TextWatcher;\r
+import android.view.KeyEvent;\r
+import android.view.MotionEvent;\r
+import android.view.View;\r
+import android.view.View.OnFocusChangeListener;\r
+import android.view.View.OnTouchListener;\r
+import android.view.Window;\r
+import android.view.inputmethod.EditorInfo;\r
+import android.widget.Button;\r
+import android.widget.CheckBox;\r
+import android.widget.EditText;\r
+import android.widget.TextView;\r
+import android.widget.TextView.OnEditorActionListener;\r
+\r
+import com.actionbarsherlock.app.SherlockDialogFragment;\r
+import com.owncloud.android.Log_OC;\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;\r
+import com.owncloud.android.network.OwnCloudClientUtils;\r
+import com.owncloud.android.operations.ExistenceCheckOperation;\r
+import com.owncloud.android.operations.OAuth2GetAccessToken;\r
+import com.owncloud.android.operations.OnRemoteOperationListener;\r
+import com.owncloud.android.operations.OwnCloudServerCheckOperation;\r
+import com.owncloud.android.operations.RemoteOperation;\r
+import com.owncloud.android.operations.RemoteOperationResult;\r
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;\r
+import com.owncloud.android.ui.CustomButton;\r
+import com.owncloud.android.ui.dialog.SamlWebViewDialog;\r
+import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
+import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
+import com.owncloud.android.utils.OwnCloudVersion;\r
+\r
+\r
+import eu.alefzero.webdav.WebdavClient;\r
+\r
+/**\r
+ * This Activity is used to add an ownCloud account to the App\r
+ * \r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
+ */\r
+public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
+implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener, SsoWebViewClientListener{\r
+\r
+ private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
+\r
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
+ public static final String EXTRA_USER_NAME = "USER_NAME";\r
+ public static final String EXTRA_HOST_NAME = "HOST_NAME";\r
+ public static final String EXTRA_ACTION = "ACTION";\r
+ public static final String EXTRA_ENFORCED_UPDATE = "ENFORCE_UPDATE";\r
+\r
+ private static final String KEY_AUTH_MESSAGE_VISIBILITY = "AUTH_MESSAGE_VISIBILITY";\r
+ private static final String KEY_AUTH_MESSAGE_TEXT = "AUTH_MESSAGE_TEXT";\r
+ private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";\r
+ private static final String KEY_OC_VERSION = "OC_VERSION";\r
+ private static final String KEY_ACCOUNT = "ACCOUNT";\r
+ private static final String KEY_SERVER_VALID = "SERVER_VALID";\r
+ private static final String KEY_SERVER_CHECKED = "SERVER_CHECKED";\r
+ private static final String KEY_SERVER_CHECK_IN_PROGRESS = "SERVER_CHECK_IN_PROGRESS"; \r
+ private static final String KEY_SERVER_STATUS_TEXT = "SERVER_STATUS_TEXT";\r
+ private static final String KEY_SERVER_STATUS_ICON = "SERVER_STATUS_ICON";\r
+ private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";\r
+ private static final String KEY_PASSWORD_VISIBLE = "PASSWORD_VISIBLE";\r
+ private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";\r
+ private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";\r
+ private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";\r
+ \r
+ private static final String KEY_OC_USERNAME_EQUALS = "oc_username=";\r
+\r
+ private static final String AUTH_ON = "on";\r
+ private static final String AUTH_OFF = "off";\r
+ private static final String AUTH_OPTIONAL = "optional";\r
+ \r
+ private static final int DIALOG_LOGIN_PROGRESS = 0;\r
+ private static final int DIALOG_SSL_VALIDATOR = 1;\r
+ private static final int DIALOG_CERT_NOT_SAVED = 2;\r
+ private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;\r
+\r
+ public static final byte ACTION_CREATE = 0;\r
+ public static final byte ACTION_UPDATE_TOKEN = 1;\r
+\r
+ private static final String TAG_SAML_DIALOG = "samlWebViewDialog";\r
+ \r
+ private String mHostBaseUrl;\r
+ private OwnCloudVersion mDiscoveredVersion;\r
+\r
+ private String mAuthMessageText;\r
+ private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;\r
+ private boolean mServerIsChecked, mServerIsValid, mIsSslConn;\r
+ private int mAuthStatusText, mAuthStatusIcon; \r
+ private TextView mAuthStatusLayout;\r
+\r
+ private final Handler mHandler = new Handler();\r
+ private Thread mOperationThread;\r
+ private OwnCloudServerCheckOperation mOcServerChkOperation;\r
+ private ExistenceCheckOperation mAuthCheckOperation;\r
+ private RemoteOperationResult mLastSslUntrustedServerResult;\r
+\r
+ private Uri mNewCapturedUriFromOAuth2Redirection;\r
+\r
+ private AccountManager mAccountMgr;\r
+ private boolean mJustCreated;\r
+ private byte mAction;\r
+ private Account mAccount;\r
+\r
+ private TextView mAuthMessage;\r
+ \r
+ private EditText mHostUrlInput;\r
+ private boolean mHostUrlInputEnabled;\r
+ private View mRefreshButton;\r
+\r
+ private String mAuthTokenType;\r
+ \r
+ private EditText mUsernameInput;\r
+ private EditText mPasswordInput;\r
+ \r
+ private CheckBox mOAuth2Check;\r
+ \r
+ private TextView mOAuthAuthEndpointText;\r
+ private TextView mOAuthTokenEndpointText;\r
+ \r
+ private SamlWebViewDialog mSamlDialog;\r
+ \r
+ private View mOkButton;\r
+ \r
+ private String mAuthToken;\r
+ \r
+ private boolean mResumed; // Control if activity is resumed\r
+\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * \r
+ * IMPORTANT ENTRY POINT 1: activity is shown to the user\r
+ */\r
+ @Override\r
+ protected void onCreate(Bundle savedInstanceState) {\r
+ super.onCreate(savedInstanceState);\r
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
+\r
+ /// set view and get references to view elements\r
+ setContentView(R.layout.account_setup);\r
+ mAuthMessage = (TextView) findViewById(R.id.auth_message);\r
+ mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);\r
+ mHostUrlInput.setText(getString(R.string.server_url)); // valid although R.string.server_url is an empty string\r
+ mUsernameInput = (EditText) findViewById(R.id.account_username);\r
+ mPasswordInput = (EditText) findViewById(R.id.account_password);\r
+ mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);\r
+ mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);\r
+ mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
+ mOkButton = (CustomButton) findViewById(R.id.buttonOK);\r
+ mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); \r
+ \r
+ /// set Host Url Input Enabled\r
+ mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input);\r
+ \r
+\r
+ /// complete label for 'register account' button\r
+ Button b = (Button) findViewById(R.id.account_register);\r
+ if (b != null) {\r
+ b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name))); \r
+ }\r
+\r
+// /// complete background of 'OK' button\r
+// boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);\r
+// if (customButtons)\r
+// mOkButton.setBackgroundResource(R.drawable.btn_default);\r
+ \r
+ /// initialization\r
+ mAccountMgr = AccountManager.get(this);\r
+ mNewCapturedUriFromOAuth2Redirection = null;\r
+ mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); \r
+ mAccount = null;\r
+ mHostBaseUrl = "";\r
+ boolean refreshButtonEnabled = false;\r
+ \r
+ // URL input configuration applied\r
+ if (!mHostUrlInputEnabled)\r
+ {\r
+ findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);\r
+ mRefreshButton = findViewById(R.id.centeredRefreshButton);\r
+\r
+ } else {\r
+ mRefreshButton = findViewById(R.id.embeddedRefreshButton);\r
+ }\r
+\r
+ if (savedInstanceState == null) {\r
+ mResumed = false;\r
+ /// connection state and info\r
+ mAuthMessageVisibility = View.GONE;\r
+ mServerStatusText = mServerStatusIcon = 0;\r
+ mServerIsValid = false;\r
+ mServerIsChecked = false;\r
+ mIsSslConn = false;\r
+ mAuthStatusText = mAuthStatusIcon = 0;\r
+\r
+ /// retrieve extras from intent\r
+ mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
+ if (mAccount != null) {\r
+ String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION);\r
+ if (ocVersion != null) {\r
+ mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+ }\r
+ mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL));\r
+ mHostUrlInput.setText(mHostBaseUrl);\r
+ String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
+ mUsernameInput.setText(userName);\r
+ }\r
+ initAuthorizationMethod(); // checks intent and setup.xml to determine mCurrentAuthorizationMethod\r
+ mJustCreated = true;\r
+ \r
+ if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) {\r
+ checkOcServer(); \r
+ }\r
+ \r
+ } else {\r
+ mResumed = true;\r
+ /// connection state and info\r
+ mAuthMessageVisibility = savedInstanceState.getInt(KEY_AUTH_MESSAGE_VISIBILITY);\r
+ mAuthMessageText = savedInstanceState.getString(KEY_AUTH_MESSAGE_TEXT);\r
+ mServerIsValid = savedInstanceState.getBoolean(KEY_SERVER_VALID);\r
+ mServerIsChecked = savedInstanceState.getBoolean(KEY_SERVER_CHECKED);\r
+ mServerStatusText = savedInstanceState.getInt(KEY_SERVER_STATUS_TEXT);\r
+ mServerStatusIcon = savedInstanceState.getInt(KEY_SERVER_STATUS_ICON);\r
+ mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);\r
+ mAuthStatusText = savedInstanceState.getInt(KEY_AUTH_STATUS_TEXT);\r
+ mAuthStatusIcon = savedInstanceState.getInt(KEY_AUTH_STATUS_ICON);\r
+ if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) {\r
+ showPassword();\r
+ }\r
+ \r
+ /// server data\r
+ String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
+ if (ocVersion != null) {\r
+ mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
+ }\r
+ mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);\r
+\r
+ // account data, if updating\r
+ mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);\r
+ mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+ if (mAuthTokenType == null) {\r
+ mAuthTokenType = MainApp.getAuthTokenTypePass();\r
+ \r
+ }\r
+\r
+ // check if server check was interrupted by a configuration change\r
+ if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) {\r
+ checkOcServer();\r
+ } \r
+ \r
+ // refresh button enabled\r
+ refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED);\r
+ \r
+\r
+ }\r
+\r
+ if (mAuthMessageVisibility== View.VISIBLE) {\r
+ showAuthMessage(mAuthMessageText);\r
+ }\r
+ else {\r
+ hideAuthMessage();\r
+ }\r
+ adaptViewAccordingToAuthenticationMethod();\r
+ showServerStatus();\r
+ showAuthStatus();\r
+ \r
+ if (mAction == ACTION_UPDATE_TOKEN) {\r
+ /// lock things that should not change\r
+ mHostUrlInput.setEnabled(false);\r
+ mHostUrlInput.setFocusable(false);\r
+ mUsernameInput.setEnabled(false);\r
+ mUsernameInput.setFocusable(false);\r
+ mOAuth2Check.setVisibility(View.GONE);\r
+ }\r
+ \r
+ //if (mServerIsChecked && !mServerIsValid && mRefreshButtonEnabled) showRefreshButton();\r
+ if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton();\r
+ mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes\r
+\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) || \r
+ !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {\r
+ mOAuth2Check.setVisibility(View.GONE);\r
+ }\r
+\r
+ mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)\r
+\r
+ /// bind view elements to listeners and other friends\r
+ mHostUrlInput.setOnFocusChangeListener(this);\r
+ mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);\r
+ mHostUrlInput.setOnEditorActionListener(this);\r
+ mHostUrlInput.addTextChangedListener(new TextWatcher() {\r
+\r
+ @Override\r
+ public void afterTextChanged(Editable s) {\r
+ if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
+ mOkButton.setEnabled(false);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {\r
+ }\r
+\r
+ @Override\r
+ public void onTextChanged(CharSequence s, int start, int before, int count) {\r
+ if (!mResumed) {\r
+ mAuthStatusIcon = 0;\r
+ mAuthStatusText = 0;\r
+ showAuthStatus(); \r
+ }\r
+ mResumed = false;\r
+ }\r
+ });\r
+ \r
+ mPasswordInput.setOnFocusChangeListener(this);\r
+ mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE);\r
+ mPasswordInput.setOnEditorActionListener(this);
+ mPasswordInput.setOnTouchListener(new RightDrawableOnTouchListener() {\r
+ @Override\r
+ public boolean onDrawableTouch(final MotionEvent event) {\r
+ if (event.getAction() == MotionEvent.ACTION_UP) {\r
+ AuthenticatorActivity.this.onViewPasswordClick();\r
+ }\r
+ return true;\r
+ }\r
+ });\r
+ \r
+ findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {\r
+ @Override\r
+ public boolean onTouch(View view, MotionEvent event) {\r
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
+ mHostUrlInput.hasFocus()) {\r
+ checkOcServer();\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ });\r
+ }\r
+ \r
+ \r
+\r
+ private void initAuthorizationMethod() {\r
+ boolean oAuthRequired = false;\r
+ boolean samlWebSsoRequired = false;\r
+\r
+ mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
+ mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
+ \r
+ // TODO could be a good moment to validate the received token type, if not null\r
+ \r
+ if (mAuthTokenType == null) { \r
+ if (mAccount != null) {\r
+ /// same authentication method than the one used to create the account to update\r
+ oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
+ samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
+ \r
+ } else {\r
+ /// use the one set in setup.xml\r
+ oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2));\r
+ samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso)); \r
+ }\r
+ if (oAuthRequired) {\r
+ mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
+ } else if (samlWebSsoRequired) {\r
+ mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie();\r
+ } else {\r
+ mAuthTokenType = MainApp.getAuthTokenTypePass();\r
+ }\r
+ }\r
+ \r
+ if (mAccount != null) {\r
+ String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
+ mUsernameInput.setText(userName);\r
+ }\r
+ \r
+ mOAuth2Check.setChecked(MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType));\r
+ \r
+ }\r
+\r
+ /**\r
+ * Saves relevant state before {@link #onPause()}\r
+ * \r
+ * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the \r
+ * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} \r
+ * \r
+ * See {@link #loadSavedInstanceState(Bundle)}\r
+ */\r
+ @Override\r
+ protected void onSaveInstanceState(Bundle outState) {\r
+ super.onSaveInstanceState(outState);\r
+\r
+ /// connection state and info\r
+ outState.putInt(KEY_AUTH_MESSAGE_VISIBILITY, mAuthMessage.getVisibility());\r
+ outState.putString(KEY_AUTH_MESSAGE_TEXT, mAuthMessage.getText().toString());\r
+ outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);\r
+ outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);\r
+ outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);\r
+ outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);\r
+ outState.putBoolean(KEY_SERVER_CHECK_IN_PROGRESS, (!mServerIsValid && mOcServerChkOperation != null));\r
+ outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);\r
+ outState.putBoolean(KEY_PASSWORD_VISIBLE, isPasswordVisible());\r
+ outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);\r
+ outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);\r
+\r
+ /// server data\r
+ if (mDiscoveredVersion != null) {\r
+ outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
+ }\r
+ outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
+\r
+ /// account data, if updating\r
+ if (mAccount != null) {\r
+ outState.putParcelable(KEY_ACCOUNT, mAccount);\r
+ }\r
+ outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType);\r
+ \r
+ // refresh button enabled\r
+ outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE));\r
+ \r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request\r
+ * is caught here.\r
+ * \r
+ * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the\r
+ * AndroidManifest.xml file.\r
+ */\r
+ @Override\r
+ protected void onNewIntent (Intent intent) {\r
+ Log_OC.d(TAG, "onNewIntent()");\r
+ Uri data = intent.getData();\r
+ if (data != null && data.toString().startsWith(getString(R.string.oauth2_redirect_uri))) {\r
+ mNewCapturedUriFromOAuth2Redirection = data;\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and \r
+ * deferred in {@link #onNewIntent(Intent)}, is processed here.\r
+ */\r
+ @Override\r
+ protected void onResume() {\r
+ super.onResume();\r
+ if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) {\r
+ if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+ //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();\r
+ showAuthMessage(getString(R.string.auth_expired_oauth_token_toast));\r
+ } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+ //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show();\r
+ showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast));\r
+ } else {\r
+ //Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();\r
+ showAuthMessage(getString(R.string.auth_expired_basic_auth_toast));\r
+ }\r
+ }\r
+\r
+ if (mNewCapturedUriFromOAuth2Redirection != null) {\r
+ getOAuth2AccessTokenFromCapturedRedirection(); \r
+ }\r
+\r
+ mJustCreated = false;\r
+ \r
+ }\r
+\r
+\r
+ /**\r
+ * Parses the redirection with the response to the GET AUTHORIZATION request to the \r
+ * oAuth server and requests for the access token (GET ACCESS TOKEN)\r
+ */\r
+ private void getOAuth2AccessTokenFromCapturedRedirection() {\r
+ /// Parse data from OAuth redirection\r
+ String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
+ mNewCapturedUriFromOAuth2Redirection = null;\r
+\r
+ /// Showing the dialog with instructions for the user.\r
+ showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+\r
+ /// GET ACCESS TOKEN to the oAuth server \r
+ RemoteOperation operation = new OAuth2GetAccessToken( getString(R.string.oauth2_client_id), \r
+ getString(R.string.oauth2_redirect_uri), \r
+ getString(R.string.oauth2_grant_type),\r
+ queryParameters);\r
+ //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
+ operation.execute(client, this, mHandler);\r
+ }\r
+\r
+\r
+\r
+ /**\r
+ * Handles the change of focus on the text inputs for the server URL and the password\r
+ */\r
+ public void onFocusChange(View view, boolean hasFocus) {\r
+ if (view.getId() == R.id.hostUrlInput) { \r
+ if (!hasFocus) {\r
+ onUrlInputFocusLost((TextView) view);\r
+ }\r
+ else {\r
+ hideRefreshButton();\r
+ }\r
+\r
+ } else if (view.getId() == R.id.account_password) {\r
+ onPasswordFocusChanged((TextView) view, hasFocus);\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Handles changes in focus on the text input for the server URL.\r
+ * \r
+ * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to \r
+ * other field. The operation to check the existence of the server in the entered URL is\r
+ * started. \r
+ * \r
+ * When hasFocus: user 'comes back' to write again the server URL.\r
+ * \r
+ * @param hostInput TextView with the URL input field receiving the change of focus.\r
+ */\r
+ private void onUrlInputFocusLost(TextView hostInput) {\r
+ if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
+ checkOcServer();\r
+ } else {\r
+ mOkButton.setEnabled(mServerIsValid);\r
+ if (!mServerIsValid) {\r
+ showRefreshButton();\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ private void checkOcServer() {\r
+ String uri = trimUrlWebdav(mHostUrlInput.getText().toString().trim());\r
+ \r
+ if (!mHostUrlInputEnabled){\r
+ uri = getString(R.string.server_url);\r
+ }\r
+ \r
+ mServerIsValid = false;\r
+ mServerIsChecked = false;\r
+ mOkButton.setEnabled(false);\r
+ mDiscoveredVersion = null;\r
+ hideRefreshButton();\r
+ if (uri.length() != 0) {\r
+ mServerStatusText = R.string.auth_testing_connection;\r
+ mServerStatusIcon = R.drawable.progress_small;\r
+ showServerStatus();\r
+ mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this, true);\r
+ mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
+ } else {\r
+ mServerStatusText = 0;\r
+ mServerStatusIcon = 0;\r
+ showServerStatus();\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Handles changes in focus on the text input for the password (basic authorization).\r
+ * \r
+ * When (hasFocus), the button to toggle password visibility is shown.\r
+ * \r
+ * When (!hasFocus), the button is made invisible and the password is hidden.\r
+ * \r
+ * @param passwordInput TextView with the password input field receiving the change of focus.\r
+ * @param hasFocus 'True' if focus is received, 'false' if is lost\r
+ */\r
+ private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {\r
+ if (hasFocus) {\r
+ showViewPasswordButton();\r
+ } else {\r
+ hidePassword();\r
+ hidePasswordButton();\r
+ }\r
+ }\r
+\r
+\r
+ private void showViewPasswordButton() {\r
+ //int drawable = android.R.drawable.ic_menu_view;\r
+ int drawable = R.drawable.ic_view;\r
+ if (isPasswordVisible()) {\r
+ //drawable = android.R.drawable.ic_secure;\r
+ drawable = R.drawable.ic_hide;\r
+ }\r
+ mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable, 0);\r
+ }\r
+\r
+ private boolean isPasswordVisible() {\r
+ return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
+ }\r
+ \r
+ private void hidePasswordButton() {\r
+ mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);\r
+ }\r
+\r
+ private void showPassword() {\r
+ mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
+ showViewPasswordButton();\r
+ }\r
+ \r
+ private void hidePassword() {\r
+ mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);\r
+ showViewPasswordButton();\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Cancels the authenticator activity\r
+ * \r
+ * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation\r
+ * \r
+ * This method is bound in the layout/acceoun_setup.xml resource file.\r
+ * \r
+ * @param view Cancel button\r
+ */\r
+ public void onCancelClick(View view) {\r
+ setResult(RESULT_CANCELED); // TODO review how is this related to AccountAuthenticator (debugging)\r
+ finish();\r
+ }\r
+\r
+\r
+\r
+ /**\r
+ * Checks the credentials of the user in the root of the ownCloud server\r
+ * before creating a new local account.\r
+ * \r
+ * For basic authorization, a check of existence of the root folder is\r
+ * performed.\r
+ * \r
+ * For OAuth, starts the flow to get an access token; the credentials test \r
+ * is postponed until it is available.\r
+ * \r
+ * IMPORTANT ENTRY POINT 4\r
+ * \r
+ * @param view OK button\r
+ */\r
+ public void onOkClick(View view) {\r
+ // this check should be unnecessary\r
+ if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {\r
+ mServerStatusIcon = R.drawable.common_error;\r
+ mServerStatusText = R.string.auth_wtf_reenter_URL;\r
+ showServerStatus();\r
+ mOkButton.setEnabled(false);\r
+ Log_OC.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!");\r
+ return;\r
+ }\r
+\r
+ if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+ startOauthorization();\r
+ } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { \r
+ startSamlBasedFederatedSingleSignOnAuthorization();\r
+ } else {\r
+ checkBasicAuthorization();\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Tests the credentials entered by the user performing a check of existence on \r
+ * the root folder of the ownCloud server.\r
+ */\r
+ private void checkBasicAuthorization() {\r
+ /// get the path to the root folder through WebDAV from the version server\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+\r
+ /// get basic credentials entered by user\r
+ String username = mUsernameInput.getText().toString();\r
+ String password = mPasswordInput.getText().toString();\r
+\r
+ /// be gentle with the user\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+\r
+ /// test credentials accessing the root folder\r
+ mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+ client.setBasicCredentials(username, password);\r
+ mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
+ }\r
+\r
+\r
+ /**\r
+ * Starts the OAuth 'grant type' flow to get an access token, with \r
+ * a GET AUTHORIZATION request to the BUILT-IN authorization server. \r
+ */\r
+ private void startOauthorization() {\r
+ // be gentle with the user\r
+ mAuthStatusIcon = R.drawable.progress_small;\r
+ mAuthStatusText = R.string.oauth_login_connection;\r
+ showAuthStatus();\r
+ \r
+\r
+ // GET AUTHORIZATION request\r
+ //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth));\r
+ Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());\r
+ Uri.Builder uriBuilder = uri.buildUpon();\r
+ uriBuilder.appendQueryParameter(OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type));\r
+ uriBuilder.appendQueryParameter(OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri)); \r
+ uriBuilder.appendQueryParameter(OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id));\r
+ uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));\r
+ //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);\r
+ uri = uriBuilder.build();\r
+ Log_OC.d(TAG, "Starting browser to view " + uri.toString());\r
+ Intent i = new Intent(Intent.ACTION_VIEW, uri);\r
+ startActivity(i);\r
+ }\r
+\r
+\r
+ /**\r
+ * Starts the Web Single Sign On flow to get access to the root folder\r
+ * in the server.\r
+ */\r
+ private void startSamlBasedFederatedSingleSignOnAuthorization() {\r
+ // be gentle with the user\r
+ mAuthStatusIcon = R.drawable.progress_small;\r
+ mAuthStatusText = R.string.auth_connecting_auth_server;\r
+ showAuthStatus();\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+ \r
+ /// get the path to the root folder through WebDAV from the version server\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+\r
+ /// test credentials accessing the root folder\r
+ mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
+ mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
+ \r
+ }\r
+\r
+ /**\r
+ * Callback method invoked when a RemoteOperation executed by this Activity finishes.\r
+ * \r
+ * Dispatches the operation flow to the right method.\r
+ */\r
+ @Override\r
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
+\r
+ if (operation instanceof OwnCloudServerCheckOperation) {\r
+ onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);\r
+\r
+ } else if (operation instanceof OAuth2GetAccessToken) {\r
+ onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
+\r
+ } else if (operation instanceof ExistenceCheckOperation) {\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+ onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result);\r
+ \r
+ } else {\r
+ onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
+ }\r
+ }\r
+ }\r
+ \r
+ \r
+ private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) {\r
+ try {\r
+ dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+
+ //if (result.isTemporalRedirection() && result.isIdPRedirection()) {\r
+ if (result.isIdPRedirection()) {
+ String url = result.getRedirectedLocation();\r
+ String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+ \r
+ // Show dialog\r
+ mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl); \r
+ mSamlDialog.show(getSupportFragmentManager(), TAG_SAML_DIALOG);\r
+ \r
+ mAuthStatusIcon = 0;\r
+ mAuthStatusText = 0;\r
+ \r
+ } else {\r
+ mAuthStatusIcon = R.drawable.common_error;\r
+ mAuthStatusText = R.string.auth_unsupported_auth_method;\r
+ \r
+ }\r
+ showAuthStatus();\r
+ }\r
+\r
+\r
+ /**\r
+ * Processes the result of the server check performed when the user finishes the enter of the\r
+ * server URL.\r
+ * \r
+ * @param operation Server check performed.\r
+ * @param result Result of the check.\r
+ */\r
+ private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {\r
+ if (operation.equals(mOcServerChkOperation)) {\r
+ /// save result state\r
+ mServerIsChecked = true;\r
+ mServerIsValid = result.isSuccess();\r
+ mIsSslConn = (result.getCode() == ResultCode.OK_SSL);\r
+ mOcServerChkOperation = null;\r
+\r
+ /// update status icon and text\r
+ if (mServerIsValid) {\r
+ hideRefreshButton();\r
+ } else {\r
+ showRefreshButton();\r
+ }\r
+ updateServerStatusIconAndText(result);\r
+ showServerStatus();\r
+\r
+ /// very special case (TODO: move to a common place for all the remote operations)\r
+ if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
+ mLastSslUntrustedServerResult = result;\r
+ showDialog(DIALOG_SSL_VALIDATOR); \r
+ }\r
+\r
+ /// retrieve discovered version and normalize server URL\r
+ mDiscoveredVersion = operation.getDiscoveredVersion();\r
+ mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
+\r
+ /// allow or not the user try to access the server\r
+ mOkButton.setEnabled(mServerIsValid);\r
+\r
+ } // else nothing ; only the last check operation is considered; \r
+ // multiple can be triggered if the user amends a URL before a previous check can be triggered\r
+ }\r
+\r
+\r
+ private String normalizeUrl(String url) {\r
+ if (url != null && url.length() > 0) {\r
+ url = url.trim();\r
+ if (!url.toLowerCase().startsWith("http://") &&\r
+ !url.toLowerCase().startsWith("https://")) {\r
+ if (mIsSslConn) {\r
+ url = "https://" + url;\r
+ } else {\r
+ url = "http://" + url;\r
+ }\r
+ }\r
+\r
+ // OC-208: Add suffix remote.php/webdav to normalize (OC-34) \r
+ url = trimUrlWebdav(url);\r
+\r
+ if (url.endsWith("/")) {\r
+ url = url.substring(0, url.length() - 1);\r
+ }\r
+\r
+ }\r
+ return (url != null ? url : "");\r
+ }\r
+\r
+\r
+ private String trimUrlWebdav(String url){ \r
+ if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_4_0)){\r
+ url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_4_0.length()); \r
+ } else if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_2_0)){\r
+ url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_2_0.length()); \r
+ } else if (url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_1_2)){\r
+ url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_1_2.length()); \r
+ } \r
+ return (url != null ? url : "");\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Chooses the right icon and text to show to the user for the received operation result.\r
+ * \r
+ * @param result Result of a remote operation performed in this activity\r
+ */\r
+ private void updateServerStatusIconAndText(RemoteOperationResult result) {\r
+ mServerStatusIcon = R.drawable.common_error; // the most common case in the switch below\r
+\r
+ switch (result.getCode()) {\r
+ case OK_SSL:\r
+ mServerStatusIcon = android.R.drawable.ic_secure;\r
+ mServerStatusText = R.string.auth_secure_connection;\r
+ break;\r
+\r
+ case OK_NO_SSL:\r
+ case OK:\r
+ if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
+ mServerStatusText = R.string.auth_connection_established;\r
+ mServerStatusIcon = R.drawable.ic_ok;\r
+ } else {\r
+ mServerStatusText = R.string.auth_nossl_plain_ok_title;\r
+ mServerStatusIcon = android.R.drawable.ic_partial_secure;\r
+ }\r
+ break;\r
+\r
+ case NO_NETWORK_CONNECTION:\r
+ mServerStatusIcon = R.drawable.no_network;\r
+ mServerStatusText = R.string.auth_no_net_conn_title;\r
+ break;\r
+\r
+ case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
+ mServerStatusText = R.string.auth_ssl_unverified_server_title;\r
+ break;\r
+ case BAD_OC_VERSION:\r
+ mServerStatusText = R.string.auth_bad_oc_version_title;\r
+ break;\r
+ case WRONG_CONNECTION:\r
+ mServerStatusText = R.string.auth_wrong_connection_title;\r
+ break;\r
+ case TIMEOUT:\r
+ mServerStatusText = R.string.auth_timeout_title;\r
+ break;\r
+ case INCORRECT_ADDRESS:\r
+ mServerStatusText = R.string.auth_incorrect_address_title;\r
+ break;\r
+ case SSL_ERROR:\r
+ mServerStatusText = R.string.auth_ssl_general_error_title;\r
+ break;\r
+ case UNAUTHORIZED:\r
+ mServerStatusText = R.string.auth_unauthorized;\r
+ break;\r
+ case HOST_NOT_AVAILABLE:\r
+ mServerStatusText = R.string.auth_unknown_host_title;\r
+ break;\r
+ case INSTANCE_NOT_CONFIGURED:\r
+ mServerStatusText = R.string.auth_not_configured_title;\r
+ break;\r
+ case FILE_NOT_FOUND:\r
+ mServerStatusText = R.string.auth_incorrect_path_title;\r
+ break;\r
+ case OAUTH2_ERROR:\r
+ mServerStatusText = R.string.auth_oauth_error;\r
+ break;\r
+ case OAUTH2_ERROR_ACCESS_DENIED:\r
+ mServerStatusText = R.string.auth_oauth_error_access_denied;\r
+ break;\r
+ case UNHANDLED_HTTP_CODE:\r
+ case UNKNOWN_ERROR:\r
+ mServerStatusText = R.string.auth_unknown_error_title;\r
+ break;\r
+ default:\r
+ mServerStatusText = 0;\r
+ mServerStatusIcon = 0;\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Chooses the right icon and text to show to the user for the received operation result.\r
+ * \r
+ * @param result Result of a remote operation performed in this activity\r
+ */\r
+ private void updateAuthStatusIconAndText(RemoteOperationResult result) {\r
+ mAuthStatusIcon = R.drawable.common_error; // the most common case in the switch below\r
+\r
+ switch (result.getCode()) {\r
+ case OK_SSL:\r
+ mAuthStatusIcon = android.R.drawable.ic_secure;\r
+ mAuthStatusText = R.string.auth_secure_connection;\r
+ break;\r
+\r
+ case OK_NO_SSL:\r
+ case OK:\r
+ if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
+ mAuthStatusText = R.string.auth_connection_established;\r
+ mAuthStatusIcon = R.drawable.ic_ok;\r
+ } else {\r
+ mAuthStatusText = R.string.auth_nossl_plain_ok_title;\r
+ mAuthStatusIcon = android.R.drawable.ic_partial_secure;\r
+ }\r
+ break;\r
+\r
+ case NO_NETWORK_CONNECTION:\r
+ mAuthStatusIcon = R.drawable.no_network;\r
+ mAuthStatusText = R.string.auth_no_net_conn_title;\r
+ break;\r
+\r
+ case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
+ mAuthStatusText = R.string.auth_ssl_unverified_server_title;\r
+ break;\r
+ case BAD_OC_VERSION:\r
+ mAuthStatusText = R.string.auth_bad_oc_version_title;\r
+ break;\r
+ case WRONG_CONNECTION:\r
+ mAuthStatusText = R.string.auth_wrong_connection_title;\r
+ break;\r
+ case TIMEOUT:\r
+ mAuthStatusText = R.string.auth_timeout_title;\r
+ break;\r
+ case INCORRECT_ADDRESS:\r
+ mAuthStatusText = R.string.auth_incorrect_address_title;\r
+ break;\r
+ case SSL_ERROR:\r
+ mAuthStatusText = R.string.auth_ssl_general_error_title;\r
+ break;\r
+ case UNAUTHORIZED:\r
+ mAuthStatusText = R.string.auth_unauthorized;\r
+ break;\r
+ case HOST_NOT_AVAILABLE:\r
+ mAuthStatusText = R.string.auth_unknown_host_title;\r
+ break;\r
+ case INSTANCE_NOT_CONFIGURED:\r
+ mAuthStatusText = R.string.auth_not_configured_title;\r
+ break;\r
+ case FILE_NOT_FOUND:\r
+ mAuthStatusText = R.string.auth_incorrect_path_title;\r
+ break;\r
+ case OAUTH2_ERROR:\r
+ mAuthStatusText = R.string.auth_oauth_error;\r
+ break;\r
+ case OAUTH2_ERROR_ACCESS_DENIED:\r
+ mAuthStatusText = R.string.auth_oauth_error_access_denied;\r
+ break;\r
+ case ACCOUNT_NOT_NEW:\r
+ mAuthStatusText = R.string.auth_account_not_new;\r
+ break;\r
+ case ACCOUNT_NOT_THE_SAME:\r
+ mAuthStatusText = R.string.auth_account_not_the_same;\r
+ break;\r
+ case UNHANDLED_HTTP_CODE:\r
+ case UNKNOWN_ERROR:\r
+ mAuthStatusText = R.string.auth_unknown_error_title;\r
+ break;\r
+ default:\r
+ mAuthStatusText = 0;\r
+ mAuthStatusIcon = 0;\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Processes the result of the request for and access token send \r
+ * to an OAuth authorization server.\r
+ * \r
+ * @param operation Operation performed requesting the access token.\r
+ * @param result Result of the operation.\r
+ */\r
+ private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {\r
+ try {\r
+ dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+\r
+ String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
+ if (result.isSuccess() && webdav_path != null) {\r
+ /// be gentle with the user\r
+ showDialog(DIALOG_LOGIN_PROGRESS);\r
+\r
+ /// time to test the retrieved access token on the ownCloud server\r
+ mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
+ Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
+ mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
+ WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+ client.setBearerCredentials(mAuthToken);\r
+ mAuthCheckOperation.execute(client, this, mHandler);\r
+\r
+ } else {\r
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Processes the result of the access check performed to try the user credentials.\r
+ * \r
+ * Creates a new account through the AccountManager.\r
+ * \r
+ * @param operation Access check performed.\r
+ * @param result Result of the operation.\r
+ */\r
+ private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
+ try {\r
+ dismissDialog(DIALOG_LOGIN_PROGRESS);\r
+ } catch (IllegalArgumentException e) {\r
+ // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
+ }\r
+\r
+ if (result.isSuccess()) {\r
+ Log_OC.d(TAG, "Successful access - time to save the account");\r
+\r
+ boolean success = false;\r
+ if (mAction == ACTION_CREATE) {\r
+ success = createAccount();\r
+\r
+ } else {\r
+ success = updateToken();\r
+ }\r
+\r
+ if (success) {\r
+ finish();\r
+ }\r
+\r
+ } else if (result.isServerFail() || result.isException()) {\r
+ /// if server fail or exception in authorization, the UI is updated as when a server check failed\r
+ mServerIsChecked = true;\r
+ mServerIsValid = false;\r
+ mIsSslConn = false;\r
+ mOcServerChkOperation = null;\r
+ mDiscoveredVersion = null;\r
+ mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
+\r
+ // update status icon and text\r
+ updateServerStatusIconAndText(result);\r
+ showServerStatus();\r
+ mAuthStatusIcon = 0;\r
+ mAuthStatusText = 0;\r
+ showAuthStatus();\r
+ \r
+ // update input controls state\r
+ showRefreshButton();\r
+ mOkButton.setEnabled(false);\r
+\r
+ // very special case (TODO: move to a common place for all the remote operations) (dangerous here?)\r
+ if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
+ mLastSslUntrustedServerResult = result;\r
+ showDialog(DIALOG_SSL_VALIDATOR); \r
+ }\r
+\r
+ } else { // authorization fail due to client side - probably wrong credentials\r
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Sets the proper response to get that the Account Authenticator that started this activity saves \r
+ * a new authorization token for mAccount.\r
+ */\r
+ private boolean updateToken() {\r
+ Bundle response = new Bundle();\r
+ response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
+ response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
+ \r
+ if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { \r
+ response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
+ // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
+ mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+ \r
+ } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+ String username = getUserNameForSamlSso();\r
+ if (!mUsernameInput.getText().toString().equals(username)) {\r
+ // fail - not a new account, but an existing one; disallow\r
+ RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); \r
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.d(TAG, result.getLogMessage());\r
+ \r
+ return false;\r
+ }\r
+ \r
+ response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
+ // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
+ mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+ \r
+ } else {\r
+ response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());\r
+ mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());\r
+ }\r
+ setAccountAuthenticatorResult(response);\r
+ \r
+ return true;\r
+ }\r
+\r
+\r
+ /**\r
+ * Creates a new account through the Account Authenticator that started this activity. \r
+ * \r
+ * This makes the account permanent.\r
+ * \r
+ * TODO Decide how to name the OAuth accounts\r
+ */\r
+ private boolean createAccount() {\r
+ /// create and save new ownCloud account\r
+ boolean isOAuth = MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType);\r
+ boolean isSaml = MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType);\r
+\r
+ Uri uri = Uri.parse(mHostBaseUrl);\r
+ String username = mUsernameInput.getText().toString().trim();\r
+ if (isSaml) {\r
+ username = getUserNameForSamlSso();\r
+ \r
+ } else if (isOAuth) {\r
+ username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();\r
+ } \r
+ String accountName = username + "@" + uri.getHost();\r
+ if (uri.getPort() >= 0) {\r
+ accountName += ":" + uri.getPort();\r
+ }\r
+ mAccount = new Account(accountName, MainApp.getAccountType());\r
+ if (AccountUtils.exists(mAccount, getApplicationContext())) {\r
+ // fail - not a new account, but an existing one; disallow\r
+ RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW); \r
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.d(TAG, result.getLogMessage());\r
+ return false;\r
+ \r
+ } else {\r
+ \r
+ if (isOAuth || isSaml) {\r
+ mAccountMgr.addAccountExplicitly(mAccount, "", null); // with external authorizations, the password is never input in the app\r
+ } else {\r
+ mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);\r
+ }\r
+ \r
+ /// add the new account as default in preferences, if there is none already\r
+ Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
+ if (defaultAccount == null) {\r
+ SharedPreferences.Editor editor = PreferenceManager\r
+ .getDefaultSharedPreferences(this).edit();\r
+ editor.putString("select_oc_account", accountName);\r
+ editor.commit();\r
+ }\r
+ \r
+ /// prepare result to return to the Authenticator\r
+ // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done\r
+ final Intent intent = new Intent(); \r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());\r
+ intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
+ /*if (!isOAuth)\r
+ intent.putExtra(AccountManager.KEY_AUTHTOKEN, MainApp.getAccountType()); */\r
+ intent.putExtra(AccountManager.KEY_USERDATA, username);\r
+ if (isOAuth || isSaml) {\r
+ mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
+ }\r
+ /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl);\r
+ if (isSaml) {\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
+ } else if (isOAuth) {\r
+ mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); \r
+ }\r
+ \r
+ setAccountAuthenticatorResult(intent.getExtras());\r
+ setResult(RESULT_OK, intent);\r
+ \r
+ /// immediately request for the synchronization of the new account\r
+ Bundle bundle = new Bundle();\r
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+ ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+ syncAccount();\r
+// Bundle bundle = new Bundle();\r
+// bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+// ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+ return true;\r
+ }\r
+ }\r
+\r
+ \r
+ private String getUserNameForSamlSso() {\r
+ if (mAuthToken != null) {\r
+ String [] cookies = mAuthToken.split(";");\r
+ for (int i=0; i<cookies.length; i++) {\r
+ if (cookies[i].startsWith(KEY_OC_USERNAME_EQUALS )) {\r
+ String value = Uri.decode(cookies[i].substring(KEY_OC_USERNAME_EQUALS.length()));\r
+ return value;\r
+ }\r
+ }\r
+ }\r
+ return "";\r
+ }\r
+\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ * \r
+ * Necessary to update the contents of the SSL Dialog\r
+ * \r
+ * TODO move to some common place for all possible untrusted SSL failures\r
+ */\r
+ @Override\r
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
+ switch (id) {\r
+ case DIALOG_LOGIN_PROGRESS:\r
+ case DIALOG_CERT_NOT_SAVED:\r
+ case DIALOG_OAUTH2_LOGIN_PROGRESS:\r
+ break;\r
+ case DIALOG_SSL_VALIDATOR: {\r
+ ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
+ break;\r
+ }\r
+ default:\r
+ Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ protected Dialog onCreateDialog(int id) {\r
+ Dialog dialog = null;\r
+ switch (id) {\r
+ case DIALOG_LOGIN_PROGRESS: {\r
+ /// simple progress dialog\r
+ ProgressDialog working_dialog = new ProgressDialog(this);\r
+ working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));\r
+ working_dialog.setIndeterminate(true);\r
+ working_dialog.setCancelable(true);\r
+ working_dialog\r
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+ @Override\r
+ public void onCancel(DialogInterface dialog) {\r
+ /// TODO study if this is enough\r
+ Log_OC.i(TAG, "Login canceled");\r
+ if (mOperationThread != null) {\r
+ mOperationThread.interrupt();\r
+ finish();\r
+ }\r
+ }\r
+ });\r
+ dialog = working_dialog;\r
+ break;\r
+ }\r
+ case DIALOG_OAUTH2_LOGIN_PROGRESS: {\r
+ ProgressDialog working_dialog = new ProgressDialog(this);\r
+ working_dialog.setMessage(String.format("Getting authorization")); \r
+ working_dialog.setIndeterminate(true);\r
+ working_dialog.setCancelable(true);\r
+ working_dialog\r
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
+ @Override\r
+ public void onCancel(DialogInterface dialog) {\r
+ Log_OC.i(TAG, "Login canceled");\r
+ finish();\r
+ }\r
+ });\r
+ dialog = working_dialog;\r
+ break;\r
+ }\r
+ case DIALOG_SSL_VALIDATOR: {\r
+ /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)\r
+ dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
+ break;\r
+ }\r
+ case DIALOG_CERT_NOT_SAVED: {\r
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
+ builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));\r
+ builder.setCancelable(false);\r
+ builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {\r
+ @Override\r
+ public void onClick(DialogInterface dialog, int which) {\r
+ dialog.dismiss();\r
+ };\r
+ });\r
+ dialog = builder.create();\r
+ break;\r
+ }\r
+ default:\r
+ Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
+ }\r
+ return dialog;\r
+ }\r
+\r
+\r
+ /**\r
+ * Starts and activity to open the 'new account' page in the ownCloud web site\r
+ * \r
+ * @param view 'Account register' button\r
+ */\r
+ public void onRegisterClick(View view) {\r
+ Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
+ setResult(RESULT_CANCELED);\r
+ startActivity(register);\r
+ }\r
+\r
+\r
+ /**\r
+ * Updates the content and visibility state of the icon and text associated\r
+ * to the last check on the ownCloud server.\r
+ */\r
+ private void showServerStatus() {\r
+ TextView tv = (TextView) findViewById(R.id.server_status_text);\r
+\r
+ if (mServerStatusIcon == 0 && mServerStatusText == 0) {\r
+ tv.setVisibility(View.INVISIBLE);\r
+\r
+ } else {\r
+ tv.setText(mServerStatusText);\r
+ tv.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);\r
+ tv.setVisibility(View.VISIBLE);\r
+ }\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * Updates the content and visibility state of the icon and text associated\r
+ * to the interactions with the OAuth authorization server.\r
+ */\r
+ private void showAuthStatus() {\r
+ if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {\r
+ mAuthStatusLayout.setVisibility(View.INVISIBLE);\r
+\r
+ } else {\r
+ mAuthStatusLayout.setText(mAuthStatusText);\r
+ mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);\r
+ mAuthStatusLayout.setVisibility(View.VISIBLE);\r
+ }\r
+ } \r
+\r
+\r
+ private void showRefreshButton() {\r
+ mRefreshButton.setVisibility(View.VISIBLE);\r
+ }\r
+\r
+ private void hideRefreshButton() {\r
+ mRefreshButton.setVisibility(View.GONE);\r
+ }\r
+\r
+ /**\r
+ * Called when the refresh button in the input field for ownCloud host is clicked.\r
+ * \r
+ * Performs a new check on the URL in the input field.\r
+ * \r
+ * @param view Refresh 'button'\r
+ */\r
+ public void onRefreshClick(View view) {\r
+ checkOcServer();\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Called when the eye icon in the password field is clicked.\r
+ * \r
+ * Toggles the visibility of the password in the field. \r
+ */\r
+ public void onViewPasswordClick() {\r
+ int selectionStart = mPasswordInput.getSelectionStart();\r
+ int selectionEnd = mPasswordInput.getSelectionEnd();\r
+ if (isPasswordVisible()) {\r
+ hidePassword();\r
+ } else {\r
+ showPassword();\r
+ }\r
+ mPasswordInput.setSelection(selectionStart, selectionEnd);\r
+ } \r
+\r
+\r
+ /**\r
+ * Called when the checkbox for OAuth authorization is clicked.\r
+ * \r
+ * Hides or shows the input fields for user & password. \r
+ * \r
+ * @param view 'View password' 'button'\r
+ */\r
+ public void onCheckClick(View view) {\r
+ CheckBox oAuth2Check = (CheckBox)view;\r
+ if (oAuth2Check.isChecked()) {\r
+ mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
+ } else {\r
+ mAuthTokenType = MainApp.getAuthTokenTypePass();\r
+ }\r
+ adaptViewAccordingToAuthenticationMethod();\r
+ }\r
+\r
+ \r
+ /**\r
+ * Changes the visibility of input elements depending on\r
+ * the current authorization method.\r
+ */\r
+ private void adaptViewAccordingToAuthenticationMethod () {\r
+ if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
+ // OAuth 2 authorization\r
+ mOAuthAuthEndpointText.setVisibility(View.VISIBLE);\r
+ mOAuthTokenEndpointText.setVisibility(View.VISIBLE);\r
+ mUsernameInput.setVisibility(View.GONE);\r
+ mPasswordInput.setVisibility(View.GONE);\r
+ \r
+ } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+ // SAML-based web Single Sign On\r
+ mOAuthAuthEndpointText.setVisibility(View.GONE);\r
+ mOAuthTokenEndpointText.setVisibility(View.GONE);\r
+ mUsernameInput.setVisibility(View.GONE);\r
+ mPasswordInput.setVisibility(View.GONE);\r
+ } else {\r
+ // basic HTTP authorization\r
+ mOAuthAuthEndpointText.setVisibility(View.GONE);\r
+ mOAuthTokenEndpointText.setVisibility(View.GONE);\r
+ mUsernameInput.setVisibility(View.VISIBLE);\r
+ mPasswordInput.setVisibility(View.VISIBLE);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Called from SslValidatorDialog when a new server certificate was correctly saved.\r
+ */\r
+ public void onSavedCertificate() {\r
+ checkOcServer();\r
+ }\r
+\r
+ /**\r
+ * Called from SslValidatorDialog when a new server certificate could not be saved \r
+ * when the user requested it.\r
+ */\r
+ @Override\r
+ public void onFailedSavingCertificate() {\r
+ showDialog(DIALOG_CERT_NOT_SAVED);\r
+ }\r
+\r
+\r
+ /**\r
+ * Called when the 'action' button in an IME is pressed ('enter' in software keyboard).\r
+ * \r
+ * Used to trigger the authentication check when the user presses 'enter' after writing the password, \r
+ * or to throw the server test when the only field on screen is the URL input field.\r
+ */\r
+ @Override\r
+ public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {\r
+ if (actionId == EditorInfo.IME_ACTION_DONE && inputField != null && inputField.equals(mPasswordInput)) {\r
+ if (mOkButton.isEnabled()) {\r
+ mOkButton.performClick();\r
+ }\r
+ \r
+ } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) {\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
+ checkOcServer();\r
+ }\r
+ }\r
+ return false; // always return false to grant that the software keyboard is hidden anyway\r
+ }\r
+\r
+\r
+ private abstract static class RightDrawableOnTouchListener implements OnTouchListener {\r
+\r
+ private int fuzz = 75;\r
+ \r
+ /**\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public boolean onTouch(View view, MotionEvent event) {\r
+ Drawable rightDrawable = null;\r
+ if (view instanceof TextView) {\r
+ Drawable[] drawables = ((TextView)view).getCompoundDrawables();\r
+ if (drawables.length > 2) {\r
+ rightDrawable = drawables[2];\r
+ }\r
+ }\r
+ if (rightDrawable != null) {\r
+ final int x = (int) event.getX();\r
+ final int y = (int) event.getY();\r
+ final Rect bounds = rightDrawable.getBounds();\r
+ if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz)\r
+ && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) {\r
+ \r
+ return onDrawableTouch(event);\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public abstract boolean onDrawableTouch(final MotionEvent event);\r
+ }\r
+\r
+\r
+ public void onSamlDialogSuccess(String sessionCookie){\r
+ mAuthToken = sessionCookie;\r
+ \r
+ if (sessionCookie != null && sessionCookie.length() > 0) {\r
+ mAuthToken = sessionCookie;\r
+ boolean success = false;\r
+ if (mAction == ACTION_CREATE) {\r
+ success = createAccount();\r
+ \r
+ } else {\r
+ success = updateToken();\r
+ }\r
+ if (success) {\r
+ finish();\r
+ }\r
+ }\r
+\r
+ \r
+ }\r
+\r
+\r
+ @Override\r
+ public void onSsoFinished(String sessionCookies) {\r
+ //Toast.makeText(this, "got cookies: " + sessionCookie, Toast.LENGTH_LONG).show();\r
+\r
+ if (sessionCookies != null && sessionCookies.length() > 0) {\r
+ Log_OC.d(TAG, "Successful SSO - time to save the account");\r
+ onSamlDialogSuccess(sessionCookies);\r
+ Fragment fd = getSupportFragmentManager().findFragmentByTag(TAG_SAML_DIALOG);\r
+ if (fd != null && fd instanceof SherlockDialogFragment) {\r
+ Dialog d = ((SherlockDialogFragment)fd).getDialog();\r
+ if (d != null && d.isShowing()) {\r
+ d.dismiss();\r
+ }\r
+ }\r
+\r
+ } else { \r
+ // TODO - show fail\r
+ Log_OC.d(TAG, "SSO failed");\r
+ }\r
+ \r
+ }\r
+ \r
+ /** Show auth_message \r
+ * \r
+ * @param message\r
+ */\r
+ private void showAuthMessage(String message) {\r
+ mAuthMessage.setVisibility(View.VISIBLE);\r
+ mAuthMessage.setText(message);\r
+ }\r
+ \r
+ private void hideAuthMessage() {\r
+ mAuthMessage.setVisibility(View.GONE);\r
+ }\r
+\r
+ private void syncAccount(){\r
+ /// immediately request for the synchronization of the new account\r
+ Bundle bundle = new Bundle();\r
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
+ ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
+ }\r
+ \r
+ @Override\r
+ public boolean onTouchEvent(MotionEvent event) {\r
+ if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
+ mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {\r
+ checkOcServer();\r
+ }\r
+ return super.onTouchEvent(event);\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.authentication;
+
+/**
+ * Constant values for OAuth 2 protocol.
+ *
+ * Includes required and optional parameter NAMES used in the 'authorization code' grant type.
+ *
+ * @author David A. Velasco
+ */
+
+public class OAuth2Constants {
+
+ /// Parameters to send to the Authorization Endpoint
+ public static final String KEY_RESPONSE_TYPE = "response_type";
+ public static final String KEY_REDIRECT_URI = "redirect_uri";
+ public static final String KEY_CLIENT_ID = "client_id";
+ public static final String KEY_SCOPE = "scope";
+ public static final String KEY_STATE = "state";
+
+ /// Additional parameters to send to the Token Endpoint
+ public static final String KEY_GRANT_TYPE = "grant_type";
+ public static final String KEY_CODE = "code";
+
+ /// Parameters received in an OK response from the Token Endpoint
+ public static final String KEY_ACCESS_TOKEN = "access_token";
+ public static final String KEY_TOKEN_TYPE = "token_type";
+ public static final String KEY_EXPIRES_IN = "expires_in";
+ public static final String KEY_REFRESH_TOKEN = "refresh_token";
+
+ /// Parameters in an ERROR response
+ public static final String KEY_ERROR = "error";
+ public static final String KEY_ERROR_DESCRIPTION = "error_description";
+ public static final String KEY_ERROR_URI = "error_uri";
+ public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.authentication;
+
+import java.lang.ref.WeakReference;
+
+import com.owncloud.android.Log_OC;
+
+
+import android.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.webkit.CookieManager;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+
+/**
+ * Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process
+ * running in the {@link WebView} that is attached to.
+ *
+ * Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
+ * authentication process.
+ *
+ * @author David A. Velasco
+ */
+public class SsoWebViewClient extends WebViewClient {
+
+ private static final String TAG = SsoWebViewClient.class.getSimpleName();
+
+ public interface SsoWebViewClientListener {
+ public void onSsoFinished(String sessionCookie);
+ }
+
+ private Handler mListenerHandler;
+ private WeakReference<SsoWebViewClientListener> mListenerRef;
+ private String mTargetUrl;
+ private String mLastReloadedUrlAtError;
+
+ public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) {
+ mListenerHandler = listenerHandler;
+ mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
+ mTargetUrl = "fake://url.to.be.set";
+ mLastReloadedUrlAtError = null;
+ }
+
+ public String getTargetUrl() {
+ return mTargetUrl;
+ }
+
+ public void setTargetUrl(String targetUrl) {
+ mTargetUrl = targetUrl;
+ }
+
+ @Override
+ public void onPageStarted (WebView view, String url, Bitmap favicon) {
+ Log_OC.d(TAG, "onPageStarted : " + url);
+ super.onPageStarted(view, url, favicon);
+ }
+
+ @Override
+ public void onFormResubmission (WebView view, Message dontResend, Message resend) {
+ Log_OC.d(TAG, "onFormResubMission ");
+
+ // necessary to grant reload of last page when device orientation is changed after sending a form
+ resend.sendToTarget();
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ return false;
+ }
+
+ @Override
+ public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
+ Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
+ if (!failingUrl.equals(mLastReloadedUrlAtError)) {
+ view.reload();
+ mLastReloadedUrlAtError = failingUrl;
+ } else {
+ mLastReloadedUrlAtError = null;
+ super.onReceivedError(view, errorCode, description, failingUrl);
+ }
+ }
+
+ @Override
+ public void onPageFinished (WebView view, String url) {
+ Log_OC.d(TAG, "onPageFinished : " + url);
+ mLastReloadedUrlAtError = null;
+ if (url.startsWith(mTargetUrl)) {
+ view.setVisibility(View.GONE);
+ CookieManager cookieManager = CookieManager.getInstance();
+ final String cookies = cookieManager.getCookie(url);
+ //Log_OC.d(TAG, "Cookies: " + cookies);
+ if (mListenerHandler != null && mListenerRef != null) {
+ // this is good idea because onPageFinished is not running in the UI thread
+ mListenerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ SsoWebViewClientListener listener = mListenerRef.get();
+ if (listener != null) {
+ listener.onSsoFinished(cookies);
+ }
+ }
+ });
+ }
+ }
+
+ }
+
+ /*
+ @Override
+ public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
+ Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
+ }
+
+ @Override
+ public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
+ Log_OC.d(TAG, "onReceivedSslError : " + error);
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
+ Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
+ }
+
+ @Override
+ public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
+ Log_OC.d(TAG, "shouldInterceptRequest : " + url);
+ return null;
+ }
+
+ @Override
+ public void onLoadResource (WebView view, String url) {
+ Log_OC.d(TAG, "onLoadResource : " + url);
+ }
+
+ @Override
+ public void onReceivedLoginRequest (WebView view, String realm, String account, String args) {
+ Log_OC.d(TAG, "onReceivedLoginRequest : " + realm + ", " + account + ", " + args);
+ }
+
+ @Override
+ public void onScaleChanged (WebView view, float oldScale, float newScale) {
+ Log_OC.d(TAG, "onScaleChanged : " + oldScale + " -> " + newScale);
+ super.onScaleChanged(view, oldScale, newScale);
+ }
+
+ @Override
+ public void onUnhandledKeyEvent (WebView view, KeyEvent event) {
+ Log_OC.d(TAG, "onUnhandledKeyEvent : " + event);
+ }
+
+ @Override
+ public boolean shouldOverrideKeyEvent (WebView view, KeyEvent event) {
+ Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
+ return false;
+ }
+ */
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.datamodel;
+
+import java.util.List;
+import java.util.Vector;
+
+public interface DataStorageManager {
+
+ public static final int ROOT_PARENT_ID = 0;
+
+ public OCFile getFileByPath(String path);
+
+ public OCFile getFileById(long id);
+
+ public boolean fileExists(String path);
+
+ public boolean fileExists(long id);
+
+ public boolean saveFile(OCFile file);
+
+ public void saveFiles(List<OCFile> files);
+
+ public Vector<OCFile> getDirectoryContent(OCFile f);
+
+ public void removeFile(OCFile file, boolean removeLocalCopy);
+
+ public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
+
+ public void moveDirectory(OCFile dir, String newPath);
+
+ public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
+
+ public void calculateFolderSize(long id);
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.datamodel;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+
+public class FileDataStorageManager implements DataStorageManager {
+
+ private ContentResolver mContentResolver;
+ private ContentProviderClient mContentProvider;
+ private Account mAccount;
+
+ private static String TAG = "FileDataStorageManager";
+
+ public FileDataStorageManager(Account account, ContentResolver cr) {
+ mContentProvider = null;
+ mContentResolver = cr;
+ mAccount = account;
+ }
+
+ public FileDataStorageManager(Account account, ContentProviderClient cp) {
+ mContentProvider = cp;
+ mContentResolver = null;
+ mAccount = account;
+ }
+
+ @Override
+ public OCFile getFileByPath(String path) {
+ Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
+ OCFile file = null;
+ if (c.moveToFirst()) {
+ file = createFileInstance(c);
+ }
+ c.close();
+ if (file == null && OCFile.PATH_SEPARATOR.equals(path)) {
+ return createRootDir(); // root should always exist
+ }
+ return file;
+ }
+
+
+ private OCFile createRootDir() {
+ OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
+ file.setMimetype("DIR");
+ file.setParentId(DataStorageManager.ROOT_PARENT_ID);
+ saveFile(file);
+ return file;
+ }
+
+ @Override
+ public OCFile getFileById(long id) {
+ Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
+ OCFile file = null;
+ if (c.moveToFirst()) {
+ file = createFileInstance(c);
+ }
+ c.close();
+ return file;
+ }
+
+ public OCFile getFileByLocalPath(String path) {
+ Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
+ OCFile file = null;
+ if (c.moveToFirst()) {
+ file = createFileInstance(c);
+ }
+ c.close();
+ return file;
+ }
+
+ @Override
+ public boolean fileExists(long id) {
+ return fileExists(ProviderTableMeta._ID, String.valueOf(id));
+ }
+
+ @Override
+ public boolean fileExists(String path) {
+ return fileExists(ProviderTableMeta.FILE_PATH, path);
+ }
+
+ @Override
+ public boolean saveFile(OCFile file) {
+ boolean overriden = false;
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
+ cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
+ cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
+ cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
+ cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
+ if (file.getParentId() != 0)
+ cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+ cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
+ if (!file.isDirectory())
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+ cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
+ cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+
+ boolean sameRemotePath = fileExists(file.getRemotePath());
+ boolean changesSizeOfAncestors = false;
+ if (sameRemotePath ||
+ fileExists(file.getFileId()) ) { // for renamed files; no more delete and create
+
+ OCFile oldFile = null;
+ if (sameRemotePath) {
+ oldFile = getFileByPath(file.getRemotePath());
+ file.setFileId(oldFile.getFileId());
+ } else {
+ oldFile = getFileById(file.getFileId());
+ }
+ changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
+
+ overriden = true;
+ if (getContentResolver() != null) {
+ getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
+ ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) });
+ } else {
+ try {
+ getContentProvider().update(ProviderTableMeta.CONTENT_URI,
+ cv, ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) });
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Fail to insert insert file to database "
+ + e.getMessage());
+ }
+ }
+ } else {
+ changesSizeOfAncestors = true;
+ Uri result_uri = null;
+ if (getContentResolver() != null) {
+ result_uri = getContentResolver().insert(
+ ProviderTableMeta.CONTENT_URI_FILE, cv);
+ } else {
+ try {
+ result_uri = getContentProvider().insert(
+ ProviderTableMeta.CONTENT_URI_FILE, cv);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Fail to insert insert file to database "
+ + e.getMessage());
+ }
+ }
+ if (result_uri != null) {
+ long new_id = Long.parseLong(result_uri.getPathSegments()
+ .get(1));
+ file.setFileId(new_id);
+ }
+ }
+
+ if (file.isDirectory()) {
+ calculateFolderSize(file.getFileId());
+ if (file.needsUpdatingWhileSaving()) {
+ for (OCFile f : getDirectoryContent(file))
+ saveFile(f);
+ }
+ }
+
+ if (changesSizeOfAncestors || file.isDirectory()) {
+ updateSizesToTheRoot(file.getParentId());
+ }
+
+ return overriden;
+ }
+
+
+ @Override
+ public void saveFiles(List<OCFile> files) {
+
+ Iterator<OCFile> filesIt = files.iterator();
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(files.size());
+ OCFile file = null;
+
+ // prepare operations to perform
+ while (filesIt.hasNext()) {
+ file = filesIt.next();
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
+ cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
+ cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
+ cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
+ cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
+ if (file.getParentId() != 0)
+ cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+ cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
+ if (!file.isDirectory())
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+ cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
+ cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+
+ if (fileExists(file.getRemotePath())) {
+ OCFile oldFile = getFileByPath(file.getRemotePath());
+ file.setFileId(oldFile.getFileId());
+
+ if (file.isDirectory()) {
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
+ file.setFileLength(oldFile.getFileLength());
+ }
+
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) })
+ .build());
+
+ } else if (fileExists(file.getFileId())) {
+ OCFile oldFile = getFileById(file.getFileId());
+ if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
+ file.setStoragePath(oldFile.getStoragePath());
+
+ if (!file.isDirectory())
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+ else {
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
+ file.setFileLength(oldFile.getFileLength());
+ }
+
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) })
+ .build());
+
+ } else {
+ operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
+ }
+ }
+
+ // apply operations in batch
+ ContentProviderResult[] results = null;
+ try {
+ if (getContentResolver() != null) {
+ results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ results = getContentProvider().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
+ }
+
+ // update new id in file objects for insertions
+ if (results != null) {
+ long newId;
+ for (int i=0; i<results.length; i++) {
+ if (results[i].uri != null) {
+ newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
+ files.get(i).setFileId(newId);
+ //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
+ }
+ }
+ }
+
+ for (OCFile aFile : files) {
+ if (aFile.isDirectory() && aFile.needsUpdatingWhileSaving())
+ saveFiles(getDirectoryContent(aFile));
+ }
+
+ }
+
+ public void setAccount(Account account) {
+ mAccount = account;
+ }
+
+ public Account getAccount() {
+ return mAccount;
+ }
+
+ public void setContentResolver(ContentResolver cr) {
+ mContentResolver = cr;
+ }
+
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public void setContentProvider(ContentProviderClient cp) {
+ mContentProvider = cp;
+ }
+
+ public ContentProviderClient getContentProvider() {
+ return mContentProvider;
+ }
+
+ @Override
+ public Vector<OCFile> getDirectoryContent(OCFile f) {
+ if (f != null && f.isDirectory() && f.getFileId() != -1) {
+ return getDirectoryContent(f.getFileId());
+
+ } else {
+ return new Vector<OCFile>();
+ }
+ }
+
+ private Vector<OCFile> getDirectoryContent(long parentId) {
+
+ Vector<OCFile> ret = new Vector<OCFile>();
+
+ Uri req_uri = Uri.withAppendedPath(
+ ProviderTableMeta.CONTENT_URI_DIR,
+ String.valueOf(parentId));
+ Cursor c = null;
+
+ if (getContentProvider() != null) {
+ try {
+ c = getContentProvider().query(req_uri, null,
+ ProviderTableMeta.FILE_PARENT + "=?" ,
+ new String[] { String.valueOf(parentId)}, null);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, e.getMessage());
+ return ret;
+ }
+ } else {
+ c = getContentResolver().query(req_uri, null,
+ ProviderTableMeta.FILE_PARENT + "=?" ,
+ new String[] { String.valueOf(parentId)}, null);
+ }
+
+ if (c.moveToFirst()) {
+ do {
+ OCFile child = createFileInstance(c);
+ ret.add(child);
+ } while (c.moveToNext());
+ }
+
+ c.close();
+
+ Collections.sort(ret);
+
+ return ret;
+ }
+
+
+
+ private boolean fileExists(String cmp_key, String value) {
+ Cursor c;
+ if (getContentResolver() != null) {
+ c = getContentResolver()
+ .query(ProviderTableMeta.CONTENT_URI,
+ null,
+ cmp_key + "=? AND "
+ + ProviderTableMeta.FILE_ACCOUNT_OWNER
+ + "=?",
+ new String[] { value, mAccount.name }, null);
+ } else {
+ try {
+ c = getContentProvider().query(
+ ProviderTableMeta.CONTENT_URI,
+ null,
+ cmp_key + "=? AND "
+ + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
+ new String[] { value, mAccount.name }, null);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Couldn't determine file existance, assuming non existance: "
+ + e.getMessage());
+ return false;
+ }
+ }
+ boolean retval = c.moveToFirst();
+ c.close();
+ return retval;
+ }
+
+ private Cursor getCursorForValue(String key, String value) {
+ Cursor c = null;
+ if (getContentResolver() != null) {
+ c = getContentResolver()
+ .query(ProviderTableMeta.CONTENT_URI,
+ null,
+ key + "=? AND "
+ + ProviderTableMeta.FILE_ACCOUNT_OWNER
+ + "=?",
+ new String[] { value, mAccount.name }, null);
+ } else {
+ try {
+ c = getContentProvider().query(
+ ProviderTableMeta.CONTENT_URI,
+ null,
+ key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
+ + "=?", new String[] { value, mAccount.name },
+ null);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
+ c = null;
+ }
+ }
+ return c;
+ }
+
+ private OCFile createFileInstance(Cursor c) {
+ OCFile file = null;
+ if (c != null) {
+ file = new OCFile(c.getString(c
+ .getColumnIndex(ProviderTableMeta.FILE_PATH)));
+ file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
+ file.setParentId(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
+ file.setMimetype(c.getString(c
+ .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
+ if (!file.isDirectory()) {
+ file.setStoragePath(c.getString(c
+ .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
+ if (file.getStoragePath() == null) {
+ // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
+ file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
+ }
+ }
+ file.setFileLength(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)));
+ file.setCreationTimestamp(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_CREATION)));
+ file.setModificationTimestamp(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
+ file.setModificationTimestampAtLastSyncForData(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
+ file.setLastSyncDateForProperties(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
+ file.setLastSyncDateForData(c.getLong(c.
+ getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
+ file.setKeepInSync(c.getInt(
+ c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
+ }
+ return file;
+ }
+
+ @Override
+ public void removeFile(OCFile file, boolean removeLocalCopy) {
+ Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
+ if (getContentProvider() != null) {
+ try {
+ getContentProvider().delete(file_uri,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+ new String[]{mAccount.name});
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ } else {
+ getContentResolver().delete(file_uri,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
+ new String[]{mAccount.name});
+ }
+ if (file.isDown() && removeLocalCopy) {
+ new File(file.getStoragePath()).delete();
+ }
+ if (file.isDirectory() && removeLocalCopy) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
+ f.delete();
+ }
+ }
+
+ if (file.getFileLength() > 0) {
+ updateSizesToTheRoot(file.getParentId());
+ }
+ }
+
+ @Override
+ public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
+ // TODO consider possible failures
+ if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
+ Vector<OCFile> children = getDirectoryContent(dir);
+ if (children.size() > 0) {
+ OCFile child = null;
+ for (int i=0; i<children.size(); i++) {
+ child = children.get(i);
+ if (child.isDirectory()) {
+ removeDirectory(child, removeDBData, removeLocalContent);
+ } else {
+ if (removeDBData) {
+ removeFile(child, removeLocalContent);
+ } else if (removeLocalContent) {
+ if (child.isDown()) {
+ new File(child.getStoragePath()).delete();
+ }
+ }
+ }
+ }
+ }
+ if (removeDBData) {
+ removeFile(dir, true);
+ }
+
+ if (dir.getFileLength() > 0) {
+ updateSizesToTheRoot(dir.getParentId());
+ }
+ }
+ }
+
+
+ /**
+ * Updates database for a folder that was moved to a different location.
+ *
+ * TODO explore better (faster) implementations
+ * TODO throw exceptions up !
+ */
+ @Override
+ public void moveDirectory(OCFile dir, String newPath) {
+ // TODO check newPath
+
+ if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
+ /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
+ Cursor c = null;
+ if (getContentProvider() != null) {
+ try {
+ c = getContentProvider().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC ");
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, e.getMessage());
+ }
+ } else {
+ c = getContentResolver().query(ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
+ new String[] { mAccount.name, dir.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC ");
+ }
+
+ /// 2. prepare a batch of update operations to change all the descendants
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
+ int lengthOfOldPath = dir.getRemotePath().length();
+ String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
+ if (c.moveToFirst()) {
+ do {
+ ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
+ OCFile child = createFileInstance(c);
+ cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
+ if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
+ }
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(child.getFileId()) })
+ .build());
+ } while (c.moveToNext());
+ }
+ c.close();
+
+ /// 3. apply updates in batch
+ try {
+ if (getContentResolver() != null) {
+ getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ getContentProvider().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
+ }
+
+ }
+ }
+
+ @Override
+ public Vector<OCFile> getDirectoryImages(OCFile directory) {
+ Vector<OCFile> ret = new Vector<OCFile>();
+ if (directory != null) {
+ // TODO better implementation, filtering in the access to database (if possible) instead of here
+ Vector<OCFile> tmp = getDirectoryContent(directory);
+ OCFile current = null;
+ for (int i=0; i<tmp.size(); i++) {
+ current = tmp.get(i);
+ if (current.isImage()) {
+ ret.add(current);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Calculate and save the folderSize on DB
+ * @param id
+ */
+ @Override
+ public void calculateFolderSize(long id) {
+ long folderSize = 0;
+
+ Vector<OCFile> files = getDirectoryContent(id);
+
+ for (OCFile f: files)
+ {
+ folderSize = folderSize + f.getFileLength();
+ }
+
+ updateSize(id, folderSize);
+ }
+
+ /**
+ * Update the size value of an OCFile in DB
+ */
+ private int updateSize(long id, long size) {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size);
+ int result = -1;
+ if (getContentResolver() != null) {
+ result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(id) });
+ } else {
+ try {
+ result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(id) });
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage());
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Update the size of a subtree of folder from a file to the root
+ * @param parentId: parent of the file
+ */
+ private void updateSizesToTheRoot(long parentId) {
+
+ OCFile file;
+
+ while (parentId != 0) {
+
+ // Update the size of the parent
+ calculateFolderSize(parentId);
+
+ // search the next parent
+ file = getFileById(parentId);
+ parentId = file.getParentId();
+
+ }
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.datamodel;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.webkit.MimeTypeMap;
+
+public class OCFile implements Parcelable, Comparable<OCFile> {
+
+ public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
+ @Override
+ public OCFile createFromParcel(Parcel source) {
+ return new OCFile(source);
+ }
+
+ @Override
+ public OCFile[] newArray(int size) {
+ return new OCFile[size];
+ }
+ };
+
+ public static final String PATH_SEPARATOR = "/";
+
+ private static final String TAG = OCFile.class.getSimpleName();
+
+ private long mId;
+ private long mParentId;
+ private long mLength;
+ private long mCreationTimestamp;
+ private long mModifiedTimestamp;
+ private long mModifiedTimestampAtLastSyncForData;
+ private String mRemotePath;
+ private String mLocalPath;
+ private String mMimeType;
+ private boolean mNeedsUpdating;
+ private long mLastSyncDateForProperties;
+ private long mLastSyncDateForData;
+ private boolean mKeepInSync;
+
+ private String mEtag;
+
+ /**
+ * Create new {@link OCFile} with given path.
+ *
+ * The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
+ *
+ * @param path The remote path of the file.
+ */
+ public OCFile(String path) {
+ resetData();
+ mNeedsUpdating = false;
+ if (path == null || path.length() <= 0 || !path.startsWith(PATH_SEPARATOR)) {
+ throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
+ }
+ mRemotePath = path;
+ }
+
+ /**
+ * Reconstruct from parcel
+ *
+ * @param source The source parcel
+ */
+ private OCFile(Parcel source) {
+ mId = source.readLong();
+ mParentId = source.readLong();
+ mLength = source.readLong();
+ mCreationTimestamp = source.readLong();
+ mModifiedTimestamp = source.readLong();
+ mModifiedTimestampAtLastSyncForData = source.readLong();
+ mRemotePath = source.readString();
+ mLocalPath = source.readString();
+ mMimeType = source.readString();
+ mNeedsUpdating = source.readInt() == 0;
+ mKeepInSync = source.readInt() == 1;
+ mLastSyncDateForProperties = source.readLong();
+ mLastSyncDateForData = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mId);
+ dest.writeLong(mParentId);
+ dest.writeLong(mLength);
+ dest.writeLong(mCreationTimestamp);
+ dest.writeLong(mModifiedTimestamp);
+ dest.writeLong(mModifiedTimestampAtLastSyncForData);
+ dest.writeString(mRemotePath);
+ dest.writeString(mLocalPath);
+ dest.writeString(mMimeType);
+ dest.writeInt(mNeedsUpdating ? 1 : 0);
+ dest.writeInt(mKeepInSync ? 1 : 0);
+ dest.writeLong(mLastSyncDateForProperties);
+ dest.writeLong(mLastSyncDateForData);
+ }
+
+ /**
+ * Gets the ID of the file
+ *
+ * @return the file ID
+ */
+ public long getFileId() {
+ return mId;
+ }
+
+ /**
+ * Returns the remote path of the file on ownCloud
+ *
+ * @return The remote path to the file
+ */
+ public String getRemotePath() {
+ return mRemotePath;
+ }
+
+ /**
+ * Can be used to check, whether or not this file exists in the database
+ * already
+ *
+ * @return true, if the file exists in the database
+ */
+ public boolean fileExists() {
+ return mId != -1;
+ }
+
+ /**
+ * Use this to find out if this file is a Directory
+ *
+ * @return true if it is a directory
+ */
+ public boolean isDirectory() {
+ return mMimeType != null && mMimeType.equals("DIR");
+ }
+
+ /**
+ * Use this to check if this file is available locally
+ *
+ * @return true if it is
+ */
+ public boolean isDown() {
+ if (mLocalPath != null && mLocalPath.length() > 0) {
+ File file = new File(mLocalPath);
+ return (file.exists());
+ }
+ return false;
+ }
+
+ /**
+ * The path, where the file is stored locally
+ *
+ * @return The local path to the file
+ */
+ public String getStoragePath() {
+ return mLocalPath;
+ }
+
+ /**
+ * Can be used to set the path where the file is stored
+ *
+ * @param storage_path to set
+ */
+ public void setStoragePath(String storage_path) {
+ mLocalPath = storage_path;
+ }
+
+ /**
+ * Get a UNIX timestamp of the file creation time
+ *
+ * @return A UNIX timestamp of the time that file was created
+ */
+ public long getCreationTimestamp() {
+ return mCreationTimestamp;
+ }
+
+ /**
+ * Set a UNIX timestamp of the time the file was created
+ *
+ * @param creation_timestamp to set
+ */
+ public void setCreationTimestamp(long creation_timestamp) {
+ mCreationTimestamp = creation_timestamp;
+ }
+
+ /**
+ * Get a UNIX timestamp of the file modification time.
+ *
+ * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
+ * in the last synchronization of the properties of this file.
+ */
+ public long getModificationTimestamp() {
+ return mModifiedTimestamp;
+ }
+
+ /**
+ * Set a UNIX timestamp of the time the time the file was modified.
+ *
+ * To update with the value returned by the server in every synchronization of the properties
+ * of this file.
+ *
+ * @param modification_timestamp to set
+ */
+ public void setModificationTimestamp(long modification_timestamp) {
+ mModifiedTimestamp = modification_timestamp;
+ }
+
+
+ /**
+ * Get a UNIX timestamp of the file modification time.
+ *
+ * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
+ * in the last synchronization of THE CONTENTS of this file.
+ */
+ public long getModificationTimestampAtLastSyncForData() {
+ return mModifiedTimestampAtLastSyncForData;
+ }
+
+ /**
+ * Set a UNIX timestamp of the time the time the file was modified.
+ *
+ * To update with the value returned by the server in every synchronization of THE CONTENTS
+ * of this file.
+ *
+ * @param modification_timestamp to set
+ */
+ public void setModificationTimestampAtLastSyncForData(long modificationTimestamp) {
+ mModifiedTimestampAtLastSyncForData = modificationTimestamp;
+ }
+
+
+
+ /**
+ * Returns the filename and "/" for the root directory
+ *
+ * @return The name of the file
+ */
+ public String getFileName() {
+ File f = new File(getRemotePath());
+ return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
+ }
+
+ /**
+ * Sets the name of the file
+ *
+ * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory
+ */
+ public void setFileName(String name) {
+ Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
+ if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
+ String parent = (new File(getRemotePath())).getParent();
+ parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
+ mRemotePath = parent + name;
+ if (isDirectory()) {
+ mRemotePath += PATH_SEPARATOR;
+ }
+ Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
+ }
+ }
+
+ /**
+ * Can be used to get the Mimetype
+ *
+ * @return the Mimetype as a String
+ */
+ public String getMimetype() {
+ return mMimeType;
+ }
+
+ /**
+ * Adds a file to this directory. If this file is not a directory, an
+ * exception gets thrown.
+ *
+ * @param file to add
+ * @throws IllegalStateException if you try to add a something and this is
+ * not a directory
+ */
+ public void addFile(OCFile file) throws IllegalStateException {
+ if (isDirectory()) {
+ file.mParentId = mId;
+ mNeedsUpdating = true;
+ return;
+ }
+ throw new IllegalStateException(
+ "This is not a directory where you can add stuff to!");
+ }
+
+ /**
+ * Used internally. Reset all file properties
+ */
+ private void resetData() {
+ mId = -1;
+ mRemotePath = null;
+ mParentId = 0;
+ mLocalPath = null;
+ mMimeType = null;
+ mLength = 0;
+ mCreationTimestamp = 0;
+ mModifiedTimestamp = 0;
+ mModifiedTimestampAtLastSyncForData = 0;
+ mLastSyncDateForProperties = 0;
+ mLastSyncDateForData = 0;
+ mKeepInSync = false;
+ mNeedsUpdating = false;
+ }
+
+ /**
+ * Sets the ID of the file
+ *
+ * @param file_id to set
+ */
+ public void setFileId(long file_id) {
+ mId = file_id;
+ }
+
+ /**
+ * Sets the Mime-Type of the
+ *
+ * @param mimetype to set
+ */
+ public void setMimetype(String mimetype) {
+ mMimeType = mimetype;
+ }
+
+ /**
+ * Sets the ID of the parent folder
+ *
+ * @param parent_id to set
+ */
+ public void setParentId(long parent_id) {
+ mParentId = parent_id;
+ }
+
+ /**
+ * Sets the file size in bytes
+ *
+ * @param file_len to set
+ */
+ public void setFileLength(long file_len) {
+ mLength = file_len;
+ }
+
+ /**
+ * Returns the size of the file in bytes
+ *
+ * @return The filesize in bytes
+ */
+ public long getFileLength() {
+ return mLength;
+ }
+
+ /**
+ * Returns the ID of the parent Folder
+ *
+ * @return The ID
+ */
+ public long getParentId() {
+ return mParentId;
+ }
+
+ /**
+ * Check, if this file needs updating
+ *
+ * @return
+ */
+ public boolean needsUpdatingWhileSaving() {
+ return mNeedsUpdating;
+ }
+
+ public long getLastSyncDateForProperties() {
+ return mLastSyncDateForProperties;
+ }
+
+ public void setLastSyncDateForProperties(long lastSyncDate) {
+ mLastSyncDateForProperties = lastSyncDate;
+ }
+
+ public long getLastSyncDateForData() {
+ return mLastSyncDateForData;
+ }
+
+ public void setLastSyncDateForData(long lastSyncDate) {
+ mLastSyncDateForData = lastSyncDate;
+ }
+
+ public void setKeepInSync(boolean keepInSync) {
+ mKeepInSync = keepInSync;
+ }
+
+ public boolean keepInSync() {
+ return mKeepInSync;
+ }
+
+ @Override
+ public int describeContents() {
+ return this.hashCode();
+ }
+
+ @Override
+ public int compareTo(OCFile another) {
+ if (isDirectory() && another.isDirectory()) {
+ return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
+ } else if (isDirectory()) {
+ return -1;
+ } else if (another.isDirectory()) {
+ return 1;
+ }
+ return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o instanceof OCFile){
+ OCFile that = (OCFile) o;
+ if(that != null){
+ return this.mId == that.mId;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
+ asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
+ return asString;
+ }
+
+ public String getEtag() {
+ return mEtag;
+ }
+
+ public long getLocalModificationTimestamp() {
+ if (mLocalPath != null && mLocalPath.length() > 0) {
+ File f = new File(mLocalPath);
+ return f.lastModified();
+ }
+ return 0;
+ }
+
+ /** @return 'True' if the file contains audio */
+ public boolean isAudio() {
+ return (mMimeType != null && mMimeType.startsWith("audio/"));
+ }
+
+ /** @return 'True' if the file contains video */
+ public boolean isVideo() {
+ return (mMimeType != null && mMimeType.startsWith("video/"));
+ }
+
+ /** @return 'True' if the file contains an image */
+ public boolean isImage() {
+ return ((mMimeType != null && mMimeType.startsWith("image/")) ||
+ getMimeTypeFromName().startsWith("image/"));
+ }
+
+ public String getMimeTypeFromName() {
+ String extension = "";
+ int pos = mRemotePath.lastIndexOf('.');
+ if (pos >= 0) {
+ extension = mRemotePath.substring(pos + 1);
+ }
+ String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
+ return (result != null) ? result : "";
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011-2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.db;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * Custom database helper for ownCloud
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class DbHandler {
+ private SQLiteDatabase mDB;
+ private OpenerHelper mHelper;
+ private final String mDatabaseName; // = "ownCloud";
+ private final int mDatabaseVersion = 3;
+
+ private final String TABLE_INSTANT_UPLOAD = "instant_upload";
+
+ public static final int UPLOAD_STATUS_UPLOAD_LATER = 0;
+ public static final int UPLOAD_STATUS_UPLOAD_FAILED = 1;
+
+ public DbHandler(Context context) {
+ mHelper = new OpenerHelper(context);
+ mDB = mHelper.getWritableDatabase();
+ mDatabaseName = MainApp.getDBName();
+ }
+
+ public void close() {
+ mDB.close();
+ }
+
+ public boolean putFileForLater(String filepath, String account, String message) {
+ ContentValues cv = new ContentValues();
+ cv.put("path", filepath);
+ cv.put("account", account);
+ cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER);
+ cv.put("message", message);
+ long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv);
+ Log_OC.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath);
+ return result != -1;
+ }
+
+ public int updateFileState(String filepath, Integer status, String message) {
+ ContentValues cv = new ContentValues();
+ cv.put("attempt", status);
+ cv.put("message", message);
+ int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath });
+ Log_OC.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath);
+ return result;
+ }
+
+ public Cursor getAwaitingFiles() {
+ return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt=" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
+ }
+
+ public Cursor getFailedFiles() {
+ return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt>" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
+ }
+
+ public void clearFiles() {
+ mDB.delete(TABLE_INSTANT_UPLOAD, null, null);
+ }
+
+ /**
+ *
+ * @param localPath
+ * @return true when one or more pending files was removed
+ */
+ public boolean removeIUPendingFile(String localPath) {
+ long result = mDB.delete(TABLE_INSTANT_UPLOAD, "path = ?", new String[] { localPath });
+ Log_OC.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath);
+ return result != 0;
+
+ }
+
+ private class OpenerHelper extends SQLiteOpenHelper {
+ public OpenerHelper(Context context) {
+ super(context, mDatabaseName, null, mDatabaseVersion);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT,"
+ + " account TEXT,attempt INTEGER,message TEXT);");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion < 2) {
+ db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;");
+ }
+ db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;");
+
+ }
+ }
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.db;\r
+\r
+import com.owncloud.android.MainApp;\r
+\r
+import android.net.Uri;\r
+import android.provider.BaseColumns;\r
+\r
+/**\r
+ * Meta-Class that holds various static field information\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class ProviderMeta {\r
+\r
+ /* These constants are now in MainApp\r
+ public static final String AUTHORITY_FILES = "org.owncloud";\r
+ public static final String DB_FILE = "owncloud.db";\r
+ */\r
+ public static final String DB_NAME = "filelist";\r
+ public static final int DB_VERSION = 4;\r
+\r
+ private ProviderMeta() {\r
+ }\r
+\r
+ static public class ProviderTableMeta implements BaseColumns {\r
+ public static final String DB_NAME = "filelist";\r
+ public static final Uri CONTENT_URI = Uri.parse("content://"\r
+ + MainApp.getAuthority() + "/");\r
+ public static final Uri CONTENT_URI_FILE = Uri.parse("content://"\r
+ + MainApp.getAuthority() + "/file");\r
+ public static final Uri CONTENT_URI_DIR = Uri.parse("content://"\r
+ + MainApp.getAuthority() + "/dir");\r
+\r
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";\r
+ public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";\r
+\r
+ public static final String FILE_PARENT = "parent";\r
+ public static final String FILE_NAME = "filename";\r
+ public static final String FILE_CREATION = "created";\r
+ public static final String FILE_MODIFIED = "modified";\r
+ public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";\r
+ public static final String FILE_CONTENT_LENGTH = "content_length";\r
+ public static final String FILE_CONTENT_TYPE = "content_type";\r
+ public static final String FILE_STORAGE_PATH = "media_path";\r
+ public static final String FILE_PATH = "path";\r
+ public static final String FILE_ACCOUNT_OWNER = "file_owner";\r
+ public static final String FILE_LAST_SYNC_DATE = "last_sync_date"; // _for_properties, but let's keep it as it is\r
+ public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
+ public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
+\r
+ public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
+ + " collate nocase asc";\r
+\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.extensions;
+
+import android.os.Bundle;
+import android.support.v4.app.FragmentManager;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+public class ExtensionsAvailableActivity extends SherlockFragmentActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ FragmentManager fm = getSupportFragmentManager();
+ ExtensionsAvailableDialog ead = new ExtensionsAvailableDialog();
+ ead.show(fm, "extensions_available_dialog");
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.extensions;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+
+public class ExtensionsAvailableDialog extends DialogFragment implements
+ OnClickListener {
+
+ public ExtensionsAvailableDialog() {
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.extensions_available_dialog,
+ container);
+ CustomButton btnYes = (CustomButton) view.findViewById(R.id.buttonYes);
+ CustomButton btnNo = (CustomButton) view.findViewById(R.id.buttonNo);
+
+ btnYes.setOnClickListener(this);
+ btnNo.setOnClickListener(this);
+ getDialog().setTitle(R.string.extensions_avail_title);
+ return view;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.buttonYes: {
+ Intent i = new Intent(getActivity(), ExtensionsListActivity.class);
+ startActivity(i);
+ getActivity().finish();
+ }
+ break;
+ case R.id.buttonNo:
+ getActivity().finish();
+ break;
+ default:
+ Log_OC.e("EAD", "Button with unknown id clicked " + v.getId());
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.extensions;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+
+
+import android.R;
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.widget.SimpleAdapter;
+
+public class ExtensionsListActivity extends ListActivity {
+
+ private static final String packages_url = "http://alefzero.eu/a/packages.php";
+
+ private Thread mGetterThread;
+ private final Handler mHandler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mGetterThread = new Thread(new JsonGetter());
+ mGetterThread.start();
+ }
+
+ public void done(JSONArray a) {
+ LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
+ for (int i = 0; i < a.length(); ++i) {
+ try {
+ ExtensionApplicationEntry ela = new ExtensionApplicationEntry(
+ ((JSONObject) a.get(i)));
+ HashMap<String, String> ss = new HashMap<String, String>();
+ ss.put("NAME", ela.getName());
+ ss.put("DESC", ela.getDescription());
+ ll.add(ss);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ setListAdapter(new SimpleAdapter(this, ll, R.layout.simple_list_item_2,
+ new String[] { "NAME", "DESC" }, new int[] {
+ android.R.id.text1, android.R.id.text2 }));
+
+ }
+
+ private class JsonGetter implements Runnable {
+
+ @Override
+ public void run() {
+ HttpClient hc = new HttpClient();
+ GetMethod gm = new GetMethod(packages_url);
+ final JSONArray ar;
+ try {
+ hc.executeMethod(gm);
+ Log_OC.e("ASD", gm.getResponseBodyAsString() + "");
+ ar = new JSONObject(gm.getResponseBodyAsString())
+ .getJSONArray("apps");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ done(ar);
+ }
+ });
+
+ }
+
+ }
+
+ private class ExtensionApplicationEntry {
+ private static final String APP_NAME = "name";
+ private static final String APP_VERSION = "version";
+ private static final String APP_DESC = "description";
+ private static final String APP_ICON = "icon";
+ private static final String APP_URL = "download";
+ private static final String APP_PLAYID = "play_id";
+
+ private String mName, mDescription, mIcon, mDownload, mPlayId;
+ private OwnCloudVersion mVersion;
+
+ public ExtensionApplicationEntry(JSONObject appentry) {
+ try {
+ mName = appentry.getString(APP_NAME);
+ mDescription = appentry.getString(APP_DESC);
+ mIcon = appentry.getString(APP_ICON);
+ mDownload = appentry.getString(APP_URL);
+ mPlayId = appentry.getString(APP_PLAYID);
+ mVersion = new OwnCloudVersion(appentry.getString(APP_VERSION));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getDescription() {
+ return mDescription;
+ }
+
+ @SuppressWarnings("unused")
+ public String getIcon() {
+ return mIcon;
+ }
+
+ @SuppressWarnings("unused")
+ public String getDownload() {
+ return mDownload;
+ }
+
+ @SuppressWarnings("unused")
+ public String getPlayId() {
+ return mPlayId;
+ }
+
+ @SuppressWarnings("unused")
+ public OwnCloudVersion getVersion() {
+ return mVersion;
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.files.services.FileObserverService;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class BootupBroadcastReceiver extends BroadcastReceiver {
+
+ private static String TAG = "BootupBroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ Log_OC.wtf(TAG, "Incorrect action sent " + intent.getAction());
+ return;
+ }
+ Log_OC.d(TAG, "Starting file observer service...");
+ Intent i = new Intent(context, FileObserverService.class);
+ i.putExtra(FileObserverService.KEY_FILE_CMD,
+ FileObserverService.CMD_INIT_OBSERVED_LIST);
+ context.startService(i);
+ Log_OC.d(TAG, "DONE");
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files;
+
+import com.owncloud.android.datamodel.OCFile;
+
+public interface FileHandler {
+
+ /**
+ * TODO
+ */
+ public void openFile(OCFile file);
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+//import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.State;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore.Images.Media;
+import android.webkit.MimeTypeMap;
+
+
+public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
+
+ private static String TAG = "InstantUploadBroadcastReceiver";
+ private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
+ //Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
+ private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
+ //Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
+ private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log_OC.d(TAG, "Received: " + intent.getAction());
+
+ FileUploader fileUploader = new FileUploader();
+
+ if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
+ handleConnectivityAction(context, intent);
+ }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
+ handleNewPhotoAction(context, intent);
+ Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
+ } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
+ handleNewPhotoAction(context, intent);
+ Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
+ } else if (intent.getAction().equals(fileUploader.getUploadFinishMessage())) {
+ handleUploadFinished(context, intent);
+ } else {
+ Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
+ }
+ }
+
+ private void handleUploadFinished(Context context, Intent intent) {
+ // remove successfull uploading, ignore rest for reupload on reconnect
+ /*
+ if (intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false)) {
+ DbHandler db = new DbHandler(context);
+ String localPath = intent.getStringExtra(FileUploader.EXTRA_OLD_FILE_PATH);
+ if (!db.removeIUPendingFile(localPath)) {
+ Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
+ }
+ db.close();
+ }
+ */
+ }
+
+ private void handleNewPhotoAction(Context context, Intent intent) {
+ if (!instantUploadEnabled(context)) {
+ Log_OC.d(TAG, "Instant upload disabled, aborting uploading");
+ return;
+ }
+
+ Account account = AccountUtils.getCurrentOwnCloudAccount(context);
+ if (account == null) {
+ Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
+ return;
+ }
+
+ Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
+
+ if (!c.moveToFirst()) {
+ Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
+ return;
+ }
+
+ String file_path = c.getString(c.getColumnIndex(Media.DATA));
+ String file_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME));
+ String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
+
+ c.close();
+ Log_OC.e(TAG, file_path + "");
+
+ // same always temporally the picture to upload
+ DbHandler db = new DbHandler(context);
+ db.putFileForLater(file_path, account.name, null);
+ db.close();
+
+ if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
+ return;
+ }
+
+ // register for upload finishe message
+ // there is a litte problem with android API, we can register for
+ // particular
+ // intent in registerReceiver but we cannot unregister from precise
+ // intent
+ // we can unregister from entire listenings but thats suck a bit.
+ // On the other hand this might be only for dynamicly registered
+ // broadcast receivers, needs investigation.
+ /*IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+ context.getApplicationContext().registerReceiver(this, filter);*/
+
+ Intent i = new Intent(context, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, account);
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, file_name));
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
+ i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+ context.startService(i);
+
+ }
+
+ private void handleConnectivityAction(Context context, Intent intent) {
+ if (!instantUploadEnabled(context)) {
+ Log_OC.d(TAG, "Instant upload disabled, abording uploading");
+ return;
+ }
+
+ if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
+ && isOnline(context)
+ && (!instantUploadViaWiFiOnly(context) || (instantUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
+ DbHandler db = new DbHandler(context);
+ Cursor c = db.getAwaitingFiles();
+ if (c.moveToFirst()) {
+ //IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
+ //context.getApplicationContext().registerReceiver(this, filter);
+ do {
+ String account_name = c.getString(c.getColumnIndex("account"));
+ String file_path = c.getString(c.getColumnIndex("path"));
+ File f = new File(file_path);
+ if (f.exists()) {
+ Account account = new Account(account_name, MainApp.getAccountType());
+
+ String mimeType = null;
+ try {
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ f.getName().substring(f.getName().lastIndexOf('.') + 1));
+
+ } catch (Throwable e) {
+ Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName());
+ }
+ if (mimeType == null)
+ mimeType = "application/octet-stream";
+
+ Intent i = new Intent(context, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, account);
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, f.getName()));
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
+ context.startService(i);
+
+ } else {
+ Log_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
+ }
+ } while (c.moveToNext());
+ }
+ c.close();
+ db.close();
+ }
+
+ }
+
+ public static boolean isOnline(Context context) {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
+ }
+
+ public static boolean isConnectedViaWiFi(Context context) {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm != null && cm.getActiveNetworkInfo() != null
+ && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
+ && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
+ }
+
+ public static boolean instantUploadEnabled(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
+ }
+
+ public static boolean instantUploadViaWiFiOnly(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+
+
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+import android.os.FileObserver;
+
+public class OwnCloudFileObserver extends FileObserver {
+
+ public static int CHANGES_ONLY = CLOSE_WRITE;
+
+ private static String TAG = OwnCloudFileObserver.class.getSimpleName();
+
+ private String mPath;
+ private int mMask;
+ private Account mOCAccount;
+ //private OCFile mFile;
+ private Context mContext;
+
+
+ public OwnCloudFileObserver(String path, Account account, Context context, int mask) {
+ super(path, mask);
+ if (path == null)
+ throw new IllegalArgumentException("NULL path argument received");
+ /*if (file == null)
+ throw new IllegalArgumentException("NULL file argument received");*/
+ if (account == null)
+ throw new IllegalArgumentException("NULL account argument received");
+ if (context == null)
+ throw new IllegalArgumentException("NULL context argument received");
+ /*if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
+ throw new IllegalArgumentException("File argument is not linked to the local file set in path argument"); */
+ mPath = path;
+ //mFile = file;
+ mOCAccount = account;
+ mContext = context;
+ mMask = mask;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
+ if ((event & mMask) == 0) {
+ Log_OC.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
+ " with registered for " + mMask + " and original path " +
+ mPath);
+ return;
+ }
+ FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
+ OCFile file = storageManager.getFileByLocalPath(mPath); // a fresh object is needed; many things could have occurred to the file since it was registered to observe
+ // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE;
+ SynchronizeFileOperation sfo = new SynchronizeFileOperation(file,
+ null,
+ storageManager,
+ mOCAccount,
+ true,
+ true,
+ mContext);
+ RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+ // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
+ Intent i = new Intent(mContext, ConflictsResolveActivity.class);
+ i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mOCAccount);
+ mContext.startActivity(i);
+ }
+ // TODO save other errors in some point where the user can inspect them later;
+ // or maybe just toast them;
+ // or nothing, very strange fails
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files.managers;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.owncloud.android.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.widget.RemoteViews;
+
+
+public class OCNotificationManager {
+
+ enum NotificationType {
+ NOTIFICATION_SIMPLE,
+ NOTIFICATION_PROGRESS
+ }
+
+ static public class NotificationData {
+ private String mText, mSubtitle;
+ private int mPercent;
+ private boolean mOngoing;
+
+ public NotificationData(String text, String subtitle, boolean ongoing) {
+ this(text, subtitle, -1, ongoing);
+ }
+
+ public NotificationData(int percent, boolean ongoing) {
+ this(null, null, percent, ongoing);
+ }
+
+ public NotificationData(String text, int percent, boolean ongoing) {
+ this(text, null, percent, ongoing);
+ }
+
+ public NotificationData(String text, String subtitle, int percent, boolean ongoing) {
+ mText = text;
+ mPercent = percent;
+ mSubtitle = subtitle;
+ mOngoing = ongoing;
+ }
+
+ public String getText() { return mText; }
+ public int getPercent() { return mPercent; }
+ public String getSubtitle() { return mSubtitle; }
+ public boolean getOngoing() { return mOngoing; }
+ }
+
+ static private OCNotificationManager mInstance = null;
+
+ private class NotificationTypePair {
+ public Notification mNotificaiton;
+ public NotificationType mType;
+ public NotificationTypePair(Notification n, NotificationType type) {
+ mNotificaiton = n;
+ mType = type;
+ }
+ }
+
+ private Context mContext;
+ private Map<Integer, NotificationTypePair> mNotificationMap;
+ private int mNotificationCounter;
+ NotificationManager mNM;
+
+ static OCNotificationManager getInstance(Context context) {
+ if (mInstance == null)
+ mInstance = new OCNotificationManager(context);
+ return mInstance;
+ }
+
+ OCNotificationManager(Context context) {
+ mContext = context;
+ mNotificationMap = new HashMap<Integer, NotificationTypePair>();
+ mNM = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mNotificationCounter = 0;
+ }
+
+ public int postNotification(NotificationType type, NotificationData data) {
+ mNotificationCounter++;
+ Notification notification = null;
+
+ switch (type) {
+ case NOTIFICATION_SIMPLE:
+ notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
+ break;
+ case NOTIFICATION_PROGRESS:
+ notification = new Notification();
+ notification.contentView = new RemoteViews(mContext.getPackageName(), R.layout.progressbar_layout);
+ notification.contentView.setTextViewText(R.id.status_text,
+ data.getText());
+ notification.contentView.setImageViewResource(R.id.status_icon,
+ R.id.icon);
+ notification.contentView.setProgressBar(R.id.status_progress,
+ 100,
+ data.getPercent(),
+ false);
+ break;
+ default:
+ return -1;
+ }
+ if (data.getOngoing()) {
+ notification.flags |= notification.flags | Notification.FLAG_ONGOING_EVENT;
+ }
+
+ mNotificationMap.put(mNotificationCounter, new NotificationTypePair(notification, type));
+ return mNotificationCounter;
+ }
+
+ public boolean updateNotification(int notification_id, NotificationData data) {
+ if (!mNotificationMap.containsKey(notification_id)) {
+ return false;
+ }
+ NotificationTypePair pair = mNotificationMap.get(notification_id);
+ switch (pair.mType) {
+ case NOTIFICATION_PROGRESS:
+ pair.mNotificaiton.contentView.setProgressBar(R.id.status_text,
+ 100,
+ data.getPercent(),
+ false);
+ return true;
+ case NOTIFICATION_SIMPLE:
+ pair.mNotificaiton = new Notification(R.drawable.icon,
+ data.getText(), System.currentTimeMillis());
+ mNM.notify(notification_id, pair.mNotificaiton);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public void discardNotification(int notification_id) {
+ mNM.cancel(notification_id);
+ mNotificationMap.remove(notification_id);
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.AbstractList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.DownloadFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.widget.RemoteViews;
+
+import eu.alefzero.webdav.WebdavClient;
+
+public class FileDownloader extends Service implements OnDatatransferProgressListener {
+
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+ public static final String EXTRA_FILE = "FILE";
+
+ private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
+ private static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
+ public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
+ public static final String EXTRA_FILE_PATH = "FILE_PATH";
+ public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
+ public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
+
+ private static final String TAG = "FileDownloader";
+
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+ private IBinder mBinder;
+ private WebdavClient mDownloadClient = null;
+ private Account mLastAccount = null;
+ private FileDataStorageManager mStorageManager;
+
+ private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
+ private DownloadFileOperation mCurrentDownload = null;
+
+ private NotificationManager mNotificationManager;
+ private Notification mNotification;
+ private int mLastPercent;
+
+
+ public String getDownloadAddedMessage() {
+ return getClass().getName().toString() + DOWNLOAD_ADDED_MESSAGE;
+ }
+
+ public String getDownloadFinishMessage() {
+ return getClass().getName().toString() + DOWNLOAD_FINISH_MESSAGE;
+ }
+
+ /**
+ * Builds a key for mPendingDownloads from the account and file to download
+ *
+ * @param account Account where the file to download is stored
+ * @param file File to download
+ */
+ private String buildRemoteName(Account account, OCFile file) {
+ return account.name + file.getRemotePath();
+ }
+
+
+ /**
+ * Service initialization
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ HandlerThread thread = new HandlerThread("FileDownloaderThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper, this);
+ mBinder = new FileDownloaderBinder();
+ }
+
+ /**
+ * Entry point to add one or several files to the queue of downloads.
+ *
+ * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working
+ * although the caller activity goes away.
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if ( !intent.hasExtra(EXTRA_ACCOUNT) ||
+ !intent.hasExtra(EXTRA_FILE)
+ /*!intent.hasExtra(EXTRA_FILE_PATH) ||
+ !intent.hasExtra(EXTRA_REMOTE_PATH)*/
+ ) {
+ Log_OC.e(TAG, "Not enough information provided in intent");
+ return START_NOT_STICKY;
+ }
+ Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
+ OCFile file = intent.getParcelableExtra(EXTRA_FILE);
+
+ AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)
+ String downloadKey = buildRemoteName(account, file);
+ try {
+ DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
+ mPendingDownloads.putIfAbsent(downloadKey, newDownload);
+ newDownload.addDatatransferProgressListener(this);
+ newDownload.addDatatransferProgressListener((FileDownloaderBinder)mBinder);
+ requestedDownloads.add(downloadKey);
+ sendBroadcastNewDownload(newDownload);
+
+ } catch (IllegalArgumentException e) {
+ Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+ return START_NOT_STICKY;
+ }
+
+ if (requestedDownloads.size() > 0) {
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = requestedDownloads;
+ mServiceHandler.sendMessage(msg);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+
+ /**
+ * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files.
+ *
+ * Implemented to perform cancellation, pause and resume of existing downloads.
+ */
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return mBinder;
+ }
+
+
+ /**
+ * Called when ALL the bound clients were onbound.
+ */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ ((FileDownloaderBinder)mBinder).clearListeners();
+ return false; // not accepting rebinding (default behaviour)
+ }
+
+
+ /**
+ * Binder to let client components to perform operations on the queue of downloads.
+ *
+ * It provides by itself the available operations.
+ */
+ public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
+
+ /**
+ * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder} instance
+ */
+ private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
+
+
+ /**
+ * Cancels a pending or current download of a remote file.
+ *
+ * @param account Owncloud account where the remote file is stored.
+ * @param file A file in the queue of pending downloads
+ */
+ public void cancel(Account account, OCFile file) {
+ DownloadFileOperation download = null;
+ synchronized (mPendingDownloads) {
+ download = mPendingDownloads.remove(buildRemoteName(account, file));
+ }
+ if (download != null) {
+ download.cancel();
+ }
+ }
+
+
+ public void clearListeners() {
+ mBoundListeners.clear();
+ }
+
+
+ /**
+ * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.
+ *
+ * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download.
+ *
+ * @param account Owncloud account where the remote file is stored.
+ * @param file A file that could be in the queue of downloads.
+ */
+ public boolean isDownloading(Account account, OCFile file) {
+ if (account == null || file == null) return false;
+ String targetKey = buildRemoteName(account, file);
+ synchronized (mPendingDownloads) {
+ if (file.isDirectory()) {
+ // this can be slow if there are many downloads :(
+ Iterator<String> it = mPendingDownloads.keySet().iterator();
+ boolean found = false;
+ while (it.hasNext() && !found) {
+ found = it.next().startsWith(targetKey);
+ }
+ return found;
+ } else {
+ return (mPendingDownloads.containsKey(targetKey));
+ }
+ }
+ }
+
+
+ /**
+ * Adds a listener interested in the progress of the download for a concrete file.
+ *
+ * @param listener Object to notify about progress of transfer.
+ * @param account ownCloud account holding the file of interest.
+ * @param file {@link OCfile} of interest for listener.
+ */
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+ if (account == null || file == null || listener == null) return;
+ String targetKey = buildRemoteName(account, file);
+ mBoundListeners.put(targetKey, listener);
+ }
+
+
+ /**
+ * Removes a listener interested in the progress of the download for a concrete file.
+ *
+ * @param listener Object to notify about progress of transfer.
+ * @param account ownCloud account holding the file of interest.
+ * @param file {@link OCfile} of interest for listener.
+ */
+ public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+ if (account == null || file == null || listener == null) return;
+ String targetKey = buildRemoteName(account, file);
+ if (mBoundListeners.get(targetKey) == listener) {
+ mBoundListeners.remove(targetKey);
+ }
+ }
+
+
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // old way, should not be in use any more
+ }
+
+
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
+ String fileName) {
+ String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile());
+ OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+ if (boundListener != null) {
+ boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
+ }
+ }
+
+ }
+
+
+ /**
+ * Download worker. Performs the pending downloads in the order they were requested.
+ *
+ * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
+ */
+ private static class ServiceHandler extends Handler {
+ // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
+ FileDownloader mService;
+ public ServiceHandler(Looper looper, FileDownloader service) {
+ super(looper);
+ if (service == null)
+ throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
+ mService = service;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ @SuppressWarnings("unchecked")
+ AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
+ if (msg.obj != null) {
+ Iterator<String> it = requestedDownloads.iterator();
+ while (it.hasNext()) {
+ mService.downloadFile(it.next());
+ }
+ }
+ mService.stopSelf(msg.arg1);
+ }
+ }
+
+
+ /**
+ * Core download method: requests a file to download and stores it.
+ *
+ * @param downloadKey Key to access the download to perform, contained in mPendingDownloads
+ */
+ private void downloadFile(String downloadKey) {
+
+ synchronized(mPendingDownloads) {
+ mCurrentDownload = mPendingDownloads.get(downloadKey);
+ }
+
+ if (mCurrentDownload != null) {
+
+ notifyDownloadStart(mCurrentDownload);
+
+ RemoteOperationResult downloadResult = null;
+ try {
+ /// prepare client object to send the request to the ownCloud server
+ if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {
+ mLastAccount = mCurrentDownload.getAccount();
+ mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
+ mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
+ }
+
+ /// perform the download
+ downloadResult = mCurrentDownload.execute(mDownloadClient);
+ if (downloadResult.isSuccess()) {
+ saveDownloadedFile();
+ }
+
+ } catch (AccountsException e) {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ downloadResult = new RemoteOperationResult(e);
+ } catch (IOException e) {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ downloadResult = new RemoteOperationResult(e);
+
+ } finally {
+ synchronized(mPendingDownloads) {
+ mPendingDownloads.remove(downloadKey);
+ }
+ }
+
+
+ /// notify result
+ notifyDownloadResult(mCurrentDownload, downloadResult);
+
+ sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);
+ }
+ }
+
+
+ /**
+ * Updates the OC File after a successful download.
+ */
+ private void saveDownloadedFile() {
+ OCFile file = mCurrentDownload.getFile();
+ long syncDate = System.currentTimeMillis();
+ file.setLastSyncDateForProperties(syncDate);
+ file.setLastSyncDateForData(syncDate);
+ file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
+ file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
+ // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available
+ file.setMimetype(mCurrentDownload.getMimeType());
+ file.setStoragePath(mCurrentDownload.getSavePath());
+ file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
+ mStorageManager.saveFile(file);
+ }
+
+
+ /**
+ * Creates a status notification to show the download progress
+ *
+ * @param download Download operation starting.
+ */
+ private void notifyDownloadStart(DownloadFileOperation download) {
+ /// create status notification with a progress bar
+ mLastPercent = 0;
+ mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
+ mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
+ mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
+ mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
+ mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
+ mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+
+ /// includes a pending intent in the notification showing the details view of the file
+ Intent showDetailsIntent = null;
+ if (PreviewImageFragment.canBePreviewed(download.getFile())) {
+ showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+ } else {
+ showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ }
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
+
+ mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+ }
+
+
+ /**
+ * Callback method to update the progress bar in the status notification.
+ */
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+ int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+ if (percent != mLastPercent) {
+ mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);
+ String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
+ mNotification.contentView.setTextViewText(R.id.status_text, text);
+ mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+ }
+ mLastPercent = percent;
+ }
+
+
+ /**
+ * Callback method to update the progress bar in the status notification (old version)
+ */
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // NOTHING TO DO HERE ANYMORE
+ }
+
+
+ /**
+ * Updates the status notification with the result of a download operation.
+ *
+ * @param downloadResult Result of the download operation.
+ * @param download Finished download operation
+ */
+ private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
+ mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
+ if (!downloadResult.isCancelled()) {
+ int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
+ int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
+ Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
+ finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
+ boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
+ // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
+ (downloadResult.isIdPRedirection()
+ && MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType())));
+ if (needsToUpdateCredentials) {
+ // let the user update credentials with one click
+ Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
+ finalNotification.setLatestEventInfo( getApplicationContext(),
+ getString(tickerId),
+ String.format(getString(contentId), new File(download.getSavePath()).getName()),
+ finalNotification.contentIntent);
+ mDownloadClient = null; // grant that future retries on the same account will get the fresh credentials
+
+ } else {
+ Intent showDetailsIntent = null;
+ if (downloadResult.isSuccess()) {
+ if (PreviewImageFragment.canBePreviewed(download.getFile())) {
+ showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+ } else {
+ showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ }
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ } else {
+ // TODO put something smart in showDetailsIntent
+ showDetailsIntent = new Intent();
+ }
+ finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
+ finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);
+ }
+ mNotificationManager.notify(tickerId, finalNotification);
+ }
+ }
+
+
+ /**
+ * Sends a broadcast when a download finishes in order to the interested activities can update their view
+ *
+ * @param download Finished download operation
+ * @param downloadResult Result of the download operation
+ */
+ private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {
+ Intent end = new Intent(getDownloadFinishMessage());
+ end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
+ end.putExtra(ACCOUNT_NAME, download.getAccount().name);
+ end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
+ end.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+ sendStickyBroadcast(end);
+ }
+
+
+ /**
+ * Sends a broadcast when a new download is added to the queue.
+ *
+ * @param download Added download operation
+ */
+ private void sendBroadcastNewDownload(DownloadFileOperation download) {
+ Intent added = new Intent(getDownloadAddedMessage());
+ added.putExtra(ACCOUNT_NAME, download.getAccount().name);
+ added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
+ added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
+ sendStickyBroadcast(added);
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files.services;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.files.OwnCloudFileObserver;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.os.Binder;
+import android.os.IBinder;
+
+public class FileObserverService extends Service {
+
+ public final static int CMD_INIT_OBSERVED_LIST = 1;
+ public final static int CMD_ADD_OBSERVED_FILE = 2;
+ public final static int CMD_DEL_OBSERVED_FILE = 3;
+
+ public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
+ public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE";
+ public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT";
+
+ private static String TAG = FileObserverService.class.getSimpleName();
+
+ private static Map<String, OwnCloudFileObserver> mObserversMap;
+ private static DownloadCompletedReceiverBis mDownloadReceiver;
+ private IBinder mBinder = new LocalBinder();
+
+ private String mDownloadAddedMessage;
+ private String mDownloadFinishMessage;
+
+ public class LocalBinder extends Binder {
+ FileObserverService getService() {
+ return FileObserverService.this;
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mDownloadReceiver = new DownloadCompletedReceiverBis();
+
+ FileDownloader downloader = new FileDownloader();
+ mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+ mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(mDownloadAddedMessage);
+ filter.addAction(mDownloadFinishMessage);
+ registerReceiver(mDownloadReceiver, filter);
+
+ mObserversMap = new HashMap<String, OwnCloudFileObserver>();
+ //initializeObservedList();
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mDownloadReceiver);
+ mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance
+ Log_OC.d(TAG, "Bye, bye");
+ }
+
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // this occurs when system tries to restart
+ // service, so we need to reinitialize observers
+ if (intent == null) {
+ initializeObservedList();
+ return Service.START_STICKY;
+ }
+
+ if (!intent.hasExtra(KEY_FILE_CMD)) {
+ Log_OC.e(TAG, "No KEY_FILE_CMD argument given");
+ return Service.START_STICKY;
+ }
+
+ switch (intent.getIntExtra(KEY_FILE_CMD, -1)) {
+ case CMD_INIT_OBSERVED_LIST:
+ initializeObservedList();
+ break;
+ case CMD_ADD_OBSERVED_FILE:
+ addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
+ (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
+ break;
+ case CMD_DEL_OBSERVED_FILE:
+ removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
+ (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
+ break;
+ default:
+ Log_OC.wtf(TAG, "Incorrect key given");
+ }
+
+ return Service.START_STICKY;
+ }
+
+
+ /**
+ * Read from the local database the list of files that must to be kept synchronized and
+ * starts file observers to monitor local changes on them
+ */
+ private void initializeObservedList() {
+ mObserversMap.clear();
+ Cursor c = getContentResolver().query(
+ ProviderTableMeta.CONTENT_URI,
+ null,
+ ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?",
+ new String[] {String.valueOf(1)},
+ null);
+ if (c == null || !c.moveToFirst()) return;
+ AccountManager acm = AccountManager.get(this);
+ Account[] accounts = acm.getAccounts();
+ do {
+ Account account = null;
+ for (Account a : accounts)
+ if (a.name.equals(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)))) {
+ account = a;
+ break;
+ }
+
+ if (account == null) continue;
+ FileDataStorageManager storage =
+ new FileDataStorageManager(account, getContentResolver());
+ if (!storage.fileExists(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))))
+ continue;
+
+ String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
+ if (path == null || path.length() <= 0)
+ continue;
+ OwnCloudFileObserver observer =
+ new OwnCloudFileObserver( path,
+ account,
+ getApplicationContext(),
+ OwnCloudFileObserver.CHANGES_ONLY);
+ mObserversMap.put(path, observer);
+ if (new File(path).exists()) {
+ observer.startWatching();
+ Log_OC.d(TAG, "Started watching file " + path);
+ }
+
+ } while (c.moveToNext());
+ c.close();
+ }
+
+
+ /**
+ * Registers the local copy of a remote file to be observed for local changes,
+ * an automatically updated in the ownCloud server.
+ *
+ * This method does NOT perform a {@link SynchronizeFileOperation} over the file.
+ *
+ * TODO We are ignoring that, currently, a local file can be linked to different files
+ * in ownCloud if it's uploaded several times. That's something pending to update: we
+ * will avoid that the same local file is linked to different remote files.
+ *
+ * @param file Object representing a remote file which local copy must be observed.
+ * @param account OwnCloud account containing file.
+ */
+ private void addObservedFile(OCFile file, Account account) {
+ if (file == null) {
+ Log_OC.e(TAG, "Trying to add a NULL file to observer");
+ return;
+ }
+ String localPath = file.getStoragePath();
+ if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time
+ localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
+ }
+ OwnCloudFileObserver observer = mObserversMap.get(localPath);
+ if (observer == null) {
+ /// the local file was never registered to observe before
+ observer = new OwnCloudFileObserver( localPath,
+ account,
+ getApplicationContext(),
+ OwnCloudFileObserver.CHANGES_ONLY);
+ mObserversMap.put(localPath, observer);
+ Log_OC.d(TAG, "Observer added for path " + localPath);
+
+ if (file.isDown()) {
+ observer.startWatching();
+ Log_OC.d(TAG, "Started watching " + localPath);
+ } // else - the observance can't be started on a file not already down; mDownloadReceiver will get noticed when the download of the file finishes
+ }
+
+ }
+
+
+ /**
+ * Unregisters the local copy of a remote file to be observed for local changes.
+ *
+ * Starts to watch it, if the file has a local copy to watch.
+ *
+ * TODO We are ignoring that, currently, a local file can be linked to different files
+ * in ownCloud if it's uploaded several times. That's something pending to update: we
+ * will avoid that the same local file is linked to different remote files.
+ *
+ * @param file Object representing a remote file which local copy must be not observed longer.
+ * @param account OwnCloud account containing file.
+ */
+ private void removeObservedFile(OCFile file, Account account) {
+ if (file == null) {
+ Log_OC.e(TAG, "Trying to remove a NULL file");
+ return;
+ }
+ String localPath = file.getStoragePath();
+ if (localPath == null || localPath.length() <= 0) {
+ localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
+ }
+
+ OwnCloudFileObserver observer = mObserversMap.get(localPath);
+ if (observer != null) {
+ observer.stopWatching();
+ mObserversMap.remove(observer);
+ Log_OC.d(TAG, "Stopped watching " + localPath);
+ }
+
+ }
+
+
+ /**
+ * Private receiver listening to events broadcast by the FileDownloader service.
+ *
+ * Starts and stops the observance on registered files when they are being download,
+ * in order to avoid to start unnecessary synchronizations.
+ */
+ private class DownloadCompletedReceiverBis extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
+ OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
+ if (observer != null) {
+ if (intent.getAction().equals(mDownloadFinishMessage) &&
+ new File(downloadPath).exists()) { // the download could be successful. not; in both cases, the file could be down, due to a former download or upload
+ observer.startWatching();
+ Log_OC.d(TAG, "Watching again " + downloadPath);
+
+ } else if (intent.getAction().equals(mDownloadAddedMessage)) {
+ observer.stopWatching();
+ Log_OC.d(TAG, "Disabling observance of " + downloadPath);
+ }
+ }
+ }
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files.services;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.AbstractList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Vector;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.ChunkedUploadFileOperation;
+import com.owncloud.android.operations.CreateFolderOperation;
+import com.owncloud.android.operations.ExistenceCheckOperation;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.UploadFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.FailedUploadActivity;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.activity.InstantUploadActivity;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountsException;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.webkit.MimeTypeMap;
+import android.widget.RemoteViews;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+public class FileUploader extends Service implements OnDatatransferProgressListener {
+
+ private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
+ public static final String EXTRA_UPLOAD_RESULT = "RESULT";
+ public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
+ public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
+ public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
+ public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
+
+ public static final String KEY_FILE = "FILE";
+ public static final String KEY_LOCAL_FILE = "LOCAL_FILE";
+ public static final String KEY_REMOTE_FILE = "REMOTE_FILE";
+ public static final String KEY_MIME_TYPE = "MIME_TYPE";
+
+ public static final String KEY_ACCOUNT = "ACCOUNT";
+
+ public static final String KEY_UPLOAD_TYPE = "UPLOAD_TYPE";
+ public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
+ public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD";
+ public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
+
+ public static final int LOCAL_BEHAVIOUR_COPY = 0;
+ public static final int LOCAL_BEHAVIOUR_MOVE = 1;
+ public static final int LOCAL_BEHAVIOUR_FORGET = 2;
+
+ public static final int UPLOAD_SINGLE_FILE = 0;
+ public static final int UPLOAD_MULTIPLE_FILES = 1;
+
+ private static final String TAG = FileUploader.class.getSimpleName();
+
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+ private IBinder mBinder;
+ private WebdavClient mUploadClient = null;
+ private Account mLastAccount = null;
+ private FileDataStorageManager mStorageManager;
+
+ private ConcurrentMap<String, UploadFileOperation> mPendingUploads = new ConcurrentHashMap<String, UploadFileOperation>();
+ private UploadFileOperation mCurrentUpload = null;
+
+ private NotificationManager mNotificationManager;
+ private Notification mNotification;
+ private int mLastPercent;
+ private RemoteViews mDefaultNotificationContentView;
+
+
+ public String getUploadFinishMessage() {
+ return getClass().getName().toString() + UPLOAD_FINISH_MESSAGE;
+ }
+
+ /**
+ * Builds a key for mPendingUploads from the account and file to upload
+ *
+ * @param account Account where the file to upload is stored
+ * @param file File to upload
+ */
+ private String buildRemoteName(Account account, OCFile file) {
+ return account.name + file.getRemotePath();
+ }
+
+ private String buildRemoteName(Account account, String remotePath) {
+ return account.name + remotePath;
+ }
+
+ /**
+ * Checks if an ownCloud server version should support chunked uploads.
+ *
+ * @param version OwnCloud version instance corresponding to an ownCloud
+ * server.
+ * @return 'True' if the ownCloud server with version supports chunked
+ * uploads.
+ */
+ private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
+ return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
+ }
+
+ /**
+ * Service initialization
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper, this);
+ mBinder = new FileUploaderBinder();
+ }
+
+ /**
+ * Entry point to add one or several files to the queue of uploads.
+ *
+ * New uploads are added calling to startService(), resulting in a call to
+ * this method. This ensures the service will keep on working although the
+ * caller activity goes away.
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)
+ || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
+ Log_OC.e(TAG, "Not enough information provided in intent");
+ return Service.START_NOT_STICKY;
+ }
+ int uploadType = intent.getIntExtra(KEY_UPLOAD_TYPE, -1);
+ if (uploadType == -1) {
+ Log_OC.e(TAG, "Incorrect upload type provided");
+ return Service.START_NOT_STICKY;
+ }
+ Account account = intent.getParcelableExtra(KEY_ACCOUNT);
+
+ String[] localPaths = null, remotePaths = null, mimeTypes = null;
+ OCFile[] files = null;
+ if (uploadType == UPLOAD_SINGLE_FILE) {
+
+ if (intent.hasExtra(KEY_FILE)) {
+ files = new OCFile[] { intent.getParcelableExtra(KEY_FILE) };
+
+ } else {
+ localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) };
+ remotePaths = new String[] { intent.getStringExtra(KEY_REMOTE_FILE) };
+ mimeTypes = new String[] { intent.getStringExtra(KEY_MIME_TYPE) };
+ }
+
+ } else { // mUploadType == UPLOAD_MULTIPLE_FILES
+
+ if (intent.hasExtra(KEY_FILE)) {
+ files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO
+ // will
+ // this
+ // casting
+ // work
+ // fine?
+
+ } else {
+ localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
+ remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
+ mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
+ }
+ }
+
+ FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
+
+ boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
+ boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
+ int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
+
+ if (intent.hasExtra(KEY_FILE) && files == null) {
+ Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");
+ return Service.START_NOT_STICKY;
+
+ } else if (!intent.hasExtra(KEY_FILE)) {
+ if (localPaths == null) {
+ Log_OC.e(TAG, "Incorrect array for local paths provided in upload intent");
+ return Service.START_NOT_STICKY;
+ }
+ if (remotePaths == null) {
+ Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent");
+ return Service.START_NOT_STICKY;
+ }
+ if (localPaths.length != remotePaths.length) {
+ Log_OC.e(TAG, "Different number of remote paths and local paths!");
+ return Service.START_NOT_STICKY;
+ }
+
+ files = new OCFile[localPaths.length];
+ for (int i = 0; i < localPaths.length; i++) {
+ files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i]
+ : (String) null), storageManager);
+ if (files[i] == null) {
+ // TODO @andomaex add failure Notiification
+ return Service.START_NOT_STICKY;
+ }
+ }
+ }
+
+ OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account,
+ AccountAuthenticator.KEY_OC_VERSION));
+ boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
+ AbstractList<String> requestedUploads = new Vector<String>();
+ String uploadKey = null;
+ UploadFileOperation newUpload = null;
+ try {
+ for (int i = 0; i < files.length; i++) {
+ uploadKey = buildRemoteName(account, files[i].getRemotePath());
+ if (chunked) {
+ newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
+ localAction);
+ } else {
+ newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
+ }
+ if (isInstant) {
+ newUpload.setRemoteFolderToBeCreated();
+ }
+ mPendingUploads.putIfAbsent(uploadKey, newUpload); // Grants that the file only upload once time
+
+ newUpload.addDatatransferProgressListener(this);
+ newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
+ requestedUploads.add(uploadKey);
+ }
+
+ } catch (IllegalArgumentException e) {
+ Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
+ return START_NOT_STICKY;
+
+ } catch (IllegalStateException e) {
+ Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
+ return START_NOT_STICKY;
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Unexpected exception while processing upload intent", e);
+ return START_NOT_STICKY;
+
+ }
+
+ if (requestedUploads.size() > 0) {
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = requestedUploads;
+ mServiceHandler.sendMessage(msg);
+ }
+ Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
+ return Service.START_NOT_STICKY;
+ }
+
+ /**
+ * Provides a binder object that clients can use to perform operations on
+ * the queue of uploads, excepting the addition of new files.
+ *
+ * Implemented to perform cancellation, pause and resume of existing
+ * uploads.
+ */
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return mBinder;
+ }
+
+ /**
+ * Called when ALL the bound clients were onbound.
+ */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ ((FileUploaderBinder)mBinder).clearListeners();
+ return false; // not accepting rebinding (default behaviour)
+ }
+
+
+ /**
+ * Binder to let client components to perform operations on the queue of
+ * uploads.
+ *
+ * It provides by itself the available operations.
+ */
+ public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener {
+
+ /**
+ * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance
+ */
+ private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
+
+ /**
+ * Cancels a pending or current upload of a remote file.
+ *
+ * @param account Owncloud account where the remote file will be stored.
+ * @param file A file in the queue of pending uploads
+ */
+ public void cancel(Account account, OCFile file) {
+ UploadFileOperation upload = null;
+ synchronized (mPendingUploads) {
+ upload = mPendingUploads.remove(buildRemoteName(account, file));
+ }
+ if (upload != null) {
+ upload.cancel();
+ }
+ }
+
+
+
+ public void clearListeners() {
+ mBoundListeners.clear();
+ }
+
+
+
+
+ /**
+ * Returns True when the file described by 'file' is being uploaded to
+ * the ownCloud account 'account' or waiting for it
+ *
+ * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload.
+ *
+ * @param account Owncloud account where the remote file will be stored.
+ * @param file A file that could be in the queue of pending uploads
+ */
+ public boolean isUploading(Account account, OCFile file) {
+ if (account == null || file == null)
+ return false;
+ String targetKey = buildRemoteName(account, file);
+ synchronized (mPendingUploads) {
+ if (file.isDirectory()) {
+ // this can be slow if there are many uploads :(
+ Iterator<String> it = mPendingUploads.keySet().iterator();
+ boolean found = false;
+ while (it.hasNext() && !found) {
+ found = it.next().startsWith(targetKey);
+ }
+ return found;
+ } else {
+ return (mPendingUploads.containsKey(targetKey));
+ }
+ }
+ }
+
+
+ /**
+ * Adds a listener interested in the progress of the upload for a concrete file.
+ *
+ * @param listener Object to notify about progress of transfer.
+ * @param account ownCloud account holding the file of interest.
+ * @param file {@link OCfile} of interest for listener.
+ */
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+ if (account == null || file == null || listener == null) return;
+ String targetKey = buildRemoteName(account, file);
+ mBoundListeners.put(targetKey, listener);
+ }
+
+
+
+ /**
+ * Removes a listener interested in the progress of the upload for a concrete file.
+ *
+ * @param listener Object to notify about progress of transfer.
+ * @param account ownCloud account holding the file of interest.
+ * @param file {@link OCfile} of interest for listener.
+ */
+ public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
+ if (account == null || file == null || listener == null) return;
+ String targetKey = buildRemoteName(account, file);
+ if (mBoundListeners.get(targetKey) == listener) {
+ mBoundListeners.remove(targetKey);
+ }
+ }
+
+
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // old way, should not be in use any more
+ }
+
+
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
+ String fileName) {
+ String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
+ OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
+ if (boundListener != null) {
+ boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
+ }
+ }
+
+ }
+
+ /**
+ * Upload worker. Performs the pending uploads in the order they were
+ * requested.
+ *
+ * Created with the Looper of a new thread, started in
+ * {@link FileUploader#onCreate()}.
+ */
+ private static class ServiceHandler extends Handler {
+ // don't make it a final class, and don't remove the static ; lint will
+ // warn about a possible memory leak
+ FileUploader mService;
+
+ public ServiceHandler(Looper looper, FileUploader service) {
+ super(looper);
+ if (service == null)
+ throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
+ mService = service;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ @SuppressWarnings("unchecked")
+ AbstractList<String> requestedUploads = (AbstractList<String>) msg.obj;
+ if (msg.obj != null) {
+ Iterator<String> it = requestedUploads.iterator();
+ while (it.hasNext()) {
+ mService.uploadFile(it.next());
+ }
+ }
+ mService.stopSelf(msg.arg1);
+ }
+ }
+
+ /**
+ * Core upload method: sends the file(s) to upload
+ *
+ * @param uploadKey Key to access the upload to perform, contained in
+ * mPendingUploads
+ */
+ public void uploadFile(String uploadKey) {
+
+ synchronized (mPendingUploads) {
+ mCurrentUpload = mPendingUploads.get(uploadKey);
+ }
+
+ if (mCurrentUpload != null) {
+
+ notifyUploadStart(mCurrentUpload);
+
+ RemoteOperationResult uploadResult = null, grantResult = null;
+
+ try {
+ /// prepare client object to send requests to the ownCloud server
+ if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
+ mLastAccount = mCurrentUpload.getAccount();
+ mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
+ mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
+ }
+
+ /// check the existence of the parent folder for the file to upload
+ String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
+ remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
+ grantResult = grantFolderExistence(remoteParentPath);
+
+ /// perform the upload
+ if (grantResult.isSuccess()) {
+ OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
+ mCurrentUpload.getFile().setParentId(parent.getFileId());
+ uploadResult = mCurrentUpload.execute(mUploadClient);
+ if (uploadResult.isSuccess()) {
+ saveUploadedFile();
+ }
+ } else {
+ uploadResult = grantResult;
+ }
+
+ } catch (AccountsException e) {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ uploadResult = new RemoteOperationResult(e);
+
+ } catch (IOException e) {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
+ uploadResult = new RemoteOperationResult(e);
+
+ } finally {
+ synchronized (mPendingUploads) {
+ mPendingUploads.remove(uploadKey);
+ Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
+ }
+ if (uploadResult.isException()) {
+ // enforce the creation of a new client object for next uploads; this grant that a new socket will
+ // be created in the future if the current exception is due to an abrupt lose of network connection
+ mUploadClient = null;
+ }
+ }
+
+ /// notify result
+
+ notifyUploadResult(uploadResult, mCurrentUpload);
+ sendFinalBroadcast(mCurrentUpload, uploadResult);
+
+ }
+
+ }
+
+ /**
+ * Checks the existence of the folder where the current file will be uploaded both in the remote server
+ * and in the local database.
+ *
+ * If the upload is set to enforce the creation of the folder, the method tries to create it both remote
+ * and locally.
+ *
+ * @param pathToGrant Full remote path whose existence will be granted.
+ * @return An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
+ */
+ private RemoteOperationResult grantFolderExistence(String pathToGrant) {
+ RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false);
+ RemoteOperationResult result = operation.execute(mUploadClient);
+ if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
+ operation = new CreateFolderOperation( pathToGrant,
+ true,
+ mStorageManager );
+ result = operation.execute(mUploadClient);
+ }
+ if (result.isSuccess()) {
+ OCFile parentDir = mStorageManager.getFileByPath(pathToGrant);
+ if (parentDir == null) {
+ parentDir = createLocalFolder(pathToGrant);
+ }
+ if (parentDir != null) {
+ result = new RemoteOperationResult(ResultCode.OK);
+ } else {
+ result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
+ }
+ }
+ return result;
+ }
+
+
+ private OCFile createLocalFolder(String remotePath) {
+ String parentPath = new File(remotePath).getParent();
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+ OCFile parent = mStorageManager.getFileByPath(parentPath);
+ if (parent == null) {
+ parent = createLocalFolder(parentPath);
+ }
+ if (parent != null) {
+ OCFile createdFolder = new OCFile(remotePath);
+ createdFolder.setMimetype("DIR");
+ createdFolder.setParentId(parent.getFileId());
+ mStorageManager.saveFile(createdFolder);
+ return createdFolder;
+ }
+ return null;
+ }
+
+
+ /**
+ * Saves a OC File after a successful upload.
+ *
+ * A PROPFIND is necessary to keep the props in the local database
+ * synchronized with the server, specially the modification time and Etag
+ * (where available)
+ *
+ * TODO refactor this ugly thing
+ */
+ private void saveUploadedFile() {
+ OCFile file = mCurrentUpload.getFile();
+ long syncDate = System.currentTimeMillis();
+ file.setLastSyncDateForData(syncDate);
+
+ // / new PROPFIND to keep data consistent with server in theory, should
+ // return the same we already have
+ PropFindMethod propfind = null;
+ RemoteOperationResult result = null;
+ try {
+ propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
+ DavConstants.PROPFIND_ALL_PROP,
+ DavConstants.DEPTH_0);
+ int status = mUploadClient.executeMethod(propfind);
+ boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
+ if (isMultiStatus) {
+ MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0], mUploadClient.getBaseUri().getPath());
+ updateOCFile(file, we);
+ file.setLastSyncDateForProperties(syncDate);
+
+ } else {
+ mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
+ }
+
+ result = new RemoteOperationResult(isMultiStatus, status, propfind.getResponseHeaders());
+ Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
+ + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
+ + result.getLogMessage(), e);
+
+ } finally {
+ if (propfind != null)
+ propfind.releaseConnection();
+ }
+
+ // / maybe this would be better as part of UploadFileOperation... or
+ // maybe all this method
+ if (mCurrentUpload.wasRenamed()) {
+ OCFile oldFile = mCurrentUpload.getOldFile();
+ if (oldFile.fileExists()) {
+ oldFile.setStoragePath(null);
+ mStorageManager.saveFile(oldFile);
+
+ } // else: it was just an automatic renaming due to a name
+ // coincidence; nothing else is needed, the storagePath is right
+ // in the instance returned by mCurrentUpload.getFile()
+ }
+
+ mStorageManager.saveFile(file);
+ }
+
+ private void updateOCFile(OCFile file, WebdavEntry we) {
+ file.setCreationTimestamp(we.createTimestamp());
+ file.setFileLength(we.contentLength());
+ file.setMimetype(we.contentType());
+ file.setModificationTimestamp(we.modifiedTimestamp());
+ file.setModificationTimestampAtLastSyncForData(we.modifiedTimestamp());
+ // file.setEtag(mCurrentUpload.getEtag()); // TODO Etag, where available
+ }
+
+ private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
+ FileDataStorageManager storageManager) {
+ OCFile newFile = new OCFile(remotePath);
+ newFile.setStoragePath(localPath);
+ newFile.setLastSyncDateForProperties(0);
+ newFile.setLastSyncDateForData(0);
+
+ // size
+ if (localPath != null && localPath.length() > 0) {
+ File localFile = new File(localPath);
+ newFile.setFileLength(localFile.length());
+ newFile.setLastSyncDateForData(localFile.lastModified());
+ } // don't worry about not assigning size, the problems with localPath
+ // are checked when the UploadFileOperation instance is created
+
+ // MIME type
+ if (mimeType == null || mimeType.length() <= 0) {
+ try {
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ remotePath.substring(remotePath.lastIndexOf('.') + 1));
+ } catch (IndexOutOfBoundsException e) {
+ Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath);
+ }
+ }
+ if (mimeType == null) {
+ mimeType = "application/octet-stream";
+ }
+ newFile.setMimetype(mimeType);
+
+ return newFile;
+ }
+
+ /**
+ * Creates a status notification to show the upload progress
+ *
+ * @param upload Upload operation starting.
+ */
+ @SuppressWarnings("deprecation")
+ private void notifyUploadStart(UploadFileOperation upload) {
+ // / create status notification with a progress bar
+ mLastPercent = 0;
+ mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker),
+ System.currentTimeMillis());
+ mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
+ mDefaultNotificationContentView = mNotification.contentView;
+ mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(),
+ R.layout.progressbar_layout);
+ mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
+ mNotification.contentView.setTextViewText(R.id.status_text,
+ String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
+ mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+
+ /// includes a pending intent in the notification showing the details view of the file
+ Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+ (int) System.currentTimeMillis(), showDetailsIntent, 0);
+
+ mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
+ }
+
+ /**
+ * Callback method to update the progress bar in the status notification
+ */
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+ int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
+ if (percent != mLastPercent) {
+ mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, false);
+ String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName);
+ mNotification.contentView.setTextViewText(R.id.status_text, text);
+ mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
+ }
+ mLastPercent = percent;
+ }
+
+ /**
+ * Callback method to update the progress bar in the status notification
+ * (old version)
+ */
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // NOTHING TO DO HERE ANYMORE
+ }
+
+ /**
+ * Updates the status notification with the result of an upload operation.
+ *
+ * @param uploadResult Result of the upload operation.
+ * @param upload Finished upload operation
+ */
+ private void notifyUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
+ Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
+ if (uploadResult.isCancelled()) {
+ // / cancelled operation -> silent removal of progress notification
+ mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
+
+ } else if (uploadResult.isSuccess()) {
+ // / success -> silent update of progress notification to success
+ // message
+ mNotification.flags ^= Notification.FLAG_ONGOING_EVENT; // remove
+ // the
+ // ongoing
+ // flag
+ mNotification.flags |= Notification.FLAG_AUTO_CANCEL;
+ mNotification.contentView = mDefaultNotificationContentView;
+
+ /// includes a pending intent in the notification showing the details view of the file
+ Intent showDetailsIntent = null;
+ if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
+ showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+ } else {
+ showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ }
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+ (int) System.currentTimeMillis(), showDetailsIntent, 0);
+
+ mNotification.setLatestEventInfo(getApplicationContext(),
+ getString(R.string.uploader_upload_succeeded_ticker),
+ String.format(getString(R.string.uploader_upload_succeeded_content_single), upload.getFileName()),
+ mNotification.contentIntent);
+
+ mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); // NOT
+ // AN
+ DbHandler db = new DbHandler(this.getBaseContext());
+ db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
+ db.close();
+
+ } else {
+
+ // / fail -> explicit failure notification
+ mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
+ Notification finalNotification = new Notification(R.drawable.icon,
+ getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
+ finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
+ String content = null;
+
+ boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
+ //(uploadResult.isTemporalRedirection() && uploadResult.isIdPRedirection() &&
+ (uploadResult.isIdPRedirection() &&
+ MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType())));
+ if (needsToUpdateCredentials) {
+ // let the user update credentials with one click
+ Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount());
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
+ content = String.format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
+ finalNotification.setLatestEventInfo(getApplicationContext(),
+ getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent);
+ mUploadClient = null; // grant that future retries on the same account will get the fresh credentials
+ } else {
+ // TODO put something smart in the contentIntent below
+ // finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+ //}
+
+ if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL
+ || uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) {
+ // TODO we need a class to provide error messages for the users
+ // from a RemoteOperationResult and a RemoteOperation
+ content = String.format(getString(R.string.error__upload__local_file_not_copied), upload.getFileName(),
+ getString(R.string.app_name));
+ } else if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
+ content = getString(R.string.failed_upload_quota_exceeded_text);
+ } else {
+ content = String
+ .format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
+ }
+
+ // we add only for instant-uploads the InstantUploadActivity and the
+ // db entry
+ Intent detailUploadIntent = null;
+ if (upload.isInstant() && InstantUploadActivity.IS_ENABLED) {
+ detailUploadIntent = new Intent(this, InstantUploadActivity.class);
+ detailUploadIntent.putExtra(FileUploader.KEY_ACCOUNT, upload.getAccount());
+ } else {
+ detailUploadIntent = new Intent(this, FailedUploadActivity.class);
+ detailUploadIntent.putExtra(FailedUploadActivity.MESSAGE, content);
+ }
+ finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+ (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | PendingIntent.FLAG_ONE_SHOT);
+
+ if (upload.isInstant()) {
+ DbHandler db = null;
+ try {
+ db = new DbHandler(this.getBaseContext());
+ String message = uploadResult.getLogMessage() + " errorCode: " + uploadResult.getCode();
+ Log_OC.e(TAG, message + " Http-Code: " + uploadResult.getHttpCode());
+ if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
+ message = getString(R.string.failed_upload_quota_exceeded_text);
+ if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED,
+ message) == 0) {
+ db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name, message);
+ }
+ }
+ } finally {
+ if (db != null) {
+ db.close();
+ }
+ }
+ }
+ }
+ finalNotification.setLatestEventInfo(getApplicationContext(),
+ getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent);
+
+ mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification);
+ }
+
+ }
+
+ /**
+ * Sends a broadcast in order to the interested activities can update their
+ * view
+ *
+ * @param upload Finished upload operation
+ * @param uploadResult Result of the upload operation
+ */
+ private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
+ Intent end = new Intent(getUploadFinishMessage());
+ end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
+ // path, after
+ // possible
+ // automatic
+ // renaming
+ if (upload.wasRenamed()) {
+ end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
+ }
+ end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
+ end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
+ end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
+ sendStickyBroadcast(end);
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.files.services;
+
+public interface OnUploadCompletedListener extends Runnable {
+
+ public boolean getUploadResult();
+
+ public void setUploadResult(boolean result);
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.location;
+
+import com.owncloud.android.Log_OC;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+public class LocationServiceLauncherReciever extends BroadcastReceiver {
+
+ private final String TAG = getClass().getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent deviceTrackingIntent = new Intent();
+ deviceTrackingIntent
+ .setAction("com.owncloud.android.location.LocationUpdateService");
+ SharedPreferences preferences = PreferenceManager
+ .getDefaultSharedPreferences(context);
+ boolean trackDevice = preferences.getBoolean("enable_devicetracking",
+ true);
+
+ // Used in Preferences activity so that tracking is disabled or
+ // reenabled
+ if (intent.hasExtra("TRACKING_SETTING")) {
+ trackDevice = intent.getBooleanExtra("TRACKING_SETTING", true);
+ }
+
+ startOrStopDeviceTracking(context, trackDevice);
+ }
+
+ /**
+ * Used internally. Starts or stops the device tracking service
+ *
+ * @param trackDevice true to start the service, false to stop it
+ */
+ private void startOrStopDeviceTracking(Context context, boolean trackDevice) {
+ Intent deviceTrackingIntent = new Intent();
+ deviceTrackingIntent
+ .setAction("com.owncloud.android.location.LocationUpdateService");
+ if (!isDeviceTrackingServiceRunning(context) && trackDevice) {
+ Log_OC.d(TAG, "Starting device tracker service");
+ context.startService(deviceTrackingIntent);
+ } else if (isDeviceTrackingServiceRunning(context) && !trackDevice) {
+ Log_OC.d(TAG, "Stopping device tracker service");
+ context.stopService(deviceTrackingIntent);
+ }
+ }
+
+ /**
+ * Checks to see whether or not the LocationUpdateService is running
+ *
+ * @return true, if it is. Otherwise false
+ */
+ private boolean isDeviceTrackingServiceRunning(Context context) {
+ ActivityManager manager = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ for (RunningServiceInfo service : manager
+ .getRunningServices(Integer.MAX_VALUE)) {
+ if (getClass().getName().equals(service.service.getClassName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.location;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+
+public class LocationUpdateService extends IntentService implements
+ LocationListener {
+
+ public static final String TAG = "LocationUpdateService";
+
+ private LocationManager mLocationManager;
+ private LocationProvider mLocationProvider;
+ private SharedPreferences mPreferences;
+
+ public LocationUpdateService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+ // Determine, how we can track the device
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
+ criteria.setPowerRequirement(Criteria.POWER_LOW);
+ mLocationProvider = mLocationManager.getProvider(mLocationManager
+ .getBestProvider(criteria, true));
+
+ // Notify user if there is no way to track the device
+ if (mLocationProvider == null) {
+ String message = String.format(getString(R.string.location_no_provider), getString(R.string.app_name));
+ Toast.makeText(this,
+ message,
+ Toast.LENGTH_LONG).show();
+ stopSelf();
+ return;
+ }
+
+ // Get preferences for device tracking
+ mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+ boolean trackDevice = mPreferences.getBoolean("enable_devicetracking",
+ true);
+ int updateIntervall = Integer.parseInt(mPreferences.getString(
+ "devicetracking_update_intervall", "30")) * 60 * 1000;
+ int distanceBetweenLocationChecks = 50;
+
+ // If we do shall track the device -> Stop
+ if (!trackDevice) {
+ Log_OC.d(TAG, "Devicetracking is disabled");
+ stopSelf();
+ return;
+ }
+
+ mLocationManager.requestLocationUpdates(mLocationProvider.getName(),
+ updateIntervall, distanceBetweenLocationChecks, this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ Log_OC.d(TAG, "Location changed: " + location);
+
+ }
+
+ @Override
+ public void onProviderDisabled(String arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onProviderEnabled(String arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ *
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.media;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.MediaController.MediaPlayerControl;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import java.util.Formatter;
+import java.util.Locale;
+
+import com.owncloud.android.R;
+
+
+/**
+ * View containing controls for a {@link MediaPlayer}.
+ *
+ * Holds buttons "play / pause", "rewind", "fast forward"
+ * and a progress slider.
+ *
+ * It synchronizes itself with the state of the
+ * {@link MediaPlayer}.
+ *
+ * @author David A. Velasco
+ */
+
+public class MediaControlView extends FrameLayout /* implements OnLayoutChangeListener, OnTouchListener */ implements OnClickListener, OnSeekBarChangeListener {
+
+ private MediaPlayerControl mPlayer;
+ private Context mContext;
+ private View mRoot;
+ private ProgressBar mProgress;
+ private TextView mEndTime, mCurrentTime;
+ private boolean mDragging;
+ private static final int SHOW_PROGRESS = 1;
+ StringBuilder mFormatBuilder;
+ Formatter mFormatter;
+ private ImageButton mPauseButton;
+ private ImageButton mFfwdButton;
+ private ImageButton mRewButton;
+
+ public MediaControlView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ );
+ LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mRoot = inflate.inflate(R.layout.media_control, null);
+ initControllerView(mRoot);
+ addView(mRoot, frameParams);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ requestFocus();
+ }
+
+ @Override
+ public void onFinishInflate() {
+ /*
+ if (mRoot != null)
+ initControllerView(mRoot);
+ */
+ }
+
+ /* TODO REMOVE
+ public MediaControlView(Context context, boolean useFastForward) {
+ super(context);
+ mContext = context;
+ mUseFastForward = useFastForward;
+ initFloatingWindowLayout();
+ //initFloatingWindow();
+ }
+ */
+
+ /* TODO REMOVE
+ public MediaControlView(Context context) {
+ this(context, true);
+ }
+ */
+
+ /* T
+ private void initFloatingWindow() {
+ mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+ mWindow = PolicyManager.makeNewWindow(mContext);
+ mWindow.setWindowManager(mWindowManager, null, null);
+ mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ mDecor = mWindow.getDecorView();
+ mDecor.setOnTouchListener(mTouchListener);
+ mWindow.setContentView(this);
+ mWindow.setBackgroundDrawableResource(android.R.color.transparent);
+
+ // While the media controller is up, the volume control keys should
+ // affect the media stream type
+ mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ setFocusable(true);
+ setFocusableInTouchMode(true);
+ setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ requestFocus();
+ }
+ */
+
+ /*
+ // Allocate and initialize the static parts of mDecorLayoutParams. Must
+ // also call updateFloatingWindowLayout() to fill in the dynamic parts
+ // (y and width) before mDecorLayoutParams can be used.
+ private void initFloatingWindowLayout() {
+ mDecorLayoutParams = new WindowManager.LayoutParams();
+ WindowManager.LayoutParams p = mDecorLayoutParams;
+ p.gravity = Gravity.TOP;
+ p.height = LayoutParams.WRAP_CONTENT;
+ p.x = 0;
+ p.format = PixelFormat.TRANSLUCENT;
+ p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
+ p.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+ p.token = null;
+ p.windowAnimations = 0; // android.R.style.DropDownAnimationDown;
+ }
+ */
+
+ // Update the dynamic parts of mDecorLayoutParams
+ // Must be called with mAnchor != NULL.
+ /*
+ private void updateFloatingWindowLayout() {
+ int [] anchorPos = new int[2];
+ mAnchor.getLocationOnScreen(anchorPos);
+
+ WindowManager.LayoutParams p = mDecorLayoutParams;
+ p.width = mAnchor.getWidth();
+ p.y = anchorPos[1] + mAnchor.getHeight();
+ }
+ */
+
+ /*
+ // This is called whenever mAnchor's layout bound changes
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight,
+ int oldBottom) {
+ //updateFloatingWindowLayout();
+ if (mShowing) {
+ mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);
+ }
+ }
+ */
+
+ /*
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (mShowing) {
+ hide();
+ }
+ }
+ return false;
+ }
+ */
+
+
+ public void setMediaPlayer(MediaPlayerControl player) {
+ mPlayer = player;
+ mHandler.sendEmptyMessage(SHOW_PROGRESS);
+ updatePausePlay();
+ }
+
+
+ private void initControllerView(View v) {
+ mPauseButton = (ImageButton) v.findViewById(R.id.playBtn);
+ if (mPauseButton != null) {
+ mPauseButton.requestFocus();
+ mPauseButton.setOnClickListener(this);
+ }
+
+ mFfwdButton = (ImageButton) v.findViewById(R.id.forwardBtn);
+ if (mFfwdButton != null) {
+ mFfwdButton.setOnClickListener(this);
+ }
+
+ mRewButton = (ImageButton) v.findViewById(R.id.rewindBtn);
+ if (mRewButton != null) {
+ mRewButton.setOnClickListener(this);
+ }
+
+ mProgress = (ProgressBar) v.findViewById(R.id.progressBar);
+ if (mProgress != null) {
+ if (mProgress instanceof SeekBar) {
+ SeekBar seeker = (SeekBar) mProgress;
+ seeker.setOnSeekBarChangeListener(this);
+ }
+ mProgress.setMax(1000);
+ }
+
+ mEndTime = (TextView) v.findViewById(R.id.totalTimeText);
+ mCurrentTime = (TextView) v.findViewById(R.id.currentTimeText);
+ mFormatBuilder = new StringBuilder();
+ mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
+
+ }
+
+
+ /**
+ * Disable pause or seek buttons if the stream cannot be paused or seeked.
+ * This requires the control interface to be a MediaPlayerControlExt
+ */
+ private void disableUnsupportedButtons() {
+ try {
+ if (mPauseButton != null && !mPlayer.canPause()) {
+ mPauseButton.setEnabled(false);
+ }
+ if (mRewButton != null && !mPlayer.canSeekBackward()) {
+ mRewButton.setEnabled(false);
+ }
+ if (mFfwdButton != null && !mPlayer.canSeekForward()) {
+ mFfwdButton.setEnabled(false);
+ }
+ } catch (IncompatibleClassChangeError ex) {
+ // We were given an old version of the interface, that doesn't have
+ // the canPause/canSeekXYZ methods. This is OK, it just means we
+ // assume the media can be paused and seeked, and so we don't disable
+ // the buttons.
+ }
+ }
+
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int pos;
+ switch (msg.what) {
+ case SHOW_PROGRESS:
+ pos = setProgress();
+ if (!mDragging) {
+ msg = obtainMessage(SHOW_PROGRESS);
+ sendMessageDelayed(msg, 1000 - (pos % 1000));
+ }
+ break;
+ }
+ }
+ };
+
+ private String stringForTime(int timeMs) {
+ int totalSeconds = timeMs / 1000;
+
+ int seconds = totalSeconds % 60;
+ int minutes = (totalSeconds / 60) % 60;
+ int hours = totalSeconds / 3600;
+
+ mFormatBuilder.setLength(0);
+ if (hours > 0) {
+ return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
+ } else {
+ return mFormatter.format("%02d:%02d", minutes, seconds).toString();
+ }
+ }
+
+ private int setProgress() {
+ if (mPlayer == null || mDragging) {
+ return 0;
+ }
+ int position = mPlayer.getCurrentPosition();
+ int duration = mPlayer.getDuration();
+ if (mProgress != null) {
+ if (duration > 0) {
+ // use long to avoid overflow
+ long pos = 1000L * position / duration;
+ mProgress.setProgress( (int) pos);
+ }
+ int percent = mPlayer.getBufferPercentage();
+ mProgress.setSecondaryProgress(percent * 10);
+ }
+
+ if (mEndTime != null)
+ mEndTime.setText(stringForTime(duration));
+ if (mCurrentTime != null)
+ mCurrentTime.setText(stringForTime(position));
+
+ return position;
+ }
+
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+ final boolean uniqueDown = event.getRepeatCount() == 0
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
+ || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+ || keyCode == KeyEvent.KEYCODE_SPACE) {
+ if (uniqueDown) {
+ doPauseResume();
+ //show(sDefaultTimeout);
+ if (mPauseButton != null) {
+ mPauseButton.requestFocus();
+ }
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
+ if (uniqueDown && !mPlayer.isPlaying()) {
+ mPlayer.start();
+ updatePausePlay();
+ //show(sDefaultTimeout);
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
+ || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+ if (uniqueDown && mPlayer.isPlaying()) {
+ mPlayer.pause();
+ updatePausePlay();
+ //show(sDefaultTimeout);
+ }
+ return true;
+ }
+
+ //show(sDefaultTimeout);
+ return super.dispatchKeyEvent(event);
+ }
+
+ public void updatePausePlay() {
+ if (mRoot == null || mPauseButton == null)
+ return;
+
+ if (mPlayer.isPlaying()) {
+ mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
+ } else {
+ mPauseButton.setImageResource(android.R.drawable.ic_media_play);
+ }
+ }
+
+ private void doPauseResume() {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ } else {
+ mPlayer.start();
+ }
+ updatePausePlay();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (mPauseButton != null) {
+ mPauseButton.setEnabled(enabled);
+ }
+ if (mFfwdButton != null) {
+ mFfwdButton.setEnabled(enabled);
+ }
+ if (mRewButton != null) {
+ mRewButton.setEnabled(enabled);
+ }
+ if (mProgress != null) {
+ mProgress.setEnabled(enabled);
+ }
+ disableUnsupportedButtons();
+ super.setEnabled(enabled);
+ }
+
+ @Override
+ public void onClick(View v) {
+ int pos;
+ boolean playing = mPlayer.isPlaying();
+ switch (v.getId()) {
+
+ case R.id.playBtn:
+ doPauseResume();
+ break;
+
+ case R.id.rewindBtn:
+ pos = mPlayer.getCurrentPosition();
+ pos -= 5000;
+ mPlayer.seekTo(pos);
+ if (!playing) mPlayer.pause(); // necessary in some 2.3.x devices
+ setProgress();
+ break;
+
+ case R.id.forwardBtn:
+ pos = mPlayer.getCurrentPosition();
+ pos += 15000;
+ mPlayer.seekTo(pos);
+ if (!playing) mPlayer.pause(); // necessary in some 2.3.x devices
+ setProgress();
+ break;
+
+ }
+ }
+
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (!fromUser) {
+ // We're not interested in programmatically generated changes to
+ // the progress bar's position.
+ return;
+ }
+
+ long duration = mPlayer.getDuration();
+ long newposition = (duration * progress) / 1000L;
+ mPlayer.seekTo( (int) newposition);
+ if (mCurrentTime != null)
+ mCurrentTime.setText(stringForTime( (int) newposition));
+ }
+
+ /**
+ * Called in devices with touchpad when the user starts to adjust the
+ * position of the seekbar's thumb.
+ *
+ * Will be followed by several onProgressChanged notifications.
+ */
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ mDragging = true; // monitors the duration of dragging
+ mHandler.removeMessages(SHOW_PROGRESS); // grants no more updates with media player progress while dragging
+ }
+
+
+ /**
+ * Called in devices with touchpad when the user finishes the
+ * adjusting of the seekbar.
+ */
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ mDragging = false;
+ setProgress();
+ updatePausePlay();
+ mHandler.sendEmptyMessage(SHOW_PROGRESS); // grants future updates with media player progress
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setClassName(MediaControlView.class.getName());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setClassName(MediaControlView.class.getName());
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.media;
+
+import android.accounts.Account;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.WifiLock;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.widget.Toast;
+
+import java.io.IOException;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+
+
+/**
+ * Service that handles media playback, both audio and video.
+ *
+ * Waits for Intents which signal the service to perform specific operations: Play, Pause,
+ * Rewind, etc.
+ *
+ * @author David A. Velasco
+ */
+public class MediaService extends Service implements OnCompletionListener, OnPreparedListener,
+ OnErrorListener, AudioManager.OnAudioFocusChangeListener {
+
+ private static final String TAG = MediaService.class.getSimpleName();
+
+ private static final String MY_PACKAGE = MediaService.class.getPackage() != null ? MediaService.class.getPackage().getName() : "com.owncloud.android.media";
+
+ /// Intent actions that we are prepared to handle
+ public static final String ACTION_PLAY_FILE = MY_PACKAGE + ".action.PLAY_FILE";
+ public static final String ACTION_STOP_ALL = MY_PACKAGE + ".action.STOP_ALL";
+
+ /// Keys to add extras to the action
+ public static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
+ public static final String EXTRA_ACCOUNT = MY_PACKAGE + ".extra.ACCOUNT";
+ public static String EXTRA_START_POSITION = MY_PACKAGE + ".extra.START_POSITION";
+ public static final String EXTRA_PLAY_ON_LOAD = MY_PACKAGE + ".extra.PLAY_ON_LOAD";
+
+
+ /** Error code for specific messages - see regular error codes at {@link MediaPlayer} */
+ public static final int OC_MEDIA_ERROR = 0;
+
+ /** Time To keep the control panel visible when the user does not use it */
+ public static final int MEDIA_CONTROL_SHORT_LIFE = 4000;
+
+ /** Time To keep the control panel visible when the user does not use it */
+ public static final int MEDIA_CONTROL_PERMANENT = 0;
+
+ /** Volume to set when audio focus is lost and ducking is allowed */
+ private static final float DUCK_VOLUME = 0.1f;
+
+ /** Media player instance */
+ private MediaPlayer mPlayer = null;
+
+ /** Reference to the system AudioManager */
+ private AudioManager mAudioManager = null;
+
+
+ /** Values to indicate the state of the service */
+ enum State {
+ STOPPED,
+ PREPARING,
+ PLAYING,
+ PAUSED
+ };
+
+
+ /** Current state */
+ private State mState = State.STOPPED;
+
+ /** Possible focus values */
+ enum AudioFocus {
+ NO_FOCUS,
+ NO_FOCUS_CAN_DUCK,
+ FOCUS
+ }
+
+ /** Current focus state */
+ private AudioFocus mAudioFocus = AudioFocus.NO_FOCUS;
+
+
+ /** 'True' when the current song is streaming from the network */
+ private boolean mIsStreaming = false;
+
+ /** Wifi lock kept to prevents the device from shutting off the radio when streaming a file. */
+ private WifiLock mWifiLock;
+
+ private static final String MEDIA_WIFI_LOCK_TAG = MY_PACKAGE + ".WIFI_LOCK";
+
+ /** Notification to keep in the notification bar while a song is playing */
+ private NotificationManager mNotificationManager;
+ private Notification mNotification = null;
+
+ /** File being played */
+ private OCFile mFile;
+
+ /** Account holding the file being played */
+ private Account mAccount;
+
+ /** Flag signaling if the audio should be played immediately when the file is prepared */
+ protected boolean mPlayOnPrepared;
+
+ /** Position, in miliseconds, where the audio should be started */
+ private int mStartPosition;
+
+ /** Interface to access the service through binding */
+ private IBinder mBinder;
+
+ /** Control panel shown to the user to control the playback, to register through binding */
+ private MediaControlView mMediaController;
+
+
+
+ /**
+ * Helper method to get an error message suitable to show to users for errors occurred in media playback,
+ *
+ * @param context A context to access string resources.
+ * @param what See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+ * @param extra See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+ * @return Message suitable to users.
+ */
+ public static String getMessageForMediaError(Context context, int what, int extra) {
+ int messageId;
+
+ if (what == OC_MEDIA_ERROR) {
+ messageId = extra;
+
+ } else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
+ /* Added in API level 17
+ Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
+ Constant Value: -1010 (0xfffffc0e)
+ */
+ messageId = R.string.media_err_unsupported;
+
+ } else if (extra == MediaPlayer.MEDIA_ERROR_IO) {
+ /* Added in API level 17
+ File or network related operation errors.
+ Constant Value: -1004 (0xfffffc14)
+ */
+ messageId = R.string.media_err_io;
+
+ } else if (extra == MediaPlayer.MEDIA_ERROR_MALFORMED) {
+ /* Added in API level 17
+ Bitstream is not conforming to the related coding standard or file spec.
+ Constant Value: -1007 (0xfffffc11)
+ */
+ messageId = R.string.media_err_malformed;
+
+ } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
+ /* Added in API level 17
+ Some operation takes too long to complete, usually more than 3-5 seconds.
+ Constant Value: -110 (0xffffff92)
+ */
+ messageId = R.string.media_err_timeout;
+
+ } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+ /* Added in API level 3
+ The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
+ Constant Value: 200 (0x000000c8)
+ */
+ messageId = R.string.media_err_invalid_progressive_playback;
+
+ } else {
+ /* MediaPlayer.MEDIA_ERROR_UNKNOWN
+ Added in API level 1
+ Unspecified media player error.
+ Constant Value: 1 (0x00000001)
+ */
+ /* MediaPlayer.MEDIA_ERROR_SERVER_DIED)
+ Added in API level 1
+ Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
+ Constant Value: 100 (0x00000064)
+ */
+ messageId = R.string.media_err_unknown;
+ }
+ return context.getString(messageId);
+ }
+
+
+
+ /**
+ * Initialize a service instance
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate() {
+ Log_OC.d(TAG, "Creating ownCloud media service");
+
+ mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)).
+ createWifiLock(WifiManager.WIFI_MODE_FULL, MEDIA_WIFI_LOCK_TAG);
+
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ mBinder = new MediaServiceBinder(this);
+ }
+
+
+ /**
+ * Entry point for Intents requesting actions, sent here via startService.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if (action.equals(ACTION_PLAY_FILE)) {
+ processPlayFileRequest(intent);
+
+ } else if (action.equals(ACTION_STOP_ALL)) {
+ processStopRequest(true);
+ }
+
+ return START_NOT_STICKY; // don't want it to restart in case it's killed.
+ }
+
+
+ /**
+ * Processes a request to play a media file received as a parameter
+ *
+ * TODO If a new request is received when a file is being prepared, it is ignored. Is this what we want?
+ *
+ * @param intent Intent received in the request with the data to identify the file to play.
+ */
+ private void processPlayFileRequest(Intent intent) {
+ if (mState != State.PREPARING) {
+ mFile = intent.getExtras().getParcelable(EXTRA_FILE);
+ mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT);
+ mPlayOnPrepared = intent.getExtras().getBoolean(EXTRA_PLAY_ON_LOAD, false);
+ mStartPosition = intent.getExtras().getInt(EXTRA_START_POSITION, 0);
+ tryToGetAudioFocus();
+ playMedia();
+ }
+ }
+
+
+ /**
+ * Processes a request to play a media file.
+ */
+ protected void processPlayRequest() {
+ // request audio focus
+ tryToGetAudioFocus();
+
+ // actually play the song
+ if (mState == State.STOPPED) {
+ // (re)start playback
+ playMedia();
+
+ } else if (mState == State.PAUSED) {
+ // continue playback
+ mState = State.PLAYING;
+ setUpAsForeground(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
+ configAndStartMediaPlayer();
+
+ }
+ }
+
+
+ /**
+ * Makes sure the media player exists and has been reset. This will create the media player
+ * if needed. reset the existing media player if one already exists.
+ */
+ protected void createMediaPlayerIfNeeded() {
+ if (mPlayer == null) {
+ mPlayer = new MediaPlayer();
+
+ // make sure the CPU won't go to sleep while media is playing
+ mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+
+ // the media player will notify the service when it's ready preparing, and when it's done playing
+ mPlayer.setOnPreparedListener(this);
+ mPlayer.setOnCompletionListener(this);
+ mPlayer.setOnErrorListener(this);
+
+ } else {
+ mPlayer.reset();
+ }
+ }
+
+ /**
+ * Processes a request to pause the current playback
+ */
+ protected void processPauseRequest() {
+ if (mState == State.PLAYING) {
+ mState = State.PAUSED;
+ mPlayer.pause();
+ releaseResources(false); // retain media player in pause
+ // TODO polite audio focus, instead of keep it owned; or not?
+ }
+ }
+
+
+ /**
+ * Processes a request to stop the playback.
+ *
+ * @param force When 'true', the playback is stopped no matter the value of mState
+ */
+ protected void processStopRequest(boolean force) {
+ if (mState != State.PREPARING || force) {
+ mState = State.STOPPED;
+ mFile = null;
+ mAccount = null;
+ releaseResources(true);
+ giveUpAudioFocus();
+ stopSelf(); // service is no longer necessary
+ }
+ }
+
+
+ /**
+ * Releases resources used by the service for playback. This includes the "foreground service"
+ * status and notification, the wake locks and possibly the MediaPlayer.
+ *
+ * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
+ */
+ protected void releaseResources(boolean releaseMediaPlayer) {
+ // stop being a foreground service
+ stopForeground(true);
+
+ // stop and release the Media Player, if it's available
+ if (releaseMediaPlayer && mPlayer != null) {
+ mPlayer.reset();
+ mPlayer.release();
+ mPlayer = null;
+ }
+
+ // release the Wifi lock, if holding it
+ if (mWifiLock.isHeld()) {
+ mWifiLock.release();
+ }
+ }
+
+
+ /**
+ * Fully releases the audio focus.
+ */
+ private void giveUpAudioFocus() {
+ if (mAudioFocus == AudioFocus.FOCUS
+ && mAudioManager != null
+ && AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this)) {
+
+ mAudioFocus = AudioFocus.NO_FOCUS;
+ }
+ }
+
+
+ /**
+ * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it.
+ */
+ protected void configAndStartMediaPlayer() {
+ if (mPlayer == null) {
+ throw new IllegalStateException("mPlayer is NULL");
+ }
+
+ if (mAudioFocus == AudioFocus.NO_FOCUS) {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause(); // have to be polite; but mState is not changed, to resume when focus is received again
+ }
+
+ } else {
+ if (mAudioFocus == AudioFocus.NO_FOCUS_CAN_DUCK) {
+ mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME);
+
+ } else {
+ mPlayer.setVolume(1.0f, 1.0f); // full volume
+ }
+
+ if (!mPlayer.isPlaying()) {
+ mPlayer.start();
+ }
+ }
+ }
+
+
+ /**
+ * Requests the audio focus to the Audio Manager
+ */
+ private void tryToGetAudioFocus() {
+ if (mAudioFocus != AudioFocus.FOCUS
+ && mAudioManager != null
+ && (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.requestAudioFocus( this,
+ AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN))
+ ) {
+ mAudioFocus = AudioFocus.FOCUS;
+ }
+ }
+
+
+ /**
+ * Starts playing the current media file.
+ */
+ protected void playMedia() {
+ mState = State.STOPPED;
+ releaseResources(false); // release everything except MediaPlayer
+
+ try {
+ if (mFile == null) {
+ Toast.makeText(this, R.string.media_err_nothing_to_play, Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+ return;
+
+ } else if (mAccount == null) {
+ Toast.makeText(this, R.string.media_err_not_in_owncloud, Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+ return;
+ }
+
+ createMediaPlayerIfNeeded();
+ mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ String url = mFile.getStoragePath();
+ /* Streaming is not possible right now
+ if (url == null || url.length() <= 0) {
+ url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
+ }
+ mIsStreaming = url.startsWith("http:") || url.startsWith("https:");
+ */
+ mIsStreaming = false;
+
+ mPlayer.setDataSource(url);
+
+ mState = State.PREPARING;
+ setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName()));
+
+ // starts preparing the media player in background
+ mPlayer.prepareAsync();
+
+ // prevent the Wifi from going to sleep when streaming
+ if (mIsStreaming) {
+ mWifiLock.acquire();
+ } else if (mWifiLock.isHeld()) {
+ mWifiLock.release();
+ }
+
+ } catch (SecurityException e) {
+ Log_OC.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e);
+ Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+
+ } catch (IOException e) {
+ Log_OC.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e);
+ Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+
+ } catch (IllegalStateException e) {
+ Log_OC.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e);
+ Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+
+ } catch (IllegalArgumentException e) {
+ Log_OC.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e);
+ Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
+ processStopRequest(true);
+ }
+ }
+
+
+ /** Called when media player is done playing current song. */
+ public void onCompletion(MediaPlayer player) {
+ Toast.makeText(this, String.format(getString(R.string.media_event_done, mFile.getFileName())), Toast.LENGTH_LONG).show();
+ if (mMediaController != null) {
+ // somebody is still bound to the service
+ player.seekTo(0);
+ processPauseRequest();
+ mMediaController.updatePausePlay();
+ } else {
+ // nobody is bound
+ processStopRequest(true);
+ }
+ return;
+ }
+
+
+ /**
+ * Called when media player is done preparing.
+ *
+ * Time to start.
+ */
+ public void onPrepared(MediaPlayer player) {
+ mState = State.PLAYING;
+ updateNotification(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
+ if (mMediaController != null) {
+ mMediaController.setEnabled(true);
+ }
+ player.seekTo(mStartPosition);
+ configAndStartMediaPlayer();
+ if (!mPlayOnPrepared) {
+ processPauseRequest();
+ }
+
+ if (mMediaController != null) {
+ mMediaController.updatePausePlay();
+ }
+ }
+
+
+ /**
+ * Updates the status notification
+ */
+ @SuppressWarnings("deprecation")
+ private void updateNotification(String content) {
+ // TODO check if updating the Intent is really necessary
+ Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+ (int)System.currentTimeMillis(),
+ showDetailsIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ mNotification.when = System.currentTimeMillis();
+ //mNotification.contentView.setTextViewText(R.id.status_text, content);
+ String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
+ mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
+ mNotificationManager.notify(R.string.media_notif_ticker, mNotification);
+ }
+
+
+ /**
+ * Configures the service as a foreground service.
+ *
+ * The system will avoid finishing the service as much as possible when resources as low.
+ *
+ * A notification must be created to keep the user aware of the existance of the service.
+ */
+ @SuppressWarnings("deprecation")
+ private void setUpAsForeground(String content) {
+ /// creates status notification
+ // TODO put a progress bar to follow the playback progress
+ mNotification = new Notification();
+ mNotification.icon = android.R.drawable.ic_media_play;
+ //mNotification.tickerText = text;
+ mNotification.when = System.currentTimeMillis();
+ mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
+ //mNotification.contentView.setTextViewText(R.id.status_text, "ownCloud Music Player"); // NULL POINTER
+ //mNotification.contentView.setTextViewText(R.id.status_text, getString(R.string.downloader_download_in_progress_content));
+
+
+ /// includes a pending intent in the notification showing the details view of the file
+ Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
+ showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
+ (int)System.currentTimeMillis(),
+ showDetailsIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+
+ //mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+ String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
+ mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
+ startForeground(R.string.media_notif_ticker, mNotification);
+
+ }
+
+ /**
+ * Called when there's an error playing media.
+ *
+ * Warns the user about the error and resets the media player.
+ */
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log_OC.e(TAG, "Error in audio playback, what = " + what + ", extra = " + extra);
+
+ String message = getMessageForMediaError(this, what, extra);
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+
+ processStopRequest(true);
+ return true;
+ }
+
+ /**
+ * Called by the system when another app tries to play some sound.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ if (focusChange > 0) {
+ // focus gain; check AudioManager.AUDIOFOCUS_* values
+ mAudioFocus = AudioFocus.FOCUS;
+ // restart media player with new focus settings
+ if (mState == State.PLAYING)
+ configAndStartMediaPlayer();
+
+ } else if (focusChange < 0) {
+ // focus loss; check AudioManager.AUDIOFOCUS_* values
+ boolean canDuck = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK == focusChange;
+ mAudioFocus = canDuck ? AudioFocus.NO_FOCUS_CAN_DUCK : AudioFocus.NO_FOCUS;
+ // start/restart/pause media player with new focus settings
+ if (mPlayer != null && mPlayer.isPlaying())
+ configAndStartMediaPlayer();
+ }
+
+ }
+
+ /**
+ * Called when the service is finished for final clean-up.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDestroy() {
+ mState = State.STOPPED;
+ releaseResources(true);
+ giveUpAudioFocus();
+ }
+
+
+ /**
+ * Provides a binder object that clients can use to perform operations on the MediaPlayer managed by the MediaService.
+ */
+ @Override
+ public IBinder onBind(Intent arg) {
+ return mBinder;
+ }
+
+
+ /**
+ * Called when ALL the bound clients were onbound.
+ *
+ * The service is destroyed if playback stopped or paused
+ */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ if (mState == State.PAUSED || mState == State.STOPPED) {
+ processStopRequest(false);
+ }
+ return false; // not accepting rebinding (default behaviour)
+ }
+
+
+ /**
+ * Accesses the current MediaPlayer instance in the service.
+ *
+ * To be handled carefully. Visibility is protected to be accessed only
+ *
+ * @return Current MediaPlayer instance handled by MediaService.
+ */
+ protected MediaPlayer getPlayer() {
+ return mPlayer;
+ }
+
+
+ /**
+ * Accesses the current OCFile loaded in the service.
+ *
+ * @return The current OCFile loaded in the service.
+ */
+ protected OCFile getCurrentFile() {
+ return mFile;
+ }
+
+
+ /**
+ * Accesses the current {@link State} of the MediaService.
+ *
+ * @return The current {@link State} of the MediaService.
+ */
+ protected State getState() {
+ return mState;
+ }
+
+
+ protected void setMediaContoller(MediaControlView mediaController) {
+ mMediaController = mediaController;
+ }
+
+ protected MediaControlView getMediaController() {
+ return mMediaController;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.media;
+
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaService.State;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.os.Binder;
+import android.widget.MediaController;
+
+
+/**
+ * Binder allowing client components to perform operations on on the MediaPlayer managed by a MediaService instance.
+ *
+ * Provides the operations of {@link MediaController.MediaPlayerControl}, and an extra method to check if
+ * an {@link OCFile} instance is handled by the MediaService.
+ *
+ * @author David A. Velasco
+ */
+public class MediaServiceBinder extends Binder implements MediaController.MediaPlayerControl {
+
+ private static final String TAG = MediaServiceBinder.class.getSimpleName();
+ /**
+ * {@link MediaService} instance to access with the binder
+ */
+ private MediaService mService = null;
+
+ /**
+ * Public constructor
+ *
+ * @param service A {@link MediaService} instance to access with the binder
+ */
+ public MediaServiceBinder(MediaService service) {
+ if (service == null) {
+ throw new IllegalArgumentException("Argument 'service' can not be null");
+ }
+ mService = service;
+ }
+
+
+ public boolean isPlaying(OCFile mFile) {
+ return (mFile != null && mFile.equals(mService.getCurrentFile()));
+ }
+
+
+ @Override
+ public boolean canPause() {
+ return true;
+ }
+
+ @Override
+ public boolean canSeekBackward() {
+ return true;
+ }
+
+ @Override
+ public boolean canSeekForward() {
+ return true;
+ }
+
+ @Override
+ public int getBufferPercentage() {
+ MediaPlayer currentPlayer = mService.getPlayer();
+ if (currentPlayer != null) {
+ return 100;
+ // TODO update for streamed playback; add OnBufferUpdateListener in MediaService
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ MediaPlayer currentPlayer = mService.getPlayer();
+ if (currentPlayer != null) {
+ int pos = currentPlayer.getCurrentPosition();
+ return pos;
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public int getDuration() {
+ MediaPlayer currentPlayer = mService.getPlayer();
+ if (currentPlayer != null) {
+ int dur = currentPlayer.getDuration();
+ return dur;
+ } else {
+ return 0;
+ }
+ }
+
+
+ /**
+ * Reports if the MediaService is playing a file or not.
+ *
+ * Considers that the file is being played when it is in preparation because the expected
+ * client of this method is a {@link MediaController} , and we do not want that the 'play'
+ * button is shown when the file is being prepared by the MediaService.
+ */
+ @Override
+ public boolean isPlaying() {
+ MediaService.State currentState = mService.getState();
+ return (currentState == State.PLAYING || (currentState == State.PREPARING && mService.mPlayOnPrepared));
+ }
+
+
+ @Override
+ public void pause() {
+ Log_OC.d(TAG, "Pausing through binder...");
+ mService.processPauseRequest();
+ }
+
+ @Override
+ public void seekTo(int pos) {
+ Log_OC.d(TAG, "Seeking " + pos + " through binder...");
+ MediaPlayer currentPlayer = mService.getPlayer();
+ MediaService.State currentState = mService.getState();
+ if (currentPlayer != null && currentState != State.PREPARING && currentState != State.STOPPED) {
+ currentPlayer.seekTo(pos);
+ }
+ }
+
+ @Override
+ public void start() {
+ Log_OC.d(TAG, "Starting through binder...");
+ mService.processPlayRequest(); // this will finish the service if there is no file preloaded to play
+ }
+
+ public void start(Account account, OCFile file, boolean playImmediately, int position) {
+ Log_OC.d(TAG, "Loading and starting through binder...");
+ Intent i = new Intent(mService, MediaService.class);
+ i.putExtra(MediaService.EXTRA_ACCOUNT, account);
+ i.putExtra(MediaService.EXTRA_FILE, file);
+ i.putExtra(MediaService.EXTRA_PLAY_ON_LOAD, playImmediately);
+ i.putExtra(MediaService.EXTRA_START_POSITION, position);
+ i.setAction(MediaService.ACTION_PLAY_FILE);
+ mService.startService(i);
+ }
+
+
+ public void registerMediaController(MediaControlView mediaController) {
+ mService.setMediaContoller(mediaController);
+ }
+
+ public void unregisterMediaController(MediaControlView mediaController) {
+ if (mediaController != null && mediaController == mService.getMediaController()) {
+ mService.setMediaContoller(null);
+ }
+
+ }
+
+ public boolean isInPlaybackState() {
+ MediaService.State currentState = mService.getState();
+ return (currentState == MediaService.State.PLAYING || currentState == MediaService.State.PAUSED);
+ }
+
+}
+
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.security.cert.X509Certificate;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with
+ * a custom SSLContext and an optional Hostname Verifier.
+ *
+ * @author David A. Velasco
+ */
+
+public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
+
+ private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
+
+ private SSLContext mSslContext = null;
+ private AdvancedX509TrustManager mTrustManager = null;
+ private X509HostnameVerifier mHostnameVerifier = null;
+
+ public SSLContext getSslContext() {
+ return mSslContext;
+ }
+
+ /**
+ * Constructor for AdvancedSSLProtocolSocketFactory.
+ */
+ public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) {
+ if (sslContext == null)
+ throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext");
+ if (trustManager == null)
+ throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager");
+ mSslContext = sslContext;
+ mTrustManager = trustManager;
+ mHostnameVerifier = hostnameVerifier;
+ }
+
+ /**
+ * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
+ */
+ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
+ Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
+ verifyPeerIdentity(host, port, socket);
+ return socket;
+ }
+
+
+ /**
+ * Attempts to get a new socket connection to the given host within the
+ * given time limit.
+ *
+ * @param host the host name/IP
+ * @param port the port on the host
+ * @param clientHost the local host name/IP to bind the socket to
+ * @param clientPort the port on the local machine
+ * @param params {@link HttpConnectionParams Http connection parameters}
+ *
+ * @return Socket a new socket
+ *
+ * @throws IOException if an I/O error occurs while creating the socket
+ * @throws UnknownHostException if the IP address of the host cannot be
+ * determined
+ */
+ public Socket createSocket(final String host, final int port,
+ final InetAddress localAddress, final int localPort,
+ final HttpConnectionParams params) throws IOException,
+ UnknownHostException, ConnectTimeoutException {
+ Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
+ if (params == null) {
+ throw new IllegalArgumentException("Parameters may not be null");
+ }
+ int timeout = params.getConnectionTimeout();
+ SocketFactory socketfactory = mSslContext.getSocketFactory();
+ Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
+ Socket socket = socketfactory.createSocket();
+ SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
+ SocketAddress remoteaddr = new InetSocketAddress(host, port);
+ socket.setSoTimeout(params.getSoTimeout());
+ socket.bind(localaddr);
+ socket.connect(remoteaddr, timeout);
+ verifyPeerIdentity(host, port, socket);
+ return socket;
+ }
+
+ /**
+ * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
+ */
+ public Socket createSocket(String host, int port) throws IOException,
+ UnknownHostException {
+ Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
+ Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
+ verifyPeerIdentity(host, port, socket);
+ return socket;
+ }
+
+ public boolean equals(Object obj) {
+ return ((obj != null) && obj.getClass().equals(
+ AdvancedSslSocketFactory.class));
+ }
+
+ public int hashCode() {
+ return AdvancedSslSocketFactory.class.hashCode();
+ }
+
+
+ public X509HostnameVerifier getHostNameVerifier() {
+ return mHostnameVerifier;
+ }
+
+
+ public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
+ mHostnameVerifier = hostnameVerifier;
+ }
+
+ /**
+ * Verifies the identity of the server.
+ *
+ * The server certificate is verified first.
+ *
+ * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any.
+ * @param socket
+ */
+ private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException {
+ try {
+ CertificateCombinedException failInHandshake = null;
+ /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager)
+ try {
+ SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect"
+ sock.startHandshake();
+
+ } catch (RuntimeException e) {
+
+ if (e instanceof CertificateCombinedException) {
+ failInHandshake = (CertificateCombinedException) e;
+ } else {
+ Throwable cause = e.getCause();
+ Throwable previousCause = null;
+ while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
+ previousCause = cause;
+ cause = cause.getCause();
+ }
+ if (cause != null && cause instanceof CertificateCombinedException) {
+ failInHandshake = (CertificateCombinedException)cause;
+ }
+ }
+ if (failInHandshake == null) {
+ throw e;
+ }
+ failInHandshake.setHostInUrl(host);
+
+ }
+
+ /// 2. VERIFY HOSTNAME
+ SSLSession newSession = null;
+ boolean verifiedHostname = true;
+ if (mHostnameVerifier != null) {
+ if (failInHandshake != null) {
+ /// 2.1 : a new SSLSession instance was NOT created in the handshake
+ X509Certificate serverCert = failInHandshake.getServerCertificate();
+ try {
+ mHostnameVerifier.verify(host, serverCert);
+ } catch (SSLException e) {
+ verifiedHostname = false;
+ }
+
+ } else {
+ /// 2.2 : a new SSLSession instance was created in the handshake
+ newSession = ((SSLSocket)socket).getSession();
+ if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
+ verifiedHostname = mHostnameVerifier.verify(host, newSession);
+ }
+ }
+ }
+
+ /// 3. Combine the exceptions to throw, if any
+ if (!verifiedHostname) {
+ SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL");
+ if (failInHandshake == null) {
+ failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
+ failInHandshake.setHostInUrl(host);
+ }
+ failInHandshake.setSslPeerUnverifiedException(pue);
+ pue.initCause(failInHandshake);
+ throw pue;
+
+ } else if (failInHandshake != null) {
+ SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified");
+ hse.initCause(failInHandshake);
+ throw hse;
+ }
+
+ } catch (IOException io) {
+ try {
+ socket.close();
+ } catch (Exception x) {
+ // NOTHING - irrelevant exception for the caller
+ }
+ throw io;
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertStoreException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * @author David A. Velasco
+ */
+public class AdvancedX509TrustManager implements X509TrustManager {
+
+ private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
+
+ private X509TrustManager mStandardTrustManager = null;
+ private KeyStore mKnownServersKeyStore;
+
+ /**
+ * Constructor for AdvancedX509TrustManager
+ *
+ * @param knownServersCertStore Local certificates store with server certificates explicitly trusted by the user.
+ * @throws CertStoreException When no default X509TrustManager instance was found in the system.
+ */
+ public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
+ throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
+ super();
+ TrustManagerFactory factory = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init((KeyStore)null);
+ mStandardTrustManager = findX509TrustManager(factory);
+
+ mKnownServersKeyStore = knownServersKeyStore;
+ }
+
+
+ /**
+ * Locates the first X509TrustManager provided by a given TrustManagerFactory
+ * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager
+ * @return The first X509TrustManager found in factory.
+ * @throws CertStoreException When no X509TrustManager instance was found in factory
+ */
+ private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
+ TrustManager tms[] = factory.getTrustManagers();
+ for (int i = 0; i < tms.length; i++) {
+ if (tms[i] instanceof X509TrustManager) {
+ return (X509TrustManager) tms[i];
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
+ * String authType)
+ */
+ public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+ mStandardTrustManager.checkClientTrusted(certificates, authType);
+ }
+
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
+ * String authType)
+ */
+ public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
+ if (!isKnownServer(certificates[0])) {
+ CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
+ try {
+ certificates[0].checkValidity();
+ } catch (CertificateExpiredException c) {
+ result.setCertificateExpiredException(c);
+
+ } catch (CertificateNotYetValidException c) {
+ result.setCertificateNotYetException(c);
+ }
+
+ try {
+ mStandardTrustManager.checkServerTrusted(certificates, authType);
+ } catch (CertificateException c) {
+ Throwable cause = c.getCause();
+ Throwable previousCause = null;
+ while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny
+ previousCause = cause;
+ cause = cause.getCause();
+ }
+ if (cause != null && cause instanceof CertPathValidatorException) {
+ result.setCertPathValidatorException((CertPathValidatorException)cause);
+ } else {
+ result.setOtherCertificateException(c);
+ }
+ }
+
+ if (result.isException())
+ throw result;
+
+ }
+ }
+
+
+ /**
+ * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
+ */
+ public X509Certificate[] getAcceptedIssuers() {
+ return mStandardTrustManager.getAcceptedIssuers();
+ }
+
+
+ public boolean isKnownServer(X509Certificate cert) {
+ try {
+ return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
+ } catch (KeyStoreException e) {
+ Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
+ return false;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.util.Map;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.auth.AuthChallengeParser;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.AuthenticationException;
+import org.apache.commons.httpclient.auth.InvalidCredentialsException;
+import org.apache.commons.httpclient.auth.MalformedChallengeException;
+
+import com.owncloud.android.Log_OC;
+
+
+/**
+ * Bearer authentication scheme as defined in RFC 6750.
+ *
+ * @author David A. Velasco
+ */
+
+public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
+
+ private static final String TAG = BearerAuthScheme.class.getSimpleName();
+
+ public static final String AUTH_POLICY = "Bearer";
+
+ /** Whether the bearer authentication process is complete */
+ private boolean mComplete;
+
+ /** Authentication parameter map */
+ private Map mParams = null;
+
+
+ /**
+ * Default constructor for the bearer authentication scheme.
+ */
+ public BearerAuthScheme() {
+ mComplete = false;
+ }
+
+ /**
+ * Constructor for the basic authentication scheme.
+ *
+ * @param challenge Authentication challenge
+ *
+ * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
+ *
+ * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
+ */
+ public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
+ processChallenge(challenge);
+ mComplete = true;
+ }
+
+ /**
+ * Returns textual designation of the bearer authentication scheme.
+ *
+ * @return "Bearer"
+ */
+ public String getSchemeName() {
+ return "bearer";
+ }
+
+ /**
+ * Processes the Bearer challenge.
+ *
+ * @param challenge The challenge string
+ *
+ * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
+ */
+ public void processChallenge(String challenge) throws MalformedChallengeException {
+ String s = AuthChallengeParser.extractScheme(challenge);
+ if (!s.equalsIgnoreCase(getSchemeName())) {
+ throw new MalformedChallengeException(
+ "Invalid " + getSchemeName() + " challenge: " + challenge);
+ }
+ mParams = AuthChallengeParser.extractParams(challenge);
+ mComplete = true;
+ }
+
+ /**
+ * Tests if the Bearer authentication process has been completed.
+ *
+ * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
+ */
+ public boolean isComplete() {
+ return this.mComplete;
+ }
+
+ /**
+ * Produces bearer authorization string for the given set of
+ * {@link Credentials}.
+ *
+ * @param credentials The set of credentials to be used for authentication
+ * @param method Method name is ignored by the bearer authentication scheme
+ * @param uri URI is ignored by the bearer authentication scheme
+ * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable
+ * for this authentication scheme
+ * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure
+ * @return A bearer authorization string
+ *
+ * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
+ */
+ public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
+ Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
+
+ BearerCredentials bearer = null;
+ try {
+ bearer = (BearerCredentials) credentials;
+ } catch (ClassCastException e) {
+ throw new InvalidCredentialsException(
+ "Credentials cannot be used for bearer authentication: "
+ + credentials.getClass().getName());
+ }
+ return BearerAuthScheme.authenticate(bearer);
+ }
+
+
+ /**
+ * Returns 'false'. Bearer authentication scheme is request based.
+ *
+ * @return 'false'.
+ */
+ public boolean isConnectionBased() {
+ return false;
+ }
+
+ /**
+ * Produces bearer authorization string for the given set of {@link Credentials}.
+ *
+ * @param credentials The set of credentials to be used for authentication
+ * @param method The method being authenticated
+ * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication
+ * scheme.
+ * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure.
+ *
+ * @return a basic authorization string
+ */
+ public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
+ Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
+
+ if (method == null) {
+ throw new IllegalArgumentException("Method may not be null");
+ }
+ BearerCredentials bearer = null;
+ try {
+ bearer = (BearerCredentials) credentials;
+ } catch (ClassCastException e) {
+ throw new InvalidCredentialsException(
+ "Credentials cannot be used for bearer authentication: "
+ + credentials.getClass().getName());
+ }
+ return BearerAuthScheme.authenticate(
+ bearer,
+ method.getParams().getCredentialCharset());
+ }
+
+ /**
+ * @deprecated Use {@link #authenticate(BearerCredentials, String)}
+ *
+ * Returns a bearer Authorization header value for the given
+ * {@link BearerCredentials}.
+ *
+ * @param credentials The credentials to encode.
+ *
+ * @return A bearer authorization string
+ */
+ public static String authenticate(BearerCredentials credentials) {
+ return authenticate(credentials, "ISO-8859-1");
+ }
+
+ /**
+ * Returns a bearer Authorization header value for the given
+ * {@link BearerCredentials} and charset.
+ *
+ * @param credentials The credentials to encode.
+ * @param charset The charset to use for encoding the credentials
+ *
+ * @return A bearer authorization string
+ *
+ * @since 3.0
+ */
+ public static String authenticate(BearerCredentials credentials, String charset) {
+ Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
+
+ if (credentials == null) {
+ throw new IllegalArgumentException("Credentials may not be null");
+ }
+ if (charset == null || charset.length() == 0) {
+ throw new IllegalArgumentException("charset may not be null or empty");
+ }
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(credentials.getAccessToken());
+
+ //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
+ return "Bearer " + buffer.toString();
+ }
+
+ /**
+ * Returns a String identifying the authentication challenge. This is
+ * used, in combination with the host and port to determine if
+ * authorization has already been attempted or not. Schemes which
+ * require multiple requests to complete the authentication should
+ * return a different value for each stage in the request.
+ *
+ * Additionally, the ID should take into account any changes to the
+ * authentication challenge and return a different value when appropriate.
+ * For example when the realm changes in basic authentication it should be
+ * considered a different authentication attempt and a different value should
+ * be returned.
+ *
+ * This method simply returns the realm for the challenge.
+ *
+ * @return String a String identifying the authentication challenge.
+ *
+ * @deprecated no longer used
+ */
+ @Override
+ public String getID() {
+ return getRealm();
+ }
+
+ /**
+ * Returns authentication parameter with the given name, if available.
+ *
+ * @param name The name of the parameter to be returned
+ *
+ * @return The parameter with the given name
+ */
+ @Override
+ public String getParameter(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Parameter name may not be null");
+ }
+ if (mParams == null) {
+ return null;
+ }
+ return (String) mParams.get(name.toLowerCase());
+ }
+
+ /**
+ * Returns authentication realm. The realm may not be null.
+ *
+ * @return The authentication realm
+ */
+ @Override
+ public String getRealm() {
+ return getParameter("realm");
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.util.LangUtils;
+
+/**
+ * Bearer token {@link Credentials}
+ *
+ * @author David A. Velasco
+ */
+public class BearerCredentials implements Credentials {
+
+
+ private String mAccessToken;
+
+
+ /**
+ * The constructor with the bearer token
+ *
+ * @param token The bearer token
+ */
+ public BearerCredentials(String token) {
+ /*if (token == null) {
+ throw new IllegalArgumentException("Bearer token may not be null");
+ }*/
+ mAccessToken = (token == null) ? "" : token;
+ }
+
+
+ /**
+ * Returns the access token
+ *
+ * @return The access token
+ */
+ public String getAccessToken() {
+ return mAccessToken;
+ }
+
+
+ /**
+ * Get this object string.
+ *
+ * @return The access token
+ */
+ public String toString() {
+ return mAccessToken;
+ }
+
+ /**
+ * Does a hash of the access token.
+ *
+ * @return The hash code of the access token
+ */
+ public int hashCode() {
+ int hash = LangUtils.HASH_SEED;
+ hash = LangUtils.hashCode(hash, mAccessToken);
+ return hash;
+ }
+
+ /**
+ * These credentials are assumed equal if accessToken is the same.
+ *
+ * @param o The other object to compare with.
+ *
+ * @return 'True' if the object is equivalent.
+ */
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ if (this == o) return true;
+ if (this.getClass().equals(o.getClass())) {
+ BearerCredentials that = (BearerCredentials) o;
+ if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+/**
+ * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
+ * a certificate chain for a server.
+ *
+ * This was initially created as an extension of CertificateException, but some
+ * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
+ * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
+ * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it.
+ *
+ * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException
+ * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
+ *
+ * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
+ * in client methods. Be sure to use it only when you know exactly where it will go.
+ *
+ * @author David A. Velasco
+ */
+public class CertificateCombinedException extends RuntimeException {
+
+ /** Generated - to refresh every time the class changes */
+ private static final long serialVersionUID = -8875782030758554999L;
+
+ private X509Certificate mServerCert = null;
+ private String mHostInUrl;
+
+ private CertificateExpiredException mCertificateExpiredException = null;
+ private CertificateNotYetValidException mCertificateNotYetValidException = null;
+ private CertPathValidatorException mCertPathValidatorException = null;
+ private CertificateException mOtherCertificateException = null;
+ private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
+
+ public CertificateCombinedException(X509Certificate x509Certificate) {
+ mServerCert = x509Certificate;
+ }
+
+ public X509Certificate getServerCertificate() {
+ return mServerCert;
+ }
+
+ public String getHostInUrl() {
+ return mHostInUrl;
+ }
+
+ public void setHostInUrl(String host) {
+ mHostInUrl = host;
+ }
+
+ public CertificateExpiredException getCertificateExpiredException() {
+ return mCertificateExpiredException;
+ }
+
+ public void setCertificateExpiredException(CertificateExpiredException c) {
+ mCertificateExpiredException = c;
+ }
+
+ public CertificateNotYetValidException getCertificateNotYetValidException() {
+ return mCertificateNotYetValidException;
+ }
+
+ public void setCertificateNotYetException(CertificateNotYetValidException c) {
+ mCertificateNotYetValidException = c;
+ }
+
+ public CertPathValidatorException getCertPathValidatorException() {
+ return mCertPathValidatorException;
+ }
+
+ public void setCertPathValidatorException(CertPathValidatorException c) {
+ mCertPathValidatorException = c;
+ }
+
+ public CertificateException getOtherCertificateException() {
+ return mOtherCertificateException;
+ }
+
+ public void setOtherCertificateException(CertificateException c) {
+ mOtherCertificateException = c;
+ }
+
+ public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
+ return mSslPeerUnverifiedException ;
+ }
+
+ public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
+ mSslPeerUnverifiedException = s;
+ }
+
+ public boolean isException() {
+ return (mCertificateExpiredException != null ||
+ mCertificateNotYetValidException != null ||
+ mCertPathValidatorException != null ||
+ mOtherCertificateException != null ||
+ mSslPeerUnverifiedException != null);
+ }
+
+ public boolean isRecoverable() {
+ return (mCertificateExpiredException != null ||
+ mCertificateNotYetValidException != null ||
+ mCertPathValidatorException != null ||
+ mSslPeerUnverifiedException != null);
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.app.Activity;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+
+public class OwnCloudClientUtils {
+
+ final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
+
+ /** Default timeout for waiting data from the server */
+ public static final int DEFAULT_DATA_TIMEOUT = 60000;
+
+ /** Default timeout for establishing a connection */
+ public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
+
+ /** Connection manager for all the WebdavClients */
+ private static MultiThreadedHttpConnectionManager mConnManager = null;
+
+ private static Protocol mDefaultHttpsProtocol = null;
+
+ private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null;
+
+ private static X509HostnameVerifier mHostnameVerifier = null;
+
+
+ /**
+ * Creates a WebdavClient setup for an ownCloud account
+ *
+ * Do not call this method from the main thread.
+ *
+ * @param account The ownCloud account
+ * @param appContext Android application context
+ * @return A WebdavClient object ready to be used
+ * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account.
+ * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account.
+ * @throws IOException If there was some I/O error while getting the authorization token for the account.
+ * @throws AccountNotFoundException If 'account' is unknown for the AccountManager
+ */
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
+ //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
+
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ AccountManager am = AccountManager.get(appContext);
+ boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
+ boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+ WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
+ if (isOauth2) {
+ String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), false);
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
+
+ } else if (isSamlSso) { // TODO avoid a call to getUserData here
+ String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), false);
+ client.setSsoSessionCookie(accessToken);
+
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
+ client.setBasicCredentials(username, password);
+ }
+
+ return client;
+ }
+
+
+ public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
+ Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
+ AccountManager am = AccountManager.get(appContext);
+ boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
+ boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
+ WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
+
+ if (isOauth2) { // TODO avoid a call to getUserData here
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ if (accessToken == null) throw new AuthenticatorException("WTF!");
+ client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
+
+ } else if (isSamlSso) { // TODO avoid a call to getUserData here
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+ if (accessToken == null) throw new AuthenticatorException("WTF!");
+ client.setSsoSessionCookie(accessToken);
+
+ } else {
+ String username = account.name.substring(0, account.name.lastIndexOf('@'));
+ //String password = am.getPassword(account);
+ //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
+ AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypePass(), null, currentActivity, null, null);
+ Bundle result = future.getResult();
+ String password = result.getString(AccountManager.KEY_AUTHTOKEN);
+ client.setBasicCredentials(username, password);
+ }
+
+ return client;
+ }
+
+ /**
+ * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
+ *
+ * @param uri URL to the ownCloud server
+ * @param context Android context where the WebdavClient is being created.
+ * @return A WebdavClient object ready to be used
+ */
+ public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
+ try {
+ registerAdvancedSslContext(true, context);
+ } catch (GeneralSecurityException e) {
+ Log_OC.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e);
+
+ } catch (IOException e) {
+ Log_OC.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
+ }
+
+ WebdavClient client = new WebdavClient(getMultiThreadedConnManager());
+
+ client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
+ client.setBaseUri(uri);
+ client.setFollowRedirects(followRedirects);
+
+ return client;
+ }
+
+
+ /**
+ * Registers or unregisters the proper components for advanced SSL handling.
+ * @throws IOException
+ */
+ private static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException {
+ Protocol pr = null;
+ try {
+ pr = Protocol.getProtocol("https");
+ if (pr != null && mDefaultHttpsProtocol == null) {
+ mDefaultHttpsProtocol = pr;
+ }
+ } catch (IllegalStateException e) {
+ // nothing to do here; really
+ }
+ boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory);
+ if (register && !isRegistered) {
+ Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443));
+
+ } else if (!register && isRegistered) {
+ if (mDefaultHttpsProtocol != null) {
+ Protocol.registerProtocol("https", mDefaultHttpsProtocol);
+ }
+ }
+ }
+
+ public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException {
+ if (mAdvancedSslSocketFactory == null) {
+ KeyStore trustStore = getKnownServersStore(context);
+ AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore);
+ TrustManager[] tms = new TrustManager[] { trustMgr };
+
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, tms, null);
+
+ mHostnameVerifier = new BrowserCompatHostnameVerifier();
+ mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier);
+ }
+ return mAdvancedSslSocketFactory;
+ }
+
+
+ private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
+
+ private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
+
+ private static KeyStore mKnownServersStore = null;
+
+ /**
+ * Returns the local store of reliable server certificates, explicitly accepted by the user.
+ *
+ * Returns a KeyStore instance with empty content if the local store was never created.
+ *
+ * Loads the store from the storage environment if needed.
+ *
+ * @param context Android context where the operation is being performed.
+ * @return KeyStore instance with explicitly-accepted server certificates.
+ * @throws KeyStoreException When the KeyStore instance could not be created.
+ * @throws IOException When an existing local trust store could not be loaded.
+ * @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
+ * @throws CertificateException When an exception occurred while loading the certificates from the local trust store.
+ */
+ private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
+ if (mKnownServersStore == null) {
+ //mKnownServersStore = KeyStore.getInstance("BKS");
+ mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
+ File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
+ Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
+ if (localTrustStoreFile.exists()) {
+ InputStream in = new FileInputStream(localTrustStoreFile);
+ try {
+ mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
+ } finally {
+ in.close();
+ }
+ } else {
+ mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance
+ }
+ }
+ return mKnownServersStore;
+ }
+
+
+ public static void addCertToKnownServersStore(Certificate cert, Context context) throws KeyStoreException, NoSuchAlgorithmException,
+ CertificateException, IOException {
+ KeyStore knownServers = getKnownServersStore(context);
+ knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
+ FileOutputStream fos = null;
+ try {
+ fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
+ knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
+ } finally {
+ fos.close();
+ }
+ }
+
+
+ static private MultiThreadedHttpConnectionManager getMultiThreadedConnManager() {
+ if (mConnManager == null) {
+ mConnManager = new MultiThreadedHttpConnectionManager();
+ mConnManager.getParams().setDefaultMaxConnectionsPerHost(5);
+ mConnManager.getParams().setMaxTotalConnections(5);
+ }
+ return mConnManager;
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.network;
+
+import java.util.Collection;
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+public interface ProgressiveDataTransferer {
+
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
+
+ public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
+
+ public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.Random;
+
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.PutMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+
+
+import android.accounts.Account;
+
+import eu.alefzero.webdav.ChunkFromFileChannelRequestEntity;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+public class ChunkedUploadFileOperation extends UploadFileOperation {
+
+ private static final long CHUNK_SIZE = 1024000;
+ private static final String OC_CHUNKED_HEADER = "OC-Chunked";
+ private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
+
+ public ChunkedUploadFileOperation( Account account,
+ OCFile file,
+ boolean isInstant,
+ boolean forceOverwrite,
+ int localBehaviour) {
+
+ super(account, file, isInstant, forceOverwrite, localBehaviour);
+ }
+
+ @Override
+ protected int uploadFile(WebdavClient client) throws HttpException, IOException {
+ int status = -1;
+
+ FileChannel channel = null;
+ RandomAccessFile raf = null;
+ try {
+ File file = new File(getStoragePath());
+ raf = new RandomAccessFile(file, "r");
+ channel = raf.getChannel();
+ mEntity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file);
+ ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners());
+ long offset = 0;
+ String uriPrefix = client.getBaseUri() + WebdavUtils.encodePath(getRemotePath()) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ;
+ long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE);
+ for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) {
+ if (mPutMethod != null) {
+ mPutMethod.releaseConnection(); // let the connection available for other methods
+ }
+ mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
+ mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
+ ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
+ mPutMethod.setRequestEntity(mEntity);
+ status = client.executeMethod(mPutMethod);
+ client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
+ Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
+ if (!isSuccess(status))
+ break;
+ }
+
+ } finally {
+ if (channel != null)
+ channel.close();
+ if (raf != null)
+ raf.close();
+ if (mPutMethod != null)
+ mPutMethod.releaseConnection(); // let the connection available for other methods
+ }
+ return status;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.File;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the creation of a new folder in the ownCloud server.
+ *
+ * @author David A. Velasco
+ */
+public class CreateFolderOperation extends RemoteOperation {
+
+ private static final String TAG = CreateFolderOperation.class.getSimpleName();
+
+ private static final int READ_TIMEOUT = 10000;
+ private static final int CONNECTION_TIMEOUT = 5000;
+
+ protected String mRemotePath;
+ protected boolean mCreateFullPath;
+ protected DataStorageManager mStorageManager;
+
+ /**
+ * Constructor
+ *
+ * @param remotePath Full path to the new directory to create in the remote server.
+ * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet.
+ * @param storageManager Reference to the local database corresponding to the account where the file is contained.
+ */
+ public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
+ mRemotePath = remotePath;
+ mCreateFullPath = createFullPath;
+ mStorageManager = storageManager;
+ }
+
+
+ /**
+ * Performs the operation
+ *
+ * @param client Client object to communicate with the remote ownCloud server.
+ */
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ MkColMethod mkcol = null;
+ try {
+ mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
+ int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
+ if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) {
+ result = createParentFolder(getParentPath(), client);
+ status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); // second (and last) try
+ }
+ if (mkcol.succeeded()) {
+ // Save new directory in local database
+ OCFile newDir = new OCFile(mRemotePath);
+ newDir.setMimetype("DIR");
+ long parentId = mStorageManager.getFileByPath(getParentPath()).getFileId();
+ newDir.setParentId(parentId);
+ newDir.setModificationTimestamp(System.currentTimeMillis());
+ mStorageManager.saveFile(newDir);
+ }
+ result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders());
+ Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
+ client.exhaustResponse(mkcol.getResponseBodyAsStream());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (mkcol != null)
+ mkcol.releaseConnection();
+ }
+ return result;
+ }
+
+
+ private String getParentPath() {
+ String parentPath = new File(mRemotePath).getParent();
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+ return parentPath;
+ }
+
+
+ private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) {
+ RemoteOperation operation = new CreateFolderOperation( parentPath,
+ mCreateFullPath,
+ mStorageManager );
+ return operation.execute(client);
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.http.HttpStatus;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.accounts.Account;
+import android.webkit.MimeTypeMap;
+
+/**
+ * Remote operation performing the download of a file to an ownCloud server
+ *
+ * @author David A. Velasco
+ */
+public class DownloadFileOperation extends RemoteOperation {
+
+ private static final String TAG = DownloadFileOperation.class.getSimpleName();
+
+ private Account mAccount;
+ private OCFile mFile;
+ private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
+ private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+ private long mModificationTimestamp = 0;
+ private GetMethod mGet;
+
+
+ public DownloadFileOperation(Account account, OCFile file) {
+ if (account == null)
+ throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
+ if (file == null)
+ throw new IllegalArgumentException("Illegal null file in DownloadFileOperation creation");
+
+ mAccount = account;
+ mFile = file;
+ }
+
+
+ public Account getAccount() {
+ return mAccount;
+ }
+
+ public OCFile getFile() {
+ return mFile;
+ }
+
+ public String getSavePath() {
+ String path = mFile.getStoragePath(); // re-downloads should be done over the original file
+ if (path != null && path.length() > 0) {
+ return path;
+ }
+ return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+ }
+
+ public String getTmpPath() {
+ return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+ }
+
+ public String getRemotePath() {
+ return mFile.getRemotePath();
+ }
+
+ public String getMimeType() {
+ String mimeType = mFile.getMimetype();
+ if (mimeType == null || mimeType.length() <= 0) {
+ try {
+ mimeType = MimeTypeMap.getSingleton()
+ .getMimeTypeFromExtension(
+ mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
+ } catch (IndexOutOfBoundsException e) {
+ Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
+ }
+ }
+ if (mimeType == null) {
+ mimeType = "application/octet-stream";
+ }
+ return mimeType;
+ }
+
+ public long getSize() {
+ return mFile.getFileLength();
+ }
+
+ public long getModificationTimestamp() {
+ return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
+ }
+
+
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.add(listener);
+ }
+ }
+
+ public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.remove(listener);
+ }
+ }
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ File newFile = null;
+ boolean moved = true;
+
+ /// download will be performed to a temporal file, then moved to the final location
+ File tmpFile = new File(getTmpPath());
+
+ /// perform the download
+ try {
+ tmpFile.getParentFile().mkdirs();
+ int status = downloadFile(client, tmpFile);
+ if (isSuccess(status)) {
+ newFile = new File(getSavePath());
+ newFile.getParentFile().mkdirs();
+ moved = tmpFile.renameTo(newFile);
+ }
+ if (!moved)
+ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
+ else
+ result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null));
+ Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
+ }
+
+ return result;
+ }
+
+
+ public boolean isSuccess(int status) {
+ return (status == HttpStatus.SC_OK);
+ }
+
+
+ protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
+ int status = -1;
+ boolean savedFile = false;
+ mGet = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
+ Iterator<OnDatatransferProgressListener> it = null;
+
+ FileOutputStream fos = null;
+ try {
+ status = client.executeMethod(mGet);
+ if (isSuccess(status)) {
+ targetFile.createNewFile();
+ BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
+ fos = new FileOutputStream(targetFile);
+ long transferred = 0;
+
+ byte[] bytes = new byte[4096];
+ int readResult = 0;
+ while ((readResult = bis.read(bytes)) != -1) {
+ synchronized(mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ mGet.abort();
+ throw new OperationCancelledException();
+ }
+ }
+ fos.write(bytes, 0, readResult);
+ transferred += readResult;
+ synchronized (mDataTransferListeners) {
+ it = mDataTransferListeners.iterator();
+ while (it.hasNext()) {
+ it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
+ }
+ }
+ }
+ savedFile = true;
+ Header modificationTime = mGet.getResponseHeader("Last-Modified");
+ if (modificationTime != null) {
+ Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
+ mModificationTimestamp = (d != null) ? d.getTime() : 0;
+ }
+
+ } else {
+ client.exhaustResponse(mGet.getResponseBodyAsStream());
+ }
+
+ } finally {
+ if (fos != null) fos.close();
+ if (!savedFile && targetFile.exists()) {
+ targetFile.delete();
+ }
+ mGet.releaseConnection(); // let the connection available for other methods
+ }
+ return status;
+ }
+
+
+ public void cancel() {
+ mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.HeadMethod;
+
+import com.owncloud.android.Log_OC;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+import android.content.Context;
+import android.net.ConnectivityManager;
+
+/**
+ * Operation to check the existence or absence of a path in a remote server.
+ *
+ * @author David A. Velasco
+ */
+public class ExistenceCheckOperation extends RemoteOperation {
+
+ /** Maximum time to wait for a response from the server in MILLISECONDs. */
+ public static final int TIMEOUT = 10000;
+
+ private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
+
+ private String mPath;
+ private Context mContext;
+ private boolean mSuccessIfAbsent;
+
+
+ /**
+ * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
+ *
+ * @param path Path to append to the URL owned by the client instance.
+ * @param context Android application context.
+ * @param successIfAbsent When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
+ */
+ public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
+ mPath = (path != null) ? path : "";
+ mContext = context;
+ mSuccessIfAbsent = successIfAbsent;
+ }
+
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ if (!isOnline()) {
+ return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+ }
+ RemoteOperationResult result = null;
+ HeadMethod head = null;
+ try {
+ head = new HeadMethod(client.getBaseUri() + WebdavUtils.encodePath(mPath));
+ int status = client.executeMethod(head, TIMEOUT, TIMEOUT);
+ client.exhaustResponse(head.getResponseBodyAsStream());
+ boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
+ result = new RemoteOperationResult(success, status, head.getResponseHeaders());
+ Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
+
+ } finally {
+ if (head != null)
+ head.releaseConnection();
+ }
+ return result;
+ }
+
+ private boolean isOnline() {
+ ConnectivityManager cm = (ConnectivityManager) mContext
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm != null && cm.getActiveNetworkInfo() != null
+ && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+ }
+
+
+}
--- /dev/null
+package com.owncloud.android.operations;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.NameValuePair;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.OAuth2Constants;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+public class OAuth2GetAccessToken extends RemoteOperation {
+
+ private static final String TAG = OAuth2GetAccessToken.class.getSimpleName();
+
+ private String mClientId;
+ private String mRedirectUri;
+ private String mGrantType;
+
+ private String mOAuth2AuthorizationResponse;
+ private Map<String, String> mOAuth2ParsedAuthorizationResponse;
+ private Map<String, String> mResultTokenMap;
+
+
+ public OAuth2GetAccessToken(String clientId, String redirectUri, String grantType, String oAuth2AuthorizationResponse) {
+ mClientId = clientId;
+ mRedirectUri = redirectUri;
+ mGrantType = grantType;
+ mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
+ mOAuth2ParsedAuthorizationResponse = new HashMap<String, String>();
+ mResultTokenMap = null;
+ }
+
+
+ public Map<String, String> getOauth2AutorizationResponse() {
+ return mOAuth2ParsedAuthorizationResponse;
+ }
+
+ public Map<String, String> getResultTokenMap() {
+ return mResultTokenMap;
+ }
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ PostMethod postMethod = null;
+
+ try {
+ parseAuthorizationResponse();
+ if (mOAuth2ParsedAuthorizationResponse.keySet().contains(OAuth2Constants.KEY_ERROR)) {
+ if (OAuth2Constants.VALUE_ERROR_ACCESS_DENIED.equals(mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_ERROR))) {
+ result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR_ACCESS_DENIED);
+ } else {
+ result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
+ }
+ }
+
+ if (result == null) {
+ NameValuePair[] nameValuePairs = new NameValuePair[4];
+ nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType);
+ nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_CODE));
+ nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri);
+ nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
+ //nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));
+
+ postMethod = new PostMethod(client.getBaseUri().toString());
+ postMethod.setRequestBody(nameValuePairs);
+ int status = client.executeMethod(postMethod);
+
+ String response = postMethod.getResponseBodyAsString();
+ if (response != null && response.length() > 0) {
+ JSONObject tokenJson = new JSONObject(response);
+ parseAccessTokenResult(tokenJson);
+ if (mResultTokenMap.get(OAuth2Constants.KEY_ERROR) != null || mResultTokenMap.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
+ result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
+
+ } else {
+ result = new RemoteOperationResult(true, status, postMethod.getResponseHeaders());
+ }
+
+ } else {
+ client.exhaustResponse(postMethod.getResponseBodyAsStream());
+ result = new RemoteOperationResult(false, status, postMethod.getResponseHeaders());
+ }
+ }
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+
+ } finally {
+ if (postMethod != null)
+ postMethod.releaseConnection(); // let the connection available for other methods
+
+ if (result.isSuccess()) {
+ Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+
+ } else if (result.getException() != null) {
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException());
+
+ } else if (result.getCode() == ResultCode.OAUTH2_ERROR) {
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
+
+ } else {
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+ }
+ }
+
+ return result;
+ }
+
+
+ private void parseAuthorizationResponse() {
+ String[] pairs = mOAuth2AuthorizationResponse.split("&");
+ int i = 0;
+ String key = "";
+ String value = "";
+ StringBuilder sb = new StringBuilder();
+ while (pairs.length > i) {
+ int j = 0;
+ String[] part = pairs[i].split("=");
+ while (part.length > j) {
+ String p = part[j];
+ if (j == 0) {
+ key = p;
+ sb.append(key + " = ");
+ } else if (j == 1) {
+ value = p;
+ mOAuth2ParsedAuthorizationResponse.put(key, value);
+ sb.append(value + "\n");
+ }
+
+ Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
+ j++;
+ }
+ i++;
+ }
+ }
+
+
+ private void parseAccessTokenResult (JSONObject tokenJson) throws JSONException {
+ mResultTokenMap = new HashMap<String, String>();
+
+ if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Constants.KEY_ACCESS_TOKEN));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Constants.KEY_TOKEN_TYPE));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Constants.KEY_EXPIRES_IN));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Constants.KEY_REFRESH_TOKEN));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.getString(OAuth2Constants.KEY_SCOPE));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.getString(OAuth2Constants.KEY_ERROR));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
+ }
+ if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
+ mResultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.getString(OAuth2Constants.KEY_ERROR_URI));
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+public interface OnRemoteOperationListener {
+
+ void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+public class OperationCancelledException extends Exception {
+
+ /**
+ * Generated serial version - to avoid Java warning
+ */
+ private static final long serialVersionUID = -6350981497740424983L;
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+
+public class OwnCloudServerCheckOperation extends RemoteOperation {
+
+ /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */
+ public static final int TRY_CONNECTION_TIMEOUT = 5000;
+
+ private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
+
+ private String mUrl;
+ private RemoteOperationResult mLatestResult;
+ private Context mContext;
+ private OwnCloudVersion mOCVersion;
+
+ public OwnCloudServerCheckOperation(String url, Context context) {
+ mUrl = url;
+ mContext = context;
+ mOCVersion = null;
+ }
+
+ public OwnCloudVersion getDiscoveredVersion() {
+ return mOCVersion;
+ }
+
+ private boolean tryConnection(WebdavClient wc, String urlSt) {
+ boolean retval = false;
+ GetMethod get = null;
+ try {
+ get = new GetMethod(urlSt);
+ int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
+ String response = get.getResponseBodyAsString();
+ if (status == HttpStatus.SC_OK) {
+ JSONObject json = new JSONObject(response);
+ if (!json.getBoolean("installed")) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+ } else {
+ mOCVersion = new OwnCloudVersion(json.getString("version"));
+ if (!mOCVersion.isVersionValid()) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+
+ } else {
+ mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ?
+ RemoteOperationResult.ResultCode.OK_SSL :
+ RemoteOperationResult.ResultCode.OK_NO_SSL
+ );
+
+ retval = true;
+ }
+ }
+
+ } else {
+ mLatestResult = new RemoteOperationResult(false, status, get.getResponseHeaders());
+ }
+
+ } catch (JSONException e) {
+ mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+
+ } catch (Exception e) {
+ mLatestResult = new RemoteOperationResult(e);
+
+ } finally {
+ if (get != null)
+ get.releaseConnection();
+ }
+
+ if (mLatestResult.isSuccess()) {
+ Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+
+ } else if (mLatestResult.getException() != null) {
+ Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
+
+ } else {
+ Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
+ }
+
+ return retval;
+ }
+
+ private boolean isOnline() {
+ ConnectivityManager cm = (ConnectivityManager) mContext
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ return cm != null && cm.getActiveNetworkInfo() != null
+ && cm.getActiveNetworkInfo().isConnectedOrConnecting();
+ }
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ if (!isOnline()) {
+ return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
+ }
+ if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
+ tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
+
+ } else {
+ client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
+ boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH);
+ if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
+ Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection");
+ client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
+ tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
+ }
+ }
+ return mLatestResult;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.IOException;
+
+import org.apache.commons.httpclient.Credentials;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.network.BearerCredentials;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountsException;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Operation which execution involves one or several interactions with an ownCloud server.
+ *
+ * Provides methods to execute the operation both synchronously or asynchronously.
+ *
+ * @author David A. Velasco
+ */
+public abstract class RemoteOperation implements Runnable {
+
+ private static final String TAG = RemoteOperation.class.getSimpleName();
+
+ /** ownCloud account in the remote ownCloud server to operate */
+ private Account mAccount = null;
+
+ /** Android Application context */
+ private Context mContext = null;
+
+ /** Object to interact with the remote server */
+ private WebdavClient mClient = null;
+
+ /** Callback object to notify about the execution of the remote operation */
+ private OnRemoteOperationListener mListener = null;
+
+ /** Handler to the thread where mListener methods will be called */
+ private Handler mListenerHandler = null;
+
+ /** Activity */
+ private Activity mCallerActivity;
+
+
+ /**
+ * Abstract method to implement the operation in derived classes.
+ */
+ protected abstract RemoteOperationResult run(WebdavClient client);
+
+
+ /**
+ * Synchronously executes the remote operation on the received ownCloud account.
+ *
+ * Do not call this method from the main thread.
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @return Result of the operation.
+ */
+ public final RemoteOperationResult execute(Account account, Context context) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ try {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ return new RemoteOperationResult(e);
+ }
+ return run(mClient);
+ }
+
+
+ /**
+ * Synchronously executes the remote operation
+ *
+ * Do not call this method from the main thread.
+ *
+ * @param client Client object to reach an ownCloud server during the execution of the operation.
+ * @return Result of the operation.
+ */
+ public final RemoteOperationResult execute(WebdavClient client) {
+ if (client == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
+ mClient = client;
+ return run(client);
+ }
+
+
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
+ if (account == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
+ if (context == null)
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
+ mAccount = account;
+ mContext = context.getApplicationContext();
+ mCallerActivity = callerActivity;
+ mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+ }
+ mListener = listener;
+
+ if (listenerHandler == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+ }
+ mListenerHandler = listenerHandler;
+
+ Thread runnerThread = new Thread(this);
+ runnerThread.start();
+ return runnerThread;
+ }
+
+
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * @param client Client object to reach an ownCloud server during the execution of the operation.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
+ if (client == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
+ }
+ mClient = client;
+
+ if (listener == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
+ }
+ mListener = listener;
+
+ if (listenerHandler == null) {
+ throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
+ }
+ mListenerHandler = listenerHandler;
+
+ Thread runnerThread = new Thread(this);
+ runnerThread.start();
+ return runnerThread;
+ }
+
+ /**
+ * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
+ *
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final RemoteOperationResult retry() {
+ return execute(mClient);
+ }
+
+ /**
+ * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
+ *
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
+ return execute(mClient, listener, listenerHandler);
+ }
+
+
+ /**
+ * Asynchronous execution of the operation
+ * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)},
+ * and result posting.
+ *
+ * TODO refactor && clean the code; now it's a mess
+ */
+ @Override
+ public final void run() {
+ RemoteOperationResult result = null;
+ boolean repeat = false;
+ do {
+ try{
+ if (mClient == null) {
+ if (mAccount != null && mContext != null) {
+ if (mCallerActivity != null) {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
+ } else {
+ mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
+ }
+ } else {
+ throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
+ }
+ }
+
+ } catch (IOException e) {
+ Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
+ result = new RemoteOperationResult(e);
+
+ } catch (AccountsException e) {
+ Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
+ result = new RemoteOperationResult(e);
+ }
+
+ if (result == null)
+ result = run(mClient);
+
+ repeat = false;
+ if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
+// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
+ (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
+ /// possible fail due to lack of authorization in an operation performed in foreground
+ Credentials cred = mClient.getCredentials();
+ String ssoSessionCookie = mClient.getSsoSessionCookie();
+ if (cred != null || ssoSessionCookie != null) {
+ /// confirmed : unauthorized operation
+ AccountManager am = AccountManager.get(mContext);
+ boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
+ boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
+ if (bearerAuthorization) {
+ am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken());
+ } else if (samlBasedSsoAuthorization ) {
+ am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie);
+ } else {
+ am.clearPassword(mAccount);
+ }
+ mClient = null;
+ repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
+ result = null;
+ }
+ }
+ } while (repeat);
+
+ final RemoteOperationResult resultToSend = result;
+ if (mListenerHandler != null && mListener != null) {
+ mListenerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
+ }
+ });
+ }
+ }
+
+
+ /**
+ * Returns the current client instance to access the remote server.
+ *
+ * @return Current client instance to access the remote server.
+ */
+ public final WebdavClient getClient() {
+ return mClient;
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLException;
+
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.jackrabbit.webdav.DavException;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.network.CertificateCombinedException;
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+
+
+/**
+ * The result of a remote operation required to an ownCloud server.
+ *
+ * Provides a common classification of remote operation results for all the
+ * application.
+ *
+ * @author David A. Velasco
+ */
+public class RemoteOperationResult implements Serializable {
+
+ /** Generated - should be refreshed every time the class changes!! */
+ private static final long serialVersionUID = -4415103901492836870L;
+
+
+
+ private static final String TAG = "RemoteOperationResult";
+
+ public enum ResultCode {
+ OK,
+ OK_SSL,
+ OK_NO_SSL,
+ UNHANDLED_HTTP_CODE,
+ UNAUTHORIZED,
+ FILE_NOT_FOUND,
+ INSTANCE_NOT_CONFIGURED,
+ UNKNOWN_ERROR,
+ WRONG_CONNECTION,
+ TIMEOUT,
+ INCORRECT_ADDRESS,
+ HOST_NOT_AVAILABLE,
+ NO_NETWORK_CONNECTION,
+ SSL_ERROR,
+ SSL_RECOVERABLE_PEER_UNVERIFIED,
+ BAD_OC_VERSION,
+ CANCELLED,
+ INVALID_LOCAL_FILE_NAME,
+ INVALID_OVERWRITE,
+ CONFLICT,
+ OAUTH2_ERROR,
+ SYNC_CONFLICT,
+ LOCAL_STORAGE_FULL,
+ LOCAL_STORAGE_NOT_MOVED,
+ LOCAL_STORAGE_NOT_COPIED,
+ OAUTH2_ERROR_ACCESS_DENIED,
+ QUOTA_EXCEEDED,
+ ACCOUNT_NOT_FOUND,
+ ACCOUNT_EXCEPTION,
+ ACCOUNT_NOT_NEW,
+ ACCOUNT_NOT_THE_SAME
+ }
+
+ private boolean mSuccess = false;
+ private int mHttpCode = -1;
+ private Exception mException = null;
+ private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
+ private String mRedirectedLocation;
+
+ public RemoteOperationResult(ResultCode code) {
+ mCode = code;
+ mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
+ }
+
+ private RemoteOperationResult(boolean success, int httpCode) {
+ mSuccess = success;
+ mHttpCode = httpCode;
+
+ if (success) {
+ mCode = ResultCode.OK;
+
+ } else if (httpCode > 0) {
+ switch (httpCode) {
+ case HttpStatus.SC_UNAUTHORIZED:
+ mCode = ResultCode.UNAUTHORIZED;
+ break;
+ case HttpStatus.SC_NOT_FOUND:
+ mCode = ResultCode.FILE_NOT_FOUND;
+ break;
+ case HttpStatus.SC_INTERNAL_SERVER_ERROR:
+ mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
+ break;
+ case HttpStatus.SC_CONFLICT:
+ mCode = ResultCode.CONFLICT;
+ break;
+ case HttpStatus.SC_INSUFFICIENT_STORAGE:
+ mCode = ResultCode.QUOTA_EXCEEDED;
+ break;
+ default:
+ mCode = ResultCode.UNHANDLED_HTTP_CODE;
+ Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
+ }
+ }
+ }
+
+ public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
+ this(success, httpCode);
+ if (headers != null) {
+ Header current;
+ for (int i=0; i<headers.length; i++) {
+ current = headers[i];
+ if ("Location".equals(current.getName())) {
+ mRedirectedLocation = current.getValue();
+ break;
+ }
+ }
+ }
+ }
+
+ public RemoteOperationResult(Exception e) {
+ mException = e;
+
+ if (e instanceof OperationCancelledException) {
+ mCode = ResultCode.CANCELLED;
+
+ } else if (e instanceof SocketException) {
+ mCode = ResultCode.WRONG_CONNECTION;
+
+ } else if (e instanceof SocketTimeoutException) {
+ mCode = ResultCode.TIMEOUT;
+
+ } else if (e instanceof ConnectTimeoutException) {
+ mCode = ResultCode.TIMEOUT;
+
+ } else if (e instanceof MalformedURLException) {
+ mCode = ResultCode.INCORRECT_ADDRESS;
+
+ } else if (e instanceof UnknownHostException) {
+ mCode = ResultCode.HOST_NOT_AVAILABLE;
+
+ } else if (e instanceof AccountNotFoundException) {
+ mCode = ResultCode.ACCOUNT_NOT_FOUND;
+
+ } else if (e instanceof AccountsException) {
+ mCode = ResultCode.ACCOUNT_EXCEPTION;
+
+ } else if (e instanceof SSLException || e instanceof RuntimeException) {
+ CertificateCombinedException se = getCertificateCombinedException(e);
+ if (se != null) {
+ mException = se;
+ if (se.isRecoverable()) {
+ mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
+ }
+ } else if (e instanceof RuntimeException) {
+ mCode = ResultCode.HOST_NOT_AVAILABLE;
+
+ } else {
+ mCode = ResultCode.SSL_ERROR;
+ }
+
+ } else {
+ mCode = ResultCode.UNKNOWN_ERROR;
+ }
+
+ }
+
+ public boolean isSuccess() {
+ return mSuccess;
+ }
+
+ public boolean isCancelled() {
+ return mCode == ResultCode.CANCELLED;
+ }
+
+ public int getHttpCode() {
+ return mHttpCode;
+ }
+
+ public ResultCode getCode() {
+ return mCode;
+ }
+
+ public Exception getException() {
+ return mException;
+ }
+
+ public boolean isSslRecoverableException() {
+ return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
+ }
+
+ private CertificateCombinedException getCertificateCombinedException(Exception e) {
+ CertificateCombinedException result = null;
+ if (e instanceof CertificateCombinedException) {
+ return (CertificateCombinedException) e;
+ }
+ Throwable cause = mException.getCause();
+ Throwable previousCause = null;
+ while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
+ previousCause = cause;
+ cause = cause.getCause();
+ }
+ if (cause != null && cause instanceof CertificateCombinedException) {
+ result = (CertificateCombinedException) cause;
+ }
+ return result;
+ }
+
+ public String getLogMessage() {
+
+ if (mException != null) {
+ if (mException instanceof OperationCancelledException) {
+ return "Operation cancelled by the caller";
+
+ } else if (mException instanceof SocketException) {
+ return "Socket exception";
+
+ } else if (mException instanceof SocketTimeoutException) {
+ return "Socket timeout exception";
+
+ } else if (mException instanceof ConnectTimeoutException) {
+ return "Connect timeout exception";
+
+ } else if (mException instanceof MalformedURLException) {
+ return "Malformed URL exception";
+
+ } else if (mException instanceof UnknownHostException) {
+ return "Unknown host exception";
+
+ } else if (mException instanceof CertificateCombinedException) {
+ if (((CertificateCombinedException) mException).isRecoverable())
+ return "SSL recoverable exception";
+ else
+ return "SSL exception";
+
+ } else if (mException instanceof SSLException) {
+ return "SSL exception";
+
+ } else if (mException instanceof DavException) {
+ return "Unexpected WebDAV exception";
+
+ } else if (mException instanceof HttpException) {
+ return "HTTP violation";
+
+ } else if (mException instanceof IOException) {
+ return "Unrecovered transport exception";
+
+ } else if (mException instanceof AccountNotFoundException) {
+ Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount();
+ return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")";
+
+ } else if (mException instanceof AccountsException) {
+ return "Exception while using account";
+
+ } else {
+ return "Unexpected exception";
+ }
+ }
+
+ if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
+ return "The ownCloud server is not configured!";
+
+ } else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
+ return "No network connection";
+
+ } else if (mCode == ResultCode.BAD_OC_VERSION) {
+ return "No valid ownCloud version was found at the server";
+
+ } else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
+ return "Local storage full";
+
+ } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
+ return "Error while moving file to final directory";
+
+ } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
+ return "Account already existing when creating a new one";
+
+ } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
+ return "Authenticated with a different account than the one updating";
+ }
+
+ return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
+
+ }
+
+ public boolean isServerFail() {
+ return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
+ }
+
+ public boolean isException() {
+ return (mException != null);
+ }
+
+ public boolean isTemporalRedirection() {
+ return (mHttpCode == 302 || mHttpCode == 307);
+ }
+
+ public String getRedirectedLocation() {
+ return mRedirectedLocation;
+ }
+
+ public boolean isIdPRedirection() {
+ return (mRedirectedLocation != null &&
+ (mRedirectedLocation.toUpperCase().contains("SAML") ||
+ mRedirectedLocation.toLowerCase().contains("wayf")));
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the removal of a remote file or folder in the ownCloud server.
+ *
+ * @author David A. Velasco
+ */
+public class RemoveFileOperation extends RemoteOperation {
+
+ private static final String TAG = RemoveFileOperation.class.getSimpleName();
+
+ private static final int REMOVE_READ_TIMEOUT = 10000;
+ private static final int REMOVE_CONNECTION_TIMEOUT = 5000;
+
+ OCFile mFileToRemove;
+ boolean mDeleteLocalCopy;
+ DataStorageManager mDataStorageManager;
+
+
+ /**
+ * Constructor
+ *
+ * @param fileToRemove OCFile instance describing the remote file or folder to remove from the server
+ * @param deleteLocalCopy When 'true', and a local copy of the file exists, it is also removed.
+ * @param storageManager Reference to the local database corresponding to the account where the file is contained.
+ */
+ public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
+ mFileToRemove = fileToRemove;
+ mDeleteLocalCopy = deleteLocalCopy;
+ mDataStorageManager = storageManager;
+ }
+
+
+ /**
+ * Getter for the file to remove (or removed, if the operation was successfully performed).
+ *
+ * @return File to remove or already removed.
+ */
+ public OCFile getFile() {
+ return mFileToRemove;
+ }
+
+
+ /**
+ * Performs the remove operation
+ *
+ * @param client Client object to communicate with the remote ownCloud server.
+ */
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ DeleteMethod delete = null;
+ try {
+ delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
+ int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
+ if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
+ if (mFileToRemove.isDirectory()) {
+ mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
+ } else {
+ mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
+ }
+ }
+ delete.getResponseBodyAsString(); // exhaust the response, although not interesting
+ result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
+ Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (delete != null)
+ delete.releaseConnection();
+ }
+ return result;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.FileStorageUtils;
+//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
+
+import android.accounts.Account;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
+ *
+ * @author David A. Velasco
+ */
+public class RenameFileOperation extends RemoteOperation {
+
+ private static final String TAG = RenameFileOperation.class.getSimpleName();
+
+ private static final int RENAME_READ_TIMEOUT = 10000;
+ private static final int RENAME_CONNECTION_TIMEOUT = 5000;
+
+
+ private OCFile mFile;
+ private Account mAccount;
+ private String mNewName;
+ private String mNewRemotePath;
+ private DataStorageManager mStorageManager;
+
+
+ /**
+ * Constructor
+ *
+ * @param file OCFile instance describing the remote file or folder to rename
+ * @param account OwnCloud account containing the remote file
+ * @param newName New name to set as the name of file.
+ * @param storageManager Reference to the local database corresponding to the account where the file is contained.
+ */
+ public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
+ mFile = file;
+ mAccount = account;
+ mNewName = newName;
+ mNewRemotePath = null;
+ mStorageManager = storageManager;
+ }
+
+ public OCFile getFile() {
+ return mFile;
+ }
+
+
+ /**
+ * Performs the rename operation.
+ *
+ * @param client Client object to communicate with the remote ownCloud server.
+ */
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+
+ LocalMoveMethod move = null;
+ mNewRemotePath = null;
+ try {
+ if (mNewName.equals(mFile.getFileName())) {
+ return new RemoteOperationResult(ResultCode.OK);
+ }
+
+ String parent = (new File(mFile.getRemotePath())).getParent();
+ parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR;
+ mNewRemotePath = parent + mNewName;
+ if (mFile.isDirectory()) {
+ mNewRemotePath += OCFile.PATH_SEPARATOR;
+ }
+
+ // check if the new name is valid in the local file system
+ if (!isValidNewName()) {
+ return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
+ }
+
+ // check if a file with the new name already exists
+ if (client.existsFile(mNewRemotePath) || // remote check could fail by network failure. by indeterminate behavior of HEAD for folders ...
+ mStorageManager.getFileByPath(mNewRemotePath) != null) { // ... so local check is convenient
+ return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
+ }
+ move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
+ client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
+ int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
+ if (move.succeeded()) {
+
+ if (mFile.isDirectory()) {
+ saveLocalDirectory();
+
+ } else {
+ saveLocalFile();
+
+ }
+
+ /*
+ *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
+ * // TODO
+ * // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
+ * // stayed in the old folder;
+ * //
+ * // easiest and heaviest solution is synchronizing the parent folder (or the full account);
+ * //
+ * // a better solution is synchronizing the folders with the old and new names;
+ *}
+ */
+
+ }
+
+ move.getResponseBodyAsString(); // exhaust response, although not interesting
+ result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders());
+ Log_OC.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (move != null)
+ move.releaseConnection();
+ }
+ return result;
+ }
+
+
+ private void saveLocalDirectory() {
+ mStorageManager.moveDirectory(mFile, mNewRemotePath);
+ String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
+ File localDir = new File(localPath);
+ if (localDir.exists()) {
+ localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
+ // TODO - if renameTo fails, children files that are already down will result unlinked
+ }
+ }
+
+ private void saveLocalFile() {
+ mFile.setFileName(mNewName);
+
+ // try to rename the local copy of the file
+ if (mFile.isDown()) {
+ File f = new File(mFile.getStoragePath());
+ String parentStoragePath = f.getParent();
+ if (!parentStoragePath.endsWith(File.separator))
+ parentStoragePath += File.separator;
+ if (f.renameTo(new File(parentStoragePath + mNewName))) {
+ mFile.setStoragePath(parentStoragePath + mNewName);
+ }
+ // else - NOTHING: the link to the local file is kept although the local name can't be updated
+ // TODO - study conditions when this could be a problem
+ }
+
+ mStorageManager.saveFile(mFile);
+ }
+
+ /**
+ * Checks if the new name to set is valid in the file system
+ *
+ * The only way to be sure is trying to create a file with that name. It's made in the temporal directory
+ * for downloads, out of any account, and then removed.
+ *
+ * IMPORTANT: The test must be made in the same file system where files are download. The internal storage
+ * could be formatted with a different file system.
+ *
+ * TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities specific for the interactions with the file system
+ *
+ * @return 'True' if a temporal file named with the name to set could be created in the file system where
+ * local files are stored.
+ * @throws IOException When the temporal folder can not be created.
+ */
+ private boolean isValidNewName() throws IOException {
+ // check tricky names
+ if (mNewName == null || mNewName.length() <= 0 || mNewName.contains(File.separator) || mNewName.contains("%")) {
+ return false;
+ }
+ // create a test file
+ String tmpFolderName = FileStorageUtils.getTemporalPath("");
+ File testFile = new File(tmpFolderName + mNewName);
+ File tmpFolder = testFile.getParentFile();
+ tmpFolder.mkdirs();
+ if (!tmpFolder.isDirectory()) {
+ throw new IOException("Unexpected error: temporal directory could not be created");
+ }
+ try {
+ testFile.createNewFile(); // return value is ignored; it could be 'false' because the file already existed, that doesn't invalidate the name
+ } catch (IOException e) {
+ Log_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
+ return false;
+ }
+ boolean result = (testFile.exists() && testFile.isFile());
+
+ // cleaning ; result is ignored, since there is not much we could do in case of failure, but repeat and repeat...
+ testFile.delete();
+
+ return result;
+ }
+
+
+ // move operation
+ private class LocalMoveMethod extends DavMethodBase {
+
+ public LocalMoveMethod(String uri, String dest) {
+ super(uri);
+ addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
+ }
+
+ @Override
+ public String getName() {
+ return "MOVE";
+ }
+
+ @Override
+ protected boolean isSuccess(int status) {
+ return status == 201 || status == 204;
+ }
+
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+public class SynchronizeFileOperation extends RemoteOperation {
+
+ private String TAG = SynchronizeFileOperation.class.getSimpleName();
+ private static final int SYNC_READ_TIMEOUT = 10000;
+ private static final int SYNC_CONNECTION_TIMEOUT = 5000;
+
+ private OCFile mLocalFile;
+ private OCFile mServerFile;
+ private DataStorageManager mStorageManager;
+ private Account mAccount;
+ private boolean mSyncFileContents;
+ private boolean mLocalChangeAlreadyKnown;
+ private Context mContext;
+
+ private boolean mTransferWasRequested = false;
+
+ public SynchronizeFileOperation(
+ OCFile localFile,
+ OCFile serverFile, // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation
+ DataStorageManager storageManager,
+ Account account,
+ boolean syncFileContents,
+ boolean localChangeAlreadyKnown,
+ Context context) {
+
+ mLocalFile = localFile;
+ mServerFile = serverFile;
+ mStorageManager = storageManager;
+ mAccount = account;
+ mSyncFileContents = syncFileContents;
+ mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
+ mContext = context;
+ }
+
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+
+ PropFindMethod propfind = null;
+ RemoteOperationResult result = null;
+ mTransferWasRequested = false;
+ try {
+ if (!mLocalFile.isDown()) {
+ /// easy decision
+ requestForDownload(mLocalFile);
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ /// local copy in the device -> need to think a bit more before do anything
+
+ if (mServerFile == null) {
+ /// take the duty of check the server for the current state of the file there
+ propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
+ DavConstants.PROPFIND_ALL_PROP,
+ DavConstants.DEPTH_0);
+ int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
+ boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
+ if (isMultiStatus) {
+ MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
+ client.getBaseUri().getPath());
+ mServerFile = fillOCFile(we);
+ mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
+
+ } else {
+ client.exhaustResponse(propfind.getResponseBodyAsStream());
+ result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
+ }
+ }
+
+ if (result == null) { // true if the server was not checked. nothing was wrong with the remote request
+
+ /// check changes in server and local file
+ boolean serverChanged = false;
+ if (mServerFile.getEtag() != null) {
+ serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
+ } else {
+ // server without etags
+ serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
+ }
+ boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+ // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
+
+ /// decide action to perform depending upon changes
+ if (localChanged && serverChanged) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+
+ } else if (localChanged) {
+ if (mSyncFileContents) {
+ requestForUpload(mLocalFile);
+ // the local update of file properties will be done by the FileUploader service when the upload finishes
+ } else {
+ // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
+ // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
+ // that an upload is necessary (for instance, in FileObserverService).
+ }
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else if (serverChanged) {
+ if (mSyncFileContents) {
+ requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
+ // the update of local data will be done later by the FileUploader service when the upload finishes
+ } else {
+ // TODO CHECK: is this really useful in some point in the code?
+ mServerFile.setKeepInSync(mLocalFile.keepInSync());
+ mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+ mServerFile.setStoragePath(mLocalFile.getStoragePath());
+ mServerFile.setParentId(mLocalFile.getParentId());
+ mStorageManager.saveFile(mServerFile);
+
+ }
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ // nothing changed, nothing to do
+ result = new RemoteOperationResult(ResultCode.OK);
+ }
+
+ }
+
+ }
+
+ Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
+
+ } finally {
+ if (propfind != null)
+ propfind.releaseConnection();
+ }
+ return result;
+ }
+
+
+ /**
+ * Requests for an upload to the FileUploader service
+ *
+ * @param file OCFile object representing the file to upload
+ */
+ private void requestForUpload(OCFile file) {
+ Intent i = new Intent(mContext, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
+ i.putExtra(FileUploader.KEY_FILE, file);
+ /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath); // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
+ mContext.startService(i);
+ mTransferWasRequested = true;
+ }
+
+
+ /**
+ * Requests for a download to the FileDownloader service
+ *
+ * @param file OCFile object representing the file to download
+ */
+ private void requestForDownload(OCFile file) {
+ Intent i = new Intent(mContext, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
+ mContext.startService(i);
+ mTransferWasRequested = true;
+ }
+
+
+ /**
+ * Creates and populates a new {@link OCFile} object with the data read from the server.
+ *
+ * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
+ * @return New OCFile instance representing the remote resource described by we.
+ */
+ private OCFile fillOCFile(WebdavEntry we) {
+ OCFile file = new OCFile(we.decodedPath());
+ file.setCreationTimestamp(we.createTimestamp());
+ file.setFileLength(we.contentLength());
+ file.setMimetype(we.contentType());
+ file.setModificationTimestamp(we.modifiedTimestamp());
+ return file;
+ }
+
+
+ public boolean transferWasRequested() {
+ return mTransferWasRequested;
+ }
+
+
+ public OCFile getLocalFile() {
+ return mLocalFile;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.apache.http.HttpStatus;
+import org.apache.jackrabbit.webdav.DavConstants;
+import org.apache.jackrabbit.webdav.MultiStatus;
+import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import android.accounts.Account;
+import android.content.Context;
+
+
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavEntry;
+import eu.alefzero.webdav.WebdavUtils;
+
+
+/**
+ * Remote operation performing the synchronization a the contents of a remote folder with the local database
+ *
+ * @author David A. Velasco
+ */
+public class SynchronizeFolderOperation extends RemoteOperation {
+
+ private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
+
+ /** Remote folder to synchronize */
+ private String mRemotePath;
+
+ /** Timestamp for the synchronization in progress */
+ private long mCurrentSyncTime;
+
+ /** Id of the folder to synchronize in the local database */
+ private long mParentId;
+
+ /** Access to the local database */
+ private DataStorageManager mStorageManager;
+
+ /** Account where the file to synchronize belongs */
+ private Account mAccount;
+
+ /** Android context; necessary to send requests to the download service; maybe something to refactor */
+ private Context mContext;
+
+ /** Files and folders contained in the synchronized folder */
+ private List<OCFile> mChildren;
+
+ private int mConflictsFound;
+
+ private int mFailsInFavouritesFound;
+
+ private Map<String, String> mForgottenLocalFiles;
+
+
+ public SynchronizeFolderOperation( String remotePath,
+ long currentSyncTime,
+ long parentId,
+ DataStorageManager dataStorageManager,
+ Account account,
+ Context context ) {
+ mRemotePath = remotePath;
+ mCurrentSyncTime = currentSyncTime;
+ mParentId = parentId;
+ mStorageManager = dataStorageManager;
+ mAccount = account;
+ mContext = context;
+ mForgottenLocalFiles = new HashMap<String, String>();
+ }
+
+
+ public int getConflictsFound() {
+ return mConflictsFound;
+ }
+
+ public int getFailsInFavouritesFound() {
+ return mFailsInFavouritesFound;
+ }
+
+ public Map<String, String> getForgottenLocalFiles() {
+ return mForgottenLocalFiles;
+ }
+
+ /**
+ * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
+ *
+ * @return List of files and folders contained in the synchronized folder.
+ */
+ public List<OCFile> getChildren() {
+ return mChildren;
+ }
+
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ mFailsInFavouritesFound = 0;
+ mConflictsFound = 0;
+ mForgottenLocalFiles.clear();
+
+ // code before in FileSyncAdapter.fetchData
+ PropFindMethod query = null;
+ try {
+ Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
+
+ // remote request
+ query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
+ DavConstants.PROPFIND_ALL_PROP,
+ DavConstants.DEPTH_1);
+ int status = client.executeMethod(query);
+
+ // check and process response - /// TODO take into account all the possible status per child-resource
+ if (isMultiStatus(status)) {
+ MultiStatus resp = query.getResponseBodyAsMultiStatus();
+
+ // synchronize properties of the parent folder, if necessary
+ if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
+ OCFile parent = fillOCFile(we);
+ mStorageManager.saveFile(parent);
+ mParentId = parent.getFileId();
+ }
+
+ // read contents in folder
+ List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
+ List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
+ for (int i = 1; i < resp.getResponses().length; ++i) {
+ /// new OCFile instance with the data from the server
+ WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
+ OCFile file = fillOCFile(we);
+
+ /// set data about local state, keeping unchanged former data if existing
+ file.setLastSyncDateForProperties(mCurrentSyncTime);
+ OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
+ if (oldFile != null) {
+ file.setKeepInSync(oldFile.keepInSync());
+ file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
+ file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData()); // must be kept unchanged when the file contents are not updated
+ checkAndFixForeignStoragePath(oldFile);
+ file.setStoragePath(oldFile.getStoragePath());
+ }
+
+ /// scan default location if local copy of file is not linked in OCFile instance
+ if (file.getStoragePath() == null && !file.isDirectory()) {
+ File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
+ if (f.exists()) {
+ file.setStoragePath(f.getAbsolutePath());
+ file.setLastSyncDateForData(f.lastModified());
+ }
+ }
+
+ /// prepare content synchronization for kept-in-sync files
+ if (file.keepInSync()) {
+ SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile,
+ file,
+ mStorageManager,
+ mAccount,
+ true,
+ false,
+ mContext
+ );
+ filesToSyncContents.add(operation);
+ }
+
+ updatedFiles.add(file);
+ }
+
+ // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
+ mStorageManager.saveFiles(updatedFiles);
+
+ // request for the synchronization of files AFTER saving last properties
+ SynchronizeFileOperation op = null;
+ RemoteOperationResult contentsResult = null;
+ for (int i=0; i < filesToSyncContents.size(); i++) {
+ op = filesToSyncContents.get(i);
+ contentsResult = op.execute(client); // returns without waiting for upload or download finishes
+ if (!contentsResult.isSuccess()) {
+ if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound++;
+ } else {
+ mFailsInFavouritesFound++;
+ if (contentsResult.getException() != null) {
+ Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage(), contentsResult.getException());
+ } else {
+ Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+ }
+ }
+ } // won't let these fails break the synchronization process
+ }
+
+
+ // removal of obsolete files
+ mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
+ OCFile file;
+ String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
+ for (int i=0; i < mChildren.size(); ) {
+ file = mChildren.get(i);
+ if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
+ Log_OC.d(TAG, "removing file: " + file);
+ mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
+ mChildren.remove(i);
+ } else {
+ i++;
+ }
+ }
+
+ } else {
+ client.exhaustResponse(query.getResponseBodyAsStream());
+ }
+
+ // prepare result object
+ if (isMultiStatus(status)) {
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
+
+ } else {
+ result = new RemoteOperationResult(true, status, query.getResponseHeaders());
+ }
+ } else {
+ result = new RemoteOperationResult(false, status, query.getResponseHeaders());
+ }
+
+
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+
+
+ } finally {
+ if (query != null)
+ query.releaseConnection(); // let the connection available for other methods
+ if (result.isSuccess()) {
+ Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+ } else {
+ if (result.isException()) {
+ Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+ } else {
+ Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+ }
+ }
+ }
+
+ return result;
+ }
+
+
+ public boolean isMultiStatus(int status) {
+ return (status == HttpStatus.SC_MULTI_STATUS);
+ }
+
+
+ /**
+ * Creates and populates a new {@link OCFile} object with the data read from the server.
+ *
+ * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
+ * @return New OCFile instance representing the remote resource described by we.
+ */
+ private OCFile fillOCFile(WebdavEntry we) {
+ OCFile file = new OCFile(we.decodedPath());
+ file.setCreationTimestamp(we.createTimestamp());
+ file.setFileLength(we.contentLength());
+ file.setMimetype(we.contentType());
+ file.setModificationTimestamp(we.modifiedTimestamp());
+ file.setParentId(mParentId);
+ return file;
+ }
+
+
+ /**
+ * Checks the storage path of the OCFile received as parameter. If it's out of the local ownCloud folder,
+ * tries to copy the file inside it.
+ *
+ * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in
+ * {@link #mForgottenLocalFiles}
+ *
+ * @param file File to check and fix.
+ */
+ private void checkAndFixForeignStoragePath(OCFile file) {
+ String storagePath = file.getStoragePath();
+ String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
+ if (storagePath != null && !storagePath.equals(expectedPath)) {
+ /// fix storagePaths out of the local ownCloud folder
+ File originalFile = new File(storagePath);
+ if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } else {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ File expectedFile = new File(expectedPath);
+ File expectedParent = expectedFile.getParentFile();
+ expectedParent.mkdirs();
+ if (!expectedParent.isDirectory()) {
+ throw new IOException("Unexpected error: parent directory could not be created");
+ }
+ expectedFile.createNewFile();
+ if (!expectedFile.isFile()) {
+ throw new IOException("Unexpected error: target file could not be created");
+ }
+ in = new FileInputStream(originalFile);
+ out = new FileOutputStream(expectedFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0){
+ out.write(buf, 0, len);
+ }
+ file.setStoragePath(expectedPath);
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
+ mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
+ file.setStoragePath(null);
+
+ } finally {
+ try {
+ if (in != null) in.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing input stream for " + storagePath + " (ignoring)", e);
+ }
+ try {
+ if (out != null) out.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+ }
+ }
+ }
+ }
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.OwnCloudVersion;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.Context;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Remote operation that checks the version of an ownCloud server and stores it locally
+ *
+ * @author David A. Velasco
+ */
+public class UpdateOCVersionOperation extends RemoteOperation {
+
+ private static final String TAG = UpdateOCVersionOperation.class.getSimpleName();
+
+ private Account mAccount;
+ private Context mContext;
+
+
+ public UpdateOCVersionOperation(Account account, Context context) {
+ mAccount = account;
+ mContext = context;
+ }
+
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ AccountManager accountMngr = AccountManager.get(mContext);
+ String statUrl = accountMngr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
+ statUrl += AccountUtils.STATUS_PATH;
+ RemoteOperationResult result = null;
+ GetMethod get = null;
+ try {
+ get = new GetMethod(statUrl);
+ int status = client.executeMethod(get);
+ if (status != HttpStatus.SC_OK) {
+ client.exhaustResponse(get.getResponseBodyAsStream());
+ result = new RemoteOperationResult(false, status, get.getResponseHeaders());
+
+ } else {
+ String response = get.getResponseBodyAsString();
+ if (response != null) {
+ JSONObject json = new JSONObject(response);
+ if (json != null && json.getString("version") != null) {
+ OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version"));
+ if (ocver.isVersionValid()) {
+ accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString());
+ Log_OC.d(TAG, "Got new OC version " + ocver.toString());
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ Log_OC.w(TAG, "Invalid version number received from server: " + json.getString("version"));
+ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
+ }
+ }
+ }
+ if (result == null) {
+ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+ }
+ }
+ Log_OC.i(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage());
+
+ } catch (JSONException e) {
+ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
+ Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(e);
+ Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+
+ } finally {
+ if (get != null)
+ get.releaseConnection();
+ }
+ return result;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.operations;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.http.HttpStatus;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import android.accounts.Account;
+
+
+import eu.alefzero.webdav.FileRequestEntity;
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+import eu.alefzero.webdav.WebdavClient;
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Remote operation performing the upload of a file to an ownCloud server
+ *
+ * @author David A. Velasco
+ */
+public class UploadFileOperation extends RemoteOperation {
+
+ private static final String TAG = UploadFileOperation.class.getSimpleName();
+
+ private Account mAccount;
+ private OCFile mFile;
+ private OCFile mOldFile;
+ private String mRemotePath = null;
+ private boolean mIsInstant = false;
+ private boolean mRemoteFolderToBeCreated = false;
+ private boolean mForceOverwrite = false;
+ private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
+ private boolean mWasRenamed = false;
+ private String mOriginalFileName = null;
+ private String mOriginalStoragePath = null;
+ PutMethod mPutMethod = null;
+ private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
+ private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+
+ protected RequestEntity mEntity = null;
+
+
+ public UploadFileOperation( Account account,
+ OCFile file,
+ boolean isInstant,
+ boolean forceOverwrite,
+ int localBehaviour) {
+ if (account == null)
+ throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
+ if (file == null)
+ throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
+ if (file.getStoragePath() == null || file.getStoragePath().length() <= 0
+ || !(new File(file.getStoragePath()).exists())) {
+ throw new IllegalArgumentException(
+ "Illegal file in UploadFileOperation; storage path invalid or file not found: "
+ + file.getStoragePath());
+ }
+
+ mAccount = account;
+ mFile = file;
+ mRemotePath = file.getRemotePath();
+ mIsInstant = isInstant;
+ mForceOverwrite = forceOverwrite;
+ mLocalBehaviour = localBehaviour;
+ mOriginalStoragePath = mFile.getStoragePath();
+ mOriginalFileName = mFile.getFileName();
+ }
+
+ public Account getAccount() {
+ return mAccount;
+ }
+
+ public String getFileName() {
+ return mOriginalFileName;
+ }
+
+ public OCFile getFile() {
+ return mFile;
+ }
+
+ public OCFile getOldFile() {
+ return mOldFile;
+ }
+
+ public String getOriginalStoragePath() {
+ return mOriginalStoragePath;
+ }
+
+ public String getStoragePath() {
+ return mFile.getStoragePath();
+ }
+
+ public String getRemotePath() {
+ return mFile.getRemotePath();
+ }
+
+ public String getMimeType() {
+ return mFile.getMimetype();
+ }
+
+ public boolean isInstant() {
+ return mIsInstant;
+ }
+
+ public boolean isRemoteFolderToBeCreated() {
+ return mRemoteFolderToBeCreated;
+ }
+
+ public void setRemoteFolderToBeCreated() {
+ mRemoteFolderToBeCreated = true;
+ }
+
+ public boolean getForceOverwrite() {
+ return mForceOverwrite;
+ }
+
+ public boolean wasRenamed() {
+ return mWasRenamed;
+ }
+
+ public Set<OnDatatransferProgressListener> getDataTransferListeners() {
+ return mDataTransferListeners;
+ }
+
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.add(listener);
+ }
+ if (mEntity != null) {
+ ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
+ }
+ }
+
+ public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.remove(listener);
+ }
+ if (mEntity != null) {
+ ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
+ }
+ }
+
+ @Override
+ protected RemoteOperationResult run(WebdavClient client) {
+ RemoteOperationResult result = null;
+ boolean localCopyPassed = false, nameCheckPassed = false;
+ File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
+ try {
+ // / rename the file to upload, if necessary
+ if (!mForceOverwrite) {
+ String remotePath = getAvailableRemotePath(client, mRemotePath);
+ mWasRenamed = !remotePath.equals(mRemotePath);
+ if (mWasRenamed) {
+ createNewOCFile(remotePath);
+ }
+ }
+ nameCheckPassed = true;
+
+ String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // /
+ // not
+ // before
+ // getAvailableRemotePath()
+ // !!!
+ expectedFile = new File(expectedPath);
+
+ // / check location of local file; if not the expected, copy to a
+ // temporal file before upload (if COPY is the expected behaviour)
+ if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
+
+ if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
+ result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
+ return result; // error condition when the file should be
+ // copied
+
+ } else {
+ String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+ mFile.setStoragePath(temporalPath);
+ temporalFile = new File(temporalPath);
+ if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
+ // weird
+ // but
+ // possible
+ // situation
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ File temporalParent = temporalFile.getParentFile();
+ temporalParent.mkdirs();
+ if (!temporalParent.isDirectory()) {
+ throw new IOException("Unexpected error: parent directory could not be created");
+ }
+ temporalFile.createNewFile();
+ if (!temporalFile.isFile()) {
+ throw new IOException("Unexpected error: target file could not be created");
+ }
+ in = new FileInputStream(originalFile);
+ out = new FileOutputStream(temporalFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+
+ } catch (Exception e) {
+ result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
+ return result;
+
+ } finally {
+ try {
+ if (in != null)
+ in.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
+ }
+ try {
+ if (out != null)
+ out.close();
+ } catch (Exception e) {
+ Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
+ }
+ }
+ }
+ }
+ }
+ localCopyPassed = true;
+
+ // / perform the upload
+ synchronized (mCancellationRequested) {
+ if (mCancellationRequested.get()) {
+ throw new OperationCancelledException();
+ } else {
+ mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
+ }
+ }
+ int status = uploadFile(client);
+
+ // / move local temporal file or original file to its corresponding
+ // location in the ownCloud local folder
+ if (isSuccess(status)) {
+ if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
+ mFile.setStoragePath(null);
+
+ } else {
+ mFile.setStoragePath(expectedPath);
+ File fileToMove = null;
+ if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
+ // ; see where temporalFile was
+ // set
+ fileToMove = temporalFile;
+ } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
+ fileToMove = originalFile;
+ }
+ if (!expectedFile.equals(fileToMove)) {
+ File expectedFolder = expectedFile.getParentFile();
+ expectedFolder.mkdirs();
+ if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
+ mFile.setStoragePath(null); // forget the local file
+ // by now, treat this as a success; the file was
+ // uploaded; the user won't like that the local file
+ // is not linked, but this should be a very rare
+ // fail;
+ // the best option could be show a warning message
+ // (but not a fail)
+ // result = new
+ // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
+ // return result;
+ }
+ }
+ }
+ }
+
+ result = new RemoteOperationResult(isSuccess(status), status, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null));
+
+ } catch (Exception e) {
+ // TODO something cleaner with cancellations
+ if (mCancellationRequested.get()) {
+ result = new RemoteOperationResult(new OperationCancelledException());
+ } else {
+ result = new RemoteOperationResult(e);
+ }
+
+ } finally {
+ if (temporalFile != null && !originalFile.equals(temporalFile)) {
+ temporalFile.delete();
+ }
+ if (result.isSuccess()) {
+ Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+ } else {
+ if (result.getException() != null) {
+ String complement = "";
+ if (!nameCheckPassed) {
+ complement = " (while checking file existence in server)";
+ } else if (!localCopyPassed) {
+ complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
+ + ")";
+ }
+ Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
+ } else {
+ Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void createNewOCFile(String newRemotePath) {
+ // a new OCFile instance must be created for a new remote path
+ OCFile newFile = new OCFile(newRemotePath);
+ newFile.setCreationTimestamp(mFile.getCreationTimestamp());
+ newFile.setFileLength(mFile.getFileLength());
+ newFile.setMimetype(mFile.getMimetype());
+ newFile.setModificationTimestamp(mFile.getModificationTimestamp());
+ newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
+ // newFile.setEtag(mFile.getEtag())
+ newFile.setKeepInSync(mFile.keepInSync());
+ newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
+ newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
+ newFile.setStoragePath(mFile.getStoragePath());
+ newFile.setParentId(mFile.getParentId());
+ mOldFile = mFile;
+ mFile = newFile;
+ }
+
+ public boolean isSuccess(int status) {
+ return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
+ }
+
+ protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
+ int status = -1;
+ try {
+ File f = new File(mFile.getStoragePath());
+ mEntity = new FileRequestEntity(f, getMimeType());
+ synchronized (mDataTransferListeners) {
+ ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners);
+ }
+ mPutMethod.setRequestEntity(mEntity);
+ status = client.executeMethod(mPutMethod);
+ client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
+
+ } finally {
+ mPutMethod.releaseConnection(); // let the connection available for
+ // other methods
+ }
+ return status;
+ }
+
+ /**
+ * Checks if remotePath does not exist in the server and returns it, or adds
+ * a suffix to it in order to avoid the server file is overwritten.
+ *
+ * @param string
+ * @return
+ */
+ private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
+ boolean check = wc.existsFile(remotePath);
+ if (!check) {
+ return remotePath;
+ }
+
+ int pos = remotePath.lastIndexOf(".");
+ String suffix = "";
+ String extension = "";
+ if (pos >= 0) {
+ extension = remotePath.substring(pos + 1);
+ remotePath = remotePath.substring(0, pos);
+ }
+ int count = 2;
+ do {
+ suffix = " (" + count + ")";
+ if (pos >= 0)
+ check = wc.existsFile(remotePath + suffix + "." + extension);
+ else
+ check = wc.existsFile(remotePath + suffix);
+ count++;
+ } while (check);
+
+ if (pos >= 0) {
+ return remotePath + suffix + "." + extension;
+ } else {
+ return remotePath + suffix;
+ }
+ }
+
+ public void cancel() {
+ synchronized (mCancellationRequested) {
+ mCancellationRequested.set(true);
+ if (mPutMethod != null)
+ mPutMethod.abort();
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.providers;
+
+import java.util.HashMap;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.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;
+
+/**
+ * The ContentProvider for the ownCloud App.
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class FileContentProvider 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_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+ ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);
+ 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);
+ mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
+ ProviderTableMeta.FILE_LAST_SYNC_DATE);
+ mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
+ ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);
+ mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
+ ProviderTableMeta.FILE_KEEP_IN_SYNC);
+ mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
+ ProviderTableMeta.FILE_ACCOUNT_OWNER);
+ }
+
+ private static final int SINGLE_FILE = 1;
+ private static final int DIRECTORY = 2;
+ private static final int ROOT_DIRECTORY = 3;
+
+ private UriMatcher mUriMatcher;
+// static {
+// mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, ROOT_DIRECTORY);
+// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE);
+// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE);
+// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY);
+// }
+
+
+ @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 &&
+ mUriMatcher.match(uri) != ROOT_DIRECTORY) {
+
+ throw new IllegalArgumentException("Unknown uri id: " + uri);
+ }
+
+ SQLiteDatabase db = mDbHelper.getWritableDatabase();
+ long rowId = db.insert(ProviderTableMeta.DB_NAME, null, 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());
+
+ String authority = getContext().getResources().getString(R.string.authority);
+ mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
+ mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
+ mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
+ mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
+
+ 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:
+ 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();
+ // DB case_sensitive
+ db.execSQL("PRAGMA case_sensitive_like = true");
+ 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) {
+ // files table
+ Log_OC.i("SQL", "Entering in onCreate");
+ 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, "
+ + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
+ + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
+ + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
+ + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log_OC.i("SQL", "Entering in onUpgrade");
+ boolean upgraded = false;
+ if (oldVersion == 1 && newVersion >= 2) {
+ Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
+ db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
+ " DEFAULT 0");
+ upgraded = true;
+ }
+ if (oldVersion < 3 && newVersion >= 3) {
+ Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
+ db.beginTransaction();
+ try {
+ db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " +
+ " DEFAULT 0");
+
+ // assume there are not local changes pending to upload
+ db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
+ " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() +
+ " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
+
+ upgraded = true;
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+ if (oldVersion < 4 && newVersion >= 4) {
+ Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
+ db.beginTransaction();
+ try {
+ db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER " +
+ " DEFAULT 0");
+
+ db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
+ " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED +
+ " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
+
+ upgraded = true;
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+ if (!upgraded)
+ Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
+ }
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.syncadapter;\r
+\r
+import java.io.IOException;\r
+import java.util.Date;\r
+\r
+import org.apache.http.HttpRequest;\r
+import org.apache.http.HttpResponse;\r
+import org.apache.http.client.ClientProtocolException;\r
+import org.apache.http.conn.ConnectionKeepAliveStrategy;\r
+import org.apache.http.protocol.HttpContext;\r
+\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;\r
+import com.owncloud.android.datamodel.DataStorageManager;\r
+import com.owncloud.android.network.OwnCloudClientUtils;\r
+\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.accounts.AuthenticatorException;\r
+import android.accounts.OperationCanceledException;\r
+import android.content.AbstractThreadedSyncAdapter;\r
+import android.content.ContentProviderClient;\r
+import android.content.Context;\r
+import eu.alefzero.webdav.WebdavClient;\r
+\r
+/**\r
+ * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete\r
+ * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..\r
+ * \r
+ * @author sassman\r
+ * \r
+ */\r
+public abstract class AbstractOwnCloudSyncAdapter extends\r
+ AbstractThreadedSyncAdapter {\r
+\r
+ private AccountManager accountManager;\r
+ private Account account;\r
+ private ContentProviderClient contentProvider;\r
+ private Date lastUpdated;\r
+ private DataStorageManager mStoreManager;\r
+\r
+ private WebdavClient mClient = null;\r
+\r
+ public AbstractOwnCloudSyncAdapter(Context context, boolean autoInitialize) {\r
+ super(context, autoInitialize);\r
+ this.setAccountManager(AccountManager.get(context));\r
+ }\r
+\r
+ public AccountManager getAccountManager() {\r
+ return accountManager;\r
+ }\r
+\r
+ public void setAccountManager(AccountManager accountManager) {\r
+ this.accountManager = accountManager;\r
+ }\r
+\r
+ public Account getAccount() {\r
+ return account;\r
+ }\r
+\r
+ public void setAccount(Account account) {\r
+ this.account = account;\r
+ }\r
+\r
+ public ContentProviderClient getContentProvider() {\r
+ return contentProvider;\r
+ }\r
+\r
+ public void setContentProvider(ContentProviderClient contentProvider) {\r
+ this.contentProvider = contentProvider;\r
+ }\r
+\r
+ public Date getLastUpdated() {\r
+ return lastUpdated;\r
+ }\r
+\r
+ public void setLastUpdated(Date lastUpdated) {\r
+ this.lastUpdated = lastUpdated;\r
+ }\r
+\r
+ public void setStorageManager(DataStorageManager storage_manager) {\r
+ mStoreManager = storage_manager;\r
+ }\r
+\r
+ public DataStorageManager getStorageManager() {\r
+ return mStoreManager;\r
+ }\r
+\r
+ protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {\r
+ return new ConnectionKeepAliveStrategy() {\r
+ public long getKeepAliveDuration(HttpResponse response,\r
+ HttpContext context) {\r
+ // Change keep alive straategy basing on response: ie\r
+ // forbidden/not found/etc\r
+ // should have keep alive 0\r
+ // default return: 5s\r
+ int statusCode = response.getStatusLine().getStatusCode();\r
+\r
+ // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed\r
+ // out\r
+ if ((statusCode >= 400 && statusCode <= 418)\r
+ || (statusCode >= 421 && statusCode <= 426)\r
+ || (statusCode >= 500 && statusCode <= 510)\r
+ || statusCode == 118) {\r
+ return 0;\r
+ }\r
+\r
+ return 5 * 1000;\r
+ }\r
+ };\r
+ }\r
+\r
+ protected HttpResponse fireRawRequest(HttpRequest query)\r
+ throws ClientProtocolException, OperationCanceledException,\r
+ AuthenticatorException, IOException {\r
+ /*\r
+ * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme\r
+ * basicAuth = new BasicScheme();\r
+ * httpContext.setAttribute("preemptive-auth", basicAuth);\r
+ * \r
+ * HttpResponse response = getClient().execute(mHost, query,\r
+ * httpContext);\r
+ */\r
+ return null;\r
+ }\r
+\r
+ protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {\r
+ AccountUtils.constructFullURLForAccount(getContext(), account);\r
+ mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());\r
+ }\r
+ \r
+ protected WebdavClient getClient() {\r
+ return mClient;\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.syncadapter;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.ByteArrayEntity;
+
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.SyncResult;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+
+public class ContactSyncAdapter extends AbstractOwnCloudSyncAdapter {
+ private String mAddrBookUri;
+
+ public ContactSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ mAddrBookUri = null;
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+ setAccount(account);
+ setContentProvider(provider);
+ Cursor c = getLocalContacts(false);
+ if (c.moveToFirst()) {
+ do {
+ String lookup = c.getString(c
+ .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
+ String a = getAddressBookUri();
+ String uri = a + lookup + ".vcf";
+ FileInputStream f;
+ try {
+ f = getContactVcard(lookup);
+ HttpPut query = new HttpPut(uri);
+ byte[] b = new byte[f.available()];
+ f.read(b);
+ query.setEntity(new ByteArrayEntity(b));
+ fireRawRequest(query);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ } catch (OperationCanceledException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (AuthenticatorException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ } while (c.moveToNext());
+ // } while (c.moveToNext());
+ }
+
+ }
+
+ private String getAddressBookUri() {
+ if (mAddrBookUri != null)
+ return mAddrBookUri;
+
+ AccountManager am = getAccountManager();
+ @SuppressWarnings("deprecation")
+ String uri = am.getUserData(getAccount(),
+ AccountAuthenticator.KEY_OC_URL).replace(
+ AccountUtils.WEBDAV_PATH_2_0, AccountUtils.CARDDAV_PATH_2_0);
+ uri += "/addressbooks/"
+ + getAccount().name.substring(0,
+ getAccount().name.lastIndexOf('@')) + "/default/";
+ mAddrBookUri = uri;
+ return uri;
+ }
+
+ private FileInputStream getContactVcard(String lookupKey)
+ throws IOException {
+ Uri uri = Uri.withAppendedPath(
+ ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
+ AssetFileDescriptor fd = getContext().getContentResolver()
+ .openAssetFileDescriptor(uri, "r");
+ return fd.createInputStream();
+ }
+
+ private Cursor getLocalContacts(boolean include_hidden_contacts) {
+ return getContext().getContentResolver().query(
+ ContactsContract.Contacts.CONTENT_URI,
+ new String[] { ContactsContract.Contacts._ID,
+ ContactsContract.Contacts.LOOKUP_KEY },
+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?",
+ new String[] { (include_hidden_contacts ? "0" : "1") },
+ ContactsContract.Contacts._ID + " DESC");
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.syncadapter;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class ContactSyncService extends Service {
+ private static final Object syncAdapterLock = new Object();
+ private static AbstractOwnCloudSyncAdapter mSyncAdapter = null;
+
+ @Override
+ public void onCreate() {
+ synchronized (syncAdapterLock) {
+ if (mSyncAdapter == null) {
+ mSyncAdapter = new ContactSyncAdapter(getApplicationContext(),
+ true);
+ }
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent arg0) {
+ return mSyncAdapter.getSyncAdapterBinder();
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.syncadapter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jackrabbit.webdav.DavException;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.SynchronizeFolderOperation;
+import com.owncloud.android.operations.UpdateOCVersionOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
+
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.os.Bundle;
+
+/**
+ * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
+ * platform ContactOperations provider.
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
+
+ private final static String TAG = "FileSyncAdapter";
+
+ /**
+ * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation
+ */
+ private static final int MAX_FAILED_RESULTS = 3;
+
+ private long mCurrentSyncTime;
+ private boolean mCancellation;
+ private boolean mIsManualSync;
+ private int mFailedResultsCounter;
+ private RemoteOperationResult mLastFailedResult;
+ private SyncResult mSyncResult;
+ private int mConflictsFound;
+ private int mFailsInFavouritesFound;
+ private Map<String, String> mForgottenLocalFiles;
+
+
+ public FileSyncAdapter(Context context, boolean autoInitialize) {
+ super(context, autoInitialize);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void onPerformSync(Account account, Bundle extras,
+ String authority, ContentProviderClient provider,
+ SyncResult syncResult) {
+
+ mCancellation = false;
+ mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+ mFailedResultsCounter = 0;
+ mLastFailedResult = null;
+ mConflictsFound = 0;
+ mFailsInFavouritesFound = 0;
+ mForgottenLocalFiles = new HashMap<String, String>();
+ mSyncResult = syncResult;
+ mSyncResult.fullSyncRequested = false;
+ mSyncResult.delayUntil = 60*60*24; // sync after 24h
+
+ this.setAccount(account);
+ this.setContentProvider(provider);
+ this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
+ try {
+ this.initClientForCurrentAccount();
+ } catch (IOException e) {
+ /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+ mSyncResult.tooManyRetries = true;
+ notifyFailedSynchronization();
+ return;
+ } catch (AccountsException e) {
+ /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
+ mSyncResult.tooManyRetries = true;
+ notifyFailedSynchronization();
+ return;
+ }
+
+ Log_OC.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");
+ sendStickyBroadcast(true, null, null); // message to signal the start of the synchronization to the UI
+
+ try {
+ updateOCVersion();
+ mCurrentSyncTime = System.currentTimeMillis();
+ if (!mCancellation) {
+ fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
+
+ } else {
+ Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");
+ }
+
+
+ } finally {
+ // it's important making this although very unexpected errors occur; that's the reason for the finally
+
+ if (mFailedResultsCounter > 0 && mIsManualSync) {
+ /// don't let the system synchronization manager retries MANUAL synchronizations
+ // (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)
+ mSyncResult.tooManyRetries = true;
+
+ /// notify the user about the failure of MANUAL synchronization
+ notifyFailedSynchronization();
+
+ }
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ notifyFailsInFavourites();
+ }
+ if (mForgottenLocalFiles.size() > 0) {
+ notifyForgottenLocalFiles();
+
+ }
+ sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI
+ }
+
+ }
+
+ /**
+ * Called by system SyncManager when a synchronization is required to be cancelled.
+ *
+ * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
+ * fetched will be still saved in the database. See onPerformSync implementation.
+ */
+ @Override
+ public void onSyncCanceled() {
+ Log_OC.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");
+ mCancellation = true;
+ super.onSyncCanceled();
+ }
+
+
+ /**
+ * Updates the locally stored version value of the ownCloud server
+ */
+ private void updateOCVersion() {
+ UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext());
+ RemoteOperationResult result = update.execute(getClient());
+ if (!result.isSuccess()) {
+ mLastFailedResult = result;
+ }
+ }
+
+
+ /**
+ * Synchronize the properties of files and folders contained in a remote folder given by remotePath.
+ *
+ * @param remotePath Remote path to the folder to synchronize.
+ * @param parentId Database Id of the folder to synchronize.
+ */
+ private void fetchData(String remotePath, long parentId) {
+
+ if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
+ return;
+
+ // perform folder synchronization
+ SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( remotePath,
+ mCurrentSyncTime,
+ parentId,
+ getStorageManager(),
+ getAccount(),
+ getContext()
+ );
+ RemoteOperationResult result = synchFolderOp.execute(getClient());
+
+
+ // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
+ sendStickyBroadcast(true, remotePath, null);
+
+ if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
+
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+ mConflictsFound += synchFolderOp.getConflictsFound();
+ mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound();
+ }
+ if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
+ mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());
+ }
+ // synchronize children folders
+ List<OCFile> children = synchFolderOp.getChildren();
+ fetchChildren(children); // beware of the 'hidden' recursion here!
+
+ sendStickyBroadcast(true, remotePath, null);
+
+ } else {
+ if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
+ // (result.isTemporalRedirection() && result.isIdPRedirection() &&
+ ( result.isIdPRedirection() &&
+ MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
+ mSyncResult.stats.numAuthExceptions++;
+
+ } else if (result.getException() instanceof DavException) {
+ mSyncResult.stats.numParseExceptions++;
+
+ } else if (result.getException() instanceof IOException) {
+ mSyncResult.stats.numIoExceptions++;
+ }
+ mFailedResultsCounter++;
+ mLastFailedResult = result;
+ }
+
+ }
+
+ /**
+ * Checks if a failed result should terminate the synchronization process immediately, according to
+ * OUR OWN POLICY
+ *
+ * @param failedResult Remote operation result to check.
+ * @return 'True' if the result should immediately finish the synchronization
+ */
+ private boolean isFinisher(RemoteOperationResult failedResult) {
+ if (failedResult != null) {
+ RemoteOperationResult.ResultCode code = failedResult.getCode();
+ return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||
+ code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||
+ code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||
+ code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));
+ }
+ return false;
+ }
+
+ /**
+ * Synchronize data of folders in the list of received files
+ *
+ * @param files Files to recursively fetch
+ */
+ private void fetchChildren(List<OCFile> files) {
+ int i;
+ for (i=0; i < files.size() && !mCancellation; i++) {
+ OCFile newFile = files.get(i);
+ if (newFile.isDirectory()) {
+ fetchData(newFile.getRemotePath(), newFile.getFileId());
+
+ // Update folder size on DB
+ getStorageManager().calculateFolderSize(newFile.getFileId());
+ }
+ }
+
+ if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");
+ }
+
+
+ /**
+ * Sends a message to any application component interested in the progress of the synchronization.
+ *
+ * @param inProgress 'True' when the synchronization progress is not finished.
+ * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
+ */
+ private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
+ FileSyncService fileSyncService = new FileSyncService();
+
+ Intent i = new Intent(fileSyncService.getSyncMessage());
+ i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
+ i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
+ if (dirRemotePath != null) {
+ i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
+ }
+ if (result != null) {
+ i.putExtra(FileSyncService.SYNC_RESULT, result);
+ }
+ getContext().sendStickyBroadcast(i);
+ }
+
+
+
+ /**
+ * Notifies the user about a failed synchronization through the status notification bar
+ */
+ private void notifyFailedSynchronization() {
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+ boolean needsToUpdateCredentials = (mLastFailedResult != null &&
+ ( mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
+ // (mLastFailedResult.isTemporalRedirection() && mLastFailedResult.isIdPRedirection() &&
+ ( mLastFailedResult.isIdPRedirection() &&
+ MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))
+ )
+ );
+ // TODO put something smart in the contentIntent below for all the possible errors
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+ if (needsToUpdateCredentials) {
+ // let the user update credentials with one click
+ Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
+ notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
+ notification.setLatestEventInfo(getContext().getApplicationContext(),
+ getContext().getString(R.string.sync_fail_ticker),
+ String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name),
+ notification.contentIntent);
+ } else {
+ notification.setLatestEventInfo(getContext().getApplicationContext(),
+ getContext().getString(R.string.sync_fail_ticker),
+ String.format(getContext().getString(R.string.sync_fail_content), getAccount().name),
+ notification.contentIntent);
+ }
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);
+ }
+
+
+ /**
+ * Notifies the user about conflicts and strange fails when trying to synchronize the contents of kept-in-sync files.
+ *
+ * By now, we won't consider a failed synchronization.
+ */
+ private void notifyFailsInFavourites() {
+ if (mFailedResultsCounter > 0) {
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+ // TODO put something smart in the contentIntent below
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+ notification.setLatestEventInfo(getContext().getApplicationContext(),
+ getContext().getString(R.string.sync_fail_in_favourites_ticker),
+ String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound),
+ notification.contentIntent);
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
+
+ } else {
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+ // TODO put something smart in the contentIntent below
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
+ notification.setLatestEventInfo(getContext().getApplicationContext(),
+ getContext().getString(R.string.sync_conflicts_in_favourites_ticker),
+ String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound),
+ notification.contentIntent);
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification);
+ }
+ }
+
+ /**
+ * Notifies the user about local copies of files out of the ownCloud local directory that were 'forgotten' because
+ * copying them inside the ownCloud local directory was not possible.
+ *
+ * We don't want links to files out of the ownCloud local directory (foreign files) anymore. It's easy to have
+ * synchronization problems if a local file is linked to more than one remote file.
+ *
+ * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
+ */
+ private void notifyForgottenLocalFiles() {
+ Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+
+ /// includes a pending intent in the notification showing a more detailed explanation
+ Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class);
+ explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount());
+ ArrayList<String> remotePaths = new ArrayList<String>();
+ ArrayList<String> localPaths = new ArrayList<String>();
+ remotePaths.addAll(mForgottenLocalFiles.keySet());
+ localPaths.addAll(mForgottenLocalFiles.values());
+ explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths);
+ explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths);
+ explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0);
+ notification.setLatestEventInfo(getContext().getApplicationContext(),
+ getContext().getString(R.string.sync_foreign_files_forgotten_ticker),
+ String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)),
+ notification.contentIntent);
+ ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification);
+
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.syncadapter;\r
+\r
+import android.app.Service;\r
+import android.content.Intent;\r
+import android.os.IBinder;\r
+\r
+/**\r
+ * Background service for syncing files to our local Database\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class FileSyncService extends Service {\r
+ public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";\r
+ public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";\r
+ public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";\r
+ public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
+ public static final String SYNC_RESULT = "SYNC_RESULT";\r
+\r
+ public String getSyncMessage(){\r
+ return getClass().getName().toString() + SYNC_MESSAGE;\r
+ }\r
+ /*\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public void onCreate() {\r
+ }\r
+\r
+ /*\r
+ * {@inheritDoc}\r
+ */\r
+ @Override\r
+ public IBinder onBind(Intent intent) {\r
+ return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui;\r
+\r
+import android.graphics.drawable.Drawable;\r
+import android.view.View.OnClickListener;\r
+\r
+/**\r
+ * Represents an Item on the ActionBar.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class ActionItem {\r
+ private Drawable mIcon;\r
+ private String mTitle;\r
+ private OnClickListener mClickListener;\r
+\r
+ public ActionItem() {\r
+ }\r
+\r
+ public void setTitle(String title) {\r
+ mTitle = title;\r
+ }\r
+\r
+ public String getTitle() {\r
+ return mTitle;\r
+ }\r
+\r
+ public void setIcon(Drawable icon) {\r
+ mIcon = icon;\r
+ }\r
+\r
+ public Drawable getIcon() {\r
+ return mIcon;\r
+ }\r
+\r
+ public void setOnClickListener(OnClickListener listener) {\r
+ mClickListener = listener;\r
+ }\r
+\r
+ public OnClickListener getOnClickListerner() {\r
+ return mClickListener;\r
+ }\r
+\r
+}\r
--- /dev/null
+package com.owncloud.android.ui;
+
+import com.owncloud.android.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Button;
+/**
+ * @author masensio
+ *
+ * Button for customizing the button background
+ */
+
+public class CustomButton extends Button {
+
+ public CustomButton(Context context) {
+ super(context);
+
+ boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+ if (customButtons)
+ {
+ this.setBackgroundResource(R.drawable.btn_default);
+ }
+ }
+
+ public CustomButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+ if (customButtons)
+ {
+ this.setBackgroundResource(R.drawable.btn_default);
+ }
+ }
+
+ public CustomButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
+ if (customButtons)
+ {
+ this.setBackgroundResource(R.drawable.btn_default);
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui;\r
+\r
+import android.content.Context;\r
+import android.graphics.Rect;\r
+import android.graphics.drawable.BitmapDrawable;\r
+import android.graphics.drawable.Drawable;\r
+import android.view.Gravity;\r
+import android.view.LayoutInflater;\r
+import android.view.MotionEvent;\r
+import android.view.View;\r
+import android.view.WindowManager;\r
+import android.view.View.OnTouchListener;\r
+import android.view.ViewGroup.LayoutParams;\r
+import android.widget.PopupWindow;\r
+\r
+/**\r
+ * Represents a custom PopupWindows\r
+ * \r
+ * @author Lorensius. W. T\r
+ * \r
+ */\r
+public class CustomPopup {\r
+ protected final View mAnchor;\r
+ protected final PopupWindow mWindow;\r
+ private View root;\r
+ private Drawable background = null;\r
+ protected final WindowManager mWManager;\r
+\r
+ public CustomPopup(View anchor) {\r
+ mAnchor = anchor;\r
+ mWindow = new PopupWindow(anchor.getContext());\r
+\r
+ mWindow.setTouchInterceptor(new OnTouchListener() {\r
+\r
+ public boolean onTouch(View v, MotionEvent event) {\r
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {\r
+ CustomPopup.this.dismiss();\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+ });\r
+\r
+ mWManager = (WindowManager) anchor.getContext().getSystemService(\r
+ Context.WINDOW_SERVICE);\r
+ onCreate();\r
+ }\r
+\r
+ public void onCreate() {\r
+ }\r
+\r
+ public void onShow() {\r
+ }\r
+\r
+ public void preShow() {\r
+ if (root == null) {\r
+ throw new IllegalStateException(\r
+ "setContentView called with a view to display");\r
+ }\r
+\r
+ onShow();\r
+\r
+ if (background == null) {\r
+ mWindow.setBackgroundDrawable(new BitmapDrawable());\r
+ } else {\r
+ mWindow.setBackgroundDrawable(background);\r
+ }\r
+\r
+ mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);\r
+ mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);\r
+ mWindow.setTouchable(true);\r
+ mWindow.setFocusable(true);\r
+ mWindow.setOutsideTouchable(true);\r
+\r
+ mWindow.setContentView(root);\r
+ }\r
+\r
+ public void setBackgroundDrawable(Drawable background) {\r
+ this.background = background;\r
+ }\r
+\r
+ public void setContentView(View root) {\r
+ this.root = root;\r
+ mWindow.setContentView(root);\r
+ }\r
+\r
+ public void setContentView(int layoutResId) {\r
+ LayoutInflater inflater = (LayoutInflater) mAnchor.getContext()\r
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+ setContentView(inflater.inflate(layoutResId, null));\r
+ }\r
+\r
+ public void showDropDown() {\r
+ showDropDown(0, 0);\r
+ }\r
+\r
+ public void showDropDown(int x, int y) {\r
+ preShow();\r
+ mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
+ mWindow.showAsDropDown(mAnchor, x, y);\r
+ }\r
+\r
+ public void showLikeQuickAction() {\r
+ showLikeQuickAction(0, 0);\r
+ }\r
+\r
+ public void showLikeQuickAction(int x, int y) {\r
+ preShow();\r
+\r
+ mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
+ int[] location = new int[2];\r
+ mAnchor.getLocationOnScreen(location);\r
+\r
+ Rect anchorRect = new Rect(location[0], location[1], location[0]\r
+ + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
+\r
+ root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
+ LayoutParams.WRAP_CONTENT));\r
+ root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
+\r
+ int rootW = root.getWidth(), rootH = root.getHeight();\r
+ int screenW = mWManager.getDefaultDisplay().getWidth();\r
+\r
+ int xpos = ((screenW - rootW) / 2) + x;\r
+ int ypos = anchorRect.top - rootH + y;\r
+\r
+ if (rootH > anchorRect.top) {\r
+ ypos = anchorRect.bottom + y;\r
+ }\r
+ mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xpos, ypos);\r
+ }\r
+\r
+ public void dismiss() {\r
+ mWindow.dismiss();\r
+ }\r
+\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * ListView allowing to specify the position of an item that should be centered in the visible area, if possible.
+ *
+ * The cleanest way I found to overcome the problem due to getHeight() returns 0 until the view is really drawn.
+ *
+ * @author David A. Velasco
+ */
+public class ExtendedListView extends ListView {
+
+ private int mPositionToSetAndCenter;
+
+ public ExtendedListView(Context context) {
+ super(context);
+ }
+
+ public ExtendedListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ExtendedListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ *
+ */
+ @Override
+ protected void onDraw (Canvas canvas) {
+ super.onDraw(canvas);
+ if (mPositionToSetAndCenter > 0) {
+ this.setSelectionFromTop(mPositionToSetAndCenter, getHeight() / 2);
+ mPositionToSetAndCenter = 0;
+ }
+ }
+
+ /**
+ * Public method to set the position of the item that should be centered in the visible area of the view.
+ *
+ * The position is saved here and checked in onDraw().
+ *
+ * @param position Position (in the list of items) of the item to center in the visible area.
+ */
+ public void setAndCenterSelection(int position) {
+ mPositionToSetAndCenter = position;
+ }
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui;\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
+import com.owncloud.android.R;\r
+\r
+\r
+/**\r
+ * Popup window, shows action list as icon and text like the one in Gallery3D\r
+ * 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\r
+ .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\r
+ * 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]\r
+ + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
+\r
+ createActionList();\r
+\r
+ root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
+ 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),\r
+ 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\r
+ * TRUE if displayed on top of anchor view and vice versa\r
+ */\r
+ private void setAnimationStyle(int screenWidth, int requestedX,\r
+ 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
+ : R.style.Animations_PopDownMenu_Left);\r
+ break;\r
+\r
+ case ANIM_GROW_FROM_RIGHT:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
+ : R.style.Animations_PopDownMenu_Right);\r
+ break;\r
+\r
+ case ANIM_GROW_FROM_CENTER:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
+ : R.style.Animations_PopDownMenu_Center);\r
+ break;\r
+\r
+ case ANIM_REFLECT:\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect\r
+ : 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
+ : R.style.Animations_PopDownMenu_Left);\r
+ } else if (arrowPos > screenWidth / 4\r
+ && arrowPos < 3 * (screenWidth / 4)) {\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
+ : R.style.Animations_PopDownMenu_Center);\r
+ } else {\r
+ mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
+ : 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,\r
+ OnClickListener listener) {\r
+ LinearLayout container = (LinearLayout) inflater.inflate(\r
+ 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\r
+ : mArrowDown;\r
+ final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown\r
+ : mArrowUp;\r
+\r
+ final int arrowWidth = mArrowUp.getMeasuredWidth();\r
+\r
+ showArrow.setVisibility(View.VISIBLE);\r
+\r
+ ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow\r
+ .getLayoutParams();\r
+\r
+ param.leftMargin = requestedX - arrowWidth / 2;\r
+\r
+ hideArrow.setVisibility(View.INVISIBLE);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.CheckedTextView;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockListActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountAuthenticator;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+
+
+public class AccountSelectActivity extends SherlockListActivity implements
+ AccountManagerCallback<Boolean> {
+
+ private static final String TAG = "AccountSelectActivity";
+
+ private static final String PREVIOUS_ACCOUNT_KEY = "ACCOUNT";
+
+ private final Handler mHandler = new Handler();
+ private Account mPreviousAccount = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mPreviousAccount = savedInstanceState.getParcelable(PREVIOUS_ACCOUNT_KEY);
+ } else {
+ mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
+ }
+
+ ActionBar action_bar = getSupportActionBar();
+ action_bar.setDisplayShowTitleEnabled(true);
+ action_bar.setDisplayHomeAsUpEnabled(false);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ populateAccountList();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (this.isFinishing()) {
+ Account current = AccountUtils.getCurrentOwnCloudAccount(this);
+ if ((mPreviousAccount == null && current != null) ||
+ (mPreviousAccount != null && !mPreviousAccount.equals(current))) {
+ /// the account set as default changed since this activity was created
+
+ // trigger synchronization
+ ContentResolver.cancelSync(null, MainApp.getAuthTokenType());
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), MainApp.getAuthTokenType(), bundle);
+
+ // restart the main activity
+ Intent i = new Intent(this, FileDisplayActivity.class);
+ i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(i);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Show Create Account if Multiaccount is enabled
+ if (getResources().getBoolean(R.bool.multiaccount_support)) {
+ MenuInflater inflater = getSherlock().getMenuInflater();
+ inflater.inflate(R.menu.account_picker, menu);
+ }
+ return true;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ getMenuInflater().inflate(R.menu.account_picker_long_click, menu);
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ String accountName = ((TextView) v.findViewById(android.R.id.text1))
+ .getText().toString();
+ AccountUtils.setCurrentOwnCloudAccount(this, accountName);
+ finish(); // immediate exit
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ if (item.getItemId() == R.id.createAccount) {
+ /*Intent intent = new Intent(
+ android.provider.Settings.ACTION_ADD_ACCOUNT);
+ intent.putExtra("authorities",
+ new String[] { MainApp.getAuthTokenType() });
+ startActivity(intent);*/
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.addAccount(MainApp.getAccountType(),
+ null,
+ null,
+ null,
+ this,
+ null,
+ null);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called when the user clicked on an item into the context menu created at
+ * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} for every
+ * ownCloud {@link Account} , containing 'secondary actions' for them.
+ *
+ * {@inheritDoc}}
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean onContextItemSelected(android.view.MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ int index = info.position;
+ HashMap<String, String> map = null;
+ try {
+ map = (HashMap<String, String>) getListAdapter().getItem(index);
+ } catch (ClassCastException e) {
+ Log_OC.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
+ return false;
+ }
+
+ String accountName = map.get("NAME");
+ AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
+ for (Account a : accounts) {
+ if (a.name.equals(accountName)) {
+ if (item.getItemId() == R.id.change_password) {
+ Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a);
+ updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+ startActivity(updateAccountCredentials);
+
+ } else if (item.getItemId() == R.id.delete_account) {
+ am.removeAccount(a, this, mHandler);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void populateAccountList() {
+ AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account accounts[] = am
+ .getAccountsByType(MainApp.getAccountType());
+ if (am.getAccountsByType(MainApp.getAccountType()).length == 0) {
+ // Show create account screen if there isn't any account
+ am.addAccount(MainApp.getAccountType(),
+ null,
+ null,
+ null,
+ this,
+ null,
+ null);
+ }
+ else {
+ LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
+ for (Account a : accounts) {
+ HashMap<String, String> h = new HashMap<String, String>();
+ h.put("NAME", a.name);
+ h.put("VER",
+ "ownCloud version: "
+ + am.getUserData(a,
+ AccountAuthenticator.KEY_OC_VERSION));
+ ll.add(h);
+ }
+
+ setListAdapter(new AccountCheckedSimpleAdepter(this, ll,
+ android.R.layout.simple_list_item_single_choice,
+ new String[] { "NAME" }, new int[] { android.R.id.text1 }));
+ registerForContextMenu(getListView());
+ }
+ }
+
+ @Override
+ public void run(AccountManagerFuture<Boolean> future) {
+ if (future.isDone()) {
+ Account a = AccountUtils.getCurrentOwnCloudAccount(this);
+ String accountName = "";
+ if (a == null) {
+ Account[] accounts = AccountManager.get(this)
+ .getAccountsByType(MainApp.getAccountType());
+ if (accounts.length != 0)
+ accountName = accounts[0].name;
+ AccountUtils.setCurrentOwnCloudAccount(this, accountName);
+ }
+ populateAccountList();
+ }
+ }
+
+ private class AccountCheckedSimpleAdepter extends SimpleAdapter {
+ private Account mCurrentAccount;
+ private List<? extends Map<String, ?>> mPrivateData;
+
+ public AccountCheckedSimpleAdepter(Context context,
+ List<? extends Map<String, ?>> data, int resource,
+ String[] from, int[] to) {
+ super(context, data, resource, from, to);
+ mCurrentAccount = AccountUtils
+ .getCurrentOwnCloudAccount(AccountSelectActivity.this);
+ mPrivateData = data;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = super.getView(position, convertView, parent);
+ CheckedTextView ctv = (CheckedTextView) v
+ .findViewById(android.R.id.text1);
+ if (mPrivateData.get(position).get("NAME")
+ .equals(mCurrentAccount.name)) {
+ ctv.setChecked(true);
+ }
+ return v;
+ }
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
+import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Wrapper activity which will be launched if keep-in-sync file will be modified by external
+ * application.
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
+
+ private String TAG = ConflictsResolveActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void ConflictDecisionMade(Decision decision) {
+ Intent i = new Intent(getApplicationContext(), FileUploader.class);
+
+ switch (decision) {
+ case CANCEL:
+ finish();
+ return;
+ case OVERWRITE:
+ i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
+ break;
+ case KEEP_BOTH:
+ i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+ break;
+ default:
+ Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
+ return;
+ }
+ i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+ i.putExtra(FileUploader.KEY_FILE, getFile());
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+
+ startService(i);
+ finish();
+ }
+
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ OCFile file = getFile();
+ if (getFile() == null) {
+ Log_OC.e(TAG, "No conflictive file received");
+ finish();
+ } else {
+ /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
+ DataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+ file = storageManager.getFileByPath(file.getRemotePath()); // file = null if not in the current Account
+ if (file != null) {
+ setFile(file);
+ ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
+ d.showDialog(this);
+
+ } else {
+ // account was changed to a different one - just finish
+ finish();
+ }
+ }
+
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ finish();
+ }
+
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.DialogFragment;
+import android.text.method.ScrollingMovementMethod;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+
+/**
+ * Activity reporting errors occurred when local files uploaded to an ownCloud account with an app in
+ * version under 1.3.16 where being copied to the ownCloud local folder.
+ *
+ * Allows the user move the files to the ownCloud local folder. let them unlinked to the remote
+ * files.
+ *
+ * Shown when the error notification summarizing the list of errors is clicked by the user.
+ *
+ * @author David A. Velasco
+ */
+public class ErrorsWhileCopyingHandlerActivity extends SherlockFragmentActivity implements OnClickListener {
+
+ private static final String TAG = ErrorsWhileCopyingHandlerActivity.class.getSimpleName();
+
+ public static final String EXTRA_ACCOUNT = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_ACCOUNT";
+ public static final String EXTRA_LOCAL_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_LOCAL_PATHS";
+ public static final String EXTRA_REMOTE_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_REMOTE_PATHS";
+
+ private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
+
+ protected Account mAccount;
+ protected FileDataStorageManager mStorageManager;
+ protected ArrayList<String> mLocalPaths;
+ protected ArrayList<String> mRemotePaths;
+ protected ArrayAdapter<String> mAdapter;
+ protected Handler mHandler;
+ private DialogFragment mCurrentDialog;
+
+ /**
+ * {@link}
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ /// read extra parameters in intent
+ Intent intent = getIntent();
+ mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
+ mRemotePaths = intent.getStringArrayListExtra(EXTRA_REMOTE_PATHS);
+ mLocalPaths = intent.getStringArrayListExtra(EXTRA_LOCAL_PATHS);
+ mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
+ mHandler = new Handler();
+ if (mCurrentDialog != null) {
+ mCurrentDialog.dismiss();
+ mCurrentDialog = null;
+ }
+
+ /// load generic layout
+ setContentView(R.layout.generic_explanation);
+
+ /// customize text message
+ TextView textView = (TextView) findViewById(R.id.message);
+ String appName = getString(R.string.app_name);
+ String message = String.format(getString(R.string.sync_foreign_files_forgotten_explanation), appName, appName, appName, appName, mAccount.name);
+ textView.setText(message);
+ textView.setMovementMethod(new ScrollingMovementMethod());
+
+ /// load the list of local and remote files that failed
+ ListView listView = (ListView) findViewById(R.id.list);
+ if (mLocalPaths != null && mLocalPaths.size() > 0) {
+ mAdapter = new ErrorsWhileCopyingListAdapter();
+ listView.setAdapter(mAdapter);
+ } else {
+ listView.setVisibility(View.GONE);
+ mAdapter = null;
+ }
+
+ /// customize buttons
+ CustomButton cancelBtn = (CustomButton) findViewById(R.id.cancel);
+ CustomButton okBtn = (CustomButton) findViewById(R.id.ok);
+
+ okBtn.setText(R.string.foreign_files_move);
+ cancelBtn.setOnClickListener(this);
+ okBtn.setOnClickListener(this);
+ }
+
+
+ /**
+ * Customized adapter, showing the local files as main text in two-lines list item and the remote files
+ * as the secondary text.
+ *
+ * @author David A. Velasco
+ */
+ public class ErrorsWhileCopyingListAdapter extends ArrayAdapter<String> {
+
+ ErrorsWhileCopyingListAdapter() {
+ super(ErrorsWhileCopyingHandlerActivity.this, android.R.layout.two_line_list_item, android.R.id.text1, mLocalPaths);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView (int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = vi.inflate(android.R.layout.two_line_list_item, null);
+ }
+ if (view != null) {
+ String localPath = getItem(position);
+ if (localPath != null) {
+ TextView text1 = (TextView) view.findViewById(android.R.id.text1);
+ if (text1 != null) {
+ text1.setText(String.format(getString(R.string.foreign_files_local_text), localPath));
+ }
+ }
+ if (mRemotePaths != null && mRemotePaths.size() > 0 && position >= 0 && position < mRemotePaths.size()) {
+ TextView text2 = (TextView) view.findViewById(android.R.id.text2);
+ String remotePath = mRemotePaths.get(position);
+ if (text2 != null && remotePath != null) {
+ text2.setText(String.format(getString(R.string.foreign_files_remote_text), remotePath));
+ }
+ }
+ }
+ return view;
+ }
+ }
+
+
+ /**
+ * Listener method to perform the MOVE / CANCEL action available in this activity.
+ *
+ * @param v Clicked view (button MOVE or CANCEL)
+ */
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.ok) {
+ /// perform movement operation in background thread
+ Log_OC.d(TAG, "Clicked MOVE, start movement");
+ new MoveFilesTask().execute();
+
+ } else if (v.getId() == R.id.cancel) {
+ /// just finish
+ Log_OC.d(TAG, "Clicked CANCEL, bye");
+ finish();
+
+ } else {
+ Log_OC.e(TAG, "Clicked phantom button, id: " + v.getId());
+ }
+ }
+
+
+ /**
+ * Asynchronous task performing the move of all the local files to the ownCloud folder.
+ *
+ * @author David A. Velasco
+ */
+ private class MoveFilesTask extends AsyncTask<Void, Void, Boolean> {
+
+ /**
+ * Updates the UI before trying the movement
+ */
+ @Override
+ protected void onPreExecute () {
+ /// progress dialog and disable 'Move' button
+ mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
+ mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
+ findViewById(R.id.ok).setEnabled(false);
+ }
+
+
+ /**
+ * Performs the movement
+ *
+ * @return 'False' when the movement of any file fails.
+ */
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ while (!mLocalPaths.isEmpty()) {
+ String currentPath = mLocalPaths.get(0);
+ File currentFile = new File(currentPath);
+ String expectedPath = FileStorageUtils.getSavePath(mAccount.name) + mRemotePaths.get(0);
+ File expectedFile = new File(expectedPath);
+
+ if (expectedFile.equals(currentFile) || currentFile.renameTo(expectedFile)) {
+ // SUCCESS
+ OCFile file = mStorageManager.getFileByPath(mRemotePaths.get(0));
+ file.setStoragePath(expectedPath);
+ mStorageManager.saveFile(file);
+ mRemotePaths.remove(0);
+ mLocalPaths.remove(0);
+
+ } else {
+ // FAIL
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Updates the activity UI after the movement of local files is tried.
+ *
+ * If the movement was successful for all the files, finishes the activity immediately.
+ *
+ * In other case, the list of remaining files is still available to retry the movement.
+ *
+ * @param result 'True' when the movement was successful.
+ */
+ @Override
+ protected void onPostExecute(Boolean result) {
+ mAdapter.notifyDataSetChanged();
+ mCurrentDialog.dismiss();
+ mCurrentDialog = null;
+ findViewById(R.id.ok).setEnabled(true);
+
+ if (result) {
+ // nothing else to do in this activity
+ Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_success), Toast.LENGTH_LONG);
+ t.show();
+ finish();
+
+ } else {
+ Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_fail), Toast.LENGTH_LONG);
+ t.show();
+ }
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.ui.activity;\r
+\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.ui.CustomButton;\r
+\r
+import android.app.Activity;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+import android.widget.TextView;\r
+\r
+\r
+/**\r
+ * This Activity is used to display a detail message for failed uploads\r
+ * \r
+ * The entry-point for this activity is the 'Failed upload Notification"\r
+ * \r
+ * @author andomaex / Matthias Baumann\r
+ */\r
+public class FailedUploadActivity extends Activity {\r
+\r
+ public static final String MESSAGE = "message";\r
+\r
+ @Override\r
+ public void onCreate(Bundle savedInstanceState) {\r
+ super.onCreate(savedInstanceState);\r
+ setContentView(R.layout.failed_upload_message_view);\r
+ String message = getIntent().getStringExtra(MESSAGE);\r
+ TextView textView = (TextView) findViewById(R.id.faild_upload_message);\r
+ textView.setText(message);\r
+ CustomButton closeBtn = (CustomButton) findViewById(R.id.failed_uploadactivity_close_button);\r
+ \r
+ closeBtn.setOnClickListener(new OnClickListener() {\r
+ @Override\r
+ public void onClick(View v) {\r
+ finish();\r
+ }\r
+ });\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.OperationCanceledException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.MimeTypeMap;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.OCFile;
+
+
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
+ *
+ * @author David A. Velasco
+ */
+public abstract class FileActivity extends SherlockFragmentActivity {
+
+ public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
+ public static final String EXTRA_ACCOUNT = "com.owncloud.android.ui.activity.ACCOUNT";
+ public static final String EXTRA_WAITING_TO_PREVIEW = "com.owncloud.android.ui.activity.WAITING_TO_PREVIEW";
+
+ public static final String TAG = FileActivity.class.getSimpleName();
+
+
+ /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */
+ private Account mAccount;
+
+ /** Main {@link OCFile} handled by the activity.*/
+ private OCFile mFile;
+
+ /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account} */
+ private boolean mRedirectingToSetupAccount = false;
+
+ /** Flag to signal when the value of mAccount was set */
+ private boolean mAccountWasSet;
+
+ /** Flag to signal when the value of mAccount was restored from a saved state */
+ private boolean mAccountWasRestored;
+
+
+ /**
+ * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of
+ * the {@link FileActivity}.
+ *
+ * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user
+ * is requested to create a new one.
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Account account;
+ if(savedInstanceState != null) {
+ account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
+ mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
+ } else {
+ account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
+ mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
+ }
+
+ setAccount(account, savedInstanceState != null);
+ }
+
+
+ /**
+ * Since ownCloud {@link Account}s can be managed from the system setting menu,
+ * the existence of the {@link Account} associated to the instance must be checked
+ * every time it is restarted.
+ */
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+ boolean validAccount = (mAccount != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), mAccount.name));
+ if (!validAccount) {
+ swapToDefaultAccount();
+ }
+
+ }
+
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (mAccountWasSet) {
+ onAccountSet(mAccountWasRestored);
+ }
+ }
+
+
+ /**
+ * Sets and validates the ownCloud {@link Account} associated to the Activity.
+ *
+ * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
+ *
+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
+ *
+ * @param account New {@link Account} to set.
+ * @param savedAccount When 'true', account was retrieved from a saved instance state.
+ */
+ private void setAccount(Account account, boolean savedAccount) {
+ Account oldAccount = mAccount;
+ boolean validAccount = (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), account.name));
+ if (validAccount) {
+ mAccount = account;
+ mAccountWasSet = true;
+ mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
+
+ } else {
+ swapToDefaultAccount();
+ }
+ }
+
+
+ /**
+ * Tries to swap the current ownCloud {@link Account} for other valid and existing.
+ *
+ * If no valid ownCloud {@link Account} exists, the the user is requested
+ * to create a new ownCloud {@link Account}.
+ *
+ * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
+ *
+ * @return 'True' if the checked {@link Account} was valid.
+ */
+ private void swapToDefaultAccount() {
+ // default to the most recently used account
+ Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
+ if (newAccount == null) {
+ /// no account available: force account creation
+ createFirstAccount();
+ mRedirectingToSetupAccount = true;
+ mAccountWasSet = false;
+ mAccountWasRestored = false;
+
+ } else {
+ mAccountWasSet = true;
+ mAccountWasRestored = (newAccount.equals(mAccount));
+ mAccount = newAccount;
+ }
+ }
+
+
+ /**
+ * Launches the account creation activity. To use when no ownCloud account is available
+ */
+ private void createFirstAccount() {
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.addAccount(MainApp.getAccountType(),
+ null,
+ null,
+ null,
+ this,
+ new AccountCreationCallback(),
+ null);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
+ outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
+ }
+
+
+ /**
+ * Getter for the main {@link OCFile} handled by the activity.
+ *
+ * @return Main {@link OCFile} handled by the activity.
+ */
+ public OCFile getFile() {
+ return mFile;
+ }
+
+
+ /**
+ * Setter for the main {@link OCFile} handled by the activity.
+ *
+ * @param file Main {@link OCFile} to be handled by the activity.
+ */
+ public void setFile(OCFile file) {
+ mFile = file;
+ }
+
+
+ /**
+ * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
+ *
+ * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
+ */
+ public Account getAccount() {
+ return mAccount;
+ }
+
+
+ /**
+ * @return 'True' when the Activity is finishing to enforce the setup of a new account.
+ */
+ protected boolean isRedirectingToSetupAccount() {
+ return mRedirectingToSetupAccount;
+ }
+
+
+ /**
+ * Helper class handling a callback from the {@link AccountManager} after the creation of
+ * a new ownCloud {@link Account} finished, successfully or not.
+ *
+ * At this moment, only called after the creation of the first account.
+ *
+ * @author David A. Velasco
+ */
+ public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
+
+ @Override
+ public void run(AccountManagerFuture<Bundle> future) {
+ FileActivity.this.mRedirectingToSetupAccount = false;
+ boolean accountWasSet = false;
+ if (future != null) {
+ try {
+ Bundle result;
+ result = future.getResult();
+ String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
+ String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
+ if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
+ setAccount(new Account(name, type), false);
+ accountWasSet = true;
+ }
+ } catch (OperationCanceledException e) {
+ Log_OC.d(TAG, "Account creation canceled");
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Account creation finished in exception: ", e);
+ }
+
+ } else {
+ Log_OC.e(TAG, "Account creation callback with null bundle");
+ }
+ if (!accountWasSet) {
+ moveTaskToBack(true);
+ }
+ }
+
+ }
+
+
+ /**
+ * Called when the ownCloud {@link Account} associated to the Activity was just updated.
+ *
+ * Child classes must grant that state depending on the {@link Account} is updated.
+ */
+ protected abstract void onAccountSet(boolean stateWasRecovered);
+
+
+
+ public void openFile(OCFile file) {
+ if (file != null) {
+ String storagePath = file.getStoragePath();
+ String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+
+ Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
+ intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+ intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ Intent intentForGuessedMimeType = null;
+ if (storagePath.lastIndexOf('.') >= 0) {
+ String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+ if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
+ intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
+ intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
+ intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+ }
+
+ Intent chooserIntent = null;
+ if (intentForGuessedMimeType != null) {
+ chooserIntent = Intent.createChooser(intentForGuessedMimeType, getString(R.string.actionbar_open_with));
+ } else {
+ chooserIntent = Intent.createChooser(intentForSavedMimeType, getString(R.string.actionbar_open_with));
+ }
+
+ startActivity(chooserIntent);
+
+ } else {
+ Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.io.File;
+
+import android.accounts.Account;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileObserverService;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.operations.CreateFolderOperation;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.syncadapter.FileSyncService;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.LoadingDialog;
+import com.owncloud.android.ui.dialog.SslValidatorDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
+import com.owncloud.android.ui.fragment.FileDetailFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.ui.fragment.OCFileListFragment;
+import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewMediaFragment;
+import com.owncloud.android.ui.preview.PreviewVideoActivity;
+
+
+/**
+ * Displays, what files the user has available in his ownCloud.
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+
+public class FileDisplayActivity extends FileActivity implements
+OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
+
+ private ArrayAdapter<String> mDirectories;
+
+ /** Access point to the cached database for the current ownCloud {@link Account} */
+ private DataStorageManager mStorageManager = null;
+
+ private SyncBroadcastReceiver mSyncBroadcastReceiver;
+ private UploadFinishReceiver mUploadFinishReceiver;
+ private DownloadFinishReceiver mDownloadFinishReceiver;
+ private FileDownloaderBinder mDownloaderBinder = null;
+ private FileUploaderBinder mUploaderBinder = null;
+ private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
+ private RemoteOperationResult mLastSslUntrustedServerResult = null;
+
+ private boolean mDualPane;
+ private View mLeftFragmentContainer;
+ private View mRightFragmentContainer;
+
+ private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+
+ public static final int DIALOG_SHORT_WAIT = 0;
+ private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
+ private static final int DIALOG_SSL_VALIDATOR = 2;
+ private static final int DIALOG_CERT_NOT_SAVED = 3;
+
+ private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
+
+ public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
+
+ private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
+ private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
+
+ private static final String TAG = FileDisplayActivity.class.getSimpleName();
+
+ private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
+ private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
+
+ private OCFile mWaitingToPreview;
+ private Handler mHandler;
+
+ private String mDownloadAddedMessage;
+ private String mDownloadFinishMessage;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreate() start");
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+
+ super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
+
+ mHandler = new Handler();
+
+ FileDownloader downloader = new FileDownloader();
+ mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+ mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+
+ /// bindings to transference services
+ mUploadConnection = new ListServiceConnection();
+ mDownloadConnection = new ListServiceConnection();
+ bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+ bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+
+ // PIN CODE request ; best location is to decide, let's try this first
+ if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
+ requestPinCode();
+ }
+
+ /// file observer
+ Intent observer_intent = new Intent(this, FileObserverService.class);
+ observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
+ startService(observer_intent);
+
+ /// Load of saved instance state
+ if(savedInstanceState != null) {
+ mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
+
+ } else {
+ mWaitingToPreview = null;
+ }
+
+ /// USER INTERFACE
+
+ // Inflate and set the layout view
+ setContentView(R.layout.files);
+ mDualPane = getResources().getBoolean(R.bool.large_land_layout);
+ mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
+ mRightFragmentContainer = findViewById(R.id.right_fragment_container);
+ if (savedInstanceState == null) {
+ createMinFragments();
+ }
+
+ // Action bar setup
+ mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+ getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
+ setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to work around bug in its implementation
+
+
+
+ Log_OC.d(TAG, "onCreate() end");
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mDownloadConnection != null)
+ unbindService(mDownloadConnection);
+ if (mUploadConnection != null)
+ unbindService(mUploadConnection);
+ }
+
+
+ /**
+ * Called when the ownCloud {@link Account} associated to the Activity was just updated.
+ */
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+
+ /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
+ OCFile file = getFile();
+ // get parent from path
+ String parentPath = "";
+ if (file != null) {
+ if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
+ // upload in progress - right now, files are not inserted in the local cache until the upload is successful
+ // get parent from path
+ parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+ if (mStorageManager.getFileByPath(parentPath) == null)
+ file = null; // not able to know the directory where the file is uploading
+ } else {
+ file = mStorageManager.getFileByPath(file.getRemotePath()); // currentDir = null if not in the current Account
+ }
+ }
+ if (file == null) {
+ // fall back to root folder
+ file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); // never returns null
+ }
+ setFile(file);
+ mDirectories.clear();
+ OCFile fileIt = file;
+ while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) {
+ if (fileIt.isDirectory()) {
+ mDirectories.add(fileIt.getFileName());
+ }
+ // get parent from path
+ parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
+ fileIt = mStorageManager.getFileByPath(parentPath);
+ }
+ mDirectories.add(OCFile.PATH_SEPARATOR);
+ if (!stateWasRecovered) {
+ Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+ initFragmentsWithFile();
+
+ } else {
+ updateFragmentsVisibility(!file.isDirectory());
+ updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
+ }
+
+
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ }
+ }
+
+
+ private void createMinFragments() {
+ OCFileListFragment listOfFiles = new OCFileListFragment();
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+ transaction.commit();
+ }
+
+ private void initFragmentsWithFile() {
+ if (getAccount() != null && getFile() != null) {
+ /// First fragment
+ OCFileListFragment listOfFiles = getListOfFilesFragment();
+ if (listOfFiles != null) {
+ listOfFiles.listDirectory(getCurrentDir());
+ } else {
+ Log.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
+ }
+
+ /// Second fragment
+ OCFile file = getFile();
+ Fragment secondFragment = chooseInitialSecondFragment(file);
+ if (secondFragment != null) {
+ setSecondFragment(secondFragment);
+ updateFragmentsVisibility(true);
+ updateNavigationElementsInActionBar(file);
+
+ } else {
+ cleanSecondFragment();
+ }
+
+ } else {
+ Log.wtf(TAG, "initFragments() called with invalid NULLs!");
+ if (getAccount() == null) {
+ Log.wtf(TAG, "\t account is NULL");
+ }
+ if (getFile() == null) {
+ Log.wtf(TAG, "\t file is NULL");
+ }
+ }
+ }
+
+ private Fragment chooseInitialSecondFragment(OCFile file) {
+ Fragment secondFragment = null;
+ if (file != null && !file.isDirectory()) {
+ if (file.isDown() && PreviewMediaFragment.canBePreviewed(file)
+ && file.getLastSyncDateForProperties() > 0 // temporal fix
+ ) {
+ int startPlaybackPosition = getIntent().getIntExtra(PreviewVideoActivity.EXTRA_START_POSITION, 0);
+ boolean autoplay = getIntent().getBooleanExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, true);
+ secondFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
+
+ } else {
+ secondFragment = new FileDetailFragment(file, getAccount());
+ }
+ }
+ return secondFragment;
+ }
+
+
+ /**
+ * Replaces the second fragment managed by the activity with the received as
+ * a parameter.
+ *
+ * Assumes never will be more than two fragments managed at the same time.
+ *
+ * @param fragment New second Fragment to set.
+ */
+ private void setSecondFragment(Fragment fragment) {
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+ transaction.replace(R.id.right_fragment_container, fragment, TAG_SECOND_FRAGMENT);
+ transaction.commit();
+ }
+
+
+ private void updateFragmentsVisibility(boolean existsSecondFragment) {
+ if (mDualPane) {
+ if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
+ mLeftFragmentContainer.setVisibility(View.VISIBLE);
+ }
+ if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
+ mRightFragmentContainer.setVisibility(View.VISIBLE);
+ }
+
+ } else if (existsSecondFragment) {
+ if (mLeftFragmentContainer.getVisibility() != View.GONE) {
+ mLeftFragmentContainer.setVisibility(View.GONE);
+ }
+ if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
+ mRightFragmentContainer.setVisibility(View.VISIBLE);
+ }
+
+ } else {
+ if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
+ mLeftFragmentContainer.setVisibility(View.VISIBLE);
+ }
+ if (mRightFragmentContainer.getVisibility() != View.GONE) {
+ mRightFragmentContainer.setVisibility(View.GONE);
+ }
+ }
+ }
+
+
+ private OCFileListFragment getListOfFilesFragment() {
+ Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
+ if (listOfFiles != null) {
+ return (OCFileListFragment)listOfFiles;
+ }
+ Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
+ return null;
+ }
+
+ protected FileFragment getSecondFragment() {
+ Fragment second = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT);
+ if (second != null) {
+ return (FileFragment)second;
+ }
+ return null;
+ }
+
+ public void cleanSecondFragment() {
+ Fragment second = getSecondFragment();
+ if (second != null) {
+ FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
+ tr.remove(second);
+ tr.commit();
+ }
+ updateFragmentsVisibility(false);
+ updateNavigationElementsInActionBar(null);
+ }
+
+ protected void refeshListOfFilesFragment() {
+ OCFileListFragment fileListFragment = getListOfFilesFragment();
+ if (fileListFragment != null) {
+ fileListFragment.listDirectory();
+ }
+ }
+
+ protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath, boolean success) {
+ FileFragment secondFragment = getSecondFragment();
+ boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
+ if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
+ FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
+ OCFile fileInFragment = detailsFragment.getFile();
+ if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
+ // the user browsed to other file ; forget the automatic preview
+ mWaitingToPreview = null;
+
+ } else if (downloadEvent.equals(mDownloadAddedMessage)) {
+ // grant that the right panel updates the progress bar
+ detailsFragment.listenForTransferProgress();
+ detailsFragment.updateFileDetails(true, false);
+
+ } else if (downloadEvent.equals(mDownloadFinishMessage)) {
+ // update the right panel
+ boolean detailsFragmentChanged = false;
+ if (waitedPreview) {
+ if (success) {
+ mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
+ if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
+ startMediaPreview(mWaitingToPreview, 0, true);
+ detailsFragmentChanged = true;
+ } else {
+ openFile(mWaitingToPreview);
+ }
+ }
+ mWaitingToPreview = null;
+ }
+ if (!detailsFragmentChanged) {
+ detailsFragment.updateFileDetails(false, (success));
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getSherlock().getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean retval = true;
+ switch (item.getItemId()) {
+ case R.id.action_create_dir: {
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.uploader_info_dirname), "", -1, -1, this);
+ dialog.show(getSupportFragmentManager(), "createdirdialog");
+ break;
+ }
+ case R.id.action_sync_account: {
+ startSynchronization();
+ break;
+ }
+ case R.id.action_upload: {
+ showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
+ break;
+ }
+ case R.id.action_settings: {
+ Intent settingsIntent = new Intent(this, Preferences.class);
+ startActivity(settingsIntent);
+ break;
+ }
+ case android.R.id.home: {
+ FileFragment second = getSecondFragment();
+ OCFile currentDir = getCurrentDir();
+ if((currentDir != null && currentDir.getParentId() != 0) ||
+ (second != null && second.getFile() != null)) {
+ onBackPressed();
+ }
+ break;
+ }
+ default:
+ retval = super.onOptionsItemSelected(item);
+ }
+ return retval;
+ }
+
+ private void startSynchronization() {
+ ContentResolver.cancelSync(null, MainApp.getAuthTokenType()); // cancel the current synchronizations of any ownCloud account
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ ContentResolver.requestSync(
+ getAccount(),
+ MainApp.getAuthTokenType(), bundle);
+ }
+
+
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ int i = itemPosition;
+ while (i-- != 0) {
+ onBackPressed();
+ }
+ // the next operation triggers a new call to this method, but it's necessary to
+ // ensure that the name exposed in the action bar is the current directory when the
+ // user selected it in the navigation list
+ if (itemPosition != 0)
+ getSupportActionBar().setSelectedNavigationItem(0);
+ return true;
+ }
+
+ /**
+ * Called, when the user selected something for uploading
+ */
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
+ requestSimpleUpload(data, resultCode);
+
+ } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
+ requestMultipleUpload(data, resultCode);
+
+ }
+ }
+
+ private void requestMultipleUpload(Intent data, int resultCode) {
+ String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
+ if (filePaths != null) {
+ String[] remotePaths = new String[filePaths.length];
+ String remotePathBase = "";
+ for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
+ remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
+ }
+ if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
+ remotePathBase += OCFile.PATH_SEPARATOR;
+ for (int j = 0; j< remotePaths.length; j++) {
+ remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
+ }
+
+ Intent i = new Intent(this, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
+ if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
+ i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+ startService(i);
+
+ } else {
+ Log_OC.d(TAG, "User clicked on 'Update' with no selection");
+ Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
+ t.show();
+ return;
+ }
+ }
+
+
+ private void requestSimpleUpload(Intent data, int resultCode) {
+ String filepath = null;
+ try {
+ Uri selectedImageUri = data.getData();
+
+ String filemanagerstring = selectedImageUri.getPath();
+ String selectedImagePath = getPath(selectedImageUri);
+
+ if (selectedImagePath != null)
+ filepath = selectedImagePath;
+ else
+ filepath = filemanagerstring;
+
+ } catch (Exception e) {
+ Log_OC.e(TAG, "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
+ e.printStackTrace();
+
+ } finally {
+ if (filepath == null) {
+ Log_OC.e(TAG, "Couldnt resolve path to file");
+ Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
+ t.show();
+ return;
+ }
+ }
+
+ Intent i = new Intent(this, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT,
+ getAccount());
+ String remotepath = new String();
+ for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
+ remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
+ }
+ if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
+ remotepath += OCFile.PATH_SEPARATOR;
+ remotepath += new File(filepath).getName();
+
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
+ i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
+ startService(i);
+ }
+
+ @Override
+ public void onBackPressed() {
+ OCFileListFragment listOfFiles = getListOfFilesFragment();
+ if (mDualPane || getSecondFragment() == null) {
+ if (listOfFiles != null) { // should never be null, indeed
+ if (mDirectories.getCount() <= 1) {
+ finish();
+ return;
+ }
+ popDirname();
+ listOfFiles.onBrowseUp();
+ }
+ }
+ if (listOfFiles != null) { // should never be null, indeed
+ setFile(listOfFiles.getCurrentFile());
+ }
+ cleanSecondFragment();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
+ Log_OC.e(TAG, "onSaveInstanceState() start");
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
+ Log_OC.d(TAG, "onSaveInstanceState() end");
+ }
+
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log_OC.e(TAG, "onResume() start");
+
+ FileUploader fileUploader = new FileUploader();
+ FileSyncService fileSyncService = new FileSyncService();
+
+ // Listen for sync messages
+ IntentFilter syncIntentFilter = new IntentFilter(fileSyncService.getSyncMessage());
+ mSyncBroadcastReceiver = new SyncBroadcastReceiver();
+ registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+
+ // Listen for upload messages
+ IntentFilter uploadIntentFilter = new IntentFilter(fileUploader.getUploadFinishMessage());
+ mUploadFinishReceiver = new UploadFinishReceiver();
+ registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
+
+ // Listen for download messages
+ IntentFilter downloadIntentFilter = new IntentFilter(mDownloadAddedMessage);
+ downloadIntentFilter.addAction(mDownloadFinishMessage);
+ mDownloadFinishReceiver = new DownloadFinishReceiver();
+ registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+
+ Log_OC.d(TAG, "onResume() end");
+ }
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log_OC.e(TAG, "onPause() start");
+ if (mSyncBroadcastReceiver != null) {
+ unregisterReceiver(mSyncBroadcastReceiver);
+ mSyncBroadcastReceiver = null;
+ }
+ if (mUploadFinishReceiver != null) {
+ unregisterReceiver(mUploadFinishReceiver);
+ mUploadFinishReceiver = null;
+ }
+ if (mDownloadFinishReceiver != null) {
+ unregisterReceiver(mDownloadFinishReceiver);
+ mDownloadFinishReceiver = null;
+ }
+
+ Log_OC.d(TAG, "onPause() end");
+ }
+
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
+ ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
+ }
+ }
+
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ Dialog dialog = null;
+ AlertDialog.Builder builder;
+ switch (id) {
+ case DIALOG_SHORT_WAIT: {
+ ProgressDialog working_dialog = new ProgressDialog(this);
+ working_dialog.setMessage(getResources().getString(
+ R.string.wait_a_moment));
+ working_dialog.setIndeterminate(true);
+ working_dialog.setCancelable(false);
+ dialog = working_dialog;
+ break;
+ }
+ case DIALOG_CHOOSE_UPLOAD_SOURCE: {
+
+ String[] items = null;
+
+ String[] allTheItems = { getString(R.string.actionbar_upload_files),
+ getString(R.string.actionbar_upload_from_apps),
+ getString(R.string.actionbar_failed_instant_upload) };
+
+ String[] commonItems = { getString(R.string.actionbar_upload_files),
+ getString(R.string.actionbar_upload_from_apps) };
+
+ if (InstantUploadActivity.IS_ENABLED)
+ items = allTheItems;
+ else
+ items = commonItems;
+
+ builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.actionbar_upload);
+ builder.setItems(items, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int item) {
+ if (item == 0) {
+ // if (!mDualPane) {
+ Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
+ action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT, FileDisplayActivity.this.getAccount());
+ startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
+ // } else {
+ // TODO create and handle new fragment
+ // LocalFileListFragment
+ // }
+ } else if (item == 1) {
+ Intent action = new Intent(Intent.ACTION_GET_CONTENT);
+ action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
+ startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)),
+ ACTION_SELECT_CONTENT_FROM_APPS);
+ } else if (item == 2 && InstantUploadActivity.IS_ENABLED) {
+ Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
+ action.putExtra(FileUploader.KEY_ACCOUNT, FileDisplayActivity.this.getAccount());
+ startActivity(action);
+ }
+ }
+ });
+ dialog = builder.create();
+ break;
+ }
+ case DIALOG_SSL_VALIDATOR: {
+ dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
+ break;
+ }
+ case DIALOG_CERT_NOT_SAVED: {
+ builder = new AlertDialog.Builder(this);
+ builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
+ builder.setCancelable(false);
+ builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ };
+ });
+ dialog = builder.create();
+ break;
+ }
+ default:
+ dialog = null;
+ }
+
+ return dialog;
+ }
+
+
+ /**
+ * Show loading dialog
+ */
+ public void showLoadingDialog() {
+ // Construct dialog
+ LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
+ FragmentManager fm = getSupportFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ loading.show(ft, DIALOG_WAIT_TAG);
+
+ }
+
+ /**
+ * Dismiss loading dialog
+ */
+ public void dismissLoadingDialog(){
+ Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
+ if (frag != null) {
+ LoadingDialog loading = (LoadingDialog) frag;
+ loading.dismiss();
+ }
+ }
+
+
+ /**
+ * Translates a content URI of an image to a physical path
+ * on the disk
+ * @param uri The URI to resolve
+ * @return The path to the image or null if it could not be found
+ */
+ public String getPath(Uri uri) {
+ String[] projection = { MediaStore.Images.Media.DATA };
+ Cursor cursor = managedQuery(uri, projection, null, null, null);
+ if (cursor != null) {
+ int column_index = cursor
+ .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
+ cursor.moveToFirst();
+ return cursor.getString(column_index);
+ }
+ return null;
+ }
+
+ /**
+ * Pushes a directory to the drop down list
+ * @param directory to push
+ * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
+ */
+ public void pushDirname(OCFile directory) {
+ if(!directory.isDirectory()){
+ throw new IllegalArgumentException("Only directories may be pushed!");
+ }
+ mDirectories.insert(directory.getFileName(), 0);
+ setFile(directory);
+ }
+
+ /**
+ * Pops a directory name from the drop down list
+ * @return True, unless the stack is empty
+ */
+ public boolean popDirname() {
+ mDirectories.remove(mDirectories.getItem(0));
+ return !mDirectories.isEmpty();
+ }
+
+ // Custom array adapter to override text colors
+ private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
+
+ public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
+ super(ctx, view);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = super.getView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+ return v;
+ }
+
+ public View getDropDownView(int position, View convertView,
+ ViewGroup parent) {
+ View v = super.getDropDownView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+
+ return v;
+ }
+
+ }
+
+ private class SyncBroadcastReceiver extends BroadcastReceiver {
+
+ /**
+ * {@link BroadcastReceiver} to enable syncing feedback in UI
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
+ String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
+
+ Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
+
+ if (getAccount() != null && accountName.equals(getAccount().name)
+ && mStorageManager != null
+ ) {
+
+ String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
+
+ boolean fillBlankRoot = false;
+ OCFile currentDir = getCurrentDir();
+ if (currentDir == null) {
+ currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+ fillBlankRoot = (currentDir != null);
+ }
+
+ if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath)))
+ || fillBlankRoot ) {
+ if (!fillBlankRoot)
+ currentDir = mStorageManager.getFileByPath(synchFolderRemotePath);
+ OCFileListFragment fileListFragment = getListOfFilesFragment();
+ if (fileListFragment != null) {
+ fileListFragment.listDirectory(currentDir);
+ }
+ if (getSecondFragment() == null)
+ setFile(currentDir);
+ }
+
+ setSupportProgressBarIndeterminateVisibility(inProgress);
+ removeStickyBroadcast(intent);
+
+ }
+
+ RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
+ if (synchResult != null) {
+ if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
+ mLastSslUntrustedServerResult = synchResult;
+ showDialog(DIALOG_SSL_VALIDATOR);
+ }
+ }
+ }
+ }
+
+
+ private class UploadFinishReceiver extends BroadcastReceiver {
+ /**
+ * Once the file upload has finished -> update view
+ * @author David A. Velasco
+ * {@link BroadcastReceiver} to enable upload feedback in UI
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+ String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
+ boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name);
+ OCFile currentDir = getCurrentDir();
+ boolean isDescendant = (currentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(currentDir.getRemotePath()));
+ if (sameAccount && isDescendant) {
+ refeshListOfFilesFragment();
+ }
+ }
+
+ }
+
+
+ /**
+ * Class waiting for broadcast events from the {@link FielDownloader} service.
+ *
+ * Updates the UI when a download is started or finished, provided that it is relevant for the
+ * current folder.
+ */
+ private class DownloadFinishReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ boolean sameAccount = isSameAccount(context, intent);
+ String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+ boolean isDescendant = isDescendant(downloadedRemotePath);
+
+ if (sameAccount && isDescendant) {
+ refeshListOfFilesFragment();
+ refreshSecondFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
+ }
+
+ removeStickyBroadcast(intent);
+ }
+
+ private boolean isDescendant(String downloadedRemotePath) {
+ OCFile currentDir = getCurrentDir();
+ return (currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()));
+ }
+
+ private boolean isSameAccount(Context context, Intent intent) {
+ String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+ return (accountName != null && getAccount() != null && accountName.equals(getAccount().name));
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DataStorageManager getStorageManager() {
+ return mStorageManager;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ *
+ * Updates action bar and second fragment, if in dual pane mode.
+ */
+ @Override
+ public void onBrowsedDownTo(OCFile directory) {
+ pushDirname(directory);
+ cleanSecondFragment();
+ }
+
+ /**
+ * Opens the image gallery showing the image {@link OCFile} received as parameter.
+ *
+ * @param file Image {@link OCFile} to show.
+ */
+ @Override
+ public void startImagePreview(OCFile file) {
+ Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
+ showDetailsIntent.putExtra(EXTRA_FILE, file);
+ showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
+ startActivity(showDetailsIntent);
+ }
+
+ /**
+ * Stars the preview of an already down media {@link OCFile}.
+ *
+ * @param file Media {@link OCFile} to preview.
+ * @param startPlaybackPosition Media position where the playback will be started, in milliseconds.
+ * @param autoplay When 'true', the playback will start without user interactions.
+ */
+ @Override
+ public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
+ Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
+ setSecondFragment(mediaFragment);
+ updateFragmentsVisibility(true);
+ updateNavigationElementsInActionBar(file);
+ setFile(file);
+ }
+
+ /**
+ * Requests the download of the received {@link OCFile} , updates the UI
+ * to monitor the download progress and prepares the activity to preview
+ * or open the file when the download finishes.
+ *
+ * @param file {@link OCFile} to download and preview.
+ */
+ @Override
+ public void startDownloadForPreview(OCFile file) {
+ Fragment detailFragment = new FileDetailFragment(file, getAccount());
+ setSecondFragment(detailFragment);
+ mWaitingToPreview = file;
+ requestForDownload();
+ updateFragmentsVisibility(true);
+ updateNavigationElementsInActionBar(file);
+ setFile(file);
+ }
+
+
+ /**
+ * Shows the information of the {@link OCFile} received as a
+ * parameter in the second fragment.
+ *
+ * @param file {@link OCFile} whose details will be shown
+ */
+ @Override
+ public void showDetails(OCFile file) {
+ Fragment detailFragment = new FileDetailFragment(file, getAccount());
+ setSecondFragment(detailFragment);
+ updateFragmentsVisibility(true);
+ updateNavigationElementsInActionBar(file);
+ setFile(file);
+ }
+
+
+ /**
+ * TODO
+ */
+ private void updateNavigationElementsInActionBar(OCFile chosenFile) {
+ ActionBar actionBar = getSupportActionBar();
+ if (chosenFile == null || mDualPane) {
+ // only list of files - set for browsing through folders
+ OCFile currentDir = getCurrentDir();
+ actionBar.setDisplayHomeAsUpEnabled(currentDir != null && currentDir.getParentId() != 0);
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
+
+ } else {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setTitle(chosenFile.getFileName());
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFileStateChanged() {
+ refeshListOfFilesFragment();
+ updateNavigationElementsInActionBar(getSecondFragment().getFile());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileDownloaderBinder getFileDownloaderBinder() {
+ return mDownloaderBinder;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileUploaderBinder getFileUploaderBinder() {
+ return mUploaderBinder;
+ }
+
+
+ /** Defines callbacks for service binding, passed to bindService() */
+ private class ListServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+ if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
+ Log_OC.d(TAG, "Download service connected");
+ mDownloaderBinder = (FileDownloaderBinder) service;
+ if (mWaitingToPreview != null) {
+ requestForDownload();
+ }
+
+ } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service connected");
+ mUploaderBinder = (FileUploaderBinder) service;
+ } else {
+ return;
+ }
+ // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
+ OCFileListFragment listOfFiles = getListOfFilesFragment();
+ if (listOfFiles != null) {
+ listOfFiles.listDirectory();
+ }
+ FileFragment secondFragment = getSecondFragment();
+ if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
+ FileDetailFragment detailFragment = (FileDetailFragment)secondFragment;
+ detailFragment.listenForTransferProgress();
+ detailFragment.updateFileDetails(false, false);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
+ Log_OC.d(TAG, "Download service disconnected");
+ mDownloaderBinder = null;
+ } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service disconnected");
+ mUploaderBinder = null;
+ }
+ }
+ };
+
+
+
+ /**
+ * Launch an intent to request the PIN code to the user before letting him use the app
+ */
+ private void requestPinCode() {
+ boolean pinStart = false;
+ SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ pinStart = appPrefs.getBoolean("set_pincode", false);
+ if (pinStart) {
+ Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+ i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
+ startActivity(i);
+ }
+ }
+
+
+ @Override
+ public void onSavedCertificate() {
+ startSynchronization();
+ }
+
+
+ @Override
+ public void onFailedSavingCertificate() {
+ showDialog(DIALOG_CERT_NOT_SAVED);
+ }
+
+
+ /**
+ * Updates the view associated to the activity after the finish of some operation over files
+ * in the current account.
+ *
+ * @param operation Removal operation performed.
+ * @param result Result of the removal.
+ */
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ if (operation instanceof RemoveFileOperation) {
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+
+ } else if (operation instanceof RenameFileOperation) {
+ onRenameFileOperationFinish((RenameFileOperation)operation, result);
+
+ } else if (operation instanceof SynchronizeFileOperation) {
+ onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
+
+ } else if (operation instanceof CreateFolderOperation) {
+ onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
+ }
+ }
+
+
+ /**
+ * Updates the view associated to the activity after the finish of an operation trying to remove a
+ * file.
+ *
+ * @param operation Removal operation performed.
+ * @param result Result of the removal.
+ */
+ private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+ dismissLoadingDialog();
+ if (result.isSuccess()) {
+ Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
+ msg.show();
+ OCFile removedFile = operation.getFile();
+ getSecondFragment();
+ FileFragment second = getSecondFragment();
+ if (second != null && removedFile.equals(second.getFile())) {
+ cleanSecondFragment();
+ }
+ if (mStorageManager.getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
+ refeshListOfFilesFragment();
+ }
+
+ } else {
+ Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ mLastSslUntrustedServerResult = result;
+ showDialog(DIALOG_SSL_VALIDATOR);
+ }
+ }
+ }
+
+ /**
+ * Updates the view associated to the activity after the finish of an operation trying create a new folder
+ *
+ * @param operation Creation operation performed.
+ * @param result Result of the creation.
+ */
+ private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ dismissLoadingDialog();
+ refeshListOfFilesFragment();
+
+ } else {
+ //dismissDialog(DIALOG_SHORT_WAIT);
+ dismissLoadingDialog();
+ try {
+ Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+
+ } catch (NotFoundException e) {
+ Log_OC.e(TAG, "Error while trying to show fail message " , e);
+ }
+ }
+ }
+
+
+ /**
+ * Updates the view associated to the activity after the finish of an operation trying to rename a
+ * file.
+ *
+ * @param operation Renaming operation performed.
+ * @param result Result of the renaming.
+ */
+ private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
+ dismissLoadingDialog();
+ OCFile renamedFile = operation.getFile();
+ if (result.isSuccess()) {
+ if (mDualPane) {
+ FileFragment details = getSecondFragment();
+ if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
+ ((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
+ }
+ }
+ if (mStorageManager.getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
+ refeshListOfFilesFragment();
+ }
+
+ } else {
+ if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
+ Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ // TODO throw again the new rename dialog
+ } else {
+ Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ mLastSslUntrustedServerResult = result;
+ showDialog(DIALOG_SSL_VALIDATOR);
+ }
+ }
+ }
+ }
+
+
+ private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
+ dismissLoadingDialog();
+ OCFile syncedFile = operation.getLocalFile();
+ if (!result.isSuccess()) {
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+ Intent i = new Intent(this, ConflictsResolveActivity.class);
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
+ startActivity(i);
+
+ } else {
+ Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ }
+
+ } else {
+ if (operation.transferWasRequested()) {
+ refeshListOfFilesFragment();
+ onTransferStateChanged(syncedFile, true, true);
+
+ } else {
+ Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
+ msg.show();
+ }
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
+ if (mDualPane) {
+ FileFragment details = getSecondFragment();
+ if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
+ if (downloading || uploading) {
+ ((FileDetailFragment)details).updateFileDetails(file, getAccount());
+ } else {
+ ((FileDetailFragment)details).updateFileDetails(false, true);
+ }
+ }
+ }
+ }
+
+
+ public void onDismiss(EditNameDialog dialog) {
+ if (dialog.getResult()) {
+ String newDirectoryName = dialog.getNewFilename().trim();
+ Log_OC.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName);
+ if (newDirectoryName.length() > 0) {
+ String path = getCurrentDir().getRemotePath();
+
+ // Create directory
+ path += newDirectoryName + OCFile.PATH_SEPARATOR;
+ RemoteOperation operation = new CreateFolderOperation(path, false, mStorageManager);
+ operation.execute( getAccount(),
+ FileDisplayActivity.this,
+ FileDisplayActivity.this,
+ mHandler,
+ FileDisplayActivity.this);
+
+ showLoadingDialog();
+ }
+ }
+ }
+
+
+ private void requestForDownload() {
+ Account account = getAccount();
+ if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
+ Intent i = new Intent(this, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
+ i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
+ startService(i);
+ }
+ }
+
+
+ private OCFile getCurrentDir() {
+ OCFile file = getFile();
+ if (file != null) {
+ if (file.isDirectory()) {
+ return file;
+ } else if (mStorageManager != null) {
+ String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
+ return mStorageManager.getFileByPath(parentPath);
+ }
+ }
+ return null;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.method.ScrollingMovementMethod;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+
+
+/**
+ * Activity showing a text message and, optionally, a couple list of single or paired text strings.
+ *
+ * Added to show explanations for notifications when the user clicks on them, and there no place
+ * better to show them.
+ *
+ * @author David A. Velasco
+ */
+public class GenericExplanationActivity extends SherlockFragmentActivity {
+
+ public static final String EXTRA_LIST = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST";
+ public static final String EXTRA_LIST_2 = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST_2";
+ public static final String MESSAGE = GenericExplanationActivity.class.getCanonicalName() + ".MESSAGE";
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ String message = intent.getStringExtra(MESSAGE);
+ ArrayList<String> list = intent.getStringArrayListExtra(EXTRA_LIST);
+ ArrayList<String> list2 = intent.getStringArrayListExtra(EXTRA_LIST_2);
+
+ setContentView(R.layout.generic_explanation);
+
+ if (message != null) {
+ TextView textView = (TextView) findViewById(R.id.message);
+ textView.setText(message);
+ textView.setMovementMethod(new ScrollingMovementMethod());
+ }
+
+ ListView listView = (ListView) findViewById(R.id.list);
+ if (list != null && list.size() > 0) {
+ //ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
+ ListAdapter adapter = new ExplanationListAdapterView(this, list, list2);
+ listView.setAdapter(adapter);
+ } else {
+ listView.setVisibility(View.GONE);
+ }
+ }
+
+ public class ExplanationListAdapterView extends ArrayAdapter<String> {
+
+ ArrayList<String> mList;
+ ArrayList<String> mList2;
+
+ ExplanationListAdapterView(Context context, ArrayList<String> list, ArrayList<String> list2) {
+ super(context, android.R.layout.two_line_list_item, android.R.id.text1, list);
+ mList = list;
+ mList2 = list2;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View getView (int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+ if (view != null) {
+ if (mList2 != null && mList2.size() > 0 && position >= 0 && position < mList2.size()) {
+ TextView text2 = (TextView) view.findViewById(android.R.id.text2);
+ if (text2 != null) {
+ text2.setText(mList2.get(position));
+ }
+ }
+ }
+ return view;
+ }
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.files.InstantUploadBroadcastReceiver;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.utils.FileStorageUtils;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+
+/**
+ * This Activity is used to display a list with images they could not be
+ * uploaded instantly. The images can be selected for delete or for a try again
+ * upload
+ *
+ * The entry-point for this activity is the 'Failed upload Notification" and a
+ * sub-menu underneath the 'Upload' menu-item
+ *
+ * @author andomaex / Matthias Baumann
+ */
+public class InstantUploadActivity extends Activity {
+
+ private static final String LOG_TAG = InstantUploadActivity.class.getSimpleName();
+ private LinearLayout listView;
+ private static final String retry_chexbox_tag = "retry_chexbox_tag";
+ public static final boolean IS_ENABLED = false;
+ private static int MAX_LOAD_IMAGES = 5;
+ private int lastLoadImageIdx = 0;
+
+ private SparseArray<String> fileList = null;
+ CheckBox failed_upload_all_cb;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.failed_upload_files);
+
+ CustomButton deleteAllBtn = (CustomButton) findViewById(R.id.failed_upload_delete_all_btn);
+ deleteAllBtn.setOnClickListener(getDeleteListner());
+ CustomButton retryAllBtn = (CustomButton) findViewById(R.id.failed_upload_retry_all_btn);
+ retryAllBtn.setOnClickListener(getRetryListner());
+ this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
+ failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener());
+ listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout);
+
+ loadListView(true);
+
+ }
+
+ /**
+ * init the listview with ImageButtons, checkboxes and filename for every
+ * Image that was not successfully uploaded
+ *
+ * this method is call at Activity creation and on delete one ore more
+ * list-entry an on retry the upload by clicking the ImageButton or by click
+ * to the 'retry all' button
+ *
+ */
+ private void loadListView(boolean reset) {
+ DbHandler db = new DbHandler(getApplicationContext());
+ Cursor c = db.getFailedFiles();
+
+ if (reset) {
+ fileList = new SparseArray<String>();
+ listView.removeAllViews();
+ lastLoadImageIdx = 0;
+ }
+ if (c != null) {
+ try {
+ c.moveToPosition(lastLoadImageIdx);
+
+ while (c.moveToNext()) {
+
+ lastLoadImageIdx++;
+ String imp_path = c.getString(1);
+ String message = c.getString(4);
+ fileList.put(lastLoadImageIdx, imp_path);
+ LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx);
+ rowLayout.addView(getFileCheckbox(lastLoadImageIdx));
+ rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx));
+ rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx));
+ listView.addView(rowLayout);
+ Log_OC.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx);
+ if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) {
+ break;
+ }
+ }
+ if (lastLoadImageIdx > 0) {
+ addLoadMoreButton(listView);
+ }
+ } finally {
+ db.close();
+ }
+ }
+ }
+
+ private void addLoadMoreButton(LinearLayout listView) {
+ if (listView != null) {
+ Button loadmoreBtn = null;
+ View oldButton = listView.findViewById(42);
+ if (oldButton != null) {
+ // remove existing button
+ listView.removeView(oldButton);
+ // to add the button at the end
+ loadmoreBtn = (Button) oldButton;
+ } else {
+ // create a new button to add to the scoll view
+ loadmoreBtn = new Button(this);
+ loadmoreBtn.setId(42);
+ loadmoreBtn.setText(getString(R.string.failed_upload_load_more_images));
+ loadmoreBtn.setBackgroundResource(R.color.background_color);
+ loadmoreBtn.setTextSize(12);
+ loadmoreBtn.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ loadListView(false);
+ }
+
+ });
+ }
+ listView.addView(loadmoreBtn);
+ }
+ }
+
+ /**
+ * provide a list of CheckBox instances, looked up from parent listview this
+ * list ist used to select/deselect all checkboxes at the list
+ *
+ * @return List<CheckBox>
+ */
+ private List<CheckBox> getCheckboxList() {
+ List<CheckBox> list = new ArrayList<CheckBox>();
+ for (int i = 0; i < listView.getChildCount(); i++) {
+ Log_OC.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount());
+ View childView = listView.getChildAt(i);
+ if (childView != null && childView instanceof ViewGroup) {
+ View checkboxView = getChildViews((ViewGroup) childView);
+ if (checkboxView != null && checkboxView instanceof CheckBox) {
+ Log_OC.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass());
+ list.add((CheckBox) checkboxView);
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * recursive called method, used from getCheckboxList method
+ *
+ * @param View
+ * @return View
+ */
+ private View getChildViews(ViewGroup view) {
+ if (view != null) {
+ for (int i = 0; i < view.getChildCount(); i++) {
+ View cb = view.getChildAt(i);
+ if (cb != null && cb instanceof ViewGroup) {
+ return getChildViews((ViewGroup) cb);
+ } else if (cb instanceof CheckBox) {
+ return cb;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * create a new OnCheckedChangeListener for the 'check all' checkbox *
+ *
+ * @return OnCheckedChangeListener to select all checkboxes at the list
+ */
+ private OnCheckedChangeListener getCheckAllListener() {
+ return new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ List<CheckBox> list = getCheckboxList();
+ for (CheckBox checkbox : list) {
+ ((CheckBox) checkbox).setChecked(isChecked);
+ }
+ }
+
+ };
+ }
+
+ /**
+ * Button click Listener for the retry button at the headline
+ *
+ * @return a Listener to perform a retry for all selected images
+ */
+ private OnClickListener getRetryListner() {
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ try {
+
+ List<CheckBox> list = getCheckboxList();
+ for (CheckBox checkbox : list) {
+ boolean to_retry = checkbox.isChecked();
+
+ Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry);
+ String img_path = fileList.get(checkbox.getId());
+ if (to_retry) {
+
+ final String msg = "Image-Path " + checkbox.getId() + " was checked: " + img_path;
+ Log_OC.d(LOG_TAG, msg);
+ startUpload(img_path);
+ }
+
+ }
+ } finally {
+ // refresh the List
+ listView.removeAllViews();
+ loadListView(true);
+ if (failed_upload_all_cb != null) {
+ failed_upload_all_cb.setChecked(false);
+ }
+ }
+
+ }
+ };
+ }
+
+ /**
+ * Button click Listener for the delete button at the headline
+ *
+ * @return a Listener to perform a delete for all selected images
+ */
+ private OnClickListener getDeleteListner() {
+
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+
+ final DbHandler dbh = new DbHandler(getApplicationContext());
+ try {
+ List<CheckBox> list = getCheckboxList();
+ for (CheckBox checkbox : list) {
+ boolean to_be_delete = checkbox.isChecked();
+
+ Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete);
+ String img_path = fileList.get(checkbox.getId());
+ Log_OC.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path);
+ if (to_be_delete) {
+ boolean deleted = dbh.removeIUPendingFile(img_path);
+ Log_OC.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted);
+
+ }
+
+ }
+ } finally {
+ dbh.close();
+ // refresh the List
+ listView.removeAllViews();
+ loadListView(true);
+ if (failed_upload_all_cb != null) {
+ failed_upload_all_cb.setChecked(false);
+ }
+ }
+
+ }
+ };
+ }
+
+ private LinearLayout getHorizontalLinearLayout(int id) {
+ LinearLayout linearLayout = new LinearLayout(getApplicationContext());
+ linearLayout.setId(id);
+ linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.MATCH_PARENT));
+ linearLayout.setGravity(Gravity.RIGHT);
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ return linearLayout;
+ }
+
+ private LinearLayout getVerticalLinearLayout() {
+ LinearLayout linearLayout = new LinearLayout(getApplicationContext());
+ linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.MATCH_PARENT));
+ linearLayout.setGravity(Gravity.TOP);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ return linearLayout;
+ }
+
+ private View getFileButton(final String img_path, String message, int id) {
+
+ TextView failureTextView = new TextView(this);
+ failureTextView.setText(getString(R.string.failed_upload_failure_text) + message);
+ failureTextView.setBackgroundResource(R.color.background_color);
+ failureTextView.setTextSize(8);
+ failureTextView.setOnLongClickListener(getOnLongClickListener(message));
+ failureTextView.setPadding(5, 5, 5, 10);
+ TextView retryButton = new TextView(this);
+ retryButton.setId(id);
+ retryButton.setText(img_path);
+ retryButton.setBackgroundResource(R.color.background_color);
+ retryButton.setTextSize(8);
+ retryButton.setOnClickListener(getImageButtonOnClickListener(img_path));
+ retryButton.setOnLongClickListener(getOnLongClickListener(message));
+ retryButton.setPadding(5, 5, 5, 10);
+ LinearLayout verticalLayout = getVerticalLinearLayout();
+ verticalLayout.addView(retryButton);
+ verticalLayout.addView(failureTextView);
+
+ return verticalLayout;
+ }
+
+ private OnLongClickListener getOnLongClickListener(final String message) {
+ return new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ Log_OC.d(LOG_TAG, message);
+ Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
+ + message, Toast.LENGTH_LONG);
+ toast.show();
+ return true;
+ }
+
+ };
+ }
+
+ private CheckBox getFileCheckbox(int id) {
+ CheckBox retryCB = new CheckBox(this);
+ retryCB.setId(id);
+ retryCB.setBackgroundResource(R.color.background_color);
+ retryCB.setTextSize(8);
+ retryCB.setTag(retry_chexbox_tag);
+ return retryCB;
+ }
+
+ private ImageButton getImageButton(String img_path, int id) {
+ ImageButton imageButton = new ImageButton(this);
+ imageButton.setId(id);
+ imageButton.setClickable(true);
+ imageButton.setOnClickListener(getImageButtonOnClickListener(img_path));
+
+ // scale and add a thumbnail to the imagebutton
+ int base_scale_size = 32;
+ if (img_path != null) {
+ Log_OC.d(LOG_TAG, "add " + img_path + " to Image Button");
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ Bitmap bitmap = BitmapFactory.decodeFile(img_path, options);
+ int width_tpm = options.outWidth, height_tmp = options.outHeight;
+ int scale = 3;
+ while (true) {
+ if (width_tpm / 2 < base_scale_size || height_tmp / 2 < base_scale_size) {
+ break;
+ }
+ width_tpm /= 2;
+ height_tmp /= 2;
+ scale++;
+ }
+
+ Log_OC.d(LOG_TAG, "scale Imgae with: " + scale);
+ BitmapFactory.Options options2 = new BitmapFactory.Options();
+ options2.inSampleSize = scale;
+ bitmap = BitmapFactory.decodeFile(img_path, options2);
+
+ if (bitmap != null) {
+ Log_OC.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes());
+ imageButton.setImageBitmap(bitmap);
+ } else {
+ Log_OC.d(LOG_TAG, "could not load imgage: " + img_path);
+ }
+ }
+ return imageButton;
+ }
+
+ private OnClickListener getImageButtonOnClickListener(final String img_path) {
+ return new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ startUpload(img_path);
+ loadListView(true);
+ }
+
+ };
+ }
+
+ /**
+ * start uploading a file to the INSTANT_UPLOD_DIR
+ *
+ * @param img_path
+ */
+ private void startUpload(String img_path) {
+ // extract filename
+ String filename = FileStorageUtils.getInstantUploadFilePath(this, img_path);
+ if (canInstantUpload()) {
+ Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this);
+ // add file again to upload queue
+ DbHandler db = new DbHandler(InstantUploadActivity.this);
+ try {
+ db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null);
+ } finally {
+ db.close();
+ }
+
+ Intent i = new Intent(InstantUploadActivity.this, FileUploader.class);
+ i.putExtra(FileUploader.KEY_ACCOUNT, account);
+ i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path);
+ i.putExtra(FileUploader.KEY_REMOTE_FILE, filename);
+ i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
+ i.putExtra(com.owncloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true);
+
+ final String msg = "try to upload file with name :" + filename;
+ Log_OC.d(LOG_TAG, msg);
+ Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
+ + filename, Toast.LENGTH_LONG);
+ toast.show();
+
+ startService(i);
+ } else {
+ Toast toast = Toast.makeText(InstantUploadActivity.this,
+ getString(R.string.failed_upload_retry_do_nothing_text) + filename, Toast.LENGTH_LONG);
+ toast.show();
+ }
+ }
+
+ private boolean canInstantUpload() {
+
+ if (!InstantUploadBroadcastReceiver.isOnline(this)
+ || (InstantUploadBroadcastReceiver.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
+ .isConnectedViaWiFi(this))) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui.activity;\r
+\r
+import com.actionbarsherlock.app.SherlockFragmentActivity;\r
+import com.owncloud.android.MainApp;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.ui.adapter.LandingScreenAdapter;\r
+\r
+import android.accounts.Account;\r
+import android.accounts.AccountManager;\r
+import android.app.AlertDialog;\r
+import android.app.Dialog;\r
+import android.content.DialogInterface;\r
+import android.content.DialogInterface.OnClickListener;\r
+import android.content.Intent;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.widget.AdapterView;\r
+import android.widget.AdapterView.OnItemClickListener;\r
+import android.widget.GridView;\r
+import android.widget.Toast;\r
+\r
+\r
+/**\r
+ * This activity is used as a landing page when the user first opens this app.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingActivity extends SherlockFragmentActivity implements\r
+ OnClickListener, OnItemClickListener {\r
+\r
+ public static final int DIALOG_SETUP_ACCOUNT = 1;\r
+\r
+ @Override\r
+ protected void onCreate(Bundle savedInstanceState) {\r
+ super.onCreate(savedInstanceState);\r
+ setContentView(R.layout.main);\r
+\r
+ // Fill the grid view of the landing screen with icons\r
+ GridView landingScreenItems = (GridView) findViewById(R.id.homeScreenGrid);\r
+ landingScreenItems.setAdapter(new LandingScreenAdapter(this));\r
+ landingScreenItems.setOnItemClickListener(this);\r
+\r
+ // Check, if there are ownCloud accounts\r
+ if (!accountsAreSetup()) {\r
+ showDialog(DIALOG_SETUP_ACCOUNT);\r
+ } else {\r
+ // Start device tracking service\r
+ Intent locationServiceIntent = new Intent();\r
+ locationServiceIntent\r
+ .setAction("com.owncloud.android.location.LocationLauncher");\r
+ sendBroadcast(locationServiceIntent);\r
+ }\r
+\r
+ }\r
+\r
+ @Override\r
+ protected void onRestart() {\r
+ super.onRestart();\r
+ // Check, if there are ownCloud accounts\r
+ if (!accountsAreSetup()) {\r
+ showDialog(DIALOG_SETUP_ACCOUNT);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {\r
+ super.onRestoreInstanceState(savedInstanceState);\r
+ // Check, if there are ownCloud accounts\r
+ if (!accountsAreSetup()) {\r
+ showDialog(DIALOG_SETUP_ACCOUNT);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ protected Dialog onCreateDialog(int id) {\r
+ Dialog dialog;\r
+ switch (id) {\r
+ case DIALOG_SETUP_ACCOUNT:\r
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
+ builder.setTitle(R.string.main_tit_accsetup);\r
+ builder.setMessage(R.string.main_wrn_accsetup);\r
+ builder.setCancelable(false);\r
+ builder.setPositiveButton(R.string.common_ok, this);\r
+ builder.setNegativeButton(R.string.common_cancel, this);\r
+ dialog = builder.create();\r
+ break;\r
+ default:\r
+ dialog = null;\r
+ }\r
+\r
+ return dialog;\r
+ }\r
+\r
+ public void onClick(DialogInterface dialog, int which) {\r
+ // In any case - we won't need it anymore\r
+ dialog.dismiss();\r
+ switch (which) {\r
+ case DialogInterface.BUTTON_POSITIVE:\r
+ Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
+ intent.putExtra("authorities",\r
+ new String[] { MainApp.getAuthTokenType() });\r
+ startActivity(intent);\r
+ break;\r
+ case DialogInterface.BUTTON_NEGATIVE:\r
+ finish();\r
+ }\r
+\r
+ }\r
+\r
+ @Override\r
+ /**\r
+ * Start an activity based on the selection\r
+ * the user made\r
+ */\r
+ public void onItemClick(AdapterView<?> parent, View view, int position,\r
+ long id) {\r
+ Intent intent;\r
+ intent = (Intent) parent.getAdapter().getItem(position);\r
+ if (intent != null) {\r
+ startActivity(intent);\r
+ } else {\r
+ // TODO: Implement all of this and make this text go away ;-)\r
+ Toast toast = Toast.makeText(this, "Not yet implemented!",\r
+ Toast.LENGTH_SHORT);\r
+ toast.show();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Checks, whether or not there are any ownCloud accounts setup.\r
+ * \r
+ * @return true, if there is at least one account.\r
+ */\r
+ private boolean accountsAreSetup() {\r
+ AccountManager accMan = AccountManager.get(this);\r
+ Account[] accounts = accMan\r
+ .getAccountsByType(MainApp.getAccountType());\r
+ return accounts.length > 0;\r
+ }\r
+\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ListView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.ui.adapter.LogListAdapter;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+
+
+public class LogHistoryActivity extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
+ String logpath = FileStorageUtils.getLogPath();
+ File logDIR = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.log_send_file);
+ setTitle("Log History");
+ ActionBar actionBar = getSherlock().getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ ListView listView = (ListView) findViewById(android.R.id.list);
+ CustomButton deleteHistoryButton = (CustomButton) findViewById(R.id.deleteLogHistoryButton);
+
+ deleteHistoryButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ File dir = new File(logpath);
+ if (dir != null) {
+ File[] files = dir.listFiles();
+ if(files!=null) {
+ for(File f: files) {
+ f.delete();
+ }
+ }
+ dir.delete();
+ }
+ Intent intent = new Intent(getBaseContext(), Preferences.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+
+ });
+
+
+ if(logpath != null){
+ logDIR = new File(logpath);
+ }
+
+ if(logDIR != null && logDIR.isDirectory()) {
+ File[] files = logDIR.listFiles();
+
+ if (files != null && files.length != 0) {
+ ArrayList<String> logfiles_name = new ArrayList<String>();
+ for (File file : files) {
+ logfiles_name.add(file.getName());
+ }
+ String[] logFiles2Array = logfiles_name.toArray(new String[logfiles_name.size()]);
+ LogListAdapter listadapter = new LogListAdapter(this,logFiles2Array);
+ listView.setAdapter(listadapter);
+ }
+ }
+ }
+
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ super.onMenuItemSelected(featureId, item);
+ Intent intent;
+
+ switch (item.getItemId()) {
+
+ case android.R.id.home:
+ intent = new Intent(getBaseContext(), Preferences.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ @Override
+ public boolean onPreferenceChange(Preference arg0, Object arg1) {
+ return false;
+ }
+}
\ No newline at end of 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 version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.util.Arrays;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnKeyListener;
+import android.widget.EditText;
+import android.widget.TextView;
+
+public class PinCodeActivity extends SherlockFragmentActivity {
+
+
+ public final static String EXTRA_ACTIVITY = "com.owncloud.android.ui.activity.PinCodeActivity.ACTIVITY";
+ public final static String EXTRA_NEW_STATE = "com.owncloud.android.ui.activity.PinCodeActivity.NEW_STATE";
+
+ CustomButton bCancel;
+ TextView mPinHdr;
+ TextView mPinHdrExplanation;
+ EditText mText1;
+ EditText mText2;
+ EditText mText3;
+ EditText mText4;
+
+ String [] tempText ={"","","",""};
+
+ String activity;
+
+ boolean confirmingPinCode = false;
+ boolean pinCodeChecked = false;
+ boolean newPasswordEntered = false;
+ boolean bChange = true; // to control that only one blocks jump
+ int tCounter ; // Count the number of attempts an user could introduce the PIN code
+
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pincodelock);
+
+ Intent intent = getIntent();
+ activity = intent.getStringExtra(EXTRA_ACTIVITY);
+
+ bCancel = (CustomButton) findViewById(R.id.cancel);
+ mPinHdr = (TextView) findViewById(R.id.pinHdr);
+ mPinHdrExplanation = (TextView) findViewById(R.id.pinHdrExpl);
+ mText1 = (EditText) findViewById(R.id.txt1);
+ mText1.requestFocus();
+ getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ mText2 = (EditText) findViewById(R.id.txt2);
+ mText3 = (EditText) findViewById(R.id.txt3);
+ mText4 = (EditText) findViewById(R.id.txt4);
+
+ SharedPreferences appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+
+ // Not PIN Code defined yet.
+ // In a previous version settings is allow from start
+ if ( (appPrefs.getString("PrefPinCode1", null) == null ) ){
+ setChangePincodeView(true);
+ pinCodeChecked = true;
+ newPasswordEntered = true;
+
+ }else{
+
+ if (appPrefs.getBoolean("set_pincode", false)){
+ // pincode activated
+ if (activity.equals("preferences")){
+ // PIN has been activated yet
+ mPinHdr.setText(R.string.pincode_configure_your_pin);
+ mPinHdrExplanation.setVisibility(View.VISIBLE);
+ pinCodeChecked = true ; // No need to check it
+ setChangePincodeView(true);
+ }else{
+ // PIN active
+ bCancel.setVisibility(View.INVISIBLE);
+ bCancel.setVisibility(View.GONE);
+ mPinHdr.setText(R.string.pincode_enter_pin_code);
+ mPinHdrExplanation.setVisibility(View.INVISIBLE);
+ setChangePincodeView(false);
+ }
+
+ }else {
+ // pincode removal
+ mPinHdr.setText(R.string.pincode_remove_your_pincode);
+ mPinHdrExplanation.setVisibility(View.INVISIBLE);
+ pinCodeChecked = false;
+ setChangePincodeView(true);
+ }
+
+ }
+ setTextListeners();
+
+
+ }
+
+
+
+ protected void setInitVars(){
+ confirmingPinCode = false;
+ pinCodeChecked = false;
+ newPasswordEntered = false;
+
+ }
+
+ protected void setInitView(){
+ bCancel.setVisibility(View.INVISIBLE);
+ bCancel.setVisibility(View.GONE);
+ mPinHdr.setText(R.string.pincode_enter_pin_code);
+ mPinHdrExplanation.setVisibility(View.INVISIBLE);
+ }
+
+
+ protected void setChangePincodeView(boolean state){
+
+ if(state){
+ bCancel.setVisibility(View.VISIBLE);
+ bCancel.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ SharedPreferences.Editor appPrefsE = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+
+ SharedPreferences appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ boolean state = appPrefs.getBoolean("set_pincode", false);
+ appPrefsE.putBoolean("set_pincode",!state);
+ appPrefsE.commit();
+ setInitVars();
+ finish();
+ }
+ });
+ }
+
+ }
+
+
+
+ /*
+ *
+ */
+ protected void setTextListeners(){
+
+ /*------------------------------------------------
+ * FIRST BOX
+ -------------------------------------------------*/
+
+ mText1.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ if (!confirmingPinCode){
+ tempText[0] = mText1.getText().toString();
+
+ }
+ mText2.requestFocus();
+ }
+ }
+ });
+
+
+
+ /*------------------------------------------------
+ * SECOND BOX
+ -------------------------------------------------*/
+ mText2.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ if (!confirmingPinCode){
+ tempText[1] = mText2.getText().toString();
+ }
+
+ mText3.requestFocus();
+ }
+ }
+ });
+
+ mText2.setOnKeyListener(new OnKeyListener() {
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+
+ mText1.setText("");
+ mText1.requestFocus();
+ if (!confirmingPinCode)
+ tempText[0] = "";
+ bChange= false;
+
+ }else if(!bChange){
+ bChange=true;
+
+ }
+ return false;
+ }
+ });
+
+ mText2.setOnFocusChangeListener(new OnFocusChangeListener() {
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mText2.setCursorVisible(true);
+ if (mText1.getText().toString().equals("")){
+ mText2.setSelected(false);
+ mText2.setCursorVisible(false);
+ mText1.requestFocus();
+ mText1.setSelected(true);
+ mText1.setSelection(0);
+ }
+
+ }
+ });
+
+
+ /*------------------------------------------------
+ * THIRD BOX
+ -------------------------------------------------*/
+ mText3.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ if (!confirmingPinCode){
+ tempText[2] = mText3.getText().toString();
+ }
+ mText4.requestFocus();
+ }
+ }
+ });
+
+ mText3.setOnKeyListener(new OnKeyListener() {
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+ mText2.requestFocus();
+ if (!confirmingPinCode)
+ tempText[1] = "";
+ mText2.setText("");
+ bChange= false;
+
+ }else if(!bChange){
+ bChange=true;
+
+ }
+ return false;
+ }
+ });
+
+ mText3.setOnFocusChangeListener(new OnFocusChangeListener() {
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mText3.setCursorVisible(true);
+ if (mText1.getText().toString().equals("")){
+ mText3.setSelected(false);
+ mText3.setCursorVisible(false);
+ mText1.requestFocus();
+ mText1.setSelected(true);
+ mText1.setSelection(0);
+ }else if (mText2.getText().toString().equals("")){
+ mText3.setSelected(false);
+ mText3.setCursorVisible(false);
+ mText2.requestFocus();
+ mText2.setSelected(true);
+ mText2.setSelection(0);
+ }
+
+ }
+ });
+
+ /*------------------------------------------------
+ * FOURTH BOX
+ -------------------------------------------------*/
+ mText4.addTextChangedListener(new TextWatcher() {
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+
+ if (!confirmingPinCode){
+ tempText[3] = mText4.getText().toString();
+ }
+ mText1.requestFocus();
+
+ if (!pinCodeChecked){
+ pinCodeChecked = checkPincode();
+ }
+
+ if (pinCodeChecked && activity.equals("FileDisplayActivity")){
+ finish();
+ } else if (pinCodeChecked){
+
+ Intent intent = getIntent();
+ String newState = intent.getStringExtra(EXTRA_NEW_STATE);
+
+ if (newState.equals("false")){
+ SharedPreferences.Editor appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+ appPrefs.putBoolean("set_pincode",false);
+ appPrefs.commit();
+
+ setInitVars();
+ pinCodeEnd(false);
+
+ }else{
+
+ if (!confirmingPinCode){
+ pinCodeChangeRequest();
+
+ } else {
+ confirmPincode();
+ }
+ }
+
+
+ }
+ }
+ }
+ });
+
+
+
+ mText4.setOnKeyListener(new OnKeyListener() {
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+ mText3.requestFocus();
+ if (!confirmingPinCode)
+ tempText[2]="";
+ mText3.setText("");
+ bChange= false;
+
+ }else if(!bChange){
+ bChange=true;
+ }
+ return false;
+ }
+ });
+
+ mText4.setOnFocusChangeListener(new OnFocusChangeListener() {
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mText4.setCursorVisible(true);
+
+ if (mText1.getText().toString().equals("")){
+ mText4.setSelected(false);
+ mText4.setCursorVisible(false);
+ mText1.requestFocus();
+ mText1.setSelected(true);
+ mText1.setSelection(0);
+ }else if (mText2.getText().toString().equals("")){
+ mText4.setSelected(false);
+ mText4.setCursorVisible(false);
+ mText2.requestFocus();
+ mText2.setSelected(true);
+ mText2.setSelection(0);
+ }else if (mText3.getText().toString().equals("")){
+ mText4.setSelected(false);
+ mText4.setCursorVisible(false);
+ mText3.requestFocus();
+ mText3.setSelected(true);
+ mText3.setSelection(0);
+ }
+
+ }
+ });
+
+
+
+ } // end setTextListener
+
+
+ protected void pinCodeChangeRequest(){
+
+ clearBoxes();
+ mPinHdr.setText(R.string.pincode_reenter_your_pincode);
+ mPinHdrExplanation.setVisibility(View.INVISIBLE);
+ confirmingPinCode =true;
+
+ }
+
+
+ protected boolean checkPincode(){
+
+
+ SharedPreferences appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ String pText1 = appPrefs.getString("PrefPinCode1", null);
+ String pText2 = appPrefs.getString("PrefPinCode2", null);
+ String pText3 = appPrefs.getString("PrefPinCode3", null);
+ String pText4 = appPrefs.getString("PrefPinCode4", null);
+
+ if ( tempText[0].equals(pText1) &&
+ tempText[1].equals(pText2) &&
+ tempText[2].equals(pText3) &&
+ tempText[3].equals(pText4) ) {
+
+ return true;
+
+
+ }else {
+ Arrays.fill(tempText, null);
+ AlertDialog aDialog = new AlertDialog.Builder(this).create();
+ CharSequence errorSeq = getString(R.string.common_error);
+ aDialog.setTitle(errorSeq);
+ CharSequence cseq = getString(R.string.pincode_wrong);
+ aDialog.setMessage(cseq);
+ CharSequence okSeq = getString(R.string.common_ok);
+ aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ return;
+ }
+
+ });
+ aDialog.show();
+ clearBoxes();
+ mPinHdr.setText(R.string.pincode_enter_pin_code);
+ mPinHdrExplanation.setVisibility(View.INVISIBLE);
+ newPasswordEntered = true;
+ confirmingPinCode = false;
+
+ }
+
+
+ return false;
+ }
+
+ protected void confirmPincode(){
+
+ confirmingPinCode = false;
+
+ String rText1 = mText1.getText().toString();
+ String rText2 = mText2.getText().toString();
+ String rText3 = mText3.getText().toString();
+ String rText4 = mText4.getText().toString();
+
+ if ( tempText[0].equals(rText1) &&
+ tempText[1].equals(rText2) &&
+ tempText[2].equals(rText3) &&
+ tempText[3].equals(rText4) ) {
+
+ savePincodeAndExit();
+
+ } else {
+
+ Arrays.fill(tempText, null);
+ AlertDialog aDialog = new AlertDialog.Builder(this).create();
+ CharSequence errorSeq = getString(R.string.common_error);
+ aDialog.setTitle(errorSeq);
+ CharSequence cseq = getString(R.string.pincode_mismatch);
+ aDialog.setMessage(cseq);
+ CharSequence okSeq = getString(R.string.common_ok);
+ aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ return;
+ }
+
+ });
+ aDialog.show();
+ mPinHdr.setText(R.string.pincode_configure_your_pin);
+ mPinHdrExplanation.setVisibility(View.VISIBLE);
+ clearBoxes();
+ }
+
+ }
+
+
+ protected void pinCodeEnd(boolean state){
+ AlertDialog aDialog = new AlertDialog.Builder(this).create();
+
+ if (state){
+ CharSequence saveSeq = getString(R.string.common_save_exit);
+ aDialog.setTitle(saveSeq);
+ CharSequence cseq = getString(R.string.pincode_stored);
+ aDialog.setMessage(cseq);
+
+ }else{
+ CharSequence saveSeq = getString(R.string.common_save_exit);
+ aDialog.setTitle(saveSeq);
+ CharSequence cseq = getString(R.string.pincode_removed);
+ aDialog.setMessage(cseq);
+
+ }
+ CharSequence okSeq = getString(R.string.common_ok);
+ aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ return;
+ }
+
+ });
+ aDialog.show();
+ }
+
+ protected void savePincodeAndExit(){
+ SharedPreferences.Editor appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+
+ appPrefs.putString("PrefPinCode1", tempText[0]);
+ appPrefs.putString("PrefPinCode2",tempText[1]);
+ appPrefs.putString("PrefPinCode3", tempText[2]);
+ appPrefs.putString("PrefPinCode4", tempText[3]);
+ appPrefs.putBoolean("set_pincode",true);
+ appPrefs.commit();
+
+ pinCodeEnd(true);
+
+
+
+ }
+
+
+ protected void clearBoxes(){
+
+ mText1.setText("");
+ mText2.setText("");
+ mText3.setText("");
+ mText4.setText("");
+ mText1.requestFocus();
+ }
+
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event){
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount()== 0){
+
+ if (activity.equals("preferences")){
+ SharedPreferences.Editor appPrefsE = PreferenceManager
+
+ .getDefaultSharedPreferences(getApplicationContext()).edit();
+
+ SharedPreferences appPrefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ boolean state = appPrefs.getBoolean("set_pincode", false);
+ appPrefsE.putBoolean("set_pincode",!state);
+ appPrefsE.commit();
+ setInitVars();
+ finish();
+ }
+ return true;
+
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+
+
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.util.Vector;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceManager;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.OwnCloudSession;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.DbHandler;
+
+
+/**
+ * An Activity that allows the user to change the application's settings.
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class Preferences extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
+
+ private static final String TAG = "OwnCloudPreferences";
+ private final int mNewSession = 47;
+ private final int mEditSession = 48;
+ private DbHandler mDbHandler;
+ private Vector<OwnCloudSession> mSessions;
+ private ListPreference mTrackingUpdateInterval;
+ private CheckBoxPreference mDeviceTracking;
+ private CheckBoxPreference pCode;
+ //private CheckBoxPreference pLogging;
+ //private Preference pLoggingHistory;
+ private Preference pAboutApp;
+ private int mSelectedMenuItem;
+
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mDbHandler = new DbHandler(getBaseContext());
+ mSessions = new Vector<OwnCloudSession>();
+ addPreferencesFromResource(R.xml.preferences);
+ //populateAccountList();
+ ActionBar actionBar = getSherlock().getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+
+ Preference p = findPreference("manage_account");
+ if (p != null)
+ p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent i = new Intent(getApplicationContext(), AccountSelectActivity.class);
+ startActivity(i);
+ return true;
+ }
+ });
+
+ pCode = (CheckBoxPreference) findPreference("set_pincode");
+ if (pCode != null){
+ pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+ i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "preferences");
+ i.putExtra(PinCodeActivity.EXTRA_NEW_STATE, newValue.toString());
+ startActivity(i);
+
+ return true;
+ }
+ });
+
+ }
+
+
+
+ PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
+
+ boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
+ Preference pHelp = findPreference("help");
+ if (pHelp != null ){
+ if (helpEnabled) {
+ pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String helpWeb =(String) getText(R.string.url_help);
+ if (helpWeb != null && helpWeb.length() > 0) {
+ Uri uriUrl = Uri.parse(helpWeb);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
+ startActivity(intent);
+ }
+ return true;
+ }
+ });
+ } else {
+ preferenceCategory.removePreference(pHelp);
+ }
+
+ }
+
+
+ boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
+ Preference pRecommend = findPreference("recommend");
+ if (pRecommend != null){
+ if (recommendEnabled) {
+ pRecommend.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+
+ Intent intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setType("text/plain");
+ Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
+ String appName = getString(R.string.app_name);
+ String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
+ String recommendSubject = String.format(getString(R.string.recommend_subject), username, appName);
+ //String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
+ intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
+ String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), username);
+ //String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), getString(R.string.url_app_download));
+ intent.putExtra(Intent.EXTRA_TEXT, recommendText);
+
+ intent.setData(Uri.parse(getString(R.string.mail_recommend)));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+
+
+ return(true);
+
+ }
+ });
+ } else {
+ preferenceCategory.removePreference(pRecommend);
+ }
+
+ }
+
+ boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
+ Preference pFeedback = findPreference("feedback");
+ if (pFeedback != null){
+ if (feedbackEnabled) {
+ pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String feedbackMail =(String) getText(R.string.mail_feedback);
+ String feedback =(String) getText(R.string.prefs_feedback);
+ Intent intent = new Intent(Intent.ACTION_SENDTO);
+ intent.setType("text/plain");
+ intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
+
+ intent.setData(Uri.parse(feedbackMail));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+
+ return true;
+ }
+ });
+ } else {
+ preferenceCategory.removePreference(pFeedback);
+ }
+
+ }
+
+ boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled);
+ Preference pImprint = findPreference("imprint");
+ if (pImprint != null) {
+ if (imprintEnabled) {
+ pImprint.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String imprintWeb = (String) getText(R.string.url_imprint);
+ if (imprintWeb != null && imprintWeb.length() > 0) {
+ Uri uriUrl = Uri.parse(imprintWeb);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
+ startActivity(intent);
+ }
+ //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
+ return true;
+ }
+ });
+ } else {
+ preferenceCategory.removePreference(pImprint);
+ }
+ }
+
+ /* About App */
+ pAboutApp = (Preference) findPreference("about_app");
+ if (pAboutApp != null) {
+ pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
+ PackageInfo pkg;
+ try {
+ pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
+ pAboutApp.setSummary(String.format(getString(R.string.about_version), pkg.versionName));
+ } catch (NameNotFoundException e) {
+ Log_OC.e(TAG, "Error while showing about dialog", e);
+ }
+ }
+
+ /* DISABLED FOR RELEASE UNTIL FIXED
+ pLogging = (CheckBoxPreference) findPreference("log_to_file");
+ if (pLogging != null) {
+ pLogging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+
+ String logpath = Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log";
+
+ if(!pLogging.isChecked()) {
+ Log_OC.d("Debug", "start logging");
+ Log_OC.v("PATH", logpath);
+ Log_OC.startLogging(logpath);
+ }
+ else {
+ Log_OC.d("Debug", "stop logging");
+ Log_OC.stopLogging();
+ }
+ return true;
+ }
+ });
+ }
+
+ pLoggingHistory = (Preference) findPreference("log_history");
+ if (pLoggingHistory != null) {
+ pLoggingHistory.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(getApplicationContext(),LogHistoryActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ });
+ }
+ */
+
+ }
+
+ @Override
+ protected void onResume() {
+ SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ boolean state = appPrefs.getBoolean("set_pincode", false);
+ pCode.setChecked(state);
+ super.onResume();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ super.onMenuItemSelected(featureId, item);
+ Intent intent;
+
+ switch (item.getItemId()) {
+ //case R.id.addSessionItem:
+ case 1:
+ 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 android.R.id.home:
+ intent = new Intent(getBaseContext(), FileDisplayActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ break;
+ default:
+ Log_OC.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);
+ }
+
+ @Override
+ protected void onDestroy() {
+ mDbHandler.close();
+ super.onDestroy();
+ }
+
+ @Override
+ /**
+ * Updates various summaries after updates. Also starts and stops
+ * the
+ */
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ // Update current account summary
+ /*if (preference.equals(mAccountList)) {
+ mAccountList.setSummary(newValue.toString());
+ }
+
+ // Update tracking interval summary
+ else*/ if (preference.equals(mTrackingUpdateInterval)) {
+ String trackingSummary = getResources().getString(
+ R.string.prefs_trackmydevice_interval_summary);
+ trackingSummary = String.format(trackingSummary,
+ newValue.toString());
+ mTrackingUpdateInterval.setSummary(trackingSummary);
+ }
+
+ // Start or stop tracking service
+ else if (preference.equals(mDeviceTracking)) {
+ Intent locationServiceIntent = new Intent();
+ locationServiceIntent
+ .setAction("com.owncloud.android.location.LocationLauncher");
+ locationServiceIntent.putExtra("TRACKING_SETTING",
+ (Boolean) newValue);
+ sendBroadcast(locationServiceIntent);
+ }
+ return true;
+ }
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2012 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+\r
+package com.owncloud.android.ui.activity;\r
+\r
+import android.accounts.AccountAuthenticatorActivity;\r
+import android.app.Activity;\r
+import android.os.Bundle;\r
+import android.view.View;\r
+import android.view.View.OnClickListener;\r
+\r
+public class PreferencesNewSession extends AccountAuthenticatorActivity\r
+ implements OnClickListener {\r
+ @Override\r
+ public void onCreate(Bundle savedInstanceState) {\r
+ super.onCreate(savedInstanceState);\r
+ // setContentView(R.layout.add_new_session);\r
+ /*\r
+ * EditText et;// = (EditText)\r
+ * findViewById(R.id.newSession_sessionName);\r
+ * \r
+ * et = (EditText) findViewById(R.id.newSession_URL); if\r
+ * (getIntent().hasExtra("sessionURL")) { try { URI uri = new\r
+ * URI(getIntent().getStringExtra("sessionURL")); String url =\r
+ * uri.getHost(); if (uri.getPort() != -1) { url += ":" +\r
+ * String.valueOf(uri.getPort()); } if (uri.getPath() != null) { url +=\r
+ * uri.getPath(); } else { url += "/"; } et.setText(url); et =\r
+ * (EditText) findViewById(R.id.newSession_username); if\r
+ * (uri.getAuthority() != null) { if (uri.getUserInfo().indexOf(':') !=\r
+ * -1) { et.setText(uri.getUserInfo().substring(0,\r
+ * uri.getUserInfo().indexOf(':'))); et = (EditText)\r
+ * findViewById(R.id.newSession_password);\r
+ * et.setText(uri.getUserInfo().substring\r
+ * (uri.getUserInfo().indexOf(':')+1)); } else {\r
+ * et.setText(uri.getUserInfo()); } }\r
+ * \r
+ * } catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
+ * e.getLocalizedMessage()); } }\r
+ * \r
+ * mReturnData = new Intent(); setResult(Activity.RESULT_OK,\r
+ * mReturnData); ((Button)\r
+ * findViewById(R.id.button1)).setOnClickListener(this); ((Button)\r
+ * findViewById(R.id.button2)).setOnClickListener(this);\r
+ */\r
+ }\r
+\r
+ @Override\r
+ protected void onResume() {\r
+ super.onResume();\r
+ }\r
+\r
+ public void onClick(View v) {\r
+ /*\r
+ * switch (v.getId()) { case R.id.button1: Intent intent = new Intent();\r
+ * if (getIntent().hasExtra("sessionId")) { intent.putExtra("sessionId",\r
+ * getIntent().getIntExtra("sessionId", -1)); } //String sessionName =\r
+ * ((EditText)\r
+ * findViewById(R.id.newSession_sessionName)).getText().toString(); //\r
+ * if (sessionName.trim().equals("") || !isNameValid(sessionName)) { //\r
+ * Toast.makeText(this, R.string.new_session_session_name_error,\r
+ * Toast.LENGTH_LONG).show(); // break; // } URI uri = prepareURI(); if\r
+ * (uri != null) { //intent.putExtra("sessionName", sessionName);\r
+ * intent.putExtra("sessionURL", uri.toString());\r
+ * setResult(Activity.RESULT_OK, intent); AccountManager accMgr =\r
+ * AccountManager.get(this); Account a = new Account("OwnCloud",\r
+ * AccountAuthenticatorService.ACCOUNT_TYPE);\r
+ * accMgr.addAccountExplicitly(a, "asd", null); finish(); } break; case\r
+ * R.id.button2: setResult(Activity.RESULT_CANCELED); finish(); break; }\r
+ */\r
+ }\r
+\r
+ /*\r
+ * private URI prepareURI() { URI uri = null; String url = ""; try { String\r
+ * username = ((EditText)\r
+ * findViewById(R.id.newSession_username)).getText().toString().trim();\r
+ * String password = ((EditText)\r
+ * findViewById(R.id.newSession_password)).getText().toString().trim();\r
+ * String hostname = ((EditText)\r
+ * findViewById(R.id.newSession_URL)).getText().toString().trim(); String\r
+ * scheme; if (hostname.matches("[A-Za-z]://")) { scheme =\r
+ * hostname.substring(0, hostname.indexOf("://")+3); hostname =\r
+ * hostname.substring(hostname.indexOf("://")+3); } else { scheme =\r
+ * "http://"; } if (!username.equals("")) { if (!password.equals("")) {\r
+ * username += ":" + password + "@"; } else { username += "@"; } } url =\r
+ * scheme + username + hostname; Log.i(TAG, url); uri = new URI(url); }\r
+ * catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
+ * e.getLocalizedMessage()); Toast.makeText(this,\r
+ * R.string.new_session_uri_error, Toast.LENGTH_LONG).show(); } return uri;\r
+ * }\r
+ * \r
+ * private boolean isNameValid(String string) { return\r
+ * string.matches("[A-Za-z0-9 _-]*"); }\r
+ */\r
+\r
+ @Override\r
+ public void onBackPressed() {\r
+ setResult(Activity.RESULT_CANCELED);\r
+ super.onBackPressed();\r
+ }\r
+\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+
+public interface TransferServiceGetter {
+
+ /**
+ * Callback method invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
+ *
+ * @return Directory to list firstly. Can be NULL.
+ */
+ public FileDownloaderBinder getFileDownloaderBinder();
+
+
+ /**
+ * Callback method invoked when the parent activity is fully created to get a reference to the FileUploader service API.
+ *
+ * @return Directory to list firstly. Can be NULL.
+ */
+ public FileUploaderBinder getFileUploaderBinder();
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.activity;
+
+import java.io.File;
+
+import android.accounts.Account;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.DialogFragment;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.CustomButton;
+import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.LocalFileListFragment;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.utils.FileStorageUtils;
+
+
+/**
+ * Displays local files and let the user choose what of them wants to upload
+ * to the current ownCloud account
+ *
+ * @author David A. Velasco
+ *
+ */
+
+public class UploadFilesActivity extends FileActivity implements
+ LocalFileListFragment.ContainerActivity, OnNavigationListener, OnClickListener, ConfirmationDialogFragmentListener {
+
+ private ArrayAdapter<String> mDirectories;
+ private File mCurrentDir = null;
+ private LocalFileListFragment mFileListFragment;
+ private CustomButton mCancelBtn;
+ private CustomButton mUploadBtn;
+ private Account mAccountOnCreation;
+ private DialogFragment mCurrentDialog;
+
+ public static final String EXTRA_CHOSEN_FILES = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES";
+
+ public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER;
+
+ private static final String KEY_DIRECTORY_PATH = UploadFilesActivity.class.getCanonicalName() + ".KEY_DIRECTORY_PATH";
+ private static final String TAG = "UploadFilesActivity";
+ private static final String WAIT_DIALOG_TAG = "WAIT";
+ private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreate() start");
+ super.onCreate(savedInstanceState);
+
+ if(savedInstanceState != null) {
+ mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH));
+ } else {
+ mCurrentDir = Environment.getExternalStorageDirectory();
+ }
+
+ mAccountOnCreation = getAccount();
+
+ /// USER INTERFACE
+
+ // Drop-down navigation
+ mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
+ File currDir = mCurrentDir;
+ while(currDir != null && currDir.getParentFile() != null) {
+ mDirectories.add(currDir.getName());
+ currDir = currDir.getParentFile();
+ }
+ mDirectories.add(File.separator);
+
+ // Inflate and set the layout view
+ setContentView(R.layout.upload_files_layout);
+ mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list);
+
+
+ // Set input controllers
+ mCancelBtn = (CustomButton) findViewById(R.id.upload_files_btn_cancel);
+ mCancelBtn.setOnClickListener(this);
+ mUploadBtn = (CustomButton) findViewById(R.id.upload_files_btn_upload);
+ mUploadBtn.setOnClickListener(this);
+
+
+ // Action bar setup
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
+ actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ actionBar.setListNavigationCallbacks(mDirectories, this);
+
+ // wait dialog
+ if (mCurrentDialog != null) {
+ mCurrentDialog.dismiss();
+ mCurrentDialog = null;
+ }
+
+ Log_OC.d(TAG, "onCreate() end");
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean retval = true;
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ if(mCurrentDir != null && mCurrentDir.getParentFile() != null){
+ onBackPressed();
+ }
+ break;
+ }
+ default:
+ retval = super.onOptionsItemSelected(item);
+ }
+ return retval;
+ }
+
+
+ @Override
+ public boolean onNavigationItemSelected(int itemPosition, long itemId) {
+ int i = itemPosition;
+ while (i-- != 0) {
+ onBackPressed();
+ }
+ // the next operation triggers a new call to this method, but it's necessary to
+ // ensure that the name exposed in the action bar is the current directory when the
+ // user selected it in the navigation list
+ if (itemPosition != 0)
+ getSupportActionBar().setSelectedNavigationItem(0);
+ return true;
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (mDirectories.getCount() <= 1) {
+ finish();
+ return;
+ }
+ popDirname();
+ mFileListFragment.onNavigateUp();
+ mCurrentDir = mFileListFragment.getCurrentDirectory();
+
+ if(mCurrentDir.getParentFile() == null){
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ }
+ }
+
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
+ Log_OC.d(TAG, "onSaveInstanceState() start");
+ super.onSaveInstanceState(outState);
+ outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath());
+ Log_OC.d(TAG, "onSaveInstanceState() end");
+ }
+
+
+ /**
+ * Pushes a directory to the drop down list
+ * @param directory to push
+ * @throws IllegalArgumentException If the {@link File#isDirectory()} returns false.
+ */
+ public void pushDirname(File directory) {
+ if(!directory.isDirectory()){
+ throw new IllegalArgumentException("Only directories may be pushed!");
+ }
+ mDirectories.insert(directory.getName(), 0);
+ mCurrentDir = directory;
+ }
+
+ /**
+ * Pops a directory name from the drop down list
+ * @return True, unless the stack is empty
+ */
+ public boolean popDirname() {
+ mDirectories.remove(mDirectories.getItem(0));
+ return !mDirectories.isEmpty();
+ }
+
+
+ // Custom array adapter to override text colors
+ private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
+
+ public CustomArrayAdapter(UploadFilesActivity ctx, int view) {
+ super(ctx, view);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = super.getView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+ return v;
+ }
+
+ public View getDropDownView(int position, View convertView,
+ ViewGroup parent) {
+ View v = super.getDropDownView(position, convertView, parent);
+
+ ((TextView) v).setTextColor(getResources().getColorStateList(
+ android.R.color.white));
+
+ return v;
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDirectoryClick(File directory) {
+ pushDirname(directory);
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFileClick(File file) {
+ // nothing to do
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public File getInitialDirectory() {
+ return mCurrentDir;
+ }
+
+
+ /**
+ * Performs corresponding action when user presses 'Cancel' or 'Upload' button
+ *
+ * TODO Make here the real request to the Upload service ; will require to receive the account and
+ * target folder where the upload must be done in the received intent.
+ */
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.upload_files_btn_cancel) {
+ setResult(RESULT_CANCELED);
+ finish();
+
+ } else if (v.getId() == R.id.upload_files_btn_upload) {
+ new CheckAvailableSpaceTask().execute();
+ }
+ }
+
+
+ /**
+ * Asynchronous task checking if there is space enough to copy all the files chosen
+ * to upload into the ownCloud local folder.
+ *
+ * Maybe an AsyncTask is not strictly necessary, but who really knows.
+ *
+ * @author David A. Velasco
+ */
+ private class CheckAvailableSpaceTask extends AsyncTask<Void, Void, Boolean> {
+
+ /**
+ * Updates the UI before trying the movement
+ */
+ @Override
+ protected void onPreExecute () {
+ /// progress dialog and disable 'Move' button
+ mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
+ mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
+ }
+
+
+ /**
+ * Checks the available space
+ *
+ * @return 'True' if there is space enough.
+ */
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ String[] checkedFilePaths = mFileListFragment.getCheckedFilePaths();
+ long total = 0;
+ for (int i=0; checkedFilePaths != null && i < checkedFilePaths.length ; i++) {
+ String localPath = checkedFilePaths[i];
+ File localFile = new File(localPath);
+ total += localFile.length();
+ }
+ return (FileStorageUtils.getUsableSpace(mAccountOnCreation.name) >= total);
+ }
+
+ /**
+ * Updates the activity UI after the check of space is done.
+ *
+ * If there is not space enough. shows a new dialog to query the user if wants to move the files instead
+ * of copy them.
+ *
+ * @param result 'True' when there is space enough to copy all the selected files.
+ */
+ @Override
+ protected void onPostExecute(Boolean result) {
+ mCurrentDialog.dismiss();
+ mCurrentDialog = null;
+
+ if (result) {
+ // return the list of selected files (success)
+ Intent data = new Intent();
+ data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
+ setResult(RESULT_OK, data);
+ finish();
+
+ } else {
+ // show a dialog to query the user if wants to move the selected files to the ownCloud folder instead of copying
+ String[] args = {getString(R.string.app_name)};
+ ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1, R.string.common_no);
+ dialog.setOnConfirmationListener(UploadFilesActivity.this);
+ dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
+ }
+ }
+ }
+
+ @Override
+ public void onConfirmation(String callerTag) {
+ Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
+ if (callerTag.equals(QUERY_TO_MOVE_DIALOG_TAG)) {
+ // return the list of selected files to the caller activity (success), signaling that they should be moved to the ownCloud folder, instead of copied
+ Intent data = new Intent();
+ data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
+ setResult(RESULT_OK_AND_MOVE, data);
+ finish();
+ }
+ }
+
+
+ @Override
+ public void onNeutral(String callerTag) {
+ Log_OC.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag);
+ }
+
+
+ @Override
+ public void onCancel(String callerTag) {
+ /// nothing to do; don't finish, let the user change the selection
+ Log_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
+ }
+
+
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ if (!mAccountOnCreation.equals(getAccount())) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui.adapter;\r
+\r
+import android.accounts.Account;\r
+import android.content.Context;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.BaseAdapter;\r
+import android.widget.ImageView;\r
+import android.widget.ListAdapter;\r
+import android.widget.ListView;\r
+import android.widget.TextView;\r
+\r
+\r
+import java.util.Vector;\r
+\r
+import com.owncloud.android.DisplayUtils;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.datamodel.DataStorageManager;\r
+import com.owncloud.android.datamodel.OCFile;\r
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;\r
+import com.owncloud.android.ui.activity.TransferServiceGetter;\r
+\r
+\r
+/**\r
+ * This Adapter populates a ListView with all files and folders in an ownCloud\r
+ * instance.\r
+ * \r
+ * @author Bartek Przybylski\r
+ * \r
+ */\r
+public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
+ private Context mContext;\r
+ private OCFile mFile = null;\r
+ private Vector<OCFile> mFiles = null;\r
+ private DataStorageManager mStorageManager;\r
+ private Account mAccount;\r
+ private TransferServiceGetter mTransferServiceGetter;\r
+ \r
+ public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) {\r
+ mContext = context;\r
+ mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
+ mTransferServiceGetter = transferServiceGetter;\r
+ }\r
+\r
+ @Override\r
+ public boolean areAllItemsEnabled() {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public boolean isEnabled(int position) {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public int getCount() {\r
+ return mFiles != null ? mFiles.size() : 0;\r
+ }\r
+\r
+ @Override\r
+ public Object getItem(int position) {\r
+ if (mFiles == null || mFiles.size() <= position)\r
+ return null;\r
+ return mFiles.get(position);\r
+ }\r
+\r
+ @Override\r
+ public long getItemId(int position) {\r
+ if (mFiles == null || mFiles.size() <= position)\r
+ return 0;\r
+ return mFiles.get(position).getFileId();\r
+ }\r
+\r
+ @Override\r
+ public int getItemViewType(int position) {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public View getView(int position, View convertView, ViewGroup parent) {\r
+ View view = convertView;\r
+ if (view == null) {\r
+ LayoutInflater inflator = (LayoutInflater) mContext\r
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
+ view = inflator.inflate(R.layout.list_item, null);\r
+ }\r
+ \r
+ if (mFiles != null && mFiles.size() > position) {\r
+ OCFile file = mFiles.get(position);\r
+ TextView fileName = (TextView) view.findViewById(R.id.Filename);\r
+ String name = file.getFileName();\r
+\r
+ fileName.setText(name);\r
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);\r
+ fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype()));\r
+ ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
+ FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
+ FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
+ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
+ localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
+ localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else if (file.isDown()) {\r
+ localStateView.setImageResource(R.drawable.local_file_indicator);\r
+ localStateView.setVisibility(View.VISIBLE);\r
+ } else {\r
+ localStateView.setVisibility(View.INVISIBLE);\r
+ }\r
+ \r
+ TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
+ TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
+ ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
+ \r
+ if (!file.isDirectory()) {\r
+ fileSizeV.setVisibility(View.VISIBLE);\r
+ fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+ lastModV.setVisibility(View.VISIBLE);\r
+ lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
+ // this if-else is needed even thoe fav icon is visible by default\r
+ // because android reuses views in listview\r
+ if (!file.keepInSync()) {\r
+ view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+ } else {\r
+ view.findViewById(R.id.imageView3).setVisibility(View.VISIBLE);\r
+ }\r
+ \r
+ ListView parentList = (ListView)parent;\r
+ if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { \r
+ checkBoxV.setVisibility(View.GONE);\r
+ } else {\r
+ if (parentList.isItemChecked(position)) {\r
+ checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);\r
+ } else {\r
+ checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
+ }\r
+ checkBoxV.setVisibility(View.VISIBLE);\r
+ }\r
+ \r
+ } \r
+ else {\r
+ \r
+ fileSizeV.setVisibility(View.VISIBLE);\r
+ fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+ lastModV.setVisibility(View.VISIBLE);\r
+ lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
+ checkBoxV.setVisibility(View.GONE);\r
+ view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+ }\r
+ }\r
+\r
+ return view;\r
+ }\r
+\r
+ @Override\r
+ public int getViewTypeCount() {\r
+ return 1;\r
+ }\r
+\r
+ @Override\r
+ public boolean hasStableIds() {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public boolean isEmpty() {\r
+ return (mFiles == null || mFiles.isEmpty());\r
+ }\r
+\r
+ /**\r
+ * Change the adapted directory for a new one\r
+ * @param directory New file to adapt. Can be NULL, meaning "no content to adapt".\r
+ * @param updatedStorageManager Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
+ */\r
+ public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {\r
+ mFile = directory;\r
+ if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
+ mStorageManager = updatedStorageManager;\r
+ mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
+ }\r
+ if (mStorageManager != null) {\r
+ mFiles = mStorageManager.getDirectoryContent(mFile);\r
+ } else {\r
+ mFiles = null;\r
+ }\r
+ notifyDataSetChanged();\r
+ }\r
+ \r
+}\r
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui.adapter;\r
+\r
+\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.authentication.AccountUtils;\r
+import com.owncloud.android.ui.activity.FileDisplayActivity;\r
+import com.owncloud.android.ui.activity.Preferences;\r
+\r
+import android.content.Context;\r
+import android.content.Intent;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.BaseAdapter;\r
+import android.widget.ImageView;\r
+import android.widget.TextView;\r
+\r
+/**\r
+ * Populates the landing screen icons.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingScreenAdapter extends BaseAdapter {\r
+\r
+ private Context mContext;\r
+\r
+ private final Integer[] mLandingScreenIcons = { R.drawable.home,\r
+ R.drawable.music, R.drawable.contacts, R.drawable.calendar,\r
+ android.R.drawable.ic_menu_agenda, R.drawable.settings };\r
+\r
+ private final Integer[] mLandingScreenTexts = { R.string.main_files,\r
+ R.string.main_music, R.string.main_contacts,\r
+ R.string.main_calendar, R.string.main_bookmarks,\r
+ R.string.main_settings };\r
+\r
+ public LandingScreenAdapter(Context context) {\r
+ mContext = context;\r
+ }\r
+\r
+ @Override\r
+ public int getCount() {\r
+ return mLandingScreenIcons.length;\r
+ }\r
+\r
+ @Override\r
+ /**\r
+ * Returns the Intent associated with this object\r
+ * or null if the functionality is not yet implemented\r
+ */\r
+ public Object getItem(int position) {\r
+ Intent intent = new Intent();\r
+\r
+ switch (position) {\r
+ case 0:\r
+ /*\r
+ * The FileDisplayActivity requires the ownCloud account as an\r
+ * parcableExtra. We will put in the one that is selected in the\r
+ * preferences\r
+ */\r
+ intent.setClass(mContext, FileDisplayActivity.class);\r
+ intent.putExtra("ACCOUNT",\r
+ AccountUtils.getCurrentOwnCloudAccount(mContext));\r
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
+ break;\r
+ case 5:\r
+ intent.setClass(mContext, Preferences.class);\r
+ break;\r
+ default:\r
+ intent = null;\r
+ }\r
+ return intent;\r
+ }\r
+\r
+ @Override\r
+ public long getItemId(int position) {\r
+ return position;\r
+ }\r
+\r
+ @Override\r
+ public View getView(int position, View convertView, ViewGroup parent) {\r
+ if (convertView == null) {\r
+ LayoutInflater inflator = LayoutInflater.from(mContext);\r
+ convertView = inflator.inflate(R.layout.landing_page_item, null);\r
+\r
+ ImageView icon = (ImageView) convertView\r
+ .findViewById(R.id.gridImage);\r
+ TextView iconText = (TextView) convertView\r
+ .findViewById(R.id.gridText);\r
+\r
+ icon.setImageResource(mLandingScreenIcons[position]);\r
+ iconText.setText(mLandingScreenTexts[position]);\r
+ }\r
+ return convertView;\r
+ }\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.adapter;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import com.owncloud.android.DisplayUtils;
+import com.owncloud.android.R;
+
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * This Adapter populates a ListView with all files and directories contained
+ * in a local directory
+ *
+ * @author David A. Velasco
+ *
+ */
+public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
+
+ private Context mContext;
+ private File mDirectory;
+ private File[] mFiles = null;
+
+ public LocalFileListAdapter(File directory, Context context) {
+ mContext = context;
+ swapDirectory(directory);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ return mFiles != null ? mFiles.length : 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (mFiles == null || mFiles.length <= position)
+ return null;
+ return mFiles[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mFiles != null && mFiles.length <= position ? position : -1;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = convertView;
+ if (view == null) {
+ LayoutInflater inflator = (LayoutInflater) mContext
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ view = inflator.inflate(R.layout.list_item, null);
+ }
+ if (mFiles != null && mFiles.length > position) {
+ File file = mFiles[position];
+
+ TextView fileName = (TextView) view.findViewById(R.id.Filename);
+ String name = file.getName();
+ fileName.setText(name);
+
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
+ if (!file.isDirectory()) {
+ fileIcon.setImageResource(R.drawable.file);
+ } else {
+ fileIcon.setImageResource(R.drawable.ic_menu_archive);
+ }
+
+ TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
+ TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
+ ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
+ if (!file.isDirectory()) {
+ fileSizeV.setVisibility(View.VISIBLE);
+ fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
+ lastModV.setVisibility(View.VISIBLE);
+ lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.lastModified()));
+ ListView parentList = (ListView)parent;
+ if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) {
+ checkBoxV.setVisibility(View.GONE);
+ } else {
+ if (parentList.isItemChecked(position)) {
+ checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
+ } else {
+ checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
+ }
+ checkBoxV.setVisibility(View.VISIBLE);
+ }
+
+ } else {
+ fileSizeV.setVisibility(View.GONE);
+ lastModV.setVisibility(View.GONE);
+ checkBoxV.setVisibility(View.GONE);
+ }
+
+ view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE); // not GONE; the alignment changes; ugly way to keep it
+ view.findViewById(R.id.imageView3).setVisibility(View.GONE);
+ }
+
+ return view;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return false;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (mFiles == null || mFiles.length == 0);
+ }
+
+ /**
+ * Change the adapted directory for a new one
+ * @param directory New file to adapt. Can be NULL, meaning "no content to adapt".
+ */
+ public void swapDirectory(File directory) {
+ mDirectory = directory;
+ mFiles = (mDirectory != null ? mDirectory.listFiles() : null);
+ if (mFiles != null) {
+ Arrays.sort(mFiles, new Comparator<File>() {
+ @Override
+ public int compare(File lhs, File rhs) {
+ if (lhs.isDirectory() && !rhs.isDirectory()) {
+ return -1;
+ } else if (!lhs.isDirectory() && rhs.isDirectory()) {
+ return 1;
+ }
+ return compareNames(lhs, rhs);
+ }
+
+ private int compareNames(File lhs, File rhs) {
+ return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());
+ }
+
+ });
+ }
+ notifyDataSetChanged();
+ }
+}
--- /dev/null
+package com.owncloud.android.ui.adapter;
+
+import java.io.File;
+
+import com.owncloud.android.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Environment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+
+
+public class LogListAdapter extends ArrayAdapter<String> {
+ private Context context = null;
+ private String[] values;
+ private Uri fileUri = null;
+
+
+ public LogListAdapter(Context context, String[] values) {
+ super(context, R.layout.log_item, values);
+ this.context = context;
+ this.values = values;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View rowView = inflater.inflate(R.layout.log_item, parent, false);
+ TextView listText = (TextView) rowView.findViewById(R.id.log_item_single);
+ listText.setText(values[position]);
+ listText.setTextSize(15);
+ fileUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log"+File.separator+values[position]));
+ listText.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+ emailIntent.setType("text/rtf");
+ emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "OwnCloud Logfile");
+ emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "This is a automatic E-mail send by owncloud/android");
+ emailIntent.putExtra(android.content.Intent.EXTRA_STREAM, fileUri);
+ emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
+ }
+ });
+ return rowView;
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.webkit.WebView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+
+
+/**
+ * Dialog to show the contents of res/raw/CHANGELOG.txt
+ */
+public class ChangelogDialog extends SherlockDialogFragment {
+
+ private static final String ARG_CANCELABLE = ChangelogDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
+
+
+ /**
+ * Public factory method to get dialog instances.
+ *
+ * @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
+ * @return New dialog instance, ready to show.
+ */
+ public static ChangelogDialog newInstance(boolean cancelable) {
+ ChangelogDialog fragment = new ChangelogDialog();
+ Bundle args = new Bundle();
+ args.putBoolean(ARG_CANCELABLE, cancelable);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ /// load the custom view to insert in the dialog, between title and
+ WebView webview = new WebView(getActivity());
+ webview.loadUrl("file:///android_res/raw/" + getResources().getResourceEntryName(R.raw.changelog) + ".html");
+
+ /// build the dialog
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ Dialog dialog = builder.setView(webview)
+ .setIcon(R.drawable.icon)
+ //.setTitle(R.string.whats_new)
+ .setPositiveButton(R.string.common_ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ }).create();
+
+ dialog.setCancelable(getArguments().getBoolean(ARG_CANCELABLE));
+ return dialog;
+ }
+
+ /**
+ * {@inheritDoc}
+ *-/
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ /// load the custom layout
+ View view = inflater.inflate(R.layout.fragment_changelog, container);
+ mEditText = (EditText) view.findViewById(R.id.txt_your_name);
+ getDialog().setTitle(R.string.whats_new);
+
+ /// read full contents of the change log file (don't make it too big)
+ InputStream changeLogStream = getResources().openRawResource(R.raw.changelog);
+ Scanner scanner = new java.util.Scanner(changeLogStream).useDelimiter("\\A");
+ String text = scanner.hasNext() ? scanner.next() : "";
+
+ /// make clickable the links in the change log file
+ SpannableString sText = new SpannableString(text);
+ Linkify.addLinks(sText, Linkify.ALL);
+
+ return view;
+ }
+ */
+}
+
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.owncloud.android.R;
+
+
+/**
+ * Dialog which will be displayed to user upon keep-in-sync file conflict.
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class ConflictsResolveDialog extends SherlockDialogFragment {
+
+ public static enum Decision {
+ CANCEL,
+ KEEP_BOTH,
+ OVERWRITE
+ }
+
+ OnConflictDecisionMadeListener mListener;
+
+ public static ConflictsResolveDialog newInstance(String path, OnConflictDecisionMadeListener listener) {
+ ConflictsResolveDialog f = new ConflictsResolveDialog();
+ Bundle args = new Bundle();
+ args.putString("remotepath", path);
+ f.setArguments(args);
+ f.mListener = listener;
+ return f;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String remotepath = getArguments().getString("remotepath");
+ return new AlertDialog.Builder(getSherlockActivity())
+ .setIcon(R.drawable.icon)
+ .setTitle(R.string.conflict_title)
+ .setMessage(String.format(getString(R.string.conflict_message), remotepath))
+ .setPositiveButton(R.string.conflict_overwrite,
+ new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mListener != null)
+ mListener.ConflictDecisionMade(Decision.OVERWRITE);
+ }
+ })
+ .setNeutralButton(R.string.conflict_keep_both,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mListener != null)
+ mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
+ }
+ })
+ .setNegativeButton(R.string.conflict_dont_upload,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mListener != null)
+ mListener.ConflictDecisionMade(Decision.CANCEL);
+ }
+ })
+ .create();
+ }
+
+ public void showDialog(SherlockFragmentActivity activity) {
+ Fragment prev = activity.getSupportFragmentManager().findFragmentByTag("dialog");
+ FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+
+ this.show(ft, "dialog");
+ }
+
+ public void dismissDialog(SherlockFragmentActivity activity) {
+ Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(getTag());
+ if (prev != null) {
+ FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
+ ft.remove(prev);
+ ft.commit();
+ }
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mListener.ConflictDecisionMade(Decision.CANCEL);
+ }
+
+ public interface OnConflictDecisionMadeListener {
+ public void ConflictDecisionMade(Decision decision);
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager.LayoutParams;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+
+
+
+/**
+ * Dialog to request the user to input a name, optionally initialized with a former name.
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class EditNameDialog extends SherlockDialogFragment implements DialogInterface.OnClickListener {
+
+ public static final String TAG = EditNameDialog.class.getSimpleName();
+
+ protected static final String ARG_TITLE = "TITLE";
+ protected static final String ARG_NAME = "NAME";
+ protected static final String ARG_SELECTION_START = "SELECTION_START";
+ protected static final String ARG_SELECTION_END = "SELECTION_END";
+
+ private String mNewFilename;
+ private boolean mResult;
+ private EditNameDialogListener mListener;
+
+ /**
+ * Public factory method to get dialog instances.
+ *
+ * @param title Text to show as title in the dialog.
+ * @param name Optional text to include in the text input field when the dialog is shown.
+ * @param listener Instance to notify when the dialog is dismissed.
+ * @param selectionStart Index to the first character to be selected in the input field; negative value for none
+ * @param selectionEnd Index to the last character to be selected in the input field; negative value for none
+ * @return New dialog instance, ready to show.
+ */
+ static public EditNameDialog newInstance(String title, String name, int selectionStart, int selectionEnd, EditNameDialogListener listener) {
+ EditNameDialog f = new EditNameDialog();
+ Bundle args = new Bundle();
+ args.putString(ARG_TITLE, title);
+ args.putString(ARG_NAME, name);
+ args.putInt(ARG_SELECTION_START, selectionStart);
+ args.putInt(ARG_SELECTION_END, selectionEnd);
+ f.setArguments(args);
+ f.setOnDismissListener(listener);
+ return f;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String currentName = getArguments().getString(ARG_NAME);
+ if (currentName == null)
+ currentName = "";
+ String title = getArguments().getString(ARG_TITLE);
+
+ // Inflate the layout for the dialog
+ LayoutInflater inflater = getSherlockActivity().getLayoutInflater();
+ View v = inflater.inflate(R.layout.edit_box_dialog, null); // null parent view because it will go in the dialog layout
+ EditText inputText = ((EditText)v.findViewById(R.id.user_input));
+ inputText.setText(currentName);
+
+ // Set it to the dialog
+ AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
+ builder.setView(v)
+ .setPositiveButton(R.string.common_ok, this)
+ .setNegativeButton(R.string.common_cancel, this);
+
+ if (title != null) {
+ builder.setTitle(title);
+ }
+
+ mResult = false;
+
+ Dialog d = builder.create();
+
+ inputText.requestFocus();
+ int selectionStart = getArguments().getInt(ARG_SELECTION_START, -1);
+ int selectionEnd = getArguments().getInt(ARG_SELECTION_END, -1);
+ if (selectionStart >= 0 && selectionEnd >= 0) {
+ inputText.setSelection(Math.min(selectionStart, selectionEnd), Math.max(selectionStart, selectionEnd));
+ }
+ d.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ return d;
+ }
+
+
+ /**
+ * Performs the corresponding action when a dialog button is clicked.
+ *
+ * Saves the text in the input field to be accessed through {@link #getNewFilename()} when the positive
+ * button is clicked.
+ *
+ * Notify the current listener in any case.
+ */
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case AlertDialog.BUTTON_POSITIVE: {
+ mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString();
+ mResult = true;
+ }
+ case AlertDialog.BUTTON_NEGATIVE: { // fall through
+ dismiss();
+ if (mListener != null)
+ mListener.onDismiss(this);
+ }
+ }
+ }
+
+ protected void setOnDismissListener(EditNameDialogListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Returns the text in the input field after the user clicked the positive button.
+ *
+ * @return Text in the input field.
+ */
+ public String getNewFilename() {
+ return mNewFilename;
+ }
+
+ /**
+ *
+ * @return True when the user clicked the positive button.
+ */
+ public boolean getResult() {
+ return mResult;
+ }
+
+
+ /**
+ * Interface to receive a notification when any button in the dialog is clicked.
+ */
+ public interface EditNameDialogListener {
+ public void onDismiss(EditNameDialog dialog);
+ }
+
+
+}
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnKeyListener;
+import android.os.Bundle;
+import android.view.KeyEvent;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+
+
+public class IndeterminateProgressDialog extends SherlockDialogFragment {
+
+ private static final String ARG_MESSAGE_ID = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_MESSAGE_ID";
+ private static final String ARG_CANCELABLE = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
+
+
+ /**
+ * Public factory method to get dialog instances.
+ *
+ * @param messageId Resource id for a message to show in the dialog.
+ * @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
+ * @return New dialog instance, ready to show.
+ */
+ public static IndeterminateProgressDialog newInstance(int messageId, boolean cancelable) {
+ IndeterminateProgressDialog fragment = new IndeterminateProgressDialog();
+ Bundle args = new Bundle();
+ args.putInt(ARG_MESSAGE_ID, messageId);
+ args.putBoolean(ARG_CANCELABLE, cancelable);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ /// create indeterminate progress dialog
+ final ProgressDialog dialog = new ProgressDialog(getActivity());
+ dialog.setIndeterminate(true);
+
+ /// set message
+ int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.placeholder_sentence);
+ dialog.setMessage(getString(messageId));
+
+ /// set cancellation behavior
+ boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE, false);
+ if (!cancelable) {
+ dialog.setCancelable(false);
+ // disable the back button
+ OnKeyListener keyListener = new OnKeyListener() {
+ @Override
+ public boolean onKey(DialogInterface dialog, int keyCode,
+ KeyEvent event) {
+
+ if( keyCode == KeyEvent.KEYCODE_BACK){
+ return true;
+ }
+ return false;
+ }
+
+ };
+ dialog.setOnKeyListener(keyListener);
+ }
+
+ return dialog;
+ }
+
+}
+
+
--- /dev/null
+package com.owncloud.android.ui.dialog;
+
+import com.owncloud.android.R;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.TextView;
+
+public class LoadingDialog extends DialogFragment {
+
+ private String mMessage;
+
+ public LoadingDialog() {
+ super();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setCancelable(false);
+ }
+
+ public LoadingDialog(String message) {
+ this.mMessage = message;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ // Create a view by inflating desired layout
+ View v = inflater.inflate(R.layout.loading_dialog, container, false);
+
+ // set value
+ TextView tv = (TextView) v.findViewById(R.id.loadingText);
+ tv.setText(mMessage);
+
+ return v;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ return dialog;
+ }
+
+ @Override
+ public void onDestroyView() {
+ if (getDialog() != null && getRetainInstance())
+ getDialog().setDismissMessage(null);
+ super.onDestroyView();
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.app.FragmentManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.CookieManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebBackForwardList;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.SsoWebViewClient;
+import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
+
+
+import eu.alefzero.webdav.WebdavClient;
+
+/**
+ * Dialog to show the WebView for SAML Authentication
+ *
+ * @author Maria Asensio
+ * @author David A. Velasco
+ */
+public class SamlWebViewDialog extends SherlockDialogFragment {
+
+ public final String SAML_DIALOG_TAG = "SamlWebViewDialog";
+
+ private final static String TAG = SamlWebViewDialog.class.getSimpleName();
+
+ private static final String ARG_INITIAL_URL = "INITIAL_URL";
+ private static final String ARG_TARGET_URL = "TARGET_URL";
+ private static final String KEY_WEBVIEW_STATE = "WEBVIEW_STATE";
+
+ private WebView mSsoWebView;
+ private SsoWebViewClient mWebViewClient;
+
+ private String mInitialUrl;
+ private String mTargetUrl;
+
+ private Handler mHandler;
+
+ private SsoWebViewClientListener mSsoWebViewClientListener;
+
+ //private View mSsoRootView;
+
+
+ /**
+ * Public factory method to get dialog instances.
+ *
+ * @param handler
+ * @param Url Url to open at WebView
+ * @param targetURL mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mCurrentAuthTokenType)
+ * @return New dialog instance, ready to show.
+ */
+ public static SamlWebViewDialog newInstance(String url, String targetUrl) {
+ Log_OC.d(TAG, "New instance");
+ SamlWebViewDialog fragment = new SamlWebViewDialog();
+ Bundle args = new Bundle();
+ args.putString(ARG_INITIAL_URL, url);
+ args.putString(ARG_TARGET_URL, targetUrl);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+
+ public SamlWebViewDialog() {
+ super();
+ Log_OC.d(TAG, "constructor");
+ }
+
+
+ @Override
+ public void onAttach(Activity activity) {
+ Log_OC.d(TAG, "onAttach");
+ super.onAttach(activity);
+ try {
+ mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
+ mHandler = new Handler();
+ mWebViewClient = new SsoWebViewClient(mHandler, mSsoWebViewClientListener);
+
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " + SsoWebViewClientListener.class.getSimpleName());
+ }
+ }
+
+
+ @SuppressLint("SetJavaScriptEnabled")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ CookieSyncManager.createInstance(getActivity());
+
+ if (savedInstanceState == null) {
+ mInitialUrl = getArguments().getString(ARG_INITIAL_URL);
+ mTargetUrl = getArguments().getString(ARG_TARGET_URL);
+ } else {
+ mInitialUrl = savedInstanceState.getString(ARG_INITIAL_URL);
+ mTargetUrl = savedInstanceState.getString(ARG_TARGET_URL);
+ }
+
+ setStyle(SherlockDialogFragment.STYLE_NO_TITLE, R.style.Theme_ownCloud_Dialog);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreateDialog");
+
+ /*
+ // build the dialog
+ AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
+ if (mSsoRootView.getParent() != null) {
+ ((ViewGroup)(mSsoRootView.getParent())).removeView(mSsoRootView);
+ }
+ builder.setView(mSsoRootView);
+ //builder.setView(mSsoWebView);
+ Dialog dialog = builder.create();
+ */
+
+ return super.onCreateDialog(savedInstanceState);
+ }
+
+ @SuppressLint("SetJavaScriptEnabled")
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Log_OC.d(TAG, "onCreateView");
+
+ // Inflate layout of the dialog
+ View rootView = inflater.inflate(R.layout.sso_dialog, container, false); // null parent view because it will go in the dialog layout
+ mSsoWebView = (WebView) rootView.findViewById(R.id.sso_webview);
+
+ mWebViewClient.setTargetUrl(mTargetUrl);
+ mSsoWebView.setWebViewClient(mWebViewClient);
+
+ if (savedInstanceState == null) {
+ Log_OC.d(TAG, " initWebView start");
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.setAcceptCookie(true);
+ cookieManager.removeAllCookie();
+ mSsoWebView.loadUrl(mInitialUrl);
+
+ } else {
+ Log_OC.d(TAG, " restoreWebView start");
+ WebBackForwardList history = mSsoWebView.restoreState(savedInstanceState.getBundle(KEY_WEBVIEW_STATE));
+ if (history == null) {
+ Log_OC.e(TAG, "Error restoring WebView state ; back to starting URL");
+ mSsoWebView.loadUrl(mInitialUrl);
+ }
+ }
+
+ WebSettings webSettings = mSsoWebView.getSettings();
+ webSettings.setJavaScriptEnabled(true);
+ webSettings.setBuiltInZoomControls(true);
+ webSettings.setLoadWithOverviewMode(false);
+ webSettings.setSavePassword(false);
+ webSettings.setUserAgentString(WebdavClient.USER_AGENT);
+ webSettings.setSaveFormData(false);
+
+ return rootView;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ Log_OC.d(SAML_DIALOG_TAG, "onSaveInstanceState being CALLED");
+ super.onSaveInstanceState(outState);
+
+ // save URLs
+ outState.putString(ARG_INITIAL_URL, mInitialUrl);
+ outState.putString(ARG_TARGET_URL, mTargetUrl);
+
+ // Save the state of the WebView
+ Bundle webviewState = new Bundle();
+ mSsoWebView.saveState(webviewState);
+ outState.putBundle(KEY_WEBVIEW_STATE, webviewState);
+ }
+
+ @Override
+ public void onDestroyView() {
+ Log_OC.d(TAG, "onDestroyView");
+
+ mSsoWebView.setWebViewClient(null);
+
+ // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
+ Dialog dialog = getDialog();
+ if ((dialog != null)) {
+ dialog.setOnDismissListener(null);
+ //dialog.dismiss();
+ //dialog.setDismissMessage(null);
+ }
+
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onDestroy() {
+ Log_OC.d(TAG, "onDestroy");
+ super.onDestroy();
+ }
+
+ @Override
+ public void onDetach() {
+ Log_OC.d(TAG, "onDetach");
+ mSsoWebViewClientListener = null;
+ mWebViewClient = null;
+ super.onDetach();
+ }
+
+ @Override
+ public void onCancel (DialogInterface dialog) {
+ Log_OC.d(SAML_DIALOG_TAG, "onCancel");
+ super.onCancel(dialog);
+ }
+
+ @Override
+ public void onDismiss (DialogInterface dialog) {
+ Log_OC.d(SAML_DIALOG_TAG, "onDismiss");
+ super.onDismiss(dialog);
+ }
+
+ @Override
+ public void onStart() {
+ Log_OC.d(SAML_DIALOG_TAG, "onStart");
+ super.onStart();
+ }
+
+ @Override
+ public void onStop() {
+ Log_OC.d(SAML_DIALOG_TAG, "onStop");
+ super.onStop();
+ }
+
+ @Override
+ public void onResume() {
+ Log_OC.d(SAML_DIALOG_TAG, "onResume");
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ Log_OC.d(SAML_DIALOG_TAG, "onPause");
+ super.onPause();
+ }
+
+ @Override
+ public int show (FragmentTransaction transaction, String tag) {
+ Log_OC.d(SAML_DIALOG_TAG, "show (transaction)");
+ return super.show(transaction, tag);
+ }
+
+ @Override
+ public void show (FragmentManager manager, String tag) {
+ Log_OC.d(SAML_DIALOG_TAG, "show (manager)");
+ super.show(manager, tag);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.x500.X500Principal;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.network.CertificateCombinedException;
+import com.owncloud.android.network.OwnCloudClientUtils;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.ui.CustomButton;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+
+
+/**
+ * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
+ *
+ * @author David A. Velasco
+ */
+public class SslValidatorDialog extends Dialog {
+
+ private final static String TAG = SslValidatorDialog.class.getSimpleName();
+
+ private OnSslValidatorListener mListener;
+ private CertificateCombinedException mException = null;
+ private View mView;
+
+
+ /**
+ * Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
+ * be trusted.
+ *
+ * @param context Android context where the dialog will live.
+ * @param result Result of a failed remote operation.
+ * @param listener Object to notice when the server certificate was added to the local certificates store.
+ * @return A new SslValidatorDialog instance. NULL if the operation can not be recovered
+ * by setting the certificate as reliable.
+ */
+ public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
+ if (result != null && result.isSslRecoverableException()) {
+ SslValidatorDialog dialog = new SslValidatorDialog(context, listener);
+ return dialog;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Private constructor.
+ *
+ * Instances have to be created through static {@link SslValidatorDialog#newInstance}.
+ *
+ * @param context Android context where the dialog will live
+ * @param e Exception causing the need of prompt the user about the server certificate.
+ * @param listener Object to notice when the server certificate was added to the local certificates store.
+ */
+ private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
+ super(context);
+ mListener = listener;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mView = getLayoutInflater().inflate(R.layout.ssl_validator_layout, null);
+ setContentView(mView);
+
+ mView.findViewById(R.id.ok).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ saveServerCert();
+ dismiss();
+ if (mListener != null)
+ mListener.onSavedCertificate();
+ else
+ Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
+
+ } catch (GeneralSecurityException e) {
+ dismiss();
+ if (mListener != null)
+ mListener.onFailedSavingCertificate();
+ Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
+
+ } catch (IOException e) {
+ dismiss();
+ if (mListener != null)
+ mListener.onFailedSavingCertificate();
+ Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
+ }
+ }
+ });
+
+ mView.findViewById(R.id.cancel).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ cancel();
+ }
+ });
+
+ mView.findViewById(R.id.details_btn).setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ View detailsScroll = findViewById(R.id.details_scroll);
+ if (detailsScroll.getVisibility() == View.VISIBLE) {
+ detailsScroll.setVisibility(View.GONE);
+ ((CustomButton)v).setText(R.string.ssl_validator_btn_details_see);
+
+ } else {
+ detailsScroll.setVisibility(View.VISIBLE);
+ ((CustomButton)v).setText(R.string.ssl_validator_btn_details_hide);
+ }
+ }
+ });
+ }
+
+
+ public void updateResult(RemoteOperationResult result) {
+ if (result.isSslRecoverableException()) {
+ mException = (CertificateCombinedException) result.getException();
+
+ /// clean
+ mView.findViewById(R.id.reason_cert_not_trusted).setVisibility(View.GONE);
+ mView.findViewById(R.id.reason_cert_expired).setVisibility(View.GONE);
+ mView.findViewById(R.id.reason_cert_not_yet_valid).setVisibility(View.GONE);
+ mView.findViewById(R.id.reason_hostname_not_verified).setVisibility(View.GONE);
+ mView.findViewById(R.id.details_scroll).setVisibility(View.GONE);
+
+ /// refresh
+ if (mException.getCertPathValidatorException() != null) {
+ ((TextView)mView.findViewById(R.id.reason_cert_not_trusted)).setVisibility(View.VISIBLE);
+ }
+
+ if (mException.getCertificateExpiredException() != null) {
+ ((TextView)mView.findViewById(R.id.reason_cert_expired)).setVisibility(View.VISIBLE);
+ }
+
+ if (mException.getCertificateNotYetValidException() != null) {
+ ((TextView)mView.findViewById(R.id.reason_cert_not_yet_valid)).setVisibility(View.VISIBLE);
+ }
+
+ if (mException.getSslPeerUnverifiedException() != null ) {
+ ((TextView)mView.findViewById(R.id.reason_hostname_not_verified)).setVisibility(View.VISIBLE);
+ }
+
+
+ showCertificateData(mException.getServerCertificate());
+ }
+
+ }
+
+ private void showCertificateData(X509Certificate cert) {
+
+ if (cert != null) {
+ showSubject(cert.getSubjectX500Principal());
+ showIssuer(cert.getIssuerX500Principal());
+ showValidity(cert.getNotBefore(), cert.getNotAfter());
+ showSignature(cert);
+
+ } else {
+ // this should not happen
+ // TODO
+ }
+ }
+
+ private void showSignature(X509Certificate cert) {
+ TextView sigView = ((TextView)mView.findViewById(R.id.value_signature));
+ TextView algorithmView = ((TextView)mView.findViewById(R.id.value_signature_algorithm));
+ sigView.setText(getHex(cert.getSignature()));
+ algorithmView.setText(cert.getSigAlgName());
+ }
+
+ public String getHex(final byte [] raw) {
+ if (raw == null) {
+ return null;
+ }
+ final StringBuilder hex = new StringBuilder(2 * raw.length);
+ for (final byte b : raw) {
+ final int hiVal = (b & 0xF0) >> 4;
+ final int loVal = b & 0x0F;
+ hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
+ hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
+ }
+ return hex.toString();
+ }
+
+ private void showValidity(Date notBefore, Date notAfter) {
+ TextView fromView = ((TextView)mView.findViewById(R.id.value_validity_from));
+ TextView toView = ((TextView)mView.findViewById(R.id.value_validity_to));
+ fromView.setText(notBefore.toLocaleString());
+ toView.setText(notAfter.toLocaleString());
+ }
+
+ private void showSubject(X500Principal subject) {
+ Map<String, String> s = parsePrincipal(subject);
+ TextView cnView = ((TextView)mView.findViewById(R.id.value_subject_CN));
+ TextView oView = ((TextView)mView.findViewById(R.id.value_subject_O));
+ TextView ouView = ((TextView)mView.findViewById(R.id.value_subject_OU));
+ TextView cView = ((TextView)mView.findViewById(R.id.value_subject_C));
+ TextView stView = ((TextView)mView.findViewById(R.id.value_subject_ST));
+ TextView lView = ((TextView)mView.findViewById(R.id.value_subject_L));
+
+ if (s.get("CN") != null) {
+ cnView.setText(s.get("CN"));
+ cnView.setVisibility(View.VISIBLE);
+ } else {
+ cnView.setVisibility(View.GONE);
+ }
+ if (s.get("O") != null) {
+ oView.setText(s.get("O"));
+ oView.setVisibility(View.VISIBLE);
+ } else {
+ oView.setVisibility(View.GONE);
+ }
+ if (s.get("OU") != null) {
+ ouView.setText(s.get("OU"));
+ ouView.setVisibility(View.VISIBLE);
+ } else {
+ ouView.setVisibility(View.GONE);
+ }
+ if (s.get("C") != null) {
+ cView.setText(s.get("C"));
+ cView.setVisibility(View.VISIBLE);
+ } else {
+ cView.setVisibility(View.GONE);
+ }
+ if (s.get("ST") != null) {
+ stView.setText(s.get("ST"));
+ stView.setVisibility(View.VISIBLE);
+ } else {
+ stView.setVisibility(View.GONE);
+ }
+ if (s.get("L") != null) {
+ lView.setText(s.get("L"));
+ lView.setVisibility(View.VISIBLE);
+ } else {
+ lView.setVisibility(View.GONE);
+ }
+ }
+
+ private void showIssuer(X500Principal issuer) {
+ Map<String, String> s = parsePrincipal(issuer);
+ TextView cnView = ((TextView)mView.findViewById(R.id.value_issuer_CN));
+ TextView oView = ((TextView)mView.findViewById(R.id.value_issuer_O));
+ TextView ouView = ((TextView)mView.findViewById(R.id.value_issuer_OU));
+ TextView cView = ((TextView)mView.findViewById(R.id.value_issuer_C));
+ TextView stView = ((TextView)mView.findViewById(R.id.value_issuer_ST));
+ TextView lView = ((TextView)mView.findViewById(R.id.value_issuer_L));
+
+ if (s.get("CN") != null) {
+ cnView.setText(s.get("CN"));
+ cnView.setVisibility(View.VISIBLE);
+ } else {
+ cnView.setVisibility(View.GONE);
+ }
+ if (s.get("O") != null) {
+ oView.setText(s.get("O"));
+ oView.setVisibility(View.VISIBLE);
+ } else {
+ oView.setVisibility(View.GONE);
+ }
+ if (s.get("OU") != null) {
+ ouView.setText(s.get("OU"));
+ ouView.setVisibility(View.VISIBLE);
+ } else {
+ ouView.setVisibility(View.GONE);
+ }
+ if (s.get("C") != null) {
+ cView.setText(s.get("C"));
+ cView.setVisibility(View.VISIBLE);
+ } else {
+ cView.setVisibility(View.GONE);
+ }
+ if (s.get("ST") != null) {
+ stView.setText(s.get("ST"));
+ stView.setVisibility(View.VISIBLE);
+ } else {
+ stView.setVisibility(View.GONE);
+ }
+ if (s.get("L") != null) {
+ lView.setText(s.get("L"));
+ lView.setVisibility(View.VISIBLE);
+ } else {
+ lView.setVisibility(View.GONE);
+ }
+ }
+
+
+ private Map<String, String> parsePrincipal(X500Principal principal) {
+ Map<String, String> result = new HashMap<String, String>();
+ String toParse = principal.getName();
+ String[] pieces = toParse.split(",");
+ String[] tokens = {"CN", "O", "OU", "C", "ST", "L"};
+ for (int i=0; i < pieces.length ; i++) {
+ for (int j=0; j<tokens.length; j++) {
+ if (pieces[i].startsWith(tokens[j] + "=")) {
+ result.put(tokens[j], pieces[i].substring(tokens[j].length()+1));
+ }
+ }
+ }
+ return result;
+ }
+
+ private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+ if (mException.getServerCertificate() != null) {
+ // TODO make this asynchronously, it can take some time
+ OwnCloudClientUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
+ }
+ }
+
+
+ public interface OnSslValidatorListener {
+ public void onSavedCertificate();
+ public void onFailedSavingCertificate();
+ }
+}
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.dialog;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.webkit.WebView;
+
+public class SsoWebView extends WebView {
+
+ public SsoWebView(Context context) {
+ super(context);
+ }
+
+ public SsoWebView(Context context, AttributeSet attr) {
+ super(context, attr);
+ }
+
+ @Override
+ public boolean onCheckIsTextEditor () {
+ return false;
+ }
+
+}
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+
+public class AuthenticatorAccountDetailsFragment extends SherlockFragment {
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+
+public class AuthenticatorGetStartedFragment extends SherlockFragment {
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.Log_OC;
+
+
+public class ConfirmationDialogFragment extends SherlockDialogFragment {
+
+ public final static String ARG_CONF_RESOURCE_ID = "resource_id";
+ public final static String ARG_CONF_ARGUMENTS = "string_array";
+
+ public final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
+ public final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res";
+ public final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res";
+
+ public static final String FTAG_CONFIRMATION = "CONFIRMATION_FRAGMENT";
+
+ private ConfirmationDialogFragmentListener mListener;
+
+ /**
+ * Public factory method to create new ConfirmationDialogFragment instances.
+ *
+ * @param string_id Resource id for a message to show in the dialog.
+ * @param arguments Arguments to complete the message, if it's a format string.
+ * @param posBtn Resource id for the text of the positive button.
+ * @param neuBtn Resource id for the text of the neutral button.
+ * @param negBtn Resource id for the text of the negative button.
+ * @return Dialog ready to show.
+ */
+ public static ConfirmationDialogFragment newInstance(int string_id, String[] arguments, int posBtn, int neuBtn, int negBtn) {
+ ConfirmationDialogFragment frag = new ConfirmationDialogFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_CONF_RESOURCE_ID, string_id);
+ args.putStringArray(ARG_CONF_ARGUMENTS, arguments);
+ args.putInt(ARG_POSITIVE_BTN_RES, posBtn);
+ args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn);
+ args.putInt(ARG_NEGATIVE_BTN_RES, negBtn);
+ frag.setArguments(args);
+ return frag;
+ }
+
+ public void setOnConfirmationListener(ConfirmationDialogFragmentListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Object[] confirmationTarget = getArguments().getStringArray(ARG_CONF_ARGUMENTS);
+ int resourceId = getArguments().getInt(ARG_CONF_RESOURCE_ID, -1);
+ int posBtn = getArguments().getInt(ARG_POSITIVE_BTN_RES, -1);
+ int neuBtn = getArguments().getInt(ARG_NEUTRAL_BTN_RES, -1);
+ int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
+
+ if (confirmationTarget == null || resourceId == -1) {
+ Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
+ return null;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setMessage(String.format(getString(resourceId), confirmationTarget))
+ .setTitle(android.R.string.dialog_alert_title);
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ }
+
+ if (posBtn != -1)
+ builder.setPositiveButton(posBtn,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mListener.onConfirmation(getTag());
+ dialog.dismiss();
+ }
+ });
+ if (neuBtn != -1)
+ builder.setNeutralButton(neuBtn,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mListener.onNeutral(getTag());
+ dialog.dismiss();
+ }
+ });
+ if (negBtn != -1)
+ builder.setNegativeButton(negBtn,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mListener.onCancel(getTag());
+ dialog.dismiss();
+ }
+ });
+ return builder.create();
+ }
+
+
+ public interface ConfirmationDialogFragmentListener {
+ public void onConfirmation(String callerTag);
+ public void onNeutral(String callerTag);
+ public void onCancel(String callerTag);
+ }
+
+}
+
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.ExtendedListView;
+
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListAdapter;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+
+/**
+ * TODO extending SherlockListFragment instead of SherlockFragment
+ */
+public class ExtendedListFragment extends SherlockFragment implements OnItemClickListener {
+
+ private static final String TAG = ExtendedListFragment.class.getSimpleName();
+
+ private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
+
+ protected ExtendedListView mList;
+
+ public void setListAdapter(ListAdapter listAdapter) {
+ mList.setAdapter(listAdapter);
+ mList.invalidate();
+ }
+
+ public ListView getListView() {
+ return mList;
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Log_OC.e(TAG, "onCreateView");
+ //mList = new ExtendedListView(getActivity());
+ View v = inflater.inflate(R.layout.list_fragment, null);
+ mList = (ExtendedListView)(v.findViewById(R.id.list_root));
+ mList.setOnItemClickListener(this);
+ //mList.setEmptyView(v.findViewById(R.id.empty_list_view)); // looks like it's not a cool idea
+ mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
+ mList.setDividerHeight(1);
+
+ if (savedInstanceState != null) {
+ int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
+ setReferencePosition(referencePosition);
+ }
+
+ return v;
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ Log_OC.e(TAG, "onSaveInstanceState()");
+ savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
+ }
+
+
+ /**
+ * Calculates the position of the item that will be used as a reference to reposition the visible items in the list when
+ * the device is turned to other position.
+ *
+ * THe current policy is take as a reference the visible item in the center of the screen.
+ *
+ * @return The position in the list of the visible item in the center of the screen.
+ */
+ protected int getReferencePosition() {
+ if (mList != null) {
+ return (mList.getFirstVisiblePosition() + mList.getLastVisiblePosition()) / 2;
+ } else {
+ return 0;
+ }
+ }
+
+
+ /**
+ * Sets the visible part of the list from the reference position.
+ *
+ * @param position Reference position previously returned by {@link LocalFileListFragment#getReferencePosition()}
+ */
+ protected void setReferencePosition(int position) {
+ if (mList != null) {
+ mList.setAndCenterSelection(position);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
+ // to be @overriden
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.DisplayUtils;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileObserverService;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.ui.activity.ConflictsResolveActivity;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+/**
+ * This Fragment is used to display the details about a file.
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
+ */
+public class FileDetailFragment extends FileFragment implements
+ OnClickListener,
+ ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {
+
+ private FileFragment.ContainerActivity mContainerActivity;
+
+ private int mLayout;
+ private View mView;
+ private Account mAccount;
+ private FileDataStorageManager mStorageManager;
+
+ private UploadFinishReceiver mUploadFinishReceiver;
+ public ProgressListener mProgressListener;
+
+ private Handler mHandler;
+ private RemoteOperation mLastRemoteOperation;
+
+ private static final String TAG = FileDetailFragment.class.getSimpleName();
+ public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
+
+
+ /**
+ * Creates an empty details fragment.
+ *
+ * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
+ */
+ public FileDetailFragment() {
+ super();
+ mAccount = null;
+ mStorageManager = null;
+ mLayout = R.layout.file_details_empty;
+ mProgressListener = null;
+ }
+
+ /**
+ * Creates a details fragment.
+ *
+ * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
+ *
+ * @param fileToDetail An {@link OCFile} to show in the fragment
+ * @param ocAccount An ownCloud account; needed to start downloads
+ */
+ public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
+ super(fileToDetail);
+ mAccount = ocAccount;
+ mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
+ mLayout = R.layout.file_details_empty;
+ mProgressListener = null;
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mHandler = new Handler();
+ setHasOptionsMenu(true);
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ //super.onCreateView(inflater, container, savedInstanceState);
+
+ if (savedInstanceState != null) {
+ setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
+ mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
+ }
+
+ if(getFile() != null && mAccount != null) {
+ mLayout = R.layout.file_details_fragment;
+ }
+
+ View view = null;
+ //view = inflater.inflate(mLayout, container, false);
+ view = inflater.inflate(mLayout, null);
+ mView = view;
+
+ if (mLayout == R.layout.file_details_fragment) {
+ mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
+ ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
+ mProgressListener = new ProgressListener(progressBar);
+ mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
+ }
+
+ updateFileDetails(false, false);
+ return view;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mContainerActivity = (ContainerActivity) activity;
+
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (mAccount != null) {
+ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+ }
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(FileActivity.EXTRA_FILE, getFile());
+ outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ listenForTransferProgress();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mUploadFinishReceiver = new UploadFinishReceiver();
+ FileUploader fileUploader = new FileUploader();
+ IntentFilter filter = new IntentFilter(fileUploader.getUploadFinishMessage());
+ getActivity().registerReceiver(mUploadFinishReceiver, filter);
+
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (mUploadFinishReceiver != null) {
+ getActivity().unregisterReceiver(mUploadFinishReceiver);
+ mUploadFinishReceiver = null;
+ }
+ }
+
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ leaveTransferProgress();
+ }
+
+
+ @Override
+ public View getView() {
+ return super.getView() == null ? mView : super.getView();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.file_actions_menu, menu);
+ MenuItem item = menu.findItem(R.id.action_see_details);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPrepareOptionsMenu (Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+
+ List<Integer> toHide = new ArrayList<Integer>();
+ List<Integer> toShow = new ArrayList<Integer>();
+ OCFile file = getFile();
+
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file);
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile());
+
+ if (downloading || uploading) {
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_rename_file);
+ toHide.add(R.id.action_remove_file);
+ toHide.add(R.id.action_open_file_with);
+ if (!downloading) {
+ toHide.add(R.id.action_cancel_download);
+ toShow.add(R.id.action_cancel_upload);
+ } else {
+ toHide.add(R.id.action_cancel_upload);
+ toShow.add(R.id.action_cancel_download);
+ }
+
+ } else if (file != null && file.isDown()) {
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+
+ toShow.add(R.id.action_rename_file);
+ toShow.add(R.id.action_remove_file);
+ toShow.add(R.id.action_open_file_with);
+ toShow.add(R.id.action_sync_file);
+
+ } else if (file != null) {
+ toHide.add(R.id.action_open_file_with);
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ toHide.add(R.id.action_sync_file);
+
+ toShow.add(R.id.action_rename_file);
+ toShow.add(R.id.action_remove_file);
+ toShow.add(R.id.action_download_file);
+
+ } else {
+ toHide.add(R.id.action_open_file_with);
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ toHide.add(R.id.action_sync_file);
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_rename_file);
+ toHide.add(R.id.action_remove_file);
+
+ }
+
+ MenuItem item = null;
+ for (int i : toHide) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+ for (int i : toShow) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(true);
+ item.setEnabled(true);
+ }
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_open_file_with: {
+ mContainerActivity.openFile(getFile());
+ return true;
+ }
+ case R.id.action_remove_file: {
+ removeFile();
+ return true;
+ }
+ case R.id.action_rename_file: {
+ renameFile();
+ return true;
+ }
+ case R.id.action_download_file:
+ case R.id.action_cancel_download:
+ case R.id.action_cancel_upload:
+ case R.id.action_sync_file: {
+ synchronizeFile();
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.fdKeepInSync: {
+ toggleKeepInSync();
+ break;
+ }
+ case R.id.fdCancelBtn: {
+ synchronizeFile();
+ break;
+ }
+ default:
+ Log_OC.e(TAG, "Incorrect view clicked!");
+ }
+ }
+
+
+ private void toggleKeepInSync() {
+ CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
+ OCFile file = getFile();
+ file.setKeepInSync(cb.isChecked());
+ mStorageManager.saveFile(file);
+
+ /// register the OCFile instance in the observer service to monitor local updates;
+ /// if necessary, the file is download
+ Intent intent = new Intent(getActivity().getApplicationContext(),
+ FileObserverService.class);
+ intent.putExtra(FileObserverService.KEY_FILE_CMD,
+ (cb.isChecked()?
+ FileObserverService.CMD_ADD_OBSERVED_FILE:
+ FileObserverService.CMD_DEL_OBSERVED_FILE));
+ intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, file);
+ intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
+ getActivity().startService(intent);
+
+ if (file.keepInSync()) {
+ synchronizeFile(); // force an immediate synchronization
+ }
+ }
+
+
+ private void removeFile() {
+ OCFile file = getFile();
+ ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+ R.string.confirmation_remove_alert,
+ new String[]{file.getFileName()},
+ file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
+ file.isDown() ? R.string.confirmation_remove_local : -1,
+ R.string.common_cancel);
+ confDialog.setOnConfirmationListener(this);
+ confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
+ }
+
+
+ private void renameFile() {
+ OCFile file = getFile();
+ String fileName = file.getFileName();
+ int extensionStart = file.isDirectory() ? -1 : fileName.lastIndexOf(".");
+ int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
+ dialog.show(getFragmentManager(), "nameeditdialog");
+ }
+
+ private void synchronizeFile() {
+ OCFile file = getFile();
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
+ downloaderBinder.cancel(mAccount, file);
+ if (file.isDown()) {
+ setButtonsForDown();
+ } else {
+ setButtonsForRemote();
+ }
+
+ } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
+ uploaderBinder.cancel(mAccount, file);
+ if (!file.fileExists()) {
+ // TODO make something better
+ ((FileDisplayActivity)getActivity()).cleanSecondFragment();
+
+ } else if (file.isDown()) {
+ setButtonsForDown();
+ } else {
+ setButtonsForRemote();
+ }
+
+ } else {
+ mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity());
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+
+ // update ui
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+
+ }
+ }
+
+ @Override
+ public void onConfirmation(String callerTag) {
+ OCFile file = getFile();
+ if (callerTag.equals(FTAG_CONFIRMATION)) {
+ if (mStorageManager.getFileById(file.getFileId()) != null) {
+ mLastRemoteOperation = new RemoveFileOperation( file,
+ true,
+ mStorageManager);
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+ }
+ }
+ }
+
+ @Override
+ public void onNeutral(String callerTag) {
+ File f = null;
+ OCFile file = getFile();
+ if (file.isDown() && (f = new File(file.getStoragePath())).exists()) {
+ f.delete();
+ file.setStoragePath(null);
+ mStorageManager.saveFile(file);
+ updateFileDetails(file, mAccount);
+ }
+ }
+
+ @Override
+ public void onCancel(String callerTag) {
+ Log_OC.d(TAG, "REMOVAL CANCELED");
+ }
+
+
+ /**
+ * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
+ *
+ * @return True when the fragment was created with the empty layout.
+ */
+ public boolean isEmpty() {
+ return (mLayout == R.layout.file_details_empty || getFile() == null || mAccount == null);
+ }
+
+
+ /**
+ * Use this method to signal this Activity that it shall update its view.
+ *
+ * @param file : An {@link OCFile}
+ */
+ public void updateFileDetails(OCFile file, Account ocAccount) {
+ setFile(file);
+ if (ocAccount != null && (
+ mStorageManager == null ||
+ (mAccount != null && !mAccount.equals(ocAccount))
+ )) {
+ mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
+ }
+ mAccount = ocAccount;
+ updateFileDetails(false, false);
+ }
+
+ /**
+ * Updates the view with all relevant details about that file.
+ *
+ * TODO Remove parameter when the transferring state of files is kept in database.
+ *
+ * TODO REFACTORING! this method called 5 times before every time the fragment is shown!
+ *
+ * @param transferring Flag signaling if the file should be considered as downloading or uploading,
+ * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and
+ * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
+ *
+ * @param refresh If 'true', try to refresh the hold file from the database
+ */
+ public void updateFileDetails(boolean transferring, boolean refresh) {
+
+ if (readyToShow()) {
+
+ if (refresh && mStorageManager != null) {
+ setFile(mStorageManager.getFileByPath(getFile().getRemotePath()));
+ }
+ OCFile file = getFile();
+
+ // set file details
+ setFilename(file.getFileName());
+ setFiletype(file.getMimetype());
+ setFilesize(file.getFileLength());
+ if(ocVersionSupportsTimeCreated()){
+ setTimeCreated(file.getCreationTimestamp());
+ }
+
+ setTimeModified(file.getModificationTimestamp());
+
+ CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
+ cb.setChecked(file.keepInSync());
+
+ // configure UI for depending upon local state of the file
+ //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
+ setButtonsForTransferring();
+
+ } else if (file.isDown()) {
+
+ setButtonsForDown();
+
+ } else {
+ // TODO load default preview image; when the local file is removed, the preview remains there
+ setButtonsForRemote();
+ }
+ }
+ getView().invalidate();
+ }
+
+ /**
+ * Checks if the fragment is ready to show details of a OCFile
+ *
+ * @return 'True' when the fragment is ready to show details of a file
+ */
+ private boolean readyToShow() {
+ return (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);
+ }
+
+
+ /**
+ * Updates the filename in view
+ * @param filename to set
+ */
+ private void setFilename(String filename) {
+ TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
+ if (tv != null)
+ tv.setText(filename);
+ }
+
+ /**
+ * Updates the MIME type in view
+ * @param mimetype to set
+ */
+ private void setFiletype(String mimetype) {
+ TextView tv = (TextView) getView().findViewById(R.id.fdType);
+ if (tv != null) {
+ String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;
+ tv.setText(printableMimetype);
+ }
+ ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
+ if (iv != null) {
+ iv.setImageResource(DisplayUtils.getResourceId(mimetype));
+ }
+ }
+
+ /**
+ * Updates the file size in view
+ * @param filesize in bytes to set
+ */
+ private void setFilesize(long filesize) {
+ TextView tv = (TextView) getView().findViewById(R.id.fdSize);
+ if (tv != null)
+ tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
+ }
+
+ /**
+ * Updates the time that the file was created in view
+ * @param milliseconds Unix time to set
+ */
+ private void setTimeCreated(long milliseconds){
+ TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
+ TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
+ if(tv != null){
+ tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
+ tv.setVisibility(View.VISIBLE);
+ tvLabel.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Updates the time that the file was last modified
+ * @param milliseconds Unix time to set
+ */
+ private void setTimeModified(long milliseconds){
+ TextView tv = (TextView) getView().findViewById(R.id.fdModified);
+ if(tv != null){
+ tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
+ }
+ }
+
+ /**
+ * Enables or disables buttons for a file being downloaded
+ */
+ private void setButtonsForTransferring() {
+ if (!isEmpty()) {
+ // let's protect the user from himself ;)
+ getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
+
+ // show the progress bar for the transfer
+ getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
+ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+ progressText.setVisibility(View.VISIBLE);
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
+ progressText.setText(R.string.downloader_download_in_progress_ticker);
+ } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
+ progressText.setText(R.string.uploader_upload_in_progress_ticker);
+ }
+ }
+ }
+
+ /**
+ * Enables or disables buttons for a file locally available
+ */
+ private void setButtonsForDown() {
+ if (!isEmpty()) {
+ getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+
+ // hides the progress bar
+ getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
+ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+ progressText.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Enables or disables buttons for a file not locally available
+ */
+ private void setButtonsForRemote() {
+ if (!isEmpty()) {
+ getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
+
+ // hides the progress bar
+ getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
+ TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
+ progressText.setVisibility(View.GONE);
+ }
+ }
+
+
+ /**
+ * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
+ * the time that the file was created. There is a chance that this will
+ * be fixed in future versions. Use this method to check if this version of
+ * ownCloud has this fix.
+ * @return True, if ownCloud the ownCloud version is supporting creation time
+ */
+ private boolean ocVersionSupportsTimeCreated(){
+ /*if(mAccount != null){
+ AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
+ OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
+ .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
+ if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
+ return true;
+ }
+ }*/
+ return false;
+ }
+
+
+ /**
+ * Once the file upload has finished -> update view
+ *
+ * Being notified about the finish of an upload is necessary for the next sequence:
+ * 1. Upload a big file.
+ * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list
+ * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.
+ * 3. Click the file in the list to see its details.
+ * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
+ */
+ private class UploadFinishReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
+
+ if (!isEmpty() && accountName.equals(mAccount.name)) {
+ boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
+ String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
+ boolean renamedInUpload = getFile().getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
+ if (getFile().getRemotePath().equals(uploadRemotePath) ||
+ renamedInUpload) {
+ if (uploadWasFine) {
+ setFile(mStorageManager.getFileByPath(uploadRemotePath));
+ }
+ if (renamedInUpload) {
+ String newName = (new File(uploadRemotePath)).getName();
+ Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);
+ msg.show();
+ }
+ getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done
+
+ updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
+
+ // Force the preview if the file is an image
+ if (uploadWasFine && PreviewImageFragment.canBePreviewed(getFile())) {
+ ((FileDisplayActivity) mContainerActivity).startImagePreview(getFile());
+ }
+ }
+ }
+ }
+ }
+
+
+ public void onDismiss(EditNameDialog dialog) {
+ if (dialog.getResult()) {
+ String newFilename = dialog.getNewFilename();
+ Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+ mLastRemoteOperation = new RenameFileOperation( getFile(),
+ mAccount,
+ newFilename,
+ new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ if (operation.equals(mLastRemoteOperation)) {
+ if (operation instanceof RemoveFileOperation) {
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+
+ } else if (operation instanceof RenameFileOperation) {
+ onRenameFileOperationFinish((RenameFileOperation)operation, result);
+
+ } else if (operation instanceof SynchronizeFileOperation) {
+ onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
+ }
+ }
+ }
+
+
+ private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+ ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+ if (result.isSuccess()) {
+ Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+ msg.show();
+ ((FileDisplayActivity)getActivity()).cleanSecondFragment();
+
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ // TODO show the SSL warning dialog
+ }
+ }
+ }
+
+ private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
+ ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+
+ if (result.isSuccess()) {
+ updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);
+ mContainerActivity.onFileStateChanged();
+
+ } else {
+ if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
+ Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ // TODO throw again the new rename dialog
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ // TODO show the SSL warning dialog
+ }
+ }
+ }
+ }
+
+ private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
+ ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+ OCFile file = getFile();
+ if (!result.isSuccess()) {
+ if (result.getCode() == ResultCode.SYNC_CONFLICT) {
+ Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
+ i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
+ i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
+ startActivity(i);
+
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ }
+
+ if (file.isDown()) {
+ setButtonsForDown();
+
+ } else {
+ setButtonsForRemote();
+ }
+
+ } else {
+ if (operation.transferWasRequested()) {
+ setButtonsForTransferring();
+ mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so
+ // checking the service to see if the file is downloading results in FALSE
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (file.isDown()) {
+ setButtonsForDown();
+
+ } else {
+ setButtonsForRemote();
+ }
+ }
+ }
+ }
+
+
+ public void listenForTransferProgress() {
+ if (mProgressListener != null) {
+ if (mContainerActivity.getFileDownloaderBinder() != null) {
+ mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ }
+ if (mContainerActivity.getFileUploaderBinder() != null) {
+ mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ }
+ }
+ }
+
+
+ public void leaveTransferProgress() {
+ if (mProgressListener != null) {
+ if (mContainerActivity.getFileDownloaderBinder() != null) {
+ mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ }
+ if (mContainerActivity.getFileUploaderBinder() != null) {
+ mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ }
+ }
+ }
+
+
+
+ /**
+ * Helper class responsible for updating the progress bar shown for file uploading or downloading
+ *
+ * @author David A. Velasco
+ */
+ private class ProgressListener implements OnDatatransferProgressListener {
+ int mLastPercent = 0;
+ WeakReference<ProgressBar> mProgressBar = null;
+
+ ProgressListener(ProgressBar progressBar) {
+ mProgressBar = new WeakReference<ProgressBar>(progressBar);
+ }
+
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // old method, nothing here
+ };
+
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
+ int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+ if (percent != mLastPercent) {
+ ProgressBar pb = mProgressBar.get();
+ if (pb != null) {
+ pb.setProgress(percent);
+ pb.postInvalidate();
+ }
+ }
+ mLastPercent = percent;
+ }
+
+ };
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import android.support.v4.app.Fragment;
+
+import com.actionbarsherlock.app.SherlockFragment;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileHandler;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
+
+
+/**
+ * Common methods for {@link Fragment}s containing {@link OCFile}s
+ *
+ * @author David A. Velasco
+ *
+ */
+public class FileFragment extends SherlockFragment {
+
+ private OCFile mFile;
+
+
+ /**
+ * Creates an empty fragment.
+ *
+ * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
+ */
+ public FileFragment() {
+ mFile = null;
+ }
+
+ /**
+ * Creates an instance for a given {@OCFile}.
+ *
+ * @param file
+ */
+ public FileFragment(OCFile file) {
+ mFile = file;
+ }
+
+ /**
+ * Getter for the hold {@link OCFile}
+ *
+ * @return The {@link OCFile} hold
+ */
+ public OCFile getFile() {
+ return mFile;
+ }
+
+
+ protected void setFile(OCFile file) {
+ mFile = file;
+ }
+
+ /**
+ * Interface to implement by any Activity that includes some instance of FileFragment
+ *
+ * @author David A. Velasco
+ */
+ public interface ContainerActivity extends TransferServiceGetter, FileHandler {
+
+ /**
+ * Callback method invoked when the detail fragment wants to notice its container
+ * activity about a relevant state the file shown by the fragment.
+ *
+ * Added to notify to FileDisplayActivity about the need of refresh the files list.
+ *
+ * Currently called when:
+ * - a download is started;
+ * - a rename is completed;
+ * - a deletion is completed;
+ * - the 'inSync' flag is changed;
+ */
+ public void onFileStateChanged();
+
+ /**
+ * Request the parent activity to show the details of an {@link OCFile}.
+ *
+ * @param file File to show details
+ */
+ public void showDetails(OCFile file);
+
+
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application\r
+ * Copyright (C) 2011 Bartek Przybylski\r
+ * Copyright (C) 2012-2013 ownCloud Inc.\r
+ *\r
+ * This program is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License version 2,\r
+ * as published by the Free Software Foundation.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
+ *\r
+ */\r
+package com.owncloud.android.ui.fragment;\r
+\r
+import com.actionbarsherlock.app.SherlockFragment;\r
+import com.owncloud.android.R;\r
+import com.owncloud.android.ui.activity.LandingActivity;\r
+import com.owncloud.android.ui.adapter.LandingScreenAdapter;\r
+\r
+import android.os.Bundle;\r
+import android.view.LayoutInflater;\r
+import android.view.View;\r
+import android.view.ViewGroup;\r
+import android.widget.ListView;\r
+\r
+/**\r
+ * Used on the Landing page to display what Components of the ownCloud there\r
+ * are. Like Files, Music, Contacts, etc.\r
+ * \r
+ * @author Lennart Rosam\r
+ * \r
+ */\r
+public class LandingPageFragment extends SherlockFragment {\r
+\r
+ @Override\r
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
+ Bundle savedInstanceState) {\r
+ View root = inflater.inflate(R.layout.landing_page_fragment, container);\r
+ return root;\r
+ }\r
+\r
+ @Override\r
+ public void onActivityCreated(Bundle savedInstanceState) {\r
+ super.onActivityCreated(savedInstanceState);\r
+\r
+ ListView landingScreenItems = (ListView) getView().findViewById(\r
+ R.id.homeScreenList);\r
+ landingScreenItems.setAdapter(new LandingScreenAdapter(getActivity()));\r
+ landingScreenItems\r
+ .setOnItemClickListener((LandingActivity) getActivity());\r
+ }\r
+\r
+}\r
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import java.io.File;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.adapter.LocalFileListAdapter;
+
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.SparseBooleanArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+
+/**
+ * A Fragment that lists all files and folders in a given LOCAL path.
+ *
+ * @author David A. Velasco
+ *
+ */
+public class LocalFileListFragment extends ExtendedListFragment {
+ private static final String TAG = "LocalFileListFragment";
+
+ /** Reference to the Activity which this fragment is attached to. For callbacks */
+ private LocalFileListFragment.ContainerActivity mContainerActivity;
+
+ /** Directory to show */
+ private File mDirectory = null;
+
+ /** Adapter to connect the data from the directory with the View object */
+ private LocalFileListAdapter mAdapter = null;
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mContainerActivity = (ContainerActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " + LocalFileListFragment.ContainerActivity.class.getSimpleName());
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ Log_OC.i(TAG, "onCreateView() start");
+ View v = super.onCreateView(inflater, container, savedInstanceState);
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ Log_OC.i(TAG, "onCreateView() end");
+ return v;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ Log_OC.i(TAG, "onActivityCreated() start");
+
+ super.onCreate(savedInstanceState);
+ mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
+ setListAdapter(mAdapter);
+
+ Log_OC.i(TAG, "onActivityCreated() stop");
+ }
+
+
+ /**
+ * Checks the file clicked over. Browses inside if it is a directory. Notifies the container activity in any case.
+ */
+ @Override
+ public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+ File file = (File) mAdapter.getItem(position);
+ if (file != null) {
+ /// Click on a directory
+ if (file.isDirectory()) {
+ // just local updates
+ listDirectory(file);
+ // notify the click to container Activity
+ mContainerActivity.onDirectoryClick(file);
+
+ } else { /// Click on a file
+ ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox);
+ if (checkBoxV != null) {
+ if (getListView().isItemChecked(position)) {
+ checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
+ } else {
+ checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
+ }
+ }
+ // notify the change to the container Activity
+ mContainerActivity.onFileClick(file);
+ }
+
+ } else {
+ Log_OC.w(TAG, "Null object in ListAdapter!!");
+ }
+ }
+
+
+ /**
+ * Call this, when the user presses the up button
+ */
+ public void onNavigateUp() {
+ File parentDir = null;
+ if(mDirectory != null) {
+ parentDir = mDirectory.getParentFile(); // can be null
+ }
+ listDirectory(parentDir);
+ }
+
+
+ /**
+ * Use this to query the {@link File} object for the directory
+ * that is currently being displayed by this fragment
+ *
+ * @return File The currently displayed directory
+ */
+ public File getCurrentDirectory(){
+ return mDirectory;
+ }
+
+
+ /**
+ * Calls {@link LocalFileListFragment#listDirectory(File)} with a null parameter
+ * to refresh the current directory.
+ */
+ public void listDirectory(){
+ listDirectory(null);
+ }
+
+
+ /**
+ * Lists the given directory on the view. When the input parameter is null,
+ * it will either refresh the last known directory. list the root
+ * if there never was a directory.
+ *
+ * @param directory Directory to be listed
+ */
+ public void listDirectory(File directory) {
+
+ // Check input parameters for null
+ if(directory == null) {
+ if(mDirectory != null){
+ directory = mDirectory;
+ } else {
+ directory = Environment.getExternalStorageDirectory(); // TODO be careful with the state of the storage; could not be available
+ if (directory == null) return; // no files to show
+ }
+ }
+
+
+ // if that's not a directory -> List its parent
+ if(!directory.isDirectory()){
+ Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+ directory = directory.getParentFile();
+ }
+
+ mList.clearChoices(); // by now, only files in the same directory will be kept as selected
+ mAdapter.swapDirectory(directory);
+ if (mDirectory == null || !mDirectory.equals(directory)) {
+ mList.setSelectionFromTop(0, 0);
+ }
+ mDirectory = directory;
+ }
+
+
+ /**
+ * Returns the fule paths to the files checked by the user
+ *
+ * @return File paths to the files checked by the user.
+ */
+ public String[] getCheckedFilePaths() {
+ String [] result = null;
+ SparseBooleanArray positions = mList.getCheckedItemPositions();
+ if (positions.size() > 0) {
+ Log_OC.d(TAG, "Returning " + positions.size() + " selected files");
+ result = new String[positions.size()];
+ for (int i=0; i<positions.size(); i++) {
+ result[i] = ((File) mList.getItemAtPosition(positions.keyAt(i))).getAbsolutePath();
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Interface to implement by any Activity that includes some instance of LocalFileListFragment
+ *
+ * @author David A. Velasco
+ */
+ public interface ContainerActivity {
+
+ /**
+ * Callback method invoked when a directory is clicked by the user on the files list
+ *
+ * @param file
+ */
+ public void onDirectoryClick(File directory);
+
+ /**
+ * Callback method invoked when a file (non directory) is clicked by the user on the files list
+ *
+ * @param file
+ */
+ public void onFileClick(File file);
+
+
+ /**
+ * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
+ *
+ * @return Directory to list firstly. Can be NULL.
+ */
+ public File getInitialDirectory();
+
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.fragment;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileHandler;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.operations.RenameFileOperation;
+import com.owncloud.android.operations.SynchronizeFileOperation;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.activity.TransferServiceGetter;
+import com.owncloud.android.ui.adapter.FileListListAdapter;
+import com.owncloud.android.ui.dialog.EditNameDialog;
+import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.ui.preview.PreviewMediaFragment;
+
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+/**
+ * A Fragment that lists all files and folders in a given path.
+ *
+ * @author Bartek Przybylski
+ *
+ */
+public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
+
+ private static final String TAG = OCFileListFragment.class.getSimpleName();
+
+ private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment";
+ private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
+
+ private OCFileListFragment.ContainerActivity mContainerActivity;
+
+ private OCFile mFile = null;
+ private FileListListAdapter mAdapter;
+
+ private Handler mHandler;
+ private OCFile mTargetFile;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ Log_OC.e(TAG, "onAttach");
+ try {
+ mContainerActivity = (ContainerActivity) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log_OC.e(TAG, "onActivityCreated() start");
+ mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
+ if (savedInstanceState != null) {
+ mFile = savedInstanceState.getParcelable(EXTRA_FILE);
+ }
+ setListAdapter(mAdapter);
+
+ registerForContextMenu(getListView());
+ getListView().setOnCreateContextMenuListener(this);
+
+ mHandler = new Handler();
+
+ }
+
+ /**
+ * Saves the current listed folder.
+ */
+ @Override
+ public void onSaveInstanceState (Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(EXTRA_FILE, mFile);
+ }
+
+
+ /**
+ * Call this, when the user presses the up button
+ */
+ public void onBrowseUp() {
+ OCFile parentDir = null;
+
+ if(mFile != null){
+ DataStorageManager storageManager = mContainerActivity.getStorageManager();
+ parentDir = storageManager.getFileById(mFile.getParentId());
+ mFile = parentDir;
+ }
+ listDirectory(parentDir);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+ OCFile file = (OCFile) mAdapter.getItem(position);
+ if (file != null) {
+ if (file.isDirectory()) {
+ // update state and view of this fragment
+ listDirectory(file);
+ // then, notify parent activity to let it update its state and view, and other fragments
+ mContainerActivity.onBrowsedDownTo(file);
+
+ } else { /// Click on a file
+ if (PreviewImageFragment.canBePreviewed(file)) {
+ // preview image - it handles the download, if needed
+ mContainerActivity.startImagePreview(file);
+
+ } else if (file.isDown()) {
+ if (PreviewMediaFragment.canBePreviewed(file)) {
+ // media preview
+ mContainerActivity.startMediaPreview(file, 0, true);
+ } else {
+ // open with
+ mContainerActivity.openFile(file);
+ }
+
+ } else {
+ // automatic download, preview on finish
+ mContainerActivity.startDownloadForPreview(file);
+ }
+
+ }
+
+ } else {
+ Log_OC.d(TAG, "Null object in ListAdapter!!");
+ }
+
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.file_actions_menu, menu);
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
+ OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
+ List<Integer> toHide = new ArrayList<Integer>();
+ List<Integer> toDisable = new ArrayList<Integer>();
+
+ MenuItem item = null;
+ if (targetFile.isDirectory()) {
+ // contextual menu for folders
+ toHide.add(R.id.action_open_file_with);
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ toHide.add(R.id.action_sync_file);
+ toHide.add(R.id.action_see_details);
+ if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
+ mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) {
+ toDisable.add(R.id.action_rename_file);
+ toDisable.add(R.id.action_remove_file);
+
+ }
+
+ } else {
+ // contextual menu for regular files
+
+ // new design: 'download' and 'open with' won't be available anymore in context menu
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_open_file_with);
+
+ if (targetFile.isDown()) {
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+
+ } else {
+ toHide.add(R.id.action_sync_file);
+ }
+ if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
+ toHide.add(R.id.action_cancel_upload);
+ toDisable.add(R.id.action_rename_file);
+ toDisable.add(R.id.action_remove_file);
+
+ } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
+ toHide.add(R.id.action_cancel_download);
+ toDisable.add(R.id.action_rename_file);
+ toDisable.add(R.id.action_remove_file);
+
+ } else {
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ }
+ }
+
+ for (int i : toHide) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+
+ for (int i : toDisable) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setEnabled(false);
+ }
+ }
+ }
+
+
+ /**
+ * {@inhericDoc}
+ */
+ @Override
+ public boolean onContextItemSelected (MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mTargetFile = (OCFile) mAdapter.getItem(info.position);
+ switch (item.getItemId()) {
+ case R.id.action_rename_file: {
+ String fileName = mTargetFile.getFileName();
+ int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
+ int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
+ EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
+ dialog.show(getFragmentManager(), EditNameDialog.TAG);
+ return true;
+ }
+ case R.id.action_remove_file: {
+ int messageStringId = R.string.confirmation_remove_alert;
+ int posBtnStringId = R.string.confirmation_remove_remote;
+ int neuBtnStringId = -1;
+ if (mTargetFile.isDirectory()) {
+ messageStringId = R.string.confirmation_remove_folder_alert;
+ posBtnStringId = R.string.confirmation_remove_remote_and_local;
+ neuBtnStringId = R.string.confirmation_remove_folder_local;
+ } else if (mTargetFile.isDown()) {
+ posBtnStringId = R.string.confirmation_remove_remote_and_local;
+ neuBtnStringId = R.string.confirmation_remove_local;
+ }
+ ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+ messageStringId,
+ new String[]{mTargetFile.getFileName()},
+ posBtnStringId,
+ neuBtnStringId,
+ R.string.common_cancel);
+ confDialog.setOnConfirmationListener(this);
+ confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
+ return true;
+ }
+ case R.id.action_sync_file: {
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
+ RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
+ operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
+ ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
+ return true;
+ }
+ case R.id.action_cancel_download: {
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+ if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
+ downloaderBinder.cancel(account, mTargetFile);
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+ return true;
+ }
+ case R.id.action_cancel_upload: {
+ FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
+ Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
+ if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
+ uploaderBinder.cancel(account, mTargetFile);
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+ return true;
+ }
+ case R.id.action_see_details: {
+ ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
+ return true;
+ }
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+
+ /**
+ * Use this to query the {@link OCFile} that is currently
+ * being displayed by this fragment
+ * @return The currently viewed OCFile
+ */
+ public OCFile getCurrentFile(){
+ return mFile;
+ }
+
+ /**
+ * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
+ */
+ public void listDirectory(){
+ listDirectory(null);
+ }
+
+ /**
+ * Lists the given directory on the view. When the input parameter is null,
+ * it will either refresh the last known directory. list the root
+ * if there never was a directory.
+ *
+ * @param directory File to be listed
+ */
+ public void listDirectory(OCFile directory) {
+ DataStorageManager storageManager = mContainerActivity.getStorageManager();
+ if (storageManager != null) {
+
+ // Check input parameters for null
+ if(directory == null){
+ if(mFile != null){
+ directory = mFile;
+ } else {
+ directory = storageManager.getFileByPath("/");
+ if (directory == null) return; // no files, wait for sync
+ }
+ }
+
+
+ // If that's not a directory -> List its parent
+ if(!directory.isDirectory()){
+ Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+ directory = storageManager.getFileById(directory.getParentId());
+ }
+
+ mAdapter.swapDirectory(directory, storageManager);
+ if (mFile == null || !mFile.equals(directory)) {
+ mList.setSelectionFromTop(0, 0);
+ }
+ mFile = directory;
+
+ }
+ }
+
+
+
+ /**
+ * Interface to implement by any Activity that includes some instance of FileListFragment
+ *
+ * @author David A. Velasco
+ */
+ public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
+
+ /**
+ * Callback method invoked when a the user browsed into a different folder through the list of files
+ *
+ * @param file
+ */
+ public void onBrowsedDownTo(OCFile folder);
+
+ public void startDownloadForPreview(OCFile file);
+
+ public void startMediaPreview(OCFile file, int i, boolean b);
+
+ public void startImagePreview(OCFile file);
+
+ /**
+ * Getter for the current DataStorageManager in the container activity
+ */
+ public DataStorageManager getStorageManager();
+
+
+ /**
+ * Callback method invoked when a the 'transfer state' of a file changes.
+ *
+ * This happens when a download or upload is started or ended for a file.
+ *
+ * This method is necessary by now to update the user interface of the double-pane layout in tablets
+ * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
+ * won't provide the needed response before the method where this is called finishes.
+ *
+ * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
+ *
+ * @param file OCFile which state changed.
+ * @param downloading Flag signaling if the file is now downloading.
+ * @param uploading Flag signaling if the file is now uploading.
+ */
+ public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
+
+ }
+
+
+ @Override
+ public void onDismiss(EditNameDialog dialog) {
+ if (dialog.getResult()) {
+ String newFilename = dialog.getNewFilename();
+ Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
+ RemoteOperation operation = new RenameFileOperation(mTargetFile,
+ AccountUtils.getCurrentOwnCloudAccount(getActivity()),
+ newFilename,
+ mContainerActivity.getStorageManager());
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+ }
+ }
+
+
+ @Override
+ public void onConfirmation(String callerTag) {
+ if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
+ if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
+ RemoteOperation operation = new RemoveFileOperation( mTargetFile,
+ true,
+ mContainerActivity.getStorageManager());
+ operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
+
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+ }
+ }
+ }
+
+ @Override
+ public void onNeutral(String callerTag) {
+ File f = null;
+ if (mTargetFile.isDirectory()) {
+ // TODO run in a secondary thread?
+ mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
+
+ } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
+ f.delete();
+ mTargetFile.setStoragePath(null);
+ mContainerActivity.getStorageManager().saveFile(mTargetFile);
+ }
+ listDirectory();
+ mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
+ }
+
+ @Override
+ public void onCancel(String callerTag) {
+ Log_OC.d(TAG, "REMOVAL CANCELED");
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ *
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import java.lang.ref.WeakReference;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+
+
+import eu.alefzero.webdav.OnDatatransferProgressListener;
+
+/**
+ * This Fragment is used to monitor the progress of a file downloading.
+ *
+ * @author David A. Velasco
+ */
+public class FileDownloadFragment extends FileFragment implements OnClickListener {
+
+ public static final String EXTRA_FILE = "FILE";
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+ private static final String EXTRA_ERROR = "ERROR";
+
+ private FileFragment.ContainerActivity mContainerActivity;
+
+ private View mView;
+ private Account mAccount;
+
+ public ProgressListener mProgressListener;
+ private boolean mListening;
+
+ private static final String TAG = FileDownloadFragment.class.getSimpleName();
+
+ private boolean mIgnoreFirstSavedState;
+ private boolean mError;
+
+
+ /**
+ * Creates an empty details fragment.
+ *
+ * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
+ */
+ public FileDownloadFragment() {
+ super();
+ mAccount = null;
+ mProgressListener = null;
+ mListening = false;
+ mIgnoreFirstSavedState = false;
+ mError = false;
+ }
+
+
+ /**
+ * Creates a details fragment.
+ *
+ * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
+ *
+ * @param fileToDetail An {@link OCFile} to show in the fragment
+ * @param ocAccount An ownCloud account; needed to start downloads
+ * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution
+ */
+ public FileDownloadFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
+ super(fileToDetail);
+ mAccount = ocAccount;
+ mProgressListener = null;
+ mListening = false;
+ mIgnoreFirstSavedState = ignoreFirstSavedState;
+ mError = false;
+ }
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+
+ if (savedInstanceState != null) {
+ if (!mIgnoreFirstSavedState) {
+ setFile((OCFile)savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_FILE));
+ mAccount = savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_ACCOUNT);
+ mError = savedInstanceState.getBoolean(FileDownloadFragment.EXTRA_ERROR);
+ } else {
+ mIgnoreFirstSavedState = false;
+ }
+ }
+
+ View view = null;
+ view = inflater.inflate(R.layout.file_download_fragment, container, false);
+ mView = view;
+
+ ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.progressBar);
+ mProgressListener = new ProgressListener(progressBar);
+
+ ((ImageButton)mView.findViewById(R.id.cancelBtn)).setOnClickListener(this);
+
+ if (mError) {
+ setButtonsForRemote();
+ } else {
+ setButtonsForTransferring();
+ }
+
+ return view;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mContainerActivity = (ContainerActivity) activity;
+
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (mAccount != null) {
+ //mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());;
+ }
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(FileDownloadFragment.EXTRA_FILE, getFile());
+ outState.putParcelable(FileDownloadFragment.EXTRA_ACCOUNT, mAccount);
+ outState.putBoolean(FileDownloadFragment.EXTRA_ERROR, mError);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ listenForTransferProgress();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ leaveTransferProgress();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+
+ @Override
+ public View getView() {
+ if (!mListening) {
+ listenForTransferProgress();
+ }
+ return super.getView() == null ? mView : super.getView();
+ }
+
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.cancelBtn: {
+ FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
+ if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
+ downloaderBinder.cancel(mAccount, getFile());
+ getActivity().finish(); // :)
+ /*
+ leaveTransferProgress();
+ if (mFile.isDown()) {
+ setButtonsForDown();
+ } else {
+ setButtonsForRemote();
+ }
+ */
+ }
+ break;
+ }
+ default:
+ Log_OC.e(TAG, "Incorrect view clicked!");
+ }
+ }
+
+
+ /**
+ * Updates the view depending upon the state of the downloading file.
+ *
+ * @param transferring When true, the view must be updated assuming that the holded file is
+ * downloading, no matter what the downloaderBinder says.
+ */
+ public void updateView(boolean transferring) {
+ // configure UI for depending upon local state of the file
+ FileDownloaderBinder downloaderBinder = (mContainerActivity == null) ? null : mContainerActivity.getFileDownloaderBinder();
+ if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile()))) {
+ setButtonsForTransferring();
+
+ } else if (getFile().isDown()) {
+
+ setButtonsForDown();
+
+ } else {
+ setButtonsForRemote();
+ }
+ getView().invalidate();
+
+ }
+
+
+ /**
+ * Enables or disables buttons for a file being downloaded
+ */
+ private void setButtonsForTransferring() {
+ getView().findViewById(R.id.cancelBtn).setVisibility(View.VISIBLE);
+
+ // show the progress bar for the transfer
+ getView().findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
+ TextView progressText = (TextView)getView().findViewById(R.id.progressText);
+ progressText.setText(R.string.downloader_download_in_progress_ticker);
+ progressText.setVisibility(View.VISIBLE);
+
+ // hides the error icon
+ getView().findViewById(R.id.errorText).setVisibility(View.GONE);
+ getView().findViewById(R.id.error_image).setVisibility(View.GONE);
+ }
+
+
+ /**
+ * Enables or disables buttons for a file locally available
+ */
+ private void setButtonsForDown() {
+ getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
+
+ // hides the progress bar
+ getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
+
+ // updates the text message
+ TextView progressText = (TextView)getView().findViewById(R.id.progressText);
+ progressText.setText(R.string.common_loading);
+ progressText.setVisibility(View.VISIBLE);
+
+ // hides the error icon
+ getView().findViewById(R.id.errorText).setVisibility(View.GONE);
+ getView().findViewById(R.id.error_image).setVisibility(View.GONE);
+ }
+
+
+ /**
+ * Enables or disables buttons for a file not locally available
+ *
+ * Currently, this is only used when a download was failed
+ */
+ private void setButtonsForRemote() {
+ getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
+
+ // hides the progress bar and message
+ getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
+ getView().findViewById(R.id.progressText).setVisibility(View.GONE);
+
+ // shows the error icon and message
+ getView().findViewById(R.id.errorText).setVisibility(View.VISIBLE);
+ getView().findViewById(R.id.error_image).setVisibility(View.VISIBLE);
+ }
+
+
+ public void listenForTransferProgress() {
+ if (mProgressListener != null && !mListening) {
+ if (mContainerActivity.getFileDownloaderBinder() != null) {
+ mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ mListening = true;
+ setButtonsForTransferring();
+ }
+ }
+ }
+
+
+ public void leaveTransferProgress() {
+ if (mProgressListener != null) {
+ if (mContainerActivity.getFileDownloaderBinder() != null) {
+ mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
+ mListening = false;
+ }
+ }
+ }
+
+
+ /**
+ * Helper class responsible for updating the progress bar shown for file uploading or downloading
+ *
+ * @author David A. Velasco
+ */
+ private class ProgressListener implements OnDatatransferProgressListener {
+ int mLastPercent = 0;
+ WeakReference<ProgressBar> mProgressBar = null;
+
+ ProgressListener(ProgressBar progressBar) {
+ mProgressBar = new WeakReference<ProgressBar>(progressBar);
+ }
+
+ @Override
+ public void onTransferProgress(long progressRate) {
+ // old method, nothing here
+ };
+
+ @Override
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
+ int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+ if (percent != mLastPercent) {
+ ProgressBar pb = mProgressBar.get();
+ if (pb != null) {
+ pb.setProgress(percent);
+ pb.postInvalidate();
+ }
+ }
+ mLastPercent = percent;
+ }
+
+ }
+
+
+ public void setError(boolean error) {
+ mError = error;
+ };
+
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
+import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.dialog.LoadingDialog;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+
+/**
+ * Holds a swiping galley where image files contained in an ownCloud directory are shown
+ *
+ * @author David A. Velasco
+ */
+public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener {
+
+ public static final int DIALOG_SHORT_WAIT = 0;
+
+ public static final String TAG = PreviewImageActivity.class.getSimpleName();
+
+ public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
+ private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER";
+
+ private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
+
+ private DataStorageManager mStorageManager;
+
+ private ViewPager mViewPager;
+ private PreviewImagePagerAdapter mPreviewImagePagerAdapter;
+
+ private FileDownloaderBinder mDownloaderBinder = null;
+ private ServiceConnection mDownloadConnection, mUploadConnection = null;
+ private FileUploaderBinder mUploaderBinder = null;
+
+ private boolean mRequestWaitingForBinder;
+
+ private DownloadFinishReceiver mDownloadFinishReceiver;
+
+ private boolean mFullScreen;
+
+ private String mDownloadAddedMessage;
+ private String mDownloadFinishMessage;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ setContentView(R.layout.preview_image_activity);
+
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.hide();
+
+ mFullScreen = true;
+ if (savedInstanceState != null) {
+ mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
+ } else {
+ mRequestWaitingForBinder = false;
+ }
+
+ FileDownloader downloader = new FileDownloader();
+ mDownloadAddedMessage = downloader.getDownloadAddedMessage();
+ mDownloadFinishMessage= downloader.getDownloadFinishMessage();
+ }
+
+ private void initViewPager() {
+ // get parent from path
+ String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
+ OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
+ //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
+ if (parentFolder == null) {
+ // should not be necessary
+ parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
+ }
+ mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
+ mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
+ int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
+ position = (position >= 0) ? position : 0;
+ mViewPager.setAdapter(mPreviewImagePagerAdapter);
+ mViewPager.setOnPageChangeListener(this);
+ mViewPager.setCurrentItem(position);
+ if (position == 0 && !getFile().isDown()) {
+ // this is necessary because mViewPager.setCurrentItem(0) just after setting the adapter does not result in a call to #onPageSelected(0)
+ mRequestWaitingForBinder = true;
+ }
+ }
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mDownloadConnection = new PreviewImageServiceConnection();
+ bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
+ mUploadConnection = new PreviewImageServiceConnection();
+ bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder);
+ }
+
+
+ /** Defines callbacks for service binding, passed to bindService() */
+ private class PreviewImageServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+
+ if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+ mDownloaderBinder = (FileDownloaderBinder) service;
+ if (mRequestWaitingForBinder) {
+ mRequestWaitingForBinder = false;
+ Log_OC.d(TAG, "Simulating reselection of current page after connection of download binder");
+ onPageSelected(mViewPager.getCurrentItem());
+ }
+
+ } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service connected");
+ mUploaderBinder = (FileUploaderBinder) service;
+ } else {
+ return;
+ }
+
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
+ Log_OC.d(TAG, "Download service suddenly disconnected");
+ mDownloaderBinder = null;
+ } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service suddenly disconnected");
+ mUploaderBinder = null;
+ }
+ }
+ };
+
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mDownloadConnection != null) {
+ unbindService(mDownloadConnection);
+ mDownloadConnection = null;
+ }
+ if (mUploadConnection != null) {
+ unbindService(mUploadConnection);
+ mUploadConnection = null;
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean returnValue = false;
+
+ switch(item.getItemId()){
+ case android.R.id.home:
+ backToDisplayActivity();
+ returnValue = true;
+ break;
+ default:
+ returnValue = super.onOptionsItemSelected(item);
+ }
+
+ return returnValue;
+ }
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ //Log.e(TAG, "ACTIVITY, ONRESUME");
+ mDownloadFinishReceiver = new DownloadFinishReceiver();
+
+ IntentFilter filter = new IntentFilter(mDownloadFinishMessage);
+ filter.addAction(mDownloadAddedMessage);
+ registerReceiver(mDownloadFinishReceiver, filter);
+ }
+
+ @Override
+ protected void onPostResume() {
+ //Log.e(TAG, "ACTIVITY, ONPOSTRESUME");
+ super.onPostResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(mDownloadFinishReceiver);
+ mDownloadFinishReceiver = null;
+ }
+
+
+ private void backToDisplayActivity() {
+ finish();
+ }
+
+ /**
+ * Show loading dialog
+ */
+ public void showLoadingDialog() {
+ // Construct dialog
+ LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
+ FragmentManager fm = getSupportFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ loading.show(ft, DIALOG_WAIT_TAG);
+
+ }
+
+ /**
+ * Dismiss loading dialog
+ */
+ public void dismissLoadingDialog(){
+ Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
+ if (frag != null) {
+ LoadingDialog loading = (LoadingDialog) frag;
+ loading.dismiss();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFileStateChanged() {
+ // nothing to do here!
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public FileDownloaderBinder getFileDownloaderBinder() {
+ return mDownloaderBinder;
+ }
+
+
+ @Override
+ public FileUploaderBinder getFileUploaderBinder() {
+ return mUploaderBinder;
+ }
+
+
+ @Override
+ public void showDetails(OCFile file) {
+ Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
+ showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file);
+ showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
+ startActivity(showDetailsIntent);
+ int pos = mPreviewImagePagerAdapter.getFilePosition(file);
+ file = mPreviewImagePagerAdapter.getFileAt(pos);
+
+ }
+
+
+ private void requestForDownload(OCFile file) {
+ if (mDownloaderBinder == null) {
+ Log_OC.d(TAG, "requestForDownload called without binder to download service");
+
+ } else if (!mDownloaderBinder.isDownloading(getAccount(), file)) {
+ Intent i = new Intent(this, FileDownloader.class);
+ i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
+ i.putExtra(FileDownloader.EXTRA_FILE, file);
+ startService(i);
+ }
+ }
+
+ /**
+ * This method will be invoked when a new page becomes selected. Animation is not necessarily complete.
+ *
+ * @param Position Position index of the new selected page
+ */
+ @Override
+ public void onPageSelected(int position) {
+ if (mDownloaderBinder == null) {
+ mRequestWaitingForBinder = true;
+
+ } else {
+ OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position);
+ getSupportActionBar().setTitle(currentFile.getFileName());
+ if (!currentFile.isDown()) {
+ if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) {
+ requestForDownload(currentFile);
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the scroll state changes. Useful for discovering when the user begins dragging,
+ * when the pager is automatically settling to the current page. when it is fully stopped/idle.
+ *
+ * @param State The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING
+ */
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+
+ /**
+ * This method will be invoked when the current page is scrolled, either as part of a programmatically
+ * initiated smooth scroll or a user initiated touch scroll.
+ *
+ * @param position Position index of the first page currently being displayed.
+ * Page position+1 will be visible if positionOffset is nonzero.
+ *
+ * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
+ * @param positionOffsetPixels Value in pixels indicating the offset from position.
+ */
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+
+ /**
+ * Class waiting for broadcast events from the {@link FielDownloader} service.
+ *
+ * Updates the UI when a download is started or finished, provided that it is relevant for the
+ * folder displayed in the gallery.
+ */
+ private class DownloadFinishReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
+ String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
+ if (getAccount().name.equals(accountName) &&
+ downloadedRemotePath != null) {
+
+ OCFile file = mStorageManager.getFileByPath(downloadedRemotePath);
+ int position = mPreviewImagePagerAdapter.getFilePosition(file);
+ boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
+ //boolean isOffscreen = Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
+
+ if (position >= 0 && intent.getAction().equals(mDownloadFinishMessage)) {
+ if (downloadWasFine) {
+ mPreviewImagePagerAdapter.updateFile(position, file);
+
+ } else {
+ mPreviewImagePagerAdapter.updateWithDownloadError(position);
+ }
+ mPreviewImagePagerAdapter.notifyDataSetChanged(); // will trigger the creation of new fragments
+
+ } else {
+ Log_OC.d(TAG, "Download finished, but the fragment is offscreen");
+ }
+
+ }
+ removeStickyBroadcast(intent);
+ }
+
+ }
+
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ toggleFullScreen();
+ }
+ return true;
+ }
+
+
+ private void toggleFullScreen() {
+ ActionBar actionBar = getSupportActionBar();
+ if (mFullScreen) {
+ actionBar.show();
+
+ } else {
+ actionBar.hide();
+
+ }
+ mFullScreen = !mFullScreen;
+ }
+
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ OCFile file = getFile();
+ /// Validate handled file (first image to preview)
+ if (file == null) {
+ throw new IllegalStateException("Instanced with a NULL OCFile");
+ }
+ if (!file.isImage()) {
+ throw new IllegalArgumentException("Non-image file passed as argument");
+ }
+ mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+
+ // Update file according to DB file, if it is possible
+ if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID)
+ file = mStorageManager.getFileById(file.getFileId());
+
+ if (file != null) {
+ /// Refresh the activity according to the Account and OCFile set
+ setFile(file); // reset after getting it fresh from mStorageManager
+ getSupportActionBar().setTitle(getFile().getFileName());
+ //if (!stateWasRecovered) {
+ initViewPager();
+ //}
+
+ } else {
+ // handled file not in the current Account
+ finish();
+ }
+
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ }
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+
+import android.accounts.Account;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapFactory.Options;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import eu.alefzero.webdav.WebdavUtils;
+
+
+/**
+ * This fragment shows a preview of a downloaded image.
+ *
+ * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
+ *
+ * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
+ *
+ * @author David A. Velasco
+ */
+public class PreviewImageFragment extends FileFragment implements OnRemoteOperationListener,
+ ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
+ public static final String EXTRA_FILE = "FILE";
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+
+ private View mView;
+ private Account mAccount;
+ private FileDataStorageManager mStorageManager;
+ private ImageView mImageView;
+ private TextView mMessageView;
+ private ProgressBar mProgressWheel;
+
+ public Bitmap mBitmap = null;
+
+ private Handler mHandler;
+ private RemoteOperation mLastRemoteOperation;
+
+ private static final String TAG = PreviewImageFragment.class.getSimpleName();
+
+ private boolean mIgnoreFirstSavedState;
+
+
+ /**
+ * Creates a fragment to preview an image.
+ *
+ * When 'imageFile' or 'ocAccount' are null
+ *
+ * @param imageFile An {@link OCFile} to preview as an image in the fragment
+ * @param ocAccount An ownCloud account; needed to start downloads
+ * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution
+ */
+ public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
+ super(fileToDetail);
+ mAccount = ocAccount;
+ mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
+ mIgnoreFirstSavedState = ignoreFirstSavedState;
+ }
+
+
+ /**
+ * Creates an empty fragment for image previews.
+ *
+ * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
+ *
+ * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction
+ */
+ public PreviewImageFragment() {
+ super();
+ mAccount = null;
+ mStorageManager = null;
+ mIgnoreFirstSavedState = false;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mHandler = new Handler();
+ setHasOptionsMenu(true);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ mView = inflater.inflate(R.layout.preview_image_fragment, container, false);
+ mImageView = (ImageView)mView.findViewById(R.id.image);
+ mImageView.setVisibility(View.GONE);
+ mView.setOnTouchListener((OnTouchListener)getActivity()); // WATCH OUT THAT CAST
+ mMessageView = (TextView)mView.findViewById(R.id.message);
+ mMessageView.setVisibility(View.GONE);
+ mProgressWheel = (ProgressBar)mView.findViewById(R.id.progressWheel);
+ mProgressWheel.setVisibility(View.VISIBLE);
+ return mView;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ if (!(activity instanceof FileFragment.ContainerActivity))
+ throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+ if (savedInstanceState != null) {
+ if (!mIgnoreFirstSavedState) {
+ setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
+ mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
+ } else {
+ mIgnoreFirstSavedState = false;
+ }
+ }
+ if (getFile() == null) {
+ throw new IllegalStateException("Instanced with a NULL OCFile");
+ }
+ if (mAccount == null) {
+ throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+ }
+ if (!getFile().isDown()) {
+ throw new IllegalStateException("There is no local file to preview");
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(PreviewImageFragment.EXTRA_FILE, getFile());
+ outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
+ }
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if (getFile() != null) {
+ BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
+ bl.execute(new String[]{getFile().getStoragePath()});
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+
+ inflater.inflate(R.menu.file_actions_menu, menu);
+ List<Integer> toHide = new ArrayList<Integer>();
+
+ MenuItem item = null;
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_rename_file); // by now
+
+ for (int i : toHide) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_open_file_with: {
+ openFile();
+ return true;
+ }
+ case R.id.action_remove_file: {
+ removeFile();
+ return true;
+ }
+ case R.id.action_see_details: {
+ seeDetails();
+ return true;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+
+ private void seeDetails() {
+ ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ }
+
+
+ /**
+ * Opens the previewed image with an external application.
+ *
+ * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,
+ * we should get a list of available apps for MIME tpye in the server and join it with the list of
+ * available apps for the MIME type known from the file extension, to let the user choose
+ */
+ private void openFile() {
+ OCFile file = getFile();
+ String storagePath = file.getStoragePath();
+ String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+
+ } catch (Throwable t) {
+ Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.getMimetype());
+ boolean toastIt = true;
+ String mimeType = "";
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+ if (mimeType == null || !mimeType.equals(file.getMimetype())) {
+ if (mimeType != null) {
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+ } else {
+ // desperate try
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
+ }
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+ toastIt = false;
+ }
+
+ } catch (IndexOutOfBoundsException e) {
+ Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+
+ } catch (ActivityNotFoundException e) {
+ Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+
+ } catch (Throwable th) {
+ Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+
+ } finally {
+ if (toastIt) {
+ Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ }
+ finish();
+ }
+
+
+ /**
+ * Starts a the removal of the previewed file.
+ *
+ * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
+ * depending upon the user selection in the dialog.
+ */
+ private void removeFile() {
+ ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+ R.string.confirmation_remove_alert,
+ new String[]{getFile().getFileName()},
+ R.string.confirmation_remove_remote_and_local,
+ R.string.confirmation_remove_local,
+ R.string.common_cancel);
+ confDialog.setOnConfirmationListener(this);
+ confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
+ }
+
+
+ /**
+ * Performs the removal of the previewed file, both locally and in the server.
+ */
+ @Override
+ public void onConfirmation(String callerTag) {
+ if (mStorageManager.getFileById(getFile().getFileId()) != null) { // check that the file is still there;
+ mLastRemoteOperation = new RemoveFileOperation( getFile(), // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
+ true,
+ mStorageManager);
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+
+ ((PreviewImageActivity) getActivity()).showLoadingDialog();
+ }
+ }
+
+
+ /**
+ * Removes the file from local storage
+ */
+ @Override
+ public void onNeutral(String callerTag) {
+ // TODO this code should be made in a secondary thread,
+ OCFile file = getFile();
+ if (file.isDown()) { // checks it is still there
+ File f = new File(file.getStoragePath());
+ f.delete();
+ file.setStoragePath(null);
+ mStorageManager.saveFile(file);
+ finish();
+ }
+ }
+
+ /**
+ * User cancelled the removal action.
+ */
+ @Override
+ public void onCancel(String callerTag) {
+ // nothing to do here
+ }
+
+
+ private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
+
+ /**
+ * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
+ *
+ * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+ */
+ private final WeakReference<ImageView> mImageViewRef;
+
+ /**
+ * Weak reference to the target {@link TextView} where error messages will be written.
+ *
+ * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+ */
+ private final WeakReference<TextView> mMessageViewRef;
+
+
+ /**
+ * Weak reference to the target {@link Progressbar} shown while the load is in progress.
+ *
+ * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
+ */
+ private final WeakReference<ProgressBar> mProgressWheelRef;
+
+
+ /**
+ * Error message to show when a load fails
+ */
+ private int mErrorMessageId;
+
+
+ /**
+ * Constructor.
+ *
+ * @param imageView Target {@link ImageView} where the bitmap will be loaded into.
+ */
+ public BitmapLoader(ImageView imageView, TextView messageView, ProgressBar progressWheel) {
+ mImageViewRef = new WeakReference<ImageView>(imageView);
+ mMessageViewRef = new WeakReference<TextView>(messageView);
+ mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
+ }
+
+
+ @SuppressWarnings("deprecation")
+ @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
+ @Override
+ protected Bitmap doInBackground(String... params) {
+ Bitmap result = null;
+ if (params.length != 1) return result;
+ String storagePath = params[0];
+ try {
+ // set desired options that will affect the size of the bitmap
+ BitmapFactory.Options options = new Options();
+ options.inScaled = true;
+ options.inPurgeable = true;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ options.inPreferQualityOverSpeed = false;
+ }
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ options.inMutable = false;
+ }
+ // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(storagePath, options);
+
+ int width = options.outWidth;
+ int height = options.outHeight;
+ int scale = 1;
+
+ Display display = getActivity().getWindowManager().getDefaultDisplay();
+ Point size = new Point();
+ int screenWidth;
+ int screenHeight;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
+ display.getSize(size);
+ screenWidth = size.x;
+ screenHeight = size.y;
+ } else {
+ screenWidth = display.getWidth();
+ screenHeight = display.getHeight();
+ }
+
+ if (width > screenWidth) {
+ // second try to scale down the image , this time depending upon the screen size
+ scale = (int) Math.floor((float)width / screenWidth);
+ }
+ if (height > screenHeight) {
+ scale = Math.max(scale, (int) Math.floor((float)height / screenHeight));
+ }
+ options.inSampleSize = scale;
+
+ // really load the bitmap
+ options.inJustDecodeBounds = false; // the next decodeFile call will be real
+ result = BitmapFactory.decodeFile(storagePath, options);
+ //Log_OC.d(TAG, "Image loaded - width: " + options.outWidth + ", loaded height: " + options.outHeight);
+
+ if (result == null) {
+ mErrorMessageId = R.string.preview_image_error_unknown_format;
+ Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+ }
+
+ } catch (OutOfMemoryError e) {
+ mErrorMessageId = R.string.preview_image_error_unknown_format;
+ Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
+
+ } catch (NoSuchFieldError e) {
+ mErrorMessageId = R.string.common_error_unknown;
+ Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " + storagePath, e);
+
+ } catch (Throwable t) {
+ mErrorMessageId = R.string.common_error_unknown;
+ Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
+
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ hideProgressWheel();
+ if (result != null) {
+ showLoadedImage(result);
+ } else {
+ showErrorMessage();
+ }
+ }
+
+ private void showLoadedImage(Bitmap result) {
+ if (mImageViewRef != null) {
+ final ImageView imageView = mImageViewRef.get();
+ if (imageView != null) {
+ imageView.setImageBitmap(result);
+ imageView.setVisibility(View.VISIBLE);
+ mBitmap = result;
+ } // else , silently finish, the fragment was destroyed
+ }
+ if (mMessageViewRef != null) {
+ final TextView messageView = mMessageViewRef.get();
+ if (messageView != null) {
+ messageView.setVisibility(View.GONE);
+ } // else , silently finish, the fragment was destroyed
+ }
+ }
+
+ private void showErrorMessage() {
+ if (mImageViewRef != null) {
+ final ImageView imageView = mImageViewRef.get();
+ if (imageView != null) {
+ // shows the default error icon
+ imageView.setVisibility(View.VISIBLE);
+ } // else , silently finish, the fragment was destroyed
+ }
+ if (mMessageViewRef != null) {
+ final TextView messageView = mMessageViewRef.get();
+ if (messageView != null) {
+ messageView.setText(mErrorMessageId);
+ messageView.setVisibility(View.VISIBLE);
+ } // else , silently finish, the fragment was destroyed
+ }
+ }
+
+ private void hideProgressWheel() {
+ if (mProgressWheelRef != null) {
+ final ProgressBar progressWheel = mProgressWheelRef.get();
+ if (progressWheel != null) {
+ progressWheel.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} to be previewed.
+ *
+ * @param file File to test if can be previewed.
+ * @return 'True' if the file can be handled by the fragment.
+ */
+ public static boolean canBePreviewed(OCFile file) {
+ return (file != null && file.isImage());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) {
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+ }
+ }
+
+ private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+ ((PreviewImageActivity) getActivity()).dismissLoadingDialog();
+
+ if (result.isSuccess()) {
+ Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+ msg.show();
+ finish();
+
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ // TODO show the SSL warning dialog
+ }
+ }
+ }
+
+ /**
+ * Finishes the preview
+ */
+ private void finish() {
+ Activity container = getActivity();
+ container.finish();
+ }
+
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import android.accounts.Account;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.ViewGroup;
+
+
+/**
+ * Adapter class that provides Fragment instances
+ *
+ * @author David A. Velasco
+ */
+//public class PreviewImagePagerAdapter extends PagerAdapter {
+public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
+
+ private Vector<OCFile> mImageFiles;
+ private Account mAccount;
+ private Set<Object> mObsoleteFragments;
+ private Set<Integer> mObsoletePositions;
+ private Set<Integer> mDownloadErrors;
+ private DataStorageManager mStorageManager;
+
+ private Map<Integer, FileFragment> mCachedFragments;
+
+ /**
+ * Constructor.
+ *
+ * @param fragmentManager {@link FragmentManager} instance that will handle the {@link Fragment}s provided by the adapter.
+ * @param parentFolder Folder where images will be searched for.
+ * @param storageManager Bridge to database.
+ */
+ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) {
+ super(fragmentManager);
+
+ if (fragmentManager == null) {
+ throw new IllegalArgumentException("NULL FragmentManager instance");
+ }
+ if (parentFolder == null) {
+ throw new IllegalArgumentException("NULL parent folder");
+ }
+ if (storageManager == null) {
+ throw new IllegalArgumentException("NULL storage manager");
+ }
+
+ mAccount = account;
+ mStorageManager = storageManager;
+ mImageFiles = mStorageManager.getDirectoryImages(parentFolder);
+ mObsoleteFragments = new HashSet<Object>();
+ mObsoletePositions = new HashSet<Integer>();
+ mDownloadErrors = new HashSet<Integer>();
+ //mFragmentManager = fragmentManager;
+ mCachedFragments = new HashMap<Integer, FileFragment>();
+ }
+
+
+ /**
+ * Returns the image files handled by the adapter.
+ *
+ * @return A vector with the image files handled by the adapter.
+ */
+ protected OCFile getFileAt(int position) {
+ return mImageFiles.get(position);
+ }
+
+
+ public Fragment getItem(int i) {
+ OCFile file = mImageFiles.get(i);
+ Fragment fragment = null;
+ if (file.isDown()) {
+ fragment = new PreviewImageFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+
+ } else if (mDownloadErrors.contains(Integer.valueOf(i))) {
+ fragment = new FileDownloadFragment(file, mAccount, true);
+ ((FileDownloadFragment)fragment).setError(true);
+ mDownloadErrors.remove(Integer.valueOf(i));
+
+ } else {
+ fragment = new FileDownloadFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
+ }
+ mObsoletePositions.remove(Integer.valueOf(i));
+ return fragment;
+ }
+
+ public int getFilePosition(OCFile file) {
+ return mImageFiles.indexOf(file);
+ }
+
+ @Override
+ public int getCount() {
+ return mImageFiles.size();
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mImageFiles.get(position).getFileName();
+ }
+
+
+ public void updateFile(int position, OCFile file) {
+ FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+ if (fragmentToUpdate != null) {
+ mObsoleteFragments.add(fragmentToUpdate);
+ }
+ mObsoletePositions.add(Integer.valueOf(position));
+ mImageFiles.set(position, file);
+ }
+
+
+ public void updateWithDownloadError(int position) {
+ FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+ if (fragmentToUpdate != null) {
+ mObsoleteFragments.add(fragmentToUpdate);
+ }
+ mDownloadErrors.add(Integer.valueOf(position));
+ }
+
+ public void clearErrorAt(int position) {
+ FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
+ if (fragmentToUpdate != null) {
+ mObsoleteFragments.add(fragmentToUpdate);
+ }
+ mDownloadErrors.remove(Integer.valueOf(position));
+ }
+
+
+ @Override
+ public int getItemPosition(Object object) {
+ if (mObsoleteFragments.contains(object)) {
+ mObsoleteFragments.remove(object);
+ return POSITION_NONE;
+ }
+ return super.getItemPosition(object);
+ }
+
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ Object fragment = super.instantiateItem(container, position);
+ mCachedFragments.put(Integer.valueOf(position), (FileFragment)fragment);
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ mCachedFragments.remove(Integer.valueOf(position));
+ super.destroyItem(container, position, object);
+ }
+
+
+ public boolean pendingErrorAt(int position) {
+ return mDownloadErrors.contains(Integer.valueOf(position));
+ }
+
+ /* -*
+ * Called when a change in the shown pages is going to start being made.
+ *
+ * @param container The containing View which is displaying this adapter's page views.
+ *- /
+ @Override
+ public void startUpdate(ViewGroup container) {
+ Log.e(TAG, "** startUpdate");
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ Log.e(TAG, "** instantiateItem " + position);
+
+ if (mFragments.size() > position) {
+ Fragment fragment = mFragments.get(position);
+ if (fragment != null) {
+ Log.e(TAG, "** \t returning cached item");
+ return fragment;
+ }
+ }
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+
+ Fragment fragment = getItem(position);
+ if (mSavedState.size() > position) {
+ Fragment.SavedState savedState = mSavedState.get(position);
+ if (savedState != null) {
+ // TODO WATCH OUT:
+ // * The Fragment must currently be attached to the FragmentManager.
+ // * A new Fragment created using this saved state must be the same class type as the Fragment it was created from.
+ // * The saved state can not contain dependencies on other fragments -- that is it can't use putFragment(Bundle, String, Fragment)
+ // to store a fragment reference
+ fragment.setInitialSavedState(savedState);
+ }
+ }
+ while (mFragments.size() <= position) {
+ mFragments.add(null);
+ }
+ fragment.setMenuVisibility(false);
+ mFragments.set(position, fragment);
+ //Log.e(TAG, "** \t adding fragment at position " + position + ", containerId " + container.getId());
+ mCurTransaction.add(container.getId(), fragment);
+
+ return fragment;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ Log.e(TAG, "** destroyItem " + position);
+ Fragment fragment = (Fragment)object;
+
+ if (mCurTransaction == null) {
+ mCurTransaction = mFragmentManager.beginTransaction();
+ }
+ Log.e(TAG, "** \t removing fragment at position " + position);
+ while (mSavedState.size() <= position) {
+ mSavedState.add(null);
+ }
+ mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+ mFragments.set(position, null);
+
+ mCurTransaction.remove(fragment);
+ }
+
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ Fragment fragment = (Fragment)object;
+ if (fragment != mCurrentPrimaryItem) {
+ if (mCurrentPrimaryItem != null) {
+ mCurrentPrimaryItem.setMenuVisibility(false);
+ }
+ if (fragment != null) {
+ fragment.setMenuVisibility(true);
+ }
+ mCurrentPrimaryItem = fragment;
+ }
+ }
+
+ @Override
+ public void finishUpdate(ViewGroup container) {
+ Log.e(TAG, "** finishUpdate (start)");
+ if (mCurTransaction != null) {
+ mCurTransaction.commitAllowingStateLoss();
+ mCurTransaction = null;
+ mFragmentManager.executePendingTransactions();
+ }
+ Log.e(TAG, "** finishUpdate (end)");
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((Fragment)object).getView() == view;
+ }
+
+ @Override
+ public Parcelable saveState() {
+ Bundle state = null;
+ if (mSavedState.size() > 0) {
+ state = new Bundle();
+ Fragment.SavedState[] savedStates = new Fragment.SavedState[mSavedState.size()];
+ mSavedState.toArray(savedStates);
+ state.putParcelableArray("states", savedStates);
+ }
+ for (int i=0; i<mFragments.size(); i++) {
+ Fragment fragment = mFragments.get(i);
+ if (fragment != null) {
+ if (state == null) {
+ state = new Bundle();
+ }
+ String key = "f" + i;
+ mFragmentManager.putFragment(state, key, fragment);
+ }
+ }
+ return state;
+ }
+
+ @Override
+ public void restoreState(Parcelable state, ClassLoader loader) {
+ if (state != null) {
+ Bundle bundle = (Bundle)state;
+ bundle.setClassLoader(loader);
+ Parcelable[] states = bundle.getParcelableArray("states");
+ mSavedState.clear();
+ mFragments.clear();
+ if (states != null) {
+ for (int i=0; i<states.length; i++) {
+ mSavedState.add((Fragment.SavedState)states[i]);
+ }
+ }
+ Iterable<String> keys = bundle.keySet();
+ for (String key: keys) {
+ if (key.startsWith("f")) {
+ int index = Integer.parseInt(key.substring(1));
+ Fragment f = mFragmentManager.getFragment(bundle, key);
+ if (f != null) {
+ while (mFragments.size() <= index) {
+ mFragments.add(null);
+ }
+ f.setMenuVisibility(false);
+ mFragments.set(index, f);
+ } else {
+ Log.w(TAG, "Bad fragment at key " + key);
+ }
+ }
+ }
+ }
+ }
+ */
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.res.Configuration;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.webkit.MimeTypeMap;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaControlView;
+import com.owncloud.android.media.MediaService;
+import com.owncloud.android.media.MediaServiceBinder;
+import com.owncloud.android.operations.OnRemoteOperationListener;
+import com.owncloud.android.operations.RemoteOperation;
+import com.owncloud.android.operations.RemoteOperationResult;
+import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
+import com.owncloud.android.ui.fragment.FileFragment;
+
+import eu.alefzero.webdav.WebdavUtils;
+
+/**
+ * This fragment shows a preview of a downloaded media file (audio or video).
+ *
+ * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
+ *
+ * By now, if the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
+ *
+ * @author David A. Velasco
+ */
+public class PreviewMediaFragment extends FileFragment implements
+ OnTouchListener,
+ ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener {
+
+ public static final String EXTRA_FILE = "FILE";
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+ private static final String EXTRA_PLAY_POSITION = "PLAY_POSITION";
+ private static final String EXTRA_PLAYING = "PLAYING";
+
+ private View mView;
+ private Account mAccount;
+ private FileDataStorageManager mStorageManager;
+ private ImageView mImagePreview;
+ private VideoView mVideoPreview;
+ private int mSavedPlaybackPosition;
+
+ private Handler mHandler;
+ private RemoteOperation mLastRemoteOperation;
+
+ private MediaServiceBinder mMediaServiceBinder = null;
+ private MediaControlView mMediaController = null;
+ private MediaServiceConnection mMediaServiceConnection = null;
+ private VideoHelper mVideoHelper;
+ private boolean mAutoplay;
+ public boolean mPrepared;
+
+ private static final String TAG = PreviewMediaFragment.class.getSimpleName();
+
+
+ /**
+ * Creates a fragment to preview a file.
+ *
+ * When 'fileToDetail' or 'ocAccount' are null
+ *
+ * @param fileToDetail An {@link OCFile} to preview in the fragment
+ * @param ocAccount An ownCloud account; needed to start downloads
+ */
+ public PreviewMediaFragment(OCFile fileToDetail, Account ocAccount, int startPlaybackPosition, boolean autoplay) {
+ super(fileToDetail);
+ mAccount = ocAccount;
+ mSavedPlaybackPosition = startPlaybackPosition;
+ mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
+ mAutoplay = autoplay;
+ }
+
+
+ /**
+ * Creates an empty fragment for previews.
+ *
+ * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
+ *
+ * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction
+ */
+ public PreviewMediaFragment() {
+ super();
+ mAccount = null;
+ mSavedPlaybackPosition = 0;
+ mStorageManager = null;
+ mAutoplay = true;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mHandler = new Handler();
+ setHasOptionsMenu(true);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ Log_OC.e(TAG, "onCreateView");
+
+
+ mView = inflater.inflate(R.layout.file_preview, container, false);
+
+ mImagePreview = (ImageView)mView.findViewById(R.id.image_preview);
+ mVideoPreview = (VideoView)mView.findViewById(R.id.video_preview);
+ mVideoPreview.setOnTouchListener(this);
+
+ mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
+
+ return mView;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ Log_OC.e(TAG, "onAttach");
+
+ if (!(activity instanceof FileFragment.ContainerActivity))
+ throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Log_OC.e(TAG, "onActivityCreated");
+
+ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+ if (savedInstanceState != null) {
+ setFile((OCFile)savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE));
+ mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
+ mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
+ mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
+
+ }
+ OCFile file = getFile();
+ if (file == null) {
+ throw new IllegalStateException("Instanced with a NULL OCFile");
+ }
+ if (mAccount == null) {
+ throw new IllegalStateException("Instanced with a NULL ownCloud Account");
+ }
+ if (!file.isDown()) {
+ throw new IllegalStateException("There is no local file to preview");
+ }
+ if (file.isVideo()) {
+ mVideoPreview.setVisibility(View.VISIBLE);
+ mImagePreview.setVisibility(View.GONE);
+ prepareVideo();
+
+ } else {
+ mVideoPreview.setVisibility(View.GONE);
+ mImagePreview.setVisibility(View.VISIBLE);
+ }
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ Log_OC.e(TAG, "onSaveInstanceState");
+
+ outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, getFile());
+ outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
+
+ if (getFile().isVideo()) {
+ mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+ mAutoplay = mVideoPreview.isPlaying();
+ outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mSavedPlaybackPosition);
+ outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mAutoplay);
+ } else {
+ outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mMediaServiceBinder.getCurrentPosition());
+ outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
+ }
+ }
+
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ Log_OC.e(TAG, "onStart");
+
+ OCFile file = getFile();
+ if (file != null) {
+ if (file.isAudio()) {
+ bindMediaService();
+
+ } else if (file.isVideo()) {
+ stopAudio();
+ playVideo();
+ }
+ }
+ }
+
+
+ private void stopAudio() {
+ Intent i = new Intent(getSherlockActivity(), MediaService.class);
+ i.setAction(MediaService.ACTION_STOP_ALL);
+ getSherlockActivity().startService(i);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+
+ inflater.inflate(R.menu.file_actions_menu, menu);
+ List<Integer> toHide = new ArrayList<Integer>();
+
+ MenuItem item = null;
+ toHide.add(R.id.action_cancel_download);
+ toHide.add(R.id.action_cancel_upload);
+ toHide.add(R.id.action_download_file);
+ toHide.add(R.id.action_sync_file);
+ toHide.add(R.id.action_rename_file); // by now
+
+ for (int i : toHide) {
+ item = menu.findItem(i);
+ if (item != null) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ }
+ }
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_open_file_with: {
+ openFile();
+ return true;
+ }
+ case R.id.action_remove_file: {
+ removeFile();
+ return true;
+ }
+ case R.id.action_see_details: {
+ seeDetails();
+ return true;
+ }
+
+ default:
+ return false;
+ }
+ }
+
+
+ private void seeDetails() {
+ stopPreview(false);
+ ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
+ }
+
+
+ private void prepareVideo() {
+ // create helper to get more control on the playback
+ mVideoHelper = new VideoHelper();
+ mVideoPreview.setOnPreparedListener(mVideoHelper);
+ mVideoPreview.setOnCompletionListener(mVideoHelper);
+ mVideoPreview.setOnErrorListener(mVideoHelper);
+ }
+
+ private void playVideo() {
+ // create and prepare control panel for the user
+ mMediaController.setMediaPlayer(mVideoPreview);
+
+ // load the video file in the video player ; when done, VideoHelper#onPrepared() will be called
+ mVideoPreview.setVideoPath(getFile().getStoragePath());
+ }
+
+
+ private class VideoHelper implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+
+ /**
+ * Called when the file is ready to be played.
+ *
+ * Just starts the playback.
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ */
+ @Override
+ public void onPrepared(MediaPlayer vp) {
+ Log_OC.e(TAG, "onPrepared");
+ mVideoPreview.seekTo(mSavedPlaybackPosition);
+ if (mAutoplay) {
+ mVideoPreview.start();
+ }
+ mMediaController.setEnabled(true);
+ mMediaController.updatePausePlay();
+ mPrepared = true;
+ }
+
+
+ /**
+ * Called when the file is finished playing.
+ *
+ * Finishes the activity.
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ */
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ Log_OC.e(TAG, "completed");
+ if (mp != null) {
+ mVideoPreview.seekTo(0);
+ // next lines are necessary to work around undesired video loops
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD) {
+ mVideoPreview.pause();
+
+ } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD_MR1) {
+ // mVideePreview.pause() is not enough
+
+ mMediaController.setEnabled(false);
+ mVideoPreview.stopPlayback();
+ mAutoplay = false;
+ mSavedPlaybackPosition = 0;
+ mVideoPreview.setVideoPath(getFile().getStoragePath());
+ }
+ } // else : called from onError()
+ mMediaController.updatePausePlay();
+ }
+
+
+ /**
+ * Called when an error in playback occurs.
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ * @param what Type of error
+ * @param extra Extra code specific to the error
+ */
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ if (mVideoPreview.getWindowToken() != null) {
+ String message = MediaService.getMessageForMediaError(getActivity(), what, extra);
+ new AlertDialog.Builder(getActivity())
+ .setMessage(message)
+ .setPositiveButton(android.R.string.VideoView_error_button,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ dialog.dismiss();
+ VideoHelper.this.onCompletion(null);
+ }
+ })
+ .setCancelable(false)
+ .show();
+ }
+ return true;
+ }
+
+ }
+
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ Log_OC.e(TAG, "onPause");
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log_OC.e(TAG, "onResume");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log_OC.e(TAG, "onDestroy");
+ }
+
+ @Override
+ public void onStop() {
+ Log_OC.e(TAG, "onStop");
+ super.onStop();
+
+ mPrepared = false;
+ if (mMediaServiceConnection != null) {
+ Log_OC.d(TAG, "Unbinding from MediaService ...");
+ if (mMediaServiceBinder != null && mMediaController != null) {
+ mMediaServiceBinder.unregisterMediaController(mMediaController);
+ }
+ getActivity().unbindService(mMediaServiceConnection);
+ mMediaServiceConnection = null;
+ mMediaServiceBinder = null;
+ }
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN && v == mVideoPreview) {
+ startFullScreenVideo();
+ return true;
+ }
+ return false;
+ }
+
+
+ private void startFullScreenVideo() {
+ Intent i = new Intent(getActivity(), PreviewVideoActivity.class);
+ i.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
+ i.putExtra(FileActivity.EXTRA_FILE, getFile());
+ i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
+ mVideoPreview.pause();
+ i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
+ startActivityForResult(i, 0);
+ }
+
+ @Override
+ public void onConfigurationChanged (Configuration newConfig) {
+ Log_OC.e(TAG, "onConfigurationChanged " + this);
+ }
+
+ @Override
+ public void onActivityResult (int requestCode, int resultCode, Intent data) {
+ Log_OC.e(TAG, "onActivityResult " + this);
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == Activity.RESULT_OK) {
+ mSavedPlaybackPosition = data.getExtras().getInt(PreviewVideoActivity.EXTRA_START_POSITION);
+ mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY);
+ }
+ }
+
+
+ private void playAudio() {
+ OCFile file = getFile();
+ if (!mMediaServiceBinder.isPlaying(file)) {
+ Log_OC.d(TAG, "starting playback of " + file.getStoragePath());
+ mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition);
+
+ } else {
+ if (!mMediaServiceBinder.isPlaying() && mAutoplay) {
+ mMediaServiceBinder.start();
+ mMediaController.updatePausePlay();
+ }
+ }
+ }
+
+
+ private void bindMediaService() {
+ Log_OC.d(TAG, "Binding to MediaService...");
+ if (mMediaServiceConnection == null) {
+ mMediaServiceConnection = new MediaServiceConnection();
+ }
+ getActivity().bindService( new Intent(getActivity(),
+ MediaService.class),
+ mMediaServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ // follow the flow in MediaServiceConnection#onServiceConnected(...)
+ }
+
+ /** Defines callbacks for service binding, passed to bindService() */
+ private class MediaServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+ if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
+ Log_OC.d(TAG, "Media service connected");
+ mMediaServiceBinder = (MediaServiceBinder) service;
+ if (mMediaServiceBinder != null) {
+ prepareMediaController();
+ playAudio(); // do not wait for the touch of nobody to play audio
+
+ Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
+
+ } else {
+ Log_OC.e(TAG, "Unexpected response from MediaService while binding");
+ }
+ }
+ }
+
+ private void prepareMediaController() {
+ mMediaServiceBinder.registerMediaController(mMediaController);
+ if (mMediaController != null) {
+ mMediaController.setMediaPlayer(mMediaServiceBinder);
+ mMediaController.setEnabled(true);
+ mMediaController.updatePausePlay();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
+ Log_OC.e(TAG, "Media service suddenly disconnected");
+ if (mMediaController != null) {
+ mMediaController.setMediaPlayer(null);
+ } else {
+ Toast.makeText(getActivity(), "No media controller to release when disconnected from media service", Toast.LENGTH_SHORT).show();
+ }
+ mMediaServiceBinder = null;
+ mMediaServiceConnection = null;
+ }
+ }
+ }
+
+
+
+ /**
+ * Opens the previewed file with an external application.
+ *
+ * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,
+ * we should get a list of available apps for MIME tpye in the server and join it with the list of
+ * available apps for the MIME type known from the file extension, to let the user choose
+ */
+ private void openFile() {
+ OCFile file = getFile();
+ stopPreview(true);
+ String storagePath = file.getStoragePath();
+ String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+
+ } catch (Throwable t) {
+ Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.getMimetype());
+ boolean toastIt = true;
+ String mimeType = "";
+ try {
+ Intent i = new Intent(Intent.ACTION_VIEW);
+ mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+ if (mimeType == null || !mimeType.equals(file.getMimetype())) {
+ if (mimeType != null) {
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
+ } else {
+ // desperate try
+ i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
+ }
+ i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ startActivity(i);
+ toastIt = false;
+ }
+
+ } catch (IndexOutOfBoundsException e) {
+ Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
+
+ } catch (ActivityNotFoundException e) {
+ Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
+
+ } catch (Throwable th) {
+ Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
+
+ } finally {
+ if (toastIt) {
+ Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ }
+ finish();
+ }
+
+ /**
+ * Starts a the removal of the previewed file.
+ *
+ * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
+ * depending upon the user selection in the dialog.
+ */
+ private void removeFile() {
+ ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
+ R.string.confirmation_remove_alert,
+ new String[]{getFile().getFileName()},
+ R.string.confirmation_remove_remote_and_local,
+ R.string.confirmation_remove_local,
+ R.string.common_cancel);
+ confDialog.setOnConfirmationListener(this);
+ confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
+ }
+
+
+ /**
+ * Performs the removal of the previewed file, both locally and in the server.
+ */
+ @Override
+ public void onConfirmation(String callerTag) {
+ OCFile file = getFile();
+ if (mStorageManager.getFileById(file.getFileId()) != null) { // check that the file is still there;
+ stopPreview(true);
+ mLastRemoteOperation = new RemoveFileOperation( file, // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
+ true,
+ mStorageManager);
+ mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
+
+ ((FileDisplayActivity) getActivity()).showLoadingDialog();
+ }
+ }
+
+
+ /**
+ * Removes the file from local storage
+ */
+ @Override
+ public void onNeutral(String callerTag) {
+ // TODO this code should be made in a secondary thread,
+ OCFile file = getFile();
+ if (file.isDown()) { // checks it is still there
+ stopPreview(true);
+ File f = new File(file.getStoragePath());
+ f.delete();
+ file.setStoragePath(null);
+ mStorageManager.saveFile(file);
+ finish();
+ }
+ }
+
+ /**
+ * User cancelled the removal action.
+ */
+ @Override
+ public void onCancel(String callerTag) {
+ // nothing to do here
+ }
+
+
+ /**
+ * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewMediaFragment} to be previewed.
+ *
+ * @param file File to test if can be previewed.
+ * @return 'True' if the file can be handled by the fragment.
+ */
+ public static boolean canBePreviewed(OCFile file) {
+ return (file != null && (file.isAudio() || file.isVideo()));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ if (operation.equals(mLastRemoteOperation)) {
+ if (operation instanceof RemoveFileOperation) {
+ onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
+ }
+ }
+ }
+
+ private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
+ ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
+ if (result.isSuccess()) {
+ Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
+ msg.show();
+ finish();
+
+ } else {
+ Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
+ msg.show();
+ if (result.isSslRecoverableException()) {
+ // TODO show the SSL warning dialog
+ }
+ }
+ }
+
+ private void stopPreview(boolean stopAudio) {
+ OCFile file = getFile();
+ if (file.isAudio() && stopAudio) {
+ mMediaServiceBinder.pause();
+
+ } else if (file.isVideo()) {
+ mVideoPreview.stopPlayback();
+ }
+ }
+
+
+
+ /**
+ * Finishes the preview
+ */
+ private void finish() {
+ getActivity().onBackPressed();
+ }
+
+
+ public int getPosition() {
+ if (mPrepared) {
+ mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
+ }
+ Log_OC.e(TAG, "getting position: " + mSavedPlaybackPosition);
+ return mSavedPlaybackPosition;
+ }
+
+ public boolean isPlaying() {
+ if (mPrepared) {
+ mAutoplay = mVideoPreview.isPlaying();
+ }
+ return mAutoplay;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.ui.preview;
+
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.datamodel.DataStorageManager;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.media.MediaService;
+import com.owncloud.android.ui.activity.FileActivity;
+
+import android.accounts.Account;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+
+/**
+ * Activity implementing a basic video player.
+ *
+ * Used as an utility to preview video files contained in an ownCloud account.
+ *
+ * Currently, it always plays in landscape mode, full screen. When the playback ends,
+ * the activity is finished.
+ *
+ * @author David A. Velasco
+ */
+public class PreviewVideoActivity extends FileActivity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
+
+ /** Key to receive a flag signaling if the video should be started immediately */
+ public static final String EXTRA_AUTOPLAY = "AUTOPLAY";
+
+ /** Key to receive the position of the playback where the video should be put at start */
+ public static final String EXTRA_START_POSITION = "START_POSITION";
+
+ private static final String TAG = PreviewVideoActivity.class.getSimpleName();
+
+ private DataStorageManager mStorageManager;
+
+ private int mSavedPlaybackPosition; // in the unit time handled by MediaPlayer.getCurrentPosition()
+ private boolean mAutoplay; // when 'true', the playback starts immediately with the activity
+ private VideoView mVideoPlayer; // view to play the file; both performs and show the playback
+ private MediaController mMediaController; // panel control used by the user to control the playback
+
+ /**
+ * Called when the activity is first created.
+ *
+ * Searches for an {@link OCFile} and ownCloud {@link Account} holding it in the starting {@link Intent}.
+ *
+ * The {@link Account} is unnecessary if the file is downloaded; else, the {@link Account} is used to
+ * try to stream the remote file - TODO get the streaming works
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log_OC.e(TAG, "ACTIVITY\t\tonCreate");
+
+ setContentView(R.layout.video_layout);
+
+ if (savedInstanceState == null) {
+ Bundle extras = getIntent().getExtras();
+ mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION);
+ mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY);
+
+ } else {
+ mSavedPlaybackPosition = savedInstanceState.getInt(EXTRA_START_POSITION);
+ mAutoplay = savedInstanceState.getBoolean(EXTRA_AUTOPLAY);
+ }
+
+ mVideoPlayer = (VideoView) findViewById(R.id.videoPlayer);
+
+ // set listeners to get more contol on the playback
+ mVideoPlayer.setOnPreparedListener(this);
+ mVideoPlayer.setOnCompletionListener(this);
+ mVideoPlayer.setOnErrorListener(this);
+
+ // keep the screen on while the playback is performed (prevents screen off by battery save)
+ mVideoPlayer.setKeepScreenOn(true);
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ Log_OC.e(TAG, "ACTIVITY\t\tonSaveInstanceState");
+ outState.putInt(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
+ outState.putBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY , mVideoPlayer.isPlaying());
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ Log_OC.e(TAG, "ACTIVTIY\t\tonBackPressed");
+ Intent i = new Intent();
+ i.putExtra(EXTRA_AUTOPLAY, mVideoPlayer.isPlaying());
+ i.putExtra(EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
+ setResult(RESULT_OK, i);
+ super.onBackPressed();
+ }
+
+
+ /**
+ * Called when the file is ready to be played.
+ *
+ * Just starts the playback.
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ */
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ Log_OC.e(TAG, "ACTIVITY\t\tonPrepare");
+ mVideoPlayer.seekTo(mSavedPlaybackPosition);
+ if (mAutoplay) {
+ mVideoPlayer.start();
+ }
+ mMediaController.show(5000);
+ }
+
+
+ /**
+ * Called when the file is finished playing.
+ *
+ * Rewinds the video
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ */
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ mVideoPlayer.seekTo(0);
+ }
+
+
+ /**
+ * Called when an error in playback occurs.
+ *
+ * @param mp {@link MediaPlayer} instance performing the playback.
+ * @param what Type of error
+ * @param extra Extra code specific to the error
+ */
+ @Override
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ Log_OC.e(TAG, "Error in video playback, what = " + what + ", extra = " + extra);
+
+ if (mMediaController != null) {
+ mMediaController.hide();
+ }
+
+ if (mVideoPlayer.getWindowToken() != null) {
+ String message = MediaService.getMessageForMediaError(this, what, extra);
+ new AlertDialog.Builder(this)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.VideoView_error_button,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ PreviewVideoActivity.this.onCompletion(null);
+ }
+ })
+ .setCancelable(false)
+ .show();
+ }
+ return true;
+ }
+
+
+ @Override
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ OCFile file = getFile();
+ /// Validate handled file (first image to preview)
+ if (file == null) {
+ throw new IllegalStateException("Instanced with a NULL OCFile");
+ }
+ if (!file.isVideo()) {
+ throw new IllegalArgumentException("Non-video file passed as argument");
+ }
+ mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+ file = mStorageManager.getFileById(file.getFileId());
+ if (file != null) {
+ if (file.isDown()) {
+ mVideoPlayer.setVideoPath(file.getStoragePath());
+
+ } else {
+ // not working yet
+ String url;
+ try {
+ url = AccountUtils.constructFullURLForAccount(this, getAccount()) + file.getRemotePath();
+ mVideoPlayer.setVideoURI(Uri.parse(url));
+ } catch (AccountNotFoundException e) {
+ onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_no_account);
+ }
+ }
+
+ // create and prepare control panel for the user
+ mMediaController = new MediaController(this);
+ mMediaController.setMediaPlayer(mVideoPlayer);
+ mMediaController.setAnchorView(mVideoPlayer);
+ mVideoPlayer.setMediaController(mMediaController);
+
+ } else {
+ finish();
+ }
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ finish();
+ }
+ }
+
+
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.utils;
+
+import java.io.File;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.StatFs;
+
+
+/**
+ * Static methods to help in access to local file system.
+ *
+ * @author David A. Velasco
+ */
+public class FileStorageUtils {
+ //private static final String LOG_TAG = "FileStorageUtils";
+
+ public static final String getSavePath(String accountName) {
+ File sdCard = Environment.getExternalStorageDirectory();
+ return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
+ // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
+ }
+
+ public static final String getDefaultSavePathFor(String accountName, OCFile file) {
+ return getSavePath(accountName) + file.getRemotePath();
+ }
+
+ public static final String getTemporalPath(String accountName) {
+ File sdCard = Environment.getExternalStorageDirectory();
+ return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/tmp/" + Uri.encode(accountName, "@");
+ // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
+ }
+
+ @SuppressLint("NewApi")
+ public static final long getUsableSpace(String accountName) {
+ File savePath = Environment.getExternalStorageDirectory();
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
+ return savePath.getUsableSpace();
+
+ } else {
+ StatFs stats = new StatFs(savePath.getAbsolutePath());
+ return stats.getAvailableBlocks() * stats.getBlockSize();
+ }
+
+ }
+
+ public static final String getLogPath() {
+ return Environment.getExternalStorageDirectory() + File.separator + MainApp.getDataFolder() + File.separator + "log";
+ }
+
+ public static String getInstantUploadFilePath(Context context, String fileName) {
+ String uploadPath = context.getString(R.string.instant_upload_path);
+ String value = uploadPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
+ return value;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.utils;
+
+public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
+ public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion(
+ 0x010000);
+ public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion(
+ 0x020000);
+ public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion(
+ 0x030000);
+ public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion(
+ 0x040000);
+ public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion(
+ 0x040500);
+
+ // format is in version
+ // 0xAABBCC
+ // for version AA.BB.CC
+ // ie version 2.0.3 will be stored as 0x030003
+ private int mVersion;
+ private boolean mIsValid;
+
+ public OwnCloudVersion(int version) {
+ mVersion = version;
+ mIsValid = true;
+ }
+
+ public OwnCloudVersion(String version) {
+ mVersion = 0;
+ mIsValid = false;
+ parseVersionString(version);
+ }
+
+ public String toString() {
+ return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "."
+ + ((mVersion) % 256);
+ }
+
+ public boolean isVersionValid() {
+ return mIsValid;
+ }
+
+ @Override
+ public int compareTo(OwnCloudVersion another) {
+ return another.mVersion == mVersion ? 0
+ : another.mVersion < mVersion ? 1 : -1;
+ }
+
+ private void parseVersionString(String version) {
+ try {
+ String[] nums = version.split("\\.");
+ if (nums.length > 0) {
+ mVersion += Integer.parseInt(nums[0]);
+ }
+ mVersion = mVersion << 8;
+ if (nums.length > 1) {
+ mVersion += Integer.parseInt(nums[1]);
+ }
+ mVersion = mVersion << 8;
+ if (nums.length > 2) {
+ mVersion += Integer.parseInt(nums[2]);
+ }
+ mIsValid = true;
+ } catch (Exception e) {
+ mIsValid = false;
+ }
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.utils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+
+import android.os.FileObserver;
+
+public class RecursiveFileObserver extends FileObserver {
+
+ public static int CHANGES_ONLY = CLOSE_WRITE | MOVE_SELF | MOVED_FROM;
+
+ List<SingleFileObserver> mObservers;
+ String mPath;
+ int mMask;
+
+ public RecursiveFileObserver(String path) {
+ this(path, ALL_EVENTS);
+ }
+
+ public RecursiveFileObserver(String path, int mask) {
+ super(path, mask);
+ mPath = path;
+ mMask = mask;
+ }
+
+ @Override
+ public void startWatching() {
+ if (mObservers != null) return;
+ mObservers = new ArrayList<SingleFileObserver>();
+ Stack<String> stack = new Stack<String>();
+ stack.push(mPath);
+
+ while (!stack.empty()) {
+ String parent = stack.pop();
+ mObservers.add(new SingleFileObserver(parent, mMask));
+ File path = new File(parent);
+ File[] files = path.listFiles();
+ if (files == null) continue;
+ for (int i = 0; i < files.length; ++i) {
+ if (files[i].isDirectory() && !files[i].getName().equals(".")
+ && !files[i].getName().equals("..")) {
+ stack.push(files[i].getPath());
+ }
+ }
+ }
+ for (int i = 0; i < mObservers.size(); i++)
+ mObservers.get(i).startWatching();
+ }
+
+ @Override
+ public void stopWatching() {
+ if (mObservers == null) return;
+
+ for (int i = 0; i < mObservers.size(); ++i)
+ mObservers.get(i).stopWatching();
+
+ mObservers.clear();
+ mObservers = null;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+
+ }
+
+ private class SingleFileObserver extends FileObserver {
+ private String mPath;
+
+ public SingleFileObserver(String path, int mask) {
+ super(path, mask);
+ mPath = path;
+ }
+
+ @Override
+ public void onEvent(int event, String path) {
+ String newPath = mPath + "/" + path;
+ RecursiveFileObserver.this.onEvent(event, newPath);
+ }
+
+ }
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.widgets;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.owncloud.android.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.EditText;
+
+public class ActionEditText extends EditText {
+ private String s;
+ private String optionOneString;
+ private int optionOneColor;
+ private String optionTwoString;
+ private int optionTwoColor;
+ private Rect mTextBounds, mButtonRect;
+
+ private String badgeClickCallback;
+ private Rect btn_rect;
+
+ public ActionEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ getAttrs(attrs);
+ s = optionOneString;
+ mTextBounds = new Rect();
+ mButtonRect = new Rect();
+ }
+
+ public ActionEditText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ getAttrs(attrs);
+ s = optionOneString;
+ mTextBounds = new Rect();
+ mButtonRect = new Rect();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ Paint p = getPaint();
+
+ p.getTextBounds(s, 0, s.length(), mTextBounds);
+
+ getDrawingRect(mButtonRect);
+ mButtonRect.top += 10;
+ mButtonRect.bottom -= 10;
+ mButtonRect.left = (int) (getWidth() - mTextBounds.width() - 18);
+ mButtonRect.right = getWidth() - 10;
+ btn_rect = mButtonRect;
+
+ if (s.equals(optionOneString))
+ p.setColor(optionOneColor);
+ else
+ p.setColor(optionTwoColor);
+ canvas.drawRect(mButtonRect, p);
+ p.setColor(Color.GRAY);
+
+ canvas.drawText(s, mButtonRect.left + 3, mButtonRect.bottom
+ - (mTextBounds.height() / 2), p);
+
+ invalidate();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ int touchX = (int) event.getX();
+ int touchY = (int) event.getY();
+ boolean r = super.onTouchEvent(event);
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (btn_rect.contains(touchX, touchY)) {
+ if (s.equals(optionTwoString))
+ s = optionOneString;
+ else
+ s = optionTwoString;
+ if (badgeClickCallback != null) {
+ @SuppressWarnings("rawtypes")
+ Class[] paramtypes = new Class[2];
+ paramtypes[0] = android.view.View.class;
+ paramtypes[1] = String.class;
+ Method method;
+ try {
+
+ method = getContext().getClass().getMethod(
+ badgeClickCallback, paramtypes);
+ method.invoke(getContext(), this, s);
+
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ invalidate();
+ }
+ }
+ }
+ return r;
+ }
+
+ private void getAttrs(AttributeSet attr) {
+ TypedArray a = getContext().obtainStyledAttributes(attr,
+ R.styleable.ActionEditText);
+ optionOneString = a
+ .getString(R.styleable.ActionEditText_optionOneString);
+ optionTwoString = a
+ .getString(R.styleable.ActionEditText_optionTwoString);
+ optionOneColor = a.getColor(R.styleable.ActionEditText_optionOneColor,
+ 0x00ff00);
+ optionTwoColor = a.getColor(R.styleable.ActionEditText_optionTwoColor,
+ 0xff0000);
+ badgeClickCallback = a
+ .getString(R.styleable.ActionEditText_onBadgeClick);
+ }
+
+}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android;\r
-\r
-import java.util.Arrays;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.Set;\r
-\r
-/**\r
- * A helper class for some string operations.\r
- * \r
- * @author Bartek Przybylski\r
- * @author David A. Velasco\r
- */\r
-public class DisplayUtils {\r
- \r
- //private static String TAG = DisplayUtils.class.getSimpleName(); \r
- \r
- private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };\r
-\r
- private static HashMap<String, String> mimeType2HUmanReadable;\r
- static {\r
- mimeType2HUmanReadable = new HashMap<String, String>();\r
- // images\r
- mimeType2HUmanReadable.put("image/jpeg", "JPEG image");\r
- mimeType2HUmanReadable.put("image/jpg", "JPEG image");\r
- mimeType2HUmanReadable.put("image/png", "PNG image");\r
- mimeType2HUmanReadable.put("image/bmp", "Bitmap image");\r
- mimeType2HUmanReadable.put("image/gif", "GIF image");\r
- mimeType2HUmanReadable.put("image/svg+xml", "JPEG image");\r
- mimeType2HUmanReadable.put("image/tiff", "TIFF image");\r
- // music\r
- mimeType2HUmanReadable.put("audio/mpeg", "MP3 music file");\r
- mimeType2HUmanReadable.put("application/ogg", "OGG music file");\r
-\r
- }\r
-\r
- private static final String TYPE_APPLICATION = "application";\r
- private static final String TYPE_AUDIO = "audio";\r
- private static final String TYPE_IMAGE = "image";\r
- private static final String TYPE_TXT = "text";\r
- private static final String TYPE_VIDEO = "video";\r
- \r
- private static final String SUBTYPE_PDF = "pdf";\r
- private static final String[] SUBTYPES_DOCUMENT = { "msword", "mspowerpoint", "msexcel", \r
- "vnd.oasis.opendocument.presentation",\r
- "vnd.oasis.opendocument.spreadsheet",\r
- "vnd.oasis.opendocument.text"\r
- };\r
- private static Set<String> SUBTYPES_DOCUMENT_SET = new HashSet<String>(Arrays.asList(SUBTYPES_DOCUMENT));\r
- private static final String[] SUBTYPES_COMPRESSED = {"x-tar", "x-gzip", "zip"};\r
- private static final Set<String> SUBTYPES_COMPRESSED_SET = new HashSet<String>(Arrays.asList(SUBTYPES_COMPRESSED));\r
- \r
- /**\r
- * Converts the file size in bytes to human readable output.\r
- * \r
- * @param bytes Input file size\r
- * @return Like something readable like "12 MB"\r
- */\r
- public static String bytesToHumanReadable(long bytes) {\r
- double result = bytes;\r
- int attachedsuff = 0;\r
- while (result > 1024 && attachedsuff < sizeSuffixes.length) {\r
- result /= 1024.;\r
- attachedsuff++;\r
- }\r
- result = ((int) (result * 100)) / 100.;\r
- return result + " " + sizeSuffixes[attachedsuff];\r
- }\r
-\r
- /**\r
- * Removes special HTML entities from a string\r
- * \r
- * @param s Input string\r
- * @return A cleaned version of the string\r
- */\r
- public static String HtmlDecode(String s) {\r
- /*\r
- * TODO: Perhaps we should use something more proven like:\r
- * http://commons.apache.org/lang/api-2.6/org/apache/commons/lang/StringEscapeUtils.html#unescapeHtml%28java.lang.String%29\r
- */\r
-\r
- String ret = "";\r
- for (int i = 0; i < s.length(); ++i) {\r
- if (s.charAt(i) == '%') {\r
- ret += (char) Integer.parseInt(s.substring(i + 1, i + 3), 16);\r
- i += 2;\r
- } else {\r
- ret += s.charAt(i);\r
- }\r
- }\r
- return ret;\r
- }\r
-\r
- /**\r
- * Converts MIME types like "image/jpg" to more end user friendly output\r
- * like "JPG image".\r
- * \r
- * @param mimetype MIME type to convert\r
- * @return A human friendly version of the MIME type\r
- */\r
- public static String convertMIMEtoPrettyPrint(String mimetype) {\r
- if (mimeType2HUmanReadable.containsKey(mimetype)) {\r
- return mimeType2HUmanReadable.get(mimetype);\r
- }\r
- if (mimetype.split("/").length >= 2)\r
- return mimetype.split("/")[1].toUpperCase() + " file";\r
- return "Unknown type";\r
- }\r
- \r
- \r
- /**\r
- * Returns the resource identifier of an image resource to use as icon associated to a \r
- * known MIME type.\r
- * \r
- * @param mimetype MIME type string.\r
- * @return Resource identifier of an image resource.\r
- */\r
- public static int getResourceId(String mimetype) {\r
-\r
- if (mimetype == null || "DIR".equals(mimetype)) {\r
- return R.drawable.ic_menu_archive;\r
- \r
- } else {\r
- String [] parts = mimetype.split("/");\r
- String type = parts[0];\r
- String subtype = (parts.length > 1) ? parts[1] : "";\r
- \r
- if(TYPE_TXT.equals(type)) {\r
- return R.drawable.file_doc;\r
- \r
- } else if(TYPE_IMAGE.equals(type)) {\r
- return R.drawable.file_image;\r
- \r
- } else if(TYPE_VIDEO.equals(type)) {\r
- return R.drawable.file_movie;\r
- \r
- } else if(TYPE_AUDIO.equals(type)) { \r
- return R.drawable.file_sound;\r
- \r
- } else if(TYPE_APPLICATION.equals(type)) {\r
- \r
- if (SUBTYPE_PDF.equals(subtype)) {\r
- return R.drawable.file_pdf;\r
- \r
- } else if (SUBTYPES_DOCUMENT_SET.contains(subtype)) {\r
- return R.drawable.file_doc;\r
-\r
- } else if (SUBTYPES_COMPRESSED_SET.contains(subtype)) {\r
- return R.drawable.file_zip;\r
- }\r
- \r
- }\r
- // problems: RAR, RTF, 3GP are send as application/octet-stream from the server ; extension in the filename should be explicitly reviewed\r
- }\r
-\r
- // default icon\r
- return R.drawable.file;\r
- }\r
-\r
- \r
-\r
- /**\r
- * Converts Unix time to human readable format\r
- * @param miliseconds that have passed since 01/01/1970\r
- * @return The human readable time for the users locale\r
- */\r
- public static String unixTimeToHumanReadable(long milliseconds) {\r
- Date date = new Date(milliseconds);\r
- return date.toLocaleString();\r
- }\r
-}\r
+++ /dev/null
-package de.mobilcom.debitel.cloud.android;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import android.util.Log;
-
-
-
-public class Log_OC {
-
-
- private static boolean isEnabled = false;
- private static File logFile;
- private static File folder;
- private static BufferedWriter buf;
-
- public static void i(String TAG, String message){
- // Printing the message to LogCat console
- Log.i(TAG, message);
- // Write the log message to the file
- appendLog(TAG+" : "+message);
- }
-
- public static void d(String TAG, String message){
- Log.d(TAG, message);
- appendLog(TAG + " : " + message);
- }
- public static void d(String TAG, String message, Exception e) {
- Log.d(TAG, message, e);
- appendLog(TAG + " : " + message + " Exception : "+ e.getStackTrace());
- }
- public static void e(String TAG, String message){
- Log.e(TAG, message);
- appendLog(TAG + " : " + message);
- }
-
- public static void e(String TAG, String message, Throwable e) {
- Log.e(TAG, message, e);
- appendLog(TAG+" : " + message +" Exception : " + e.getStackTrace());
- }
-
- public static void v(String TAG, String message){
- Log.v(TAG, message);
- appendLog(TAG+" : "+ message);
- }
-
- public static void w(String TAG, String message) {
- Log.w(TAG,message);
- appendLog(TAG+" : "+ message);
- }
-
- public static void wtf(String TAG, String message) {
- Log.wtf(TAG,message);
- appendLog(TAG+" : "+ message);
- }
-
- public static void startLogging(String logPath) {
- folder = new File(logPath);
- logFile = new File(folder + File.separator + "log.txt");
-
- if (!folder.exists()) {
- folder.mkdirs();
- }
- if (logFile.exists()) {
- logFile.delete();
- }
- try {
- logFile.createNewFile();
- buf = new BufferedWriter(new FileWriter(logFile, true));
- isEnabled = true;
- appendPhoneInfo();
- }catch (IOException e){
- e.printStackTrace();
- }
- }
-
- public static void stopLogging() {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault());
- String currentDateandTime = sdf.format(new Date());
- if (logFile != null) {
- logFile.renameTo(new File(folder + File.separator + MainApp.getLogName() + currentDateandTime+".log"));
-
- isEnabled = false;
- try {
- buf.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- }
-
- }
-
- private static void appendPhoneInfo() {
- appendLog("Model : " + android.os.Build.MODEL);
- appendLog("Brand : " + android.os.Build.BRAND);
- appendLog("Product : " + android.os.Build.PRODUCT);
- appendLog("Device : " + android.os.Build.DEVICE);
- appendLog("Version-Codename : " + android.os.Build.VERSION.CODENAME);
- appendLog("Version-Release : " + android.os.Build.VERSION.RELEASE);
- }
-
- private static void appendLog(String text) {
- if (isEnabled) {
- try {
- buf.append(text);
- buf.newLine();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}
-
-
-
-
-
-
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android;
-
-import android.app.Application;
-import android.content.Context;
-/**
- * Main Application of the project
- *
- * Contains methods to build the "static" strings. These strings were before constants in different classes
- *
- * @author masensio
- */
-public class MainApp extends Application {
-
- private static Context mContext;
-
- public void onCreate(){
- super.onCreate();
- MainApp.mContext = getApplicationContext();
- }
-
- public static Context getAppContext() {
- return MainApp.mContext;
- }
-
- // Methods to obtain Strings referring app_name
- // From AccountAuthenticator
- // public static final String ACCOUNT_TYPE = "owncloud";
- public static String getAccountType() {
- return getAppContext().getResources().getString(R.string.account_type);
- }
-
- // From AccountAuthenticator
- // public static final String AUTHORITY = "org.owncloud";
- public static String getAuthority() {
- return getAppContext().getResources().getString(R.string.authority);
- }
-
- // From AccountAuthenticator
- // public static final String AUTH_TOKEN_TYPE = "org.owncloud";
- public static String getAuthTokenType() {
- return getAppContext().getResources().getString(R.string.authority);
- }
-
- // From AccountAuthenticator
- // public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
- public static String getAuthTokenTypePass() {
- return getAppContext().getResources().getString(R.string.account_type) + ".password";
- }
-
- // From AccountAuthenticator
- // public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
- public static String getAuthTokenTypeAccessToken() {
- return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.access_token";
- }
-
- // From AccountAuthenticator
- // public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
- public static String getAuthTokenTypeRefreshToken() {
- return getAppContext().getResources().getString(R.string.account_type) + ".oauth2.refresh_token";
- }
-
- // From AccountAuthenticator
- // public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
- public static String getAuthTokenTypeSamlSessionCookie() {
- return getAppContext().getResources().getString(R.string.account_type) + ".saml.web_sso.session_cookie";
- }
-
- // From ProviderMeta
- // public static final String DB_FILE = "owncloud.db";
- public static String getDBFile() {
- return getAppContext().getResources().getString(R.string.db_file);
- }
-
- // From ProviderMeta
- // private final String mDatabaseName = "ownCloud";
- public static String getDBName() {
- return getAppContext().getResources().getString(R.string.db_name);
- }
-
- // data_folder
- public static String getDataFolder() {
- return getAppContext().getResources().getString(R.string.data_folder);
- }
-
- // log_name
- public static String getLogName() {
- return getAppContext().getResources().getString(R.string.log_name);
- }
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android;\r
-\r
-/**\r
- * Represents a session to an ownCloud instance\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class OwnCloudSession {\r
- private String mSessionName;\r
- private String mSessionUrl;\r
- private int mEntryId;\r
-\r
- public OwnCloudSession(String name, String url, int entryId) {\r
- mSessionName = name;\r
- mSessionUrl = url;\r
- mEntryId = entryId;\r
- }\r
-\r
- public void setName(String name) {\r
- mSessionName = name;\r
- }\r
-\r
- public String getName() {\r
- return mSessionName;\r
- }\r
-\r
- public void setUrl(String url) {\r
- mSessionUrl = url;\r
- }\r
-\r
- public String getUrl() {\r
- return mSessionUrl;\r
- }\r
-\r
- public int getEntryId() {\r
- return mEntryId;\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Stack;
-import java.util.Vector;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.view.View;
-import android.view.Window;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.EditText;
-import android.widget.SimpleAdapter;
-import android.widget.Toast;
-
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-/**
- * This can be used to upload things to an ownCloud instance.
- *
- * @author Bartek Przybylski
- *
- */
-public class Uploader extends ListActivity implements OnItemClickListener, android.view.View.OnClickListener {
- private static final String TAG = "ownCloudUploader";
-
- private Account mAccount;
- private AccountManager mAccountManager;
- private Stack<String> mParents;
- private ArrayList<Parcelable> mStreamsToUpload;
- private boolean mCreateDir;
- private String mUploadPath;
- private DataStorageManager mStorageManager;
- private OCFile mFile;
-
- 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 REQUEST_CODE_SETUP_ACCOUNT = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- mParents = new Stack<String>();
- mParents.add("");
- if (prepareStreamsToUpload()) {
- mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
- Account[] accounts = mAccountManager.getAccountsByType(MainApp.getAccountType());
- if (accounts.length == 0) {
- Log_OC.i(TAG, "No ownCloud account is available");
- showDialog(DIALOG_NO_ACCOUNT);
- } else if (accounts.length > 1) {
- Log_OC.i(TAG, "More then one ownCloud is available");
- showDialog(DIALOG_MULTIPLE_ACCOUNT);
- } else {
- mAccount = accounts[0];
- mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
- 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(String.format(getString(R.string.uploader_wrn_no_account_text), getString(R.string.app_name)));
- builder.setCancelable(false);
- builder.setPositiveButton(R.string.uploader_wrn_no_account_setup_btn_text, new OnClickListener() {
- @Override
- 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.provider.Settings.ACTION_ADD_ACCOUNT);
- intent.putExtra("authorities", new String[] { MainApp.getAuthTokenType() });
- 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() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- });
- return builder.create();
- case DIALOG_MULTIPLE_ACCOUNT:
- CharSequence ac[] = new CharSequence[mAccountManager.getAccountsByType(MainApp.getAccountType()).length];
- for (int i = 0; i < ac.length; ++i) {
- ac[i] = mAccountManager.getAccountsByType(MainApp.getAccountType())[i].name;
- }
- builder.setTitle(R.string.common_choose_account);
- builder.setItems(ac, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mAccount = mAccountManager.getAccountsByType(MainApp.getAccountType())[which];
- mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
- populateDirectoryList();
- }
- });
- builder.setCancelable(true);
- builder.setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- dialog.cancel();
- finish();
- }
- });
- return builder.create();
- case DIALOG_NO_STREAM:
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setTitle(R.string.uploader_wrn_no_content_title);
- builder.setMessage(R.string.uploader_wrn_no_content_text);
- builder.setCancelable(false);
- builder.setNegativeButton(R.string.common_cancel, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- 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;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uploader.this.mUploadPath = mPath + mDirname.getText().toString();
- Uploader.this.mCreateDir = true;
- uploadFiles();
- }
- }
-
- @Override
- public void onBackPressed() {
-
- if (mParents.size() <= 1) {
- super.onBackPressed();
- return;
- } else {
- mParents.pop();
- populateDirectoryList();
- }
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- // click on folder in the list
- Log_OC.d(TAG, "on item click");
- Vector<OCFile> tmpfiles = mStorageManager.getDirectoryContent(mFile);
- if (tmpfiles.size() <= 0) return;
- // filter on dirtype
- Vector<OCFile> files = new Vector<OCFile>();
- for (OCFile f : tmpfiles)
- if (f.isDirectory())
- files.add(f);
- if (files.size() < position) {
- throw new IndexOutOfBoundsException("Incorrect item selected");
- }
- mParents.push(files.get(position).getFileName());
- populateDirectoryList();
- }
-
- @Override
- public void onClick(View v) {
- // click on button
- switch (v.getId()) {
- case R.id.uploader_choose_folder:
- mUploadPath = ""; // first element in mParents is root dir, represented by ""; init mUploadPath with "/" results in a "//" prefix
- for (String p : mParents)
- mUploadPath += p + OCFile.PATH_SEPARATOR;
- Log_OC.d(TAG, "Uploading file to dir " + mUploadPath);
-
- uploadFiles();
-
- break;
- default:
- throw new IllegalArgumentException("Wrong element clicked");
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- Log_OC.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(MainApp.getAuthTokenType());
- 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() {
- setContentView(R.layout.uploader_layout);
-
- String full_path = "";
- for (String a : mParents)
- full_path += a + "/";
-
- Log_OC.d(TAG, "Populating view with content of : " + full_path);
-
- mFile = mStorageManager.getFileByPath(full_path);
- if (mFile != null) {
- Vector<OCFile> files = mStorageManager.getDirectoryContent(mFile);
- List<HashMap<String, Object>> data = new LinkedList<HashMap<String,Object>>();
- for (OCFile f : files) {
- HashMap<String, Object> h = new HashMap<String, Object>();
- if (f.isDirectory()) {
- h.put("dirname", f.getFileName());
- data.add(h);
- }
- }
- SimpleAdapter sa = new SimpleAdapter(this,
- data,
- R.layout.uploader_list_item_layout,
- new String[] {"dirname"},
- new int[] {R.id.textView1});
- setListAdapter(sa);
- CustomButton btn = (CustomButton) findViewById(R.id.uploader_choose_folder);
- btn.setOnClickListener(this);
- getListView().setOnItemClickListener(this);
- }
- }
-
- private boolean 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);
- }
- return (mStreamsToUpload != null && mStreamsToUpload.get(0) != null);
- }
-
- public void uploadFiles() {
- try {
- //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
-
- ArrayList<String> local = new ArrayList<String>();
- ArrayList<String> remote = new ArrayList<String>();
-
- /* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed
- WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
- // create last directory in path if necessary
- if (mCreateDir) {
- wdc.createDirectory(mUploadPath);
- }
- */
-
- // this checks the mimeType
- for (Parcelable mStream : mStreamsToUpload) {
-
- Uri uri = (Uri) mStream;
- if (uri !=null) {
- if (uri.getScheme().equals("content")) {
-
- String mimeType = getContentResolver().getType(uri);
-
- if (mimeType.contains("image")) {
- String[] CONTENT_PROJECTION = { Images.Media.DATA, Images.Media.DISPLAY_NAME, Images.Media.MIME_TYPE, Images.Media.SIZE};
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Images.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)));
-
- }
- else if (mimeType.contains("video")) {
- String[] CONTENT_PROJECTION = { Video.Media.DATA, Video.Media.DISPLAY_NAME, Video.Media.MIME_TYPE, Video.Media.SIZE, Video.Media.DATE_MODIFIED };
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Video.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Video.Media.DISPLAY_NAME)));
-
- }
- else if (mimeType.contains("audio")) {
- String[] CONTENT_PROJECTION = { Audio.Media.DATA, Audio.Media.DISPLAY_NAME, Audio.Media.MIME_TYPE, Audio.Media.SIZE };
- Cursor c = getContentResolver().query(uri, CONTENT_PROJECTION, null, null, null);
- c.moveToFirst();
- int index = c.getColumnIndex(Audio.Media.DATA);
- String data = c.getString(index);
- local.add(data);
- remote.add(mUploadPath + c.getString(c.getColumnIndex(Audio.Media.DISPLAY_NAME)));
-
- }
- else {
- String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
- // cut everything whats before mnt. It occured to me that sometimes apps send their name into the URI
- if (filePath.contains("mnt")) {
- String splitedFilePath[] = filePath.split("/mnt");
- filePath = splitedFilePath[1];
- }
- final File file = new File(filePath);
- local.add(file.getAbsolutePath());
- remote.add(mUploadPath + file.getName());
- }
-
- } else if (uri.getScheme().equals("file")) {
- String filePath = Uri.decode(uri.toString()).replace(uri.getScheme() + "://", "");
- if (filePath.contains("mnt")) {
- String splitedFilePath[] = filePath.split("/mnt");
- filePath = splitedFilePath[1];
- }
- final File file = new File(filePath);
- local.add(file.getAbsolutePath());
- remote.add(mUploadPath + file.getName());
- }
- else {
- throw new SecurityException();
- }
- }
- else {
- throw new SecurityException();
- }
-
- Intent intent = new Intent(getApplicationContext(), FileUploader.class);
- intent.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
- intent.putExtra(FileUploader.KEY_LOCAL_FILE, local.toArray(new String[local.size()]));
- intent.putExtra(FileUploader.KEY_REMOTE_FILE, remote.toArray(new String[remote.size()]));
- intent.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
- startService(intent);
- finish();
- }
-
- } catch (SecurityException e) {
- String message = String.format(getString(R.string.uploader_error_forbidden_content), getString(R.string.app_name));
- Toast.makeText(this, message, Toast.LENGTH_LONG).show();
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.authentication;
-
-import android.accounts.*;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.widget.Toast;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-
-
-/**
- * Authenticator for ownCloud accounts.
- *
- * Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system.
- *
- * TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
- * TODO - review completeness
- *
- * @author David A. Velasco
- */
-public class AccountAuthenticator extends AbstractAccountAuthenticator {
-
- /**
- * Is used by android system to assign accounts to authenticators. Should be
- * used by application and all extensions.
- */
- /* These constants are now in MainApp
- public static final String ACCOUNT_TYPE = "owncloud";
- public static final String AUTHORITY = "org.owncloud";
- public static final String AUTH_TOKEN_TYPE = "org.owncloud";
- public static final String AUTH_TOKEN_TYPE_PASSWORD = "owncloud.password";
- public static final String AUTH_TOKEN_TYPE_ACCESS_TOKEN = "owncloud.oauth2.access_token";
- public static final String AUTH_TOKEN_TYPE_REFRESH_TOKEN = "owncloud.oauth2.refresh_token";
- public static final String AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE = "owncloud.saml.web_sso.session_cookie";
- */
- 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";
-
- /**
- * Value under this key should handle path to webdav php script. Will be
- * removed and usage should be replaced by combining
- * {@link de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
- * {@link de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion}
- *
- * @deprecated
- */
- public static final String KEY_OC_URL = "oc_url";
- /**
- * Version should be 3 numbers separated by dot so it can be parsed by
- * {@link de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion}
- */
- public static final String KEY_OC_VERSION = "oc_version";
- /**
- * Base url should point to owncloud installation without trailing / ie:
- * http://server/path or https://owncloud.server
- */
- public static final String KEY_OC_BASE_URL = "oc_base_url";
- /**
- * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
- */
- public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
- /**
- * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
- */
- public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
-
- private static final String TAG = AccountAuthenticator.class.getSimpleName();
-
- private Context mContext;
-
- private Handler mHandler;
-
- public AccountAuthenticator(Context context) {
- super(context);
- mContext = context;
- mHandler = new Handler();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Bundle addAccount(AccountAuthenticatorResponse response,
- String accountType, String authTokenType,
- String[] requiredFeatures, Bundle options)
- throws NetworkErrorException {
- Log_OC.i(TAG, "Adding account with type " + accountType
- + " and auth token " + authTokenType);
-
- final Bundle bundle = new Bundle();
-
- AccountManager accountManager = AccountManager.get(mContext);
- Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
-
- if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
- try {
- validateAccountType(accountType);
- } catch (AuthenticatorException e) {
- Log_OC.e(TAG, "Failed to validate account type " + accountType + ": "
- + e.getMessage());
- 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);
- intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
-
- setIntentFlags(intent);
-
- bundle.putParcelable(AccountManager.KEY_INTENT, intent);
-
- } else {
-
- // Return an error
- bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
- final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name));
- bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
-
- mHandler.post(new Runnable() {
-
- @Override
- public void run() {
- Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
- }
- });
-
- }
-
- return bundle;
- }
-
- /**\r
- * {@inheritDoc}\r
- */\r
- @Override\r
- public Bundle confirmCredentials(AccountAuthenticatorResponse response,\r
- Account account, Bundle options) throws NetworkErrorException {\r
- try {\r
- validateAccountType(account.type);\r
- } catch (AuthenticatorException e) {\r
- Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "\r
- + e.getMessage());\r
- e.printStackTrace();\r
- return e.getFailureBundle();\r
- }\r
- Intent intent = new Intent(mContext, AuthenticatorActivity.class);\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,\r
- response);\r
- intent.putExtra(KEY_ACCOUNT, account);\r
- intent.putExtra(KEY_LOGIN_OPTIONS, options);\r
-\r
- setIntentFlags(intent);\r
-\r
- Bundle resultBundle = new Bundle();\r
- resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);\r
- return resultBundle;\r
- }\r
-\r
- @Override\r
- public Bundle editProperties(AccountAuthenticatorResponse response,\r
- String accountType) {\r
- return null;\r
- }\r
-\r
- /**
- * {@inheritDoc}
- */
- @Override
- public Bundle getAuthToken(AccountAuthenticatorResponse response,
- Account account, String authTokenType, Bundle options)
- throws NetworkErrorException {
- /// validate parameters
- try {
- validateAccountType(account.type);
- validateAuthTokenType(authTokenType);
- } catch (AuthenticatorException e) {
- Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
- + e.getMessage());
- e.printStackTrace();
- return e.getFailureBundle();
- }
-
- /// check if required token is stored
- final AccountManager am = AccountManager.get(mContext);
- String accessToken;
- if (authTokenType.equals(MainApp.getAuthTokenTypePass())) {
- accessToken = am.getPassword(account);
- } else {
- accessToken = am.peekAuthToken(account, authTokenType);
- }
- if (accessToken != null) {
- final Bundle result = new Bundle();
- result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
- result.putString(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());
- result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
- return result;
- }
-
- /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
- 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.EXTRA_ACCOUNT, account);
- intent.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
- intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
-
-
- 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, true);
- 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_EXCLUDE_FROM_RECENTS);
- intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
- }
-
- private void validateAccountType(String type)
- throws UnsupportedAccountTypeException {
- if (!type.equals(MainApp.getAccountType())) {
- throw new UnsupportedAccountTypeException();
- }
- }
-
- private void validateAuthTokenType(String authTokenType)\r
- throws UnsupportedAuthTokenTypeException {\r
- if (!authTokenType.equals(MainApp.getAuthTokenType()) &&\r
- !authTokenType.equals(MainApp.getAuthTokenTypePass()) &&\r
- !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) &&\r
- !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) &&
- !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) {\r
- throw new UnsupportedAuthTokenTypeException();\r
- }\r
- }\r
-\r
- public static class AuthenticatorException extends Exception {\r
- private static final long serialVersionUID = 1L;\r
- private Bundle mFailureBundle;\r
-\r
- public AuthenticatorException(int code, String errorMsg) {\r
- mFailureBundle = new Bundle();\r
- mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);\r
- mFailureBundle\r
- .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);\r
- }\r
-\r
- public Bundle getFailureBundle() {\r
- return mFailureBundle;\r
- }\r
- }\r
-\r
- public static class UnsupportedAccountTypeException extends\r
- AuthenticatorException {\r
- private static final long serialVersionUID = 1L;\r
-\r
- public UnsupportedAccountTypeException() {\r
- super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
- "Unsupported account type");\r
- }\r
- }\r
-\r
- public static class UnsupportedAuthTokenTypeException extends\r
- AuthenticatorException {\r
- private static final long serialVersionUID = 1L;\r
-\r
- public UnsupportedAuthTokenTypeException() {\r
- super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
- "Unsupported auth token type");\r
- }\r
- }\r
-\r
- public static class UnsupportedFeaturesException extends\r
- AuthenticatorException {\r
- public static final long serialVersionUID = 1L;\r
-\r
- public UnsupportedFeaturesException() {\r
- super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,\r
- "Unsupported features");\r
- }\r
- }\r
-\r
- public static class AccessDeniedException extends AuthenticatorException {\r
- public AccessDeniedException(int code, String errorMsg) {\r
- super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");\r
- }\r
-\r
- private static final long serialVersionUID = 1L;\r
-\r
- }\r
-}\r
+++ /dev/null
-/*
- * Copyright (C) 2009 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.
- */
-
-package de.mobilcom.debitel.cloud.android.authentication;
-
-import android.accounts.AccountAuthenticatorResponse;
-import android.accounts.AccountManager;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-
-/*
- * Base class for implementing an Activity that is used to help implement an AbstractAccountAuthenticator.
- * If the AbstractAccountAuthenticator needs to use an activity to handle the request then it can have the activity extend
- * AccountAuthenticatorActivity. The AbstractAccountAuthenticator passes in the response to the intent using the following:
- * intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
- *
- * The activity then sets the result that is to be handed to the response via setAccountAuthenticatorResult(android.os.Bundle).
- * This result will be sent as the result of the request when the activity finishes. If this is never set or if it is set to null
- * then error AccountManager.ERROR_CODE_CANCELED will be called on the response.
- */
-
-public class AccountAuthenticatorActivity extends SherlockFragmentActivity {
-
- private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
- private Bundle mResultBundle = null;
-
-
- /**
- * Set the result that is to be sent as the result of the request that caused this Activity to be launched.
- * If result is null or this method is never called then the request will be canceled.
- *
- * @param result this is returned as the result of the AbstractAccountAuthenticator request
- */
- public final void setAccountAuthenticatorResult(Bundle result) {
- mResultBundle = result;
- }
-
- /**
- * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
- * icicle is non-zero.
- * @param icicle the save instance data of this Activity, may be null
- */
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mAccountAuthenticatorResponse =
- getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
-
- if (mAccountAuthenticatorResponse != null) {
- mAccountAuthenticatorResponse.onRequestContinued();
- }
- }
-
- /**
- * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
- */
- public void finish() {
- if (mAccountAuthenticatorResponse != null) {
- // send the result bundle back if set, otherwise send an error.
- if (mResultBundle != null) {
- mAccountAuthenticatorResponse.onResult(mResultBundle);
- } else {
- mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED,
- "canceled");
- }
- mAccountAuthenticatorResponse = null;
- }
- super.finish();
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.authentication;
-
-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\r
- * Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android.authentication;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.accounts.AccountsException;\r
-import android.content.Context;\r
-import android.content.SharedPreferences;\r
-import android.preference.PreferenceManager;\r
-\r
-public class AccountUtils {\r
- public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";\r
- public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";\r
- public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";\r
- private static final String ODAV_PATH = "/remote.php/odav";\r
- private static final String SAML_SSO_PATH = "/remote.php/webdav";\r
- public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";\r
- public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";\r
- public static final String STATUS_PATH = "/status.php";\r
-\r
- /**\r
- * Can be used to get the currently selected ownCloud {@link Account} in the\r
- * application preferences.\r
- * \r
- * @param context The current application {@link Context}\r
- * @return The ownCloud {@link Account} currently saved in preferences, or the first \r
- * {@link Account} available, if valid (still registered in the system as ownCloud \r
- * account). If none is available and valid, returns null.\r
- */\r
- public static Account getCurrentOwnCloudAccount(Context context) {\r
- Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
- MainApp.getAccountType());\r
- Account defaultAccount = null;\r
-\r
- SharedPreferences appPreferences = PreferenceManager\r
- .getDefaultSharedPreferences(context);\r
- String accountName = appPreferences\r
- .getString("select_oc_account", null);\r
-\r
- // account validation: the saved account MUST be in the list of ownCloud Accounts known by the AccountManager\r
- if (accountName != null) {\r
- for (Account account : ocAccounts) {\r
- if (account.name.equals(accountName)) {\r
- defaultAccount = account;\r
- break;\r
- }\r
- }\r
- }\r
- \r
- if (defaultAccount == null && ocAccounts.length != 0) {\r
- // take first account as fallback\r
- defaultAccount = ocAccounts[0];\r
- }\r
-\r
- return defaultAccount;\r
- }\r
-\r
- \r
- public static boolean exists(Account account, Context context) {\r
- Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
- MainApp.getAccountType());\r
-\r
- if (account != null && account.name != null) {\r
- for (Account ac : ocAccounts) {\r
- if (ac.name.equals(account.name)) {\r
- return true;\r
- }\r
- }\r
- }\r
- return false;\r
- }\r
- \r
-\r
- /**\r
- * Checks, whether or not there are any ownCloud accounts setup.\r
- * \r
- * @return true, if there is at least one account.\r
- */\r
- public static boolean accountsAreSetup(Context context) {\r
- AccountManager accMan = AccountManager.get(context);\r
- Account[] accounts = accMan\r
- .getAccountsByType(MainApp.getAccountType());\r
- return accounts.length > 0;\r
- }\r
- \r
- \r
- public static boolean setCurrentOwnCloudAccount(Context context, String accountName) {\r
- boolean result = false;\r
- if (accountName != null) {\r
- Account[] ocAccounts = AccountManager.get(context).getAccountsByType(\r
- MainApp.getAccountType());\r
- boolean found = false;\r
- for (Account account : ocAccounts) {\r
- found = (account.name.equals(accountName));\r
- if (found) {\r
- SharedPreferences.Editor appPrefs = PreferenceManager\r
- .getDefaultSharedPreferences(context).edit();\r
- appPrefs.putString("select_oc_account", accountName);\r
- \r
- appPrefs.commit();\r
- result = true;\r
- break;\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * \r
- * @param version version of owncloud\r
- * @return webdav path for given OC version, null if OC version unknown\r
- */\r
- public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {\r
- if (version != null) {\r
- if (supportsOAuth) {\r
- return ODAV_PATH;\r
- }\r
- if (supportsSamlSso) {\r
- return SAML_SSO_PATH;\r
- }\r
- if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
- return WEBDAV_PATH_4_0;\r
- if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
- || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
- return WEBDAV_PATH_2_0;\r
- if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
- return WEBDAV_PATH_1_2;\r
- }\r
- return null;\r
- }\r
- \r
- /**\r
- * Returns the proper URL path to access the WebDAV interface of an ownCloud server,\r
- * according to its version and the authorization method used.\r
- * \r
- * @param version Version of ownCloud server.\r
- * @param authTokenType Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}. \r
- * @return WebDAV path for given OC version and authorization method, null if OC version is unknown.\r
- */\r
- public static String getWebdavPath(OwnCloudVersion version, String authTokenType) {\r
- if (version != null) {\r
- if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) {\r
- return ODAV_PATH;\r
- }\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) {\r
- return SAML_SSO_PATH;\r
- }\r
- if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)\r
- return WEBDAV_PATH_4_0;\r
- if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0\r
- || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)\r
- return WEBDAV_PATH_2_0;\r
- if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)\r
- return WEBDAV_PATH_1_2;\r
- }\r
- return null;\r
- }\r
- \r
- /**\r
- * Constructs full url to host and webdav resource basing on host version\r
- * @param context\r
- * @param account\r
- * @return url or null on failure\r
- * @throws AccountNotFoundException When 'account' is unknown for the AccountManager\r
- */\r
- public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {\r
- AccountManager ama = AccountManager.get(context);\r
- String baseurl = ama.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);\r
- String strver = ama.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);\r
- boolean supportsOAuth = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
- boolean supportsSamlSso = (ama.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
- OwnCloudVersion ver = new OwnCloudVersion(strver);\r
- String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);\r
-\r
- if (baseurl == null || webdavpath == null) \r
- throw new AccountNotFoundException(account, "Account not found", null);\r
- \r
- return baseurl + webdavpath;\r
- }\r
- \r
- \r
- public static class AccountNotFoundException extends AccountsException {\r
- \r
- /** Generated - should be refreshed every time the class changes!! */\r
- private static final long serialVersionUID = -9013287181793186830L;\r
- \r
- private Account mFailedAccount; \r
- \r
- public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {\r
- super(message, cause);\r
- mFailedAccount = failedAccount;\r
- }\r
- \r
- public Account getFailedAccount() {\r
- return mFailedAccount;\r
- }\r
- }\r
-\r
-}\r
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android.authentication;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.app.AlertDialog;\r
-import android.app.Dialog;\r
-import android.app.ProgressDialog;\r
-import android.content.ContentResolver;\r
-import android.content.DialogInterface;\r
-import android.content.Intent;\r
-import android.content.SharedPreferences;\r
-import android.graphics.Rect;\r
-import android.graphics.drawable.Drawable;\r
-import android.net.Uri;\r
-import android.os.Bundle;\r
-import android.os.Handler;\r
-import android.preference.PreferenceManager;\r
-import android.support.v4.app.Fragment;\r
-import android.text.Editable;\r
-import android.text.InputType;\r
-import android.text.TextWatcher;\r
-import android.view.KeyEvent;\r
-import android.view.MotionEvent;\r
-import android.view.View;\r
-import android.view.View.OnFocusChangeListener;\r
-import android.view.View.OnTouchListener;\r
-import android.view.Window;\r
-import android.view.inputmethod.EditorInfo;\r
-import android.widget.Button;\r
-import android.widget.CheckBox;\r
-import android.widget.EditText;\r
-import android.widget.TextView;\r
-import android.widget.TextView.OnEditorActionListener;\r
-\r
-import com.actionbarsherlock.app.SherlockDialogFragment;\r
-\r
-import de.mobilcom.debitel.cloud.android.Log_OC;\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;\r
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;\r
-import de.mobilcom.debitel.cloud.android.operations.ExistenceCheckOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.OAuth2GetAccessToken;\r
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;\r
-import de.mobilcom.debitel.cloud.android.operations.OwnCloudServerCheckOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;\r
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;\r
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SamlWebViewDialog;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog;\r
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;\r
-\r
-import eu.alefzero.webdav.WebdavClient;\r
-\r
-/**\r
- * This Activity is used to add an ownCloud account to the App\r
- * \r
- * @author Bartek Przybylski\r
- * @author David A. Velasco\r
- */\r
-public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
-implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener, SsoWebViewClientListener{\r
-\r
- private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
-\r
- public static final String EXTRA_ACCOUNT = "ACCOUNT";\r
- public static final String EXTRA_USER_NAME = "USER_NAME";\r
- public static final String EXTRA_HOST_NAME = "HOST_NAME";\r
- public static final String EXTRA_ACTION = "ACTION";\r
- public static final String EXTRA_ENFORCED_UPDATE = "ENFORCE_UPDATE";\r
-\r
- private static final String KEY_AUTH_MESSAGE_VISIBILITY = "AUTH_MESSAGE_VISIBILITY";\r
- private static final String KEY_AUTH_MESSAGE_TEXT = "AUTH_MESSAGE_TEXT";\r
- private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";\r
- private static final String KEY_OC_VERSION = "OC_VERSION";\r
- private static final String KEY_ACCOUNT = "ACCOUNT";\r
- private static final String KEY_SERVER_VALID = "SERVER_VALID";\r
- private static final String KEY_SERVER_CHECKED = "SERVER_CHECKED";\r
- private static final String KEY_SERVER_CHECK_IN_PROGRESS = "SERVER_CHECK_IN_PROGRESS"; \r
- private static final String KEY_SERVER_STATUS_TEXT = "SERVER_STATUS_TEXT";\r
- private static final String KEY_SERVER_STATUS_ICON = "SERVER_STATUS_ICON";\r
- private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";\r
- private static final String KEY_PASSWORD_VISIBLE = "PASSWORD_VISIBLE";\r
- private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";\r
- private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";\r
- private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";\r
- \r
- private static final String KEY_OC_USERNAME_EQUALS = "oc_username=";\r
-\r
- private static final String AUTH_ON = "on";\r
- private static final String AUTH_OFF = "off";\r
- private static final String AUTH_OPTIONAL = "optional";\r
- \r
- private static final int DIALOG_LOGIN_PROGRESS = 0;\r
- private static final int DIALOG_SSL_VALIDATOR = 1;\r
- private static final int DIALOG_CERT_NOT_SAVED = 2;\r
- private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;\r
-\r
- public static final byte ACTION_CREATE = 0;\r
- public static final byte ACTION_UPDATE_TOKEN = 1;\r
-\r
- private static final String TAG_SAML_DIALOG = "samlWebViewDialog";\r
- \r
- private String mHostBaseUrl;\r
- private OwnCloudVersion mDiscoveredVersion;\r
-\r
- private String mAuthMessageText;\r
- private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;\r
- private boolean mServerIsChecked, mServerIsValid, mIsSslConn;\r
- private int mAuthStatusText, mAuthStatusIcon; \r
- private TextView mAuthStatusLayout;\r
-\r
- private final Handler mHandler = new Handler();\r
- private Thread mOperationThread;\r
- private OwnCloudServerCheckOperation mOcServerChkOperation;\r
- private ExistenceCheckOperation mAuthCheckOperation;\r
- private RemoteOperationResult mLastSslUntrustedServerResult;\r
-\r
- private Uri mNewCapturedUriFromOAuth2Redirection;\r
-\r
- private AccountManager mAccountMgr;\r
- private boolean mJustCreated;\r
- private byte mAction;\r
- private Account mAccount;\r
-\r
- private TextView mAuthMessage;\r
- \r
- private EditText mHostUrlInput;\r
- private boolean mHostUrlInputEnabled;\r
- private View mRefreshButton;\r
-\r
- private String mAuthTokenType;\r
- \r
- private EditText mUsernameInput;\r
- private EditText mPasswordInput;\r
- \r
- private CheckBox mOAuth2Check;\r
- \r
- private TextView mOAuthAuthEndpointText;\r
- private TextView mOAuthTokenEndpointText;\r
- \r
- private SamlWebViewDialog mSamlDialog;\r
- \r
- private View mOkButton;\r
- \r
- private String mAuthToken;\r
- \r
- private boolean mResumed; // Control if activity is resumed\r
-\r
-\r
- /**\r
- * {@inheritDoc}\r
- * \r
- * IMPORTANT ENTRY POINT 1: activity is shown to the user\r
- */\r
- @Override\r
- protected void onCreate(Bundle savedInstanceState) {\r
- super.onCreate(savedInstanceState);\r
- getWindow().requestFeature(Window.FEATURE_NO_TITLE);\r
-\r
- /// set view and get references to view elements\r
- setContentView(R.layout.account_setup);\r
- mAuthMessage = (TextView) findViewById(R.id.auth_message);\r
- mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);\r
- mHostUrlInput.setText(getString(R.string.server_url)); // valid although R.string.server_url is an empty string\r
- mUsernameInput = (EditText) findViewById(R.id.account_username);\r
- mPasswordInput = (EditText) findViewById(R.id.account_password);\r
- mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);\r
- mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);\r
- mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);\r
- mOkButton = (CustomButton) findViewById(R.id.buttonOK);\r
- mAuthStatusLayout = (TextView) findViewById(R.id.auth_status_text); \r
- \r
- /// set Host Url Input Enabled\r
- mHostUrlInputEnabled = getResources().getBoolean(R.bool.show_server_url_input);\r
- \r
-\r
- /// complete label for 'register account' button\r
- Button b = (Button) findViewById(R.id.account_register);\r
- if (b != null) {\r
- b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name))); \r
- }\r
-\r
-// /// complete background of 'OK' button\r
-// boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);\r
-// if (customButtons)\r
-// mOkButton.setBackgroundResource(R.drawable.btn_default);\r
- \r
- /// initialization\r
- mAccountMgr = AccountManager.get(this);\r
- mNewCapturedUriFromOAuth2Redirection = null;\r
- mAction = getIntent().getByteExtra(EXTRA_ACTION, ACTION_CREATE); \r
- mAccount = null;\r
- mHostBaseUrl = "";\r
- boolean refreshButtonEnabled = false;\r
- \r
- // URL input configuration applied\r
- if (!mHostUrlInputEnabled)\r
- {\r
- findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);\r
- mRefreshButton = findViewById(R.id.centeredRefreshButton);\r
-\r
- } else {\r
- mRefreshButton = findViewById(R.id.embeddedRefreshButton);\r
- }\r
-\r
- if (savedInstanceState == null) {\r
- mResumed = false;\r
- /// connection state and info\r
- mAuthMessageVisibility = View.GONE;\r
- mServerStatusText = mServerStatusIcon = 0;\r
- mServerIsValid = false;\r
- mServerIsChecked = false;\r
- mIsSslConn = false;\r
- mAuthStatusText = mAuthStatusIcon = 0;\r
-\r
- /// retrieve extras from intent\r
- mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
- if (mAccount != null) {\r
- String ocVersion = mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION);\r
- if (ocVersion != null) {\r
- mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
- }\r
- mHostBaseUrl = normalizeUrl(mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL));\r
- mHostUrlInput.setText(mHostBaseUrl);\r
- String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
- mUsernameInput.setText(userName);\r
- }\r
- initAuthorizationMethod(); // checks intent and setup.xml to determine mCurrentAuthorizationMethod\r
- mJustCreated = true;\r
- \r
- if (mAction == ACTION_UPDATE_TOKEN || !mHostUrlInputEnabled) {\r
- checkOcServer(); \r
- }\r
- \r
- } else {\r
- mResumed = true;\r
- /// connection state and info\r
- mAuthMessageVisibility = savedInstanceState.getInt(KEY_AUTH_MESSAGE_VISIBILITY);\r
- mAuthMessageText = savedInstanceState.getString(KEY_AUTH_MESSAGE_TEXT);\r
- mServerIsValid = savedInstanceState.getBoolean(KEY_SERVER_VALID);\r
- mServerIsChecked = savedInstanceState.getBoolean(KEY_SERVER_CHECKED);\r
- mServerStatusText = savedInstanceState.getInt(KEY_SERVER_STATUS_TEXT);\r
- mServerStatusIcon = savedInstanceState.getInt(KEY_SERVER_STATUS_ICON);\r
- mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);\r
- mAuthStatusText = savedInstanceState.getInt(KEY_AUTH_STATUS_TEXT);\r
- mAuthStatusIcon = savedInstanceState.getInt(KEY_AUTH_STATUS_ICON);\r
- if (savedInstanceState.getBoolean(KEY_PASSWORD_VISIBLE, false)) {\r
- showPassword();\r
- }\r
- \r
- /// server data\r
- String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
- if (ocVersion != null) {\r
- mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
- }\r
- mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);\r
-\r
- // account data, if updating\r
- mAccount = savedInstanceState.getParcelable(KEY_ACCOUNT);\r
- mAuthTokenType = savedInstanceState.getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
- if (mAuthTokenType == null) {\r
- mAuthTokenType = MainApp.getAuthTokenTypePass();\r
- \r
- }\r
-\r
- // check if server check was interrupted by a configuration change\r
- if (savedInstanceState.getBoolean(KEY_SERVER_CHECK_IN_PROGRESS, false)) {\r
- checkOcServer();\r
- } \r
- \r
- // refresh button enabled\r
- refreshButtonEnabled = savedInstanceState.getBoolean(KEY_REFRESH_BUTTON_ENABLED);\r
- \r
-\r
- }\r
-\r
- if (mAuthMessageVisibility== View.VISIBLE) {\r
- showAuthMessage(mAuthMessageText);\r
- }\r
- else {\r
- hideAuthMessage();\r
- }\r
- adaptViewAccordingToAuthenticationMethod();\r
- showServerStatus();\r
- showAuthStatus();\r
- \r
- if (mAction == ACTION_UPDATE_TOKEN) {\r
- /// lock things that should not change\r
- mHostUrlInput.setEnabled(false);\r
- mHostUrlInput.setFocusable(false);\r
- mUsernameInput.setEnabled(false);\r
- mUsernameInput.setFocusable(false);\r
- mOAuth2Check.setVisibility(View.GONE);\r
- }\r
- \r
- //if (mServerIsChecked && !mServerIsValid && mRefreshButtonEnabled) showRefreshButton();\r
- if (mServerIsChecked && !mServerIsValid && refreshButtonEnabled) showRefreshButton();\r
- mOkButton.setEnabled(mServerIsValid); // state not automatically recovered in configuration changes\r
-\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) || \r
- !AUTH_OPTIONAL.equals(getString(R.string.auth_method_oauth2))) {\r
- mOAuth2Check.setVisibility(View.GONE);\r
- }\r
-\r
- mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)\r
-\r
- /// bind view elements to listeners and other friends\r
- mHostUrlInput.setOnFocusChangeListener(this);\r
- mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);\r
- mHostUrlInput.setOnEditorActionListener(this);\r
- mHostUrlInput.addTextChangedListener(new TextWatcher() {\r
-\r
- @Override\r
- public void afterTextChanged(Editable s) {\r
- if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
- mOkButton.setEnabled(false);\r
- }\r
- }\r
-\r
- @Override\r
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {\r
- }\r
-\r
- @Override\r
- public void onTextChanged(CharSequence s, int start, int before, int count) {\r
- if (!mResumed) {\r
- mAuthStatusIcon = 0;\r
- mAuthStatusText = 0;\r
- showAuthStatus(); \r
- }\r
- mResumed = false;\r
- }\r
- });\r
- \r
- mPasswordInput.setOnFocusChangeListener(this);\r
- mPasswordInput.setImeOptions(EditorInfo.IME_ACTION_DONE);\r
- mPasswordInput.setOnEditorActionListener(this);
- mPasswordInput.setOnTouchListener(new RightDrawableOnTouchListener() {\r
- @Override\r
- public boolean onDrawableTouch(final MotionEvent event) {\r
- if (event.getAction() == MotionEvent.ACTION_UP) {\r
- AuthenticatorActivity.this.onViewPasswordClick();\r
- }\r
- return true;\r
- }\r
- });\r
- \r
- findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {\r
- @Override\r
- public boolean onTouch(View view, MotionEvent event) {\r
- if (event.getAction() == MotionEvent.ACTION_DOWN) {\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
- mHostUrlInput.hasFocus()) {\r
- checkOcServer();\r
- }\r
- }\r
- return false;\r
- }\r
- });\r
- }\r
- \r
- \r
-\r
- private void initAuthorizationMethod() {\r
- boolean oAuthRequired = false;\r
- boolean samlWebSsoRequired = false;\r
-\r
- mAuthTokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);\r
- mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
- \r
- // TODO could be a good moment to validate the received token type, if not null\r
- \r
- if (mAuthTokenType == null) { \r
- if (mAccount != null) {\r
- /// same authentication method than the one used to create the account to update\r
- oAuthRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null);\r
- samlWebSsoRequired = (mAccountMgr.getUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null);\r
- \r
- } else {\r
- /// use the one set in setup.xml\r
- oAuthRequired = AUTH_ON.equals(getString(R.string.auth_method_oauth2));\r
- samlWebSsoRequired = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso)); \r
- }\r
- if (oAuthRequired) {\r
- mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
- } else if (samlWebSsoRequired) {\r
- mAuthTokenType = MainApp.getAuthTokenTypeSamlSessionCookie();\r
- } else {\r
- mAuthTokenType = MainApp.getAuthTokenTypePass();\r
- }\r
- }\r
- \r
- if (mAccount != null) {\r
- String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
- mUsernameInput.setText(userName);\r
- }\r
- \r
- mOAuth2Check.setChecked(MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType));\r
- \r
- }\r
-\r
- /**\r
- * Saves relevant state before {@link #onPause()}\r
- * \r
- * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the \r
- * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()} \r
- * \r
- * See {@link #loadSavedInstanceState(Bundle)}\r
- */\r
- @Override\r
- protected void onSaveInstanceState(Bundle outState) {\r
- super.onSaveInstanceState(outState);\r
-\r
- /// connection state and info\r
- outState.putInt(KEY_AUTH_MESSAGE_VISIBILITY, mAuthMessage.getVisibility());\r
- outState.putString(KEY_AUTH_MESSAGE_TEXT, mAuthMessage.getText().toString());\r
- outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);\r
- outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);\r
- outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);\r
- outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);\r
- outState.putBoolean(KEY_SERVER_CHECK_IN_PROGRESS, (!mServerIsValid && mOcServerChkOperation != null));\r
- outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);\r
- outState.putBoolean(KEY_PASSWORD_VISIBLE, isPasswordVisible());\r
- outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);\r
- outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);\r
-\r
- /// server data\r
- if (mDiscoveredVersion != null) {\r
- outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
- }\r
- outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
-\r
- /// account data, if updating\r
- if (mAccount != null) {\r
- outState.putParcelable(KEY_ACCOUNT, mAccount);\r
- }\r
- outState.putString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE, mAuthTokenType);\r
- \r
- // refresh button enabled\r
- outState.putBoolean(KEY_REFRESH_BUTTON_ENABLED, (mRefreshButton.getVisibility() == View.VISIBLE));\r
- \r
-\r
- }\r
-\r
-\r
- /**\r
- * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request\r
- * is caught here.\r
- * \r
- * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the\r
- * AndroidManifest.xml file.\r
- */\r
- @Override\r
- protected void onNewIntent (Intent intent) {\r
- Log_OC.d(TAG, "onNewIntent()");\r
- Uri data = intent.getData();\r
- if (data != null && data.toString().startsWith(getString(R.string.oauth2_redirect_uri))) {\r
- mNewCapturedUriFromOAuth2Redirection = data;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and \r
- * deferred in {@link #onNewIntent(Intent)}, is processed here.\r
- */\r
- @Override\r
- protected void onResume() {\r
- super.onResume();\r
- if (mAction == ACTION_UPDATE_TOKEN && mJustCreated && getIntent().getBooleanExtra(EXTRA_ENFORCED_UPDATE, false)) {\r
- if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
- //Toast.makeText(this, R.string.auth_expired_oauth_token_toast, Toast.LENGTH_LONG).show();\r
- showAuthMessage(getString(R.string.auth_expired_oauth_token_toast));\r
- } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
- //Toast.makeText(this, R.string.auth_expired_saml_sso_token_toast, Toast.LENGTH_LONG).show();\r
- showAuthMessage(getString(R.string.auth_expired_saml_sso_token_toast));\r
- } else {\r
- //Toast.makeText(this, R.string.auth_expired_basic_auth_toast, Toast.LENGTH_LONG).show();\r
- showAuthMessage(getString(R.string.auth_expired_basic_auth_toast));\r
- }\r
- }\r
-\r
- if (mNewCapturedUriFromOAuth2Redirection != null) {\r
- getOAuth2AccessTokenFromCapturedRedirection(); \r
- }\r
-\r
- mJustCreated = false;\r
- \r
- }\r
-\r
-\r
- /**\r
- * Parses the redirection with the response to the GET AUTHORIZATION request to the \r
- * oAuth server and requests for the access token (GET ACCESS TOKEN)\r
- */\r
- private void getOAuth2AccessTokenFromCapturedRedirection() {\r
- /// Parse data from OAuth redirection\r
- String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();\r
- mNewCapturedUriFromOAuth2Redirection = null;\r
-\r
- /// Showing the dialog with instructions for the user.\r
- showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
-\r
- /// GET ACCESS TOKEN to the oAuth server \r
- RemoteOperation operation = new OAuth2GetAccessToken( getString(R.string.oauth2_client_id), \r
- getString(R.string.oauth2_redirect_uri), \r
- getString(R.string.oauth2_grant_type),\r
- queryParameters);\r
- //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
- operation.execute(client, this, mHandler);\r
- }\r
-\r
-\r
-\r
- /**\r
- * Handles the change of focus on the text inputs for the server URL and the password\r
- */\r
- public void onFocusChange(View view, boolean hasFocus) {\r
- if (view.getId() == R.id.hostUrlInput) { \r
- if (!hasFocus) {\r
- onUrlInputFocusLost((TextView) view);\r
- }\r
- else {\r
- hideRefreshButton();\r
- }\r
-\r
- } else if (view.getId() == R.id.account_password) {\r
- onPasswordFocusChanged((TextView) view, hasFocus);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Handles changes in focus on the text input for the server URL.\r
- * \r
- * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to \r
- * other field. The operation to check the existence of the server in the entered URL is\r
- * started. \r
- * \r
- * When hasFocus: user 'comes back' to write again the server URL.\r
- * \r
- * @param hostInput TextView with the URL input field receiving the change of focus.\r
- */\r
- private void onUrlInputFocusLost(TextView hostInput) {\r
- if (!mHostBaseUrl.equals(normalizeUrl(mHostUrlInput.getText().toString()))) {\r
- checkOcServer();\r
- } else {\r
- mOkButton.setEnabled(mServerIsValid);\r
- if (!mServerIsValid) {\r
- showRefreshButton();\r
- }\r
- }\r
- }\r
-\r
-\r
- private void checkOcServer() {\r
- String uri = trimUrlWebdav(mHostUrlInput.getText().toString().trim());\r
- \r
- if (!mHostUrlInputEnabled){\r
- uri = getString(R.string.server_url);\r
- }\r
- \r
- mServerIsValid = false;\r
- mServerIsChecked = false;\r
- mOkButton.setEnabled(false);\r
- mDiscoveredVersion = null;\r
- hideRefreshButton();\r
- if (uri.length() != 0) {\r
- mServerStatusText = R.string.auth_testing_connection;\r
- mServerStatusIcon = R.drawable.progress_small;\r
- showServerStatus();\r
- mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this);\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this, true);\r
- mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
- } else {\r
- mServerStatusText = 0;\r
- mServerStatusIcon = 0;\r
- showServerStatus();\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Handles changes in focus on the text input for the password (basic authorization).\r
- * \r
- * When (hasFocus), the button to toggle password visibility is shown.\r
- * \r
- * When (!hasFocus), the button is made invisible and the password is hidden.\r
- * \r
- * @param passwordInput TextView with the password input field receiving the change of focus.\r
- * @param hasFocus 'True' if focus is received, 'false' if is lost\r
- */\r
- private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {\r
- if (hasFocus) {\r
- showViewPasswordButton();\r
- } else {\r
- hidePassword();\r
- hidePasswordButton();\r
- }\r
- }\r
-\r
-\r
- private void showViewPasswordButton() {\r
- //int drawable = android.R.drawable.ic_menu_view;\r
- int drawable = R.drawable.ic_view;\r
- if (isPasswordVisible()) {\r
- //drawable = android.R.drawable.ic_secure;\r
- drawable = R.drawable.ic_hide;\r
- }\r
- mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable, 0);\r
- }\r
-\r
- private boolean isPasswordVisible() {\r
- return ((mPasswordInput.getInputType() & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
- }\r
- \r
- private void hidePasswordButton() {\r
- mPasswordInput.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);\r
- }\r
-\r
- private void showPassword() {\r
- mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);\r
- showViewPasswordButton();\r
- }\r
- \r
- private void hidePassword() {\r
- mPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);\r
- showViewPasswordButton();\r
- }\r
- \r
- \r
- /**\r
- * Cancels the authenticator activity\r
- * \r
- * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation\r
- * \r
- * This method is bound in the layout/acceoun_setup.xml resource file.\r
- * \r
- * @param view Cancel button\r
- */\r
- public void onCancelClick(View view) {\r
- setResult(RESULT_CANCELED); // TODO review how is this related to AccountAuthenticator (debugging)\r
- finish();\r
- }\r
-\r
-\r
-\r
- /**\r
- * Checks the credentials of the user in the root of the ownCloud server\r
- * before creating a new local account.\r
- * \r
- * For basic authorization, a check of existence of the root folder is\r
- * performed.\r
- * \r
- * For OAuth, starts the flow to get an access token; the credentials test \r
- * is postponed until it is available.\r
- * \r
- * IMPORTANT ENTRY POINT 4\r
- * \r
- * @param view OK button\r
- */\r
- public void onOkClick(View view) {\r
- // this check should be unnecessary\r
- if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {\r
- mServerStatusIcon = R.drawable.common_error;\r
- mServerStatusText = R.string.auth_wtf_reenter_URL;\r
- showServerStatus();\r
- mOkButton.setEnabled(false);\r
- Log_OC.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!");\r
- return;\r
- }\r
-\r
- if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
- startOauthorization();\r
- } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) { \r
- startSamlBasedFederatedSingleSignOnAuthorization();\r
- } else {\r
- checkBasicAuthorization();\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Tests the credentials entered by the user performing a check of existence on \r
- * the root folder of the ownCloud server.\r
- */\r
- private void checkBasicAuthorization() {\r
- /// get the path to the root folder through WebDAV from the version server\r
- String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-\r
- /// get basic credentials entered by user\r
- String username = mUsernameInput.getText().toString();\r
- String password = mPasswordInput.getText().toString();\r
-\r
- /// be gentle with the user\r
- showDialog(DIALOG_LOGIN_PROGRESS);\r
-\r
- /// test credentials accessing the root folder\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
- client.setBasicCredentials(username, password);\r
- mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
- }\r
-\r
-\r
- /**\r
- * Starts the OAuth 'grant type' flow to get an access token, with \r
- * a GET AUTHORIZATION request to the BUILT-IN authorization server. \r
- */\r
- private void startOauthorization() {\r
- // be gentle with the user\r
- mAuthStatusIcon = R.drawable.progress_small;\r
- mAuthStatusText = R.string.oauth_login_connection;\r
- showAuthStatus();\r
- \r
-\r
- // GET AUTHORIZATION request\r
- //Uri uri = Uri.parse(getString(R.string.oauth2_url_endpoint_auth));\r
- Uri uri = Uri.parse(mOAuthAuthEndpointText.getText().toString().trim());\r
- Uri.Builder uriBuilder = uri.buildUpon();\r
- uriBuilder.appendQueryParameter(OAuth2Constants.KEY_RESPONSE_TYPE, getString(R.string.oauth2_response_type));\r
- uriBuilder.appendQueryParameter(OAuth2Constants.KEY_REDIRECT_URI, getString(R.string.oauth2_redirect_uri)); \r
- uriBuilder.appendQueryParameter(OAuth2Constants.KEY_CLIENT_ID, getString(R.string.oauth2_client_id));\r
- uriBuilder.appendQueryParameter(OAuth2Constants.KEY_SCOPE, getString(R.string.oauth2_scope));\r
- //uriBuilder.appendQueryParameter(OAuth2Constants.KEY_STATE, whateverwewant);\r
- uri = uriBuilder.build();\r
- Log_OC.d(TAG, "Starting browser to view " + uri.toString());\r
- Intent i = new Intent(Intent.ACTION_VIEW, uri);\r
- startActivity(i);\r
- }\r
-\r
-\r
- /**\r
- * Starts the Web Single Sign On flow to get access to the root folder\r
- * in the server.\r
- */\r
- private void startSamlBasedFederatedSingleSignOnAuthorization() {\r
- // be gentle with the user\r
- mAuthStatusIcon = R.drawable.progress_small;\r
- mAuthStatusText = R.string.auth_connecting_auth_server;\r
- showAuthStatus();\r
- showDialog(DIALOG_LOGIN_PROGRESS);\r
- \r
- /// get the path to the root folder through WebDAV from the version server\r
- String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
-\r
- /// test credentials accessing the root folder\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
- mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
- \r
- }\r
-\r
- /**\r
- * Callback method invoked when a RemoteOperation executed by this Activity finishes.\r
- * \r
- * Dispatches the operation flow to the right method.\r
- */\r
- @Override\r
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {\r
-\r
- if (operation instanceof OwnCloudServerCheckOperation) {\r
- onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);\r
-\r
- } else if (operation instanceof OAuth2GetAccessToken) {\r
- onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
-\r
- } else if (operation instanceof ExistenceCheckOperation) {\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
- onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result);\r
- \r
- } else {\r
- onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
- }\r
- }\r
- }\r
- \r
- \r
- private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) {\r
- try {\r
- dismissDialog(DIALOG_LOGIN_PROGRESS);\r
- } catch (IllegalArgumentException e) {\r
- // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
- }\r
-
- //if (result.isTemporalRedirection() && result.isIdPRedirection()) {\r
- if (result.isIdPRedirection()) {
- String url = result.getRedirectedLocation();\r
- String targetUrl = mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
- \r
- // Show dialog\r
- mSamlDialog = SamlWebViewDialog.newInstance(url, targetUrl); \r
- mSamlDialog.show(getSupportFragmentManager(), TAG_SAML_DIALOG);\r
- \r
- mAuthStatusIcon = 0;\r
- mAuthStatusText = 0;\r
- \r
- } else {\r
- mAuthStatusIcon = R.drawable.common_error;\r
- mAuthStatusText = R.string.auth_unsupported_auth_method;\r
- \r
- }\r
- showAuthStatus();\r
- }\r
-\r
-\r
- /**\r
- * Processes the result of the server check performed when the user finishes the enter of the\r
- * server URL.\r
- * \r
- * @param operation Server check performed.\r
- * @param result Result of the check.\r
- */\r
- private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {\r
- if (operation.equals(mOcServerChkOperation)) {\r
- /// save result state\r
- mServerIsChecked = true;\r
- mServerIsValid = result.isSuccess();\r
- mIsSslConn = (result.getCode() == ResultCode.OK_SSL);\r
- mOcServerChkOperation = null;\r
-\r
- /// update status icon and text\r
- if (mServerIsValid) {\r
- hideRefreshButton();\r
- } else {\r
- showRefreshButton();\r
- }\r
- updateServerStatusIconAndText(result);\r
- showServerStatus();\r
-\r
- /// very special case (TODO: move to a common place for all the remote operations)\r
- if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
- mLastSslUntrustedServerResult = result;\r
- showDialog(DIALOG_SSL_VALIDATOR); \r
- }\r
-\r
- /// retrieve discovered version and normalize server URL\r
- mDiscoveredVersion = operation.getDiscoveredVersion();\r
- mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
-\r
- /// allow or not the user try to access the server\r
- mOkButton.setEnabled(mServerIsValid);\r
-\r
- } // else nothing ; only the last check operation is considered; \r
- // multiple can be triggered if the user amends a URL before a previous check can be triggered\r
- }\r
-\r
-\r
- private String normalizeUrl(String url) {\r
- if (url != null && url.length() > 0) {\r
- url = url.trim();\r
- if (!url.toLowerCase().startsWith("http://") &&\r
- !url.toLowerCase().startsWith("https://")) {\r
- if (mIsSslConn) {\r
- url = "https://" + url;\r
- } else {\r
- url = "http://" + url;\r
- }\r
- }\r
-\r
- // OC-208: Add suffix remote.php/webdav to normalize (OC-34) \r
- url = trimUrlWebdav(url);\r
-\r
- if (url.endsWith("/")) {\r
- url = url.substring(0, url.length() - 1);\r
- }\r
-\r
- }\r
- return (url != null ? url : "");\r
- }\r
-\r
-\r
- private String trimUrlWebdav(String url){ \r
- if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_4_0)){\r
- url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_4_0.length()); \r
- } else if(url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_2_0)){\r
- url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_2_0.length()); \r
- } else if (url.toLowerCase().endsWith(AccountUtils.WEBDAV_PATH_1_2)){\r
- url = url.substring(0, url.length() - AccountUtils.WEBDAV_PATH_1_2.length()); \r
- } \r
- return (url != null ? url : "");\r
- }\r
- \r
- \r
- /**\r
- * Chooses the right icon and text to show to the user for the received operation result.\r
- * \r
- * @param result Result of a remote operation performed in this activity\r
- */\r
- private void updateServerStatusIconAndText(RemoteOperationResult result) {\r
- mServerStatusIcon = R.drawable.common_error; // the most common case in the switch below\r
-\r
- switch (result.getCode()) {\r
- case OK_SSL:\r
- mServerStatusIcon = android.R.drawable.ic_secure;\r
- mServerStatusText = R.string.auth_secure_connection;\r
- break;\r
-\r
- case OK_NO_SSL:\r
- case OK:\r
- if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
- mServerStatusText = R.string.auth_connection_established;\r
- mServerStatusIcon = R.drawable.ic_ok;\r
- } else {\r
- mServerStatusText = R.string.auth_nossl_plain_ok_title;\r
- mServerStatusIcon = android.R.drawable.ic_partial_secure;\r
- }\r
- break;\r
-\r
- case NO_NETWORK_CONNECTION:\r
- mServerStatusIcon = R.drawable.no_network;\r
- mServerStatusText = R.string.auth_no_net_conn_title;\r
- break;\r
-\r
- case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
- mServerStatusText = R.string.auth_ssl_unverified_server_title;\r
- break;\r
- case BAD_OC_VERSION:\r
- mServerStatusText = R.string.auth_bad_oc_version_title;\r
- break;\r
- case WRONG_CONNECTION:\r
- mServerStatusText = R.string.auth_wrong_connection_title;\r
- break;\r
- case TIMEOUT:\r
- mServerStatusText = R.string.auth_timeout_title;\r
- break;\r
- case INCORRECT_ADDRESS:\r
- mServerStatusText = R.string.auth_incorrect_address_title;\r
- break;\r
- case SSL_ERROR:\r
- mServerStatusText = R.string.auth_ssl_general_error_title;\r
- break;\r
- case UNAUTHORIZED:\r
- mServerStatusText = R.string.auth_unauthorized;\r
- break;\r
- case HOST_NOT_AVAILABLE:\r
- mServerStatusText = R.string.auth_unknown_host_title;\r
- break;\r
- case INSTANCE_NOT_CONFIGURED:\r
- mServerStatusText = R.string.auth_not_configured_title;\r
- break;\r
- case FILE_NOT_FOUND:\r
- mServerStatusText = R.string.auth_incorrect_path_title;\r
- break;\r
- case OAUTH2_ERROR:\r
- mServerStatusText = R.string.auth_oauth_error;\r
- break;\r
- case OAUTH2_ERROR_ACCESS_DENIED:\r
- mServerStatusText = R.string.auth_oauth_error_access_denied;\r
- break;\r
- case UNHANDLED_HTTP_CODE:\r
- case UNKNOWN_ERROR:\r
- mServerStatusText = R.string.auth_unknown_error_title;\r
- break;\r
- default:\r
- mServerStatusText = 0;\r
- mServerStatusIcon = 0;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Chooses the right icon and text to show to the user for the received operation result.\r
- * \r
- * @param result Result of a remote operation performed in this activity\r
- */\r
- private void updateAuthStatusIconAndText(RemoteOperationResult result) {\r
- mAuthStatusIcon = R.drawable.common_error; // the most common case in the switch below\r
-\r
- switch (result.getCode()) {\r
- case OK_SSL:\r
- mAuthStatusIcon = android.R.drawable.ic_secure;\r
- mAuthStatusText = R.string.auth_secure_connection;\r
- break;\r
-\r
- case OK_NO_SSL:\r
- case OK:\r
- if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {\r
- mAuthStatusText = R.string.auth_connection_established;\r
- mAuthStatusIcon = R.drawable.ic_ok;\r
- } else {\r
- mAuthStatusText = R.string.auth_nossl_plain_ok_title;\r
- mAuthStatusIcon = android.R.drawable.ic_partial_secure;\r
- }\r
- break;\r
-\r
- case NO_NETWORK_CONNECTION:\r
- mAuthStatusIcon = R.drawable.no_network;\r
- mAuthStatusText = R.string.auth_no_net_conn_title;\r
- break;\r
-\r
- case SSL_RECOVERABLE_PEER_UNVERIFIED:\r
- mAuthStatusText = R.string.auth_ssl_unverified_server_title;\r
- break;\r
- case BAD_OC_VERSION:\r
- mAuthStatusText = R.string.auth_bad_oc_version_title;\r
- break;\r
- case WRONG_CONNECTION:\r
- mAuthStatusText = R.string.auth_wrong_connection_title;\r
- break;\r
- case TIMEOUT:\r
- mAuthStatusText = R.string.auth_timeout_title;\r
- break;\r
- case INCORRECT_ADDRESS:\r
- mAuthStatusText = R.string.auth_incorrect_address_title;\r
- break;\r
- case SSL_ERROR:\r
- mAuthStatusText = R.string.auth_ssl_general_error_title;\r
- break;\r
- case UNAUTHORIZED:\r
- mAuthStatusText = R.string.auth_unauthorized;\r
- break;\r
- case HOST_NOT_AVAILABLE:\r
- mAuthStatusText = R.string.auth_unknown_host_title;\r
- break;\r
- case INSTANCE_NOT_CONFIGURED:\r
- mAuthStatusText = R.string.auth_not_configured_title;\r
- break;\r
- case FILE_NOT_FOUND:\r
- mAuthStatusText = R.string.auth_incorrect_path_title;\r
- break;\r
- case OAUTH2_ERROR:\r
- mAuthStatusText = R.string.auth_oauth_error;\r
- break;\r
- case OAUTH2_ERROR_ACCESS_DENIED:\r
- mAuthStatusText = R.string.auth_oauth_error_access_denied;\r
- break;\r
- case ACCOUNT_NOT_NEW:\r
- mAuthStatusText = R.string.auth_account_not_new;\r
- break;\r
- case ACCOUNT_NOT_THE_SAME:\r
- mAuthStatusText = R.string.auth_account_not_the_same;\r
- break;\r
- case UNHANDLED_HTTP_CODE:\r
- case UNKNOWN_ERROR:\r
- mAuthStatusText = R.string.auth_unknown_error_title;\r
- break;\r
- default:\r
- mAuthStatusText = 0;\r
- mAuthStatusIcon = 0;\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Processes the result of the request for and access token send \r
- * to an OAuth authorization server.\r
- * \r
- * @param operation Operation performed requesting the access token.\r
- * @param result Result of the operation.\r
- */\r
- private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {\r
- try {\r
- dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);\r
- } catch (IllegalArgumentException e) {\r
- // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
- }\r
-\r
- String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
- if (result.isSuccess() && webdav_path != null) {\r
- /// be gentle with the user\r
- showDialog(DIALOG_LOGIN_PROGRESS);\r
-\r
- /// time to test the retrieved access token on the ownCloud server\r
- mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
- Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
- client.setBearerCredentials(mAuthToken);\r
- mAuthCheckOperation.execute(client, this, mHandler);\r
-\r
- } else {\r
- updateAuthStatusIconAndText(result);\r
- showAuthStatus();\r
- Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Processes the result of the access check performed to try the user credentials.\r
- * \r
- * Creates a new account through the AccountManager.\r
- * \r
- * @param operation Access check performed.\r
- * @param result Result of the operation.\r
- */\r
- private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
- try {\r
- dismissDialog(DIALOG_LOGIN_PROGRESS);\r
- } catch (IllegalArgumentException e) {\r
- // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens\r
- }\r
-\r
- if (result.isSuccess()) {\r
- Log_OC.d(TAG, "Successful access - time to save the account");\r
-\r
- boolean success = false;\r
- if (mAction == ACTION_CREATE) {\r
- success = createAccount();\r
-\r
- } else {\r
- success = updateToken();\r
- }\r
-\r
- if (success) {\r
- finish();\r
- }\r
-\r
- } else if (result.isServerFail() || result.isException()) {\r
- /// if server fail or exception in authorization, the UI is updated as when a server check failed\r
- mServerIsChecked = true;\r
- mServerIsValid = false;\r
- mIsSslConn = false;\r
- mOcServerChkOperation = null;\r
- mDiscoveredVersion = null;\r
- mHostBaseUrl = normalizeUrl(mHostUrlInput.getText().toString());\r
-\r
- // update status icon and text\r
- updateServerStatusIconAndText(result);\r
- showServerStatus();\r
- mAuthStatusIcon = 0;\r
- mAuthStatusText = 0;\r
- showAuthStatus();\r
- \r
- // update input controls state\r
- showRefreshButton();\r
- mOkButton.setEnabled(false);\r
-\r
- // very special case (TODO: move to a common place for all the remote operations) (dangerous here?)\r
- if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {\r
- mLastSslUntrustedServerResult = result;\r
- showDialog(DIALOG_SSL_VALIDATOR); \r
- }\r
-\r
- } else { // authorization fail due to client side - probably wrong credentials\r
- updateAuthStatusIconAndText(result);\r
- showAuthStatus();\r
- Log_OC.d(TAG, "Access failed: " + result.getLogMessage());\r
- }\r
-\r
- }\r
-\r
-\r
- /**\r
- * Sets the proper response to get that the Account Authenticator that started this activity saves \r
- * a new authorization token for mAccount.\r
- */\r
- private boolean updateToken() {\r
- Bundle response = new Bundle();\r
- response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
- response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
- \r
- if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) { \r
- response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
- // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
- mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
- \r
- } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
- String username = getUserNameForSamlSso();\r
- if (!mUsernameInput.getText().toString().equals(username)) {\r
- // fail - not a new account, but an existing one; disallow\r
- RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); \r
- updateAuthStatusIconAndText(result);\r
- showAuthStatus();\r
- Log_OC.d(TAG, result.getLogMessage());\r
- \r
- return false;\r
- }\r
- \r
- response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
- // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
- mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
- \r
- } else {\r
- response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());\r
- mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());\r
- }\r
- setAccountAuthenticatorResult(response);\r
- \r
- return true;\r
- }\r
-\r
-\r
- /**\r
- * Creates a new account through the Account Authenticator that started this activity. \r
- * \r
- * This makes the account permanent.\r
- * \r
- * TODO Decide how to name the OAuth accounts\r
- */\r
- private boolean createAccount() {\r
- /// create and save new ownCloud account\r
- boolean isOAuth = MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType);\r
- boolean isSaml = MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType);\r
-\r
- Uri uri = Uri.parse(mHostBaseUrl);\r
- String username = mUsernameInput.getText().toString().trim();\r
- if (isSaml) {\r
- username = getUserNameForSamlSso();\r
- \r
- } else if (isOAuth) {\r
- username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();\r
- } \r
- String accountName = username + "@" + uri.getHost();\r
- if (uri.getPort() >= 0) {\r
- accountName += ":" + uri.getPort();\r
- }\r
- mAccount = new Account(accountName, MainApp.getAccountType());\r
- if (AccountUtils.exists(mAccount, getApplicationContext())) {\r
- // fail - not a new account, but an existing one; disallow\r
- RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW); \r
- updateAuthStatusIconAndText(result);\r
- showAuthStatus();\r
- Log_OC.d(TAG, result.getLogMessage());\r
- return false;\r
- \r
- } else {\r
- \r
- if (isOAuth || isSaml) {\r
- mAccountMgr.addAccountExplicitly(mAccount, "", null); // with external authorizations, the password is never input in the app\r
- } else {\r
- mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);\r
- }\r
- \r
- /// add the new account as default in preferences, if there is none already\r
- Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);\r
- if (defaultAccount == null) {\r
- SharedPreferences.Editor editor = PreferenceManager\r
- .getDefaultSharedPreferences(this).edit();\r
- editor.putString("select_oc_account", accountName);\r
- editor.commit();\r
- }\r
- \r
- /// prepare result to return to the Authenticator\r
- // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done\r
- final Intent intent = new Intent(); \r
- intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());\r
- intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
- /*if (!isOAuth)\r
- intent.putExtra(AccountManager.KEY_AUTHTOKEN, MainApp.getAccountType()); */\r
- intent.putExtra(AccountManager.KEY_USERDATA, username);\r
- if (isOAuth || isSaml) {\r
- mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
- }\r
- /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
- mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
- mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl);\r
- if (isSaml) {\r
- mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
- } else if (isOAuth) {\r
- mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); \r
- }\r
- \r
- setAccountAuthenticatorResult(intent.getExtras());\r
- setResult(RESULT_OK, intent);\r
- \r
- /// immediately request for the synchronization of the new account\r
- Bundle bundle = new Bundle();\r
- bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
- ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
- syncAccount();\r
-// Bundle bundle = new Bundle();\r
-// bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
-// ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
- return true;\r
- }\r
- }\r
-\r
- \r
- private String getUserNameForSamlSso() {\r
- if (mAuthToken != null) {\r
- String [] cookies = mAuthToken.split(";");\r
- for (int i=0; i<cookies.length; i++) {\r
- if (cookies[i].startsWith(KEY_OC_USERNAME_EQUALS )) {\r
- String value = Uri.decode(cookies[i].substring(KEY_OC_USERNAME_EQUALS.length()));\r
- return value;\r
- }\r
- }\r
- }\r
- return "";\r
- }\r
-\r
-\r
- /**\r
- * {@inheritDoc}\r
- * \r
- * Necessary to update the contents of the SSL Dialog\r
- * \r
- * TODO move to some common place for all possible untrusted SSL failures\r
- */\r
- @Override\r
- protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {\r
- switch (id) {\r
- case DIALOG_LOGIN_PROGRESS:\r
- case DIALOG_CERT_NOT_SAVED:\r
- case DIALOG_OAUTH2_LOGIN_PROGRESS:\r
- break;\r
- case DIALOG_SSL_VALIDATOR: {\r
- ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);\r
- break;\r
- }\r
- default:\r
- Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * {@inheritDoc}\r
- */\r
- @Override\r
- protected Dialog onCreateDialog(int id) {\r
- Dialog dialog = null;\r
- switch (id) {\r
- case DIALOG_LOGIN_PROGRESS: {\r
- /// simple progress dialog\r
- ProgressDialog working_dialog = new ProgressDialog(this);\r
- working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));\r
- working_dialog.setIndeterminate(true);\r
- working_dialog.setCancelable(true);\r
- working_dialog\r
- .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
- @Override\r
- public void onCancel(DialogInterface dialog) {\r
- /// TODO study if this is enough\r
- Log_OC.i(TAG, "Login canceled");\r
- if (mOperationThread != null) {\r
- mOperationThread.interrupt();\r
- finish();\r
- }\r
- }\r
- });\r
- dialog = working_dialog;\r
- break;\r
- }\r
- case DIALOG_OAUTH2_LOGIN_PROGRESS: {\r
- ProgressDialog working_dialog = new ProgressDialog(this);\r
- working_dialog.setMessage(String.format("Getting authorization")); \r
- working_dialog.setIndeterminate(true);\r
- working_dialog.setCancelable(true);\r
- working_dialog\r
- .setOnCancelListener(new DialogInterface.OnCancelListener() {\r
- @Override\r
- public void onCancel(DialogInterface dialog) {\r
- Log_OC.i(TAG, "Login canceled");\r
- finish();\r
- }\r
- });\r
- dialog = working_dialog;\r
- break;\r
- }\r
- case DIALOG_SSL_VALIDATOR: {\r
- /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)\r
- dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);\r
- break;\r
- }\r
- case DIALOG_CERT_NOT_SAVED: {\r
- AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
- builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));\r
- builder.setCancelable(false);\r
- builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {\r
- @Override\r
- public void onClick(DialogInterface dialog, int which) {\r
- dialog.dismiss();\r
- };\r
- });\r
- dialog = builder.create();\r
- break;\r
- }\r
- default:\r
- Log_OC.e(TAG, "Incorrect dialog called with id = " + id);\r
- }\r
- return dialog;\r
- }\r
-\r
-\r
- /**\r
- * Starts and activity to open the 'new account' page in the ownCloud web site\r
- * \r
- * @param view 'Account register' button\r
- */\r
- public void onRegisterClick(View view) {\r
- Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));\r
- setResult(RESULT_CANCELED);\r
- startActivity(register);\r
- }\r
-\r
-\r
- /**\r
- * Updates the content and visibility state of the icon and text associated\r
- * to the last check on the ownCloud server.\r
- */\r
- private void showServerStatus() {\r
- TextView tv = (TextView) findViewById(R.id.server_status_text);\r
-\r
- if (mServerStatusIcon == 0 && mServerStatusText == 0) {\r
- tv.setVisibility(View.INVISIBLE);\r
-\r
- } else {\r
- tv.setText(mServerStatusText);\r
- tv.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);\r
- tv.setVisibility(View.VISIBLE);\r
- }\r
-\r
- }\r
-\r
-\r
- /**\r
- * Updates the content and visibility state of the icon and text associated\r
- * to the interactions with the OAuth authorization server.\r
- */\r
- private void showAuthStatus() {\r
- if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {\r
- mAuthStatusLayout.setVisibility(View.INVISIBLE);\r
-\r
- } else {\r
- mAuthStatusLayout.setText(mAuthStatusText);\r
- mAuthStatusLayout.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);\r
- mAuthStatusLayout.setVisibility(View.VISIBLE);\r
- }\r
- } \r
-\r
-\r
- private void showRefreshButton() {\r
- mRefreshButton.setVisibility(View.VISIBLE);\r
- }\r
-\r
- private void hideRefreshButton() {\r
- mRefreshButton.setVisibility(View.GONE);\r
- }\r
-\r
- /**\r
- * Called when the refresh button in the input field for ownCloud host is clicked.\r
- * \r
- * Performs a new check on the URL in the input field.\r
- * \r
- * @param view Refresh 'button'\r
- */\r
- public void onRefreshClick(View view) {\r
- checkOcServer();\r
- }\r
- \r
- \r
- /**\r
- * Called when the eye icon in the password field is clicked.\r
- * \r
- * Toggles the visibility of the password in the field. \r
- */\r
- public void onViewPasswordClick() {\r
- int selectionStart = mPasswordInput.getSelectionStart();\r
- int selectionEnd = mPasswordInput.getSelectionEnd();\r
- if (isPasswordVisible()) {\r
- hidePassword();\r
- } else {\r
- showPassword();\r
- }\r
- mPasswordInput.setSelection(selectionStart, selectionEnd);\r
- } \r
-\r
-\r
- /**\r
- * Called when the checkbox for OAuth authorization is clicked.\r
- * \r
- * Hides or shows the input fields for user & password. \r
- * \r
- * @param view 'View password' 'button'\r
- */\r
- public void onCheckClick(View view) {\r
- CheckBox oAuth2Check = (CheckBox)view;\r
- if (oAuth2Check.isChecked()) {\r
- mAuthTokenType = MainApp.getAuthTokenTypeAccessToken();\r
- } else {\r
- mAuthTokenType = MainApp.getAuthTokenTypePass();\r
- }\r
- adaptViewAccordingToAuthenticationMethod();\r
- }\r
-\r
- \r
- /**\r
- * Changes the visibility of input elements depending on\r
- * the current authorization method.\r
- */\r
- private void adaptViewAccordingToAuthenticationMethod () {\r
- if (MainApp.getAuthTokenTypeAccessToken().equals(mAuthTokenType)) {\r
- // OAuth 2 authorization\r
- mOAuthAuthEndpointText.setVisibility(View.VISIBLE);\r
- mOAuthTokenEndpointText.setVisibility(View.VISIBLE);\r
- mUsernameInput.setVisibility(View.GONE);\r
- mPasswordInput.setVisibility(View.GONE);\r
- \r
- } else if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
- // SAML-based web Single Sign On\r
- mOAuthAuthEndpointText.setVisibility(View.GONE);\r
- mOAuthTokenEndpointText.setVisibility(View.GONE);\r
- mUsernameInput.setVisibility(View.GONE);\r
- mPasswordInput.setVisibility(View.GONE);\r
- } else {\r
- // basic HTTP authorization\r
- mOAuthAuthEndpointText.setVisibility(View.GONE);\r
- mOAuthTokenEndpointText.setVisibility(View.GONE);\r
- mUsernameInput.setVisibility(View.VISIBLE);\r
- mPasswordInput.setVisibility(View.VISIBLE);\r
- }\r
- }\r
- \r
- /**\r
- * Called from SslValidatorDialog when a new server certificate was correctly saved.\r
- */\r
- public void onSavedCertificate() {\r
- checkOcServer();\r
- }\r
-\r
- /**\r
- * Called from SslValidatorDialog when a new server certificate could not be saved \r
- * when the user requested it.\r
- */\r
- @Override\r
- public void onFailedSavingCertificate() {\r
- showDialog(DIALOG_CERT_NOT_SAVED);\r
- }\r
-\r
-\r
- /**\r
- * Called when the 'action' button in an IME is pressed ('enter' in software keyboard).\r
- * \r
- * Used to trigger the authentication check when the user presses 'enter' after writing the password, \r
- * or to throw the server test when the only field on screen is the URL input field.\r
- */\r
- @Override\r
- public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {\r
- if (actionId == EditorInfo.IME_ACTION_DONE && inputField != null && inputField.equals(mPasswordInput)) {\r
- if (mOkButton.isEnabled()) {\r
- mOkButton.performClick();\r
- }\r
- \r
- } else if (actionId == EditorInfo.IME_ACTION_NEXT && inputField != null && inputField.equals(mHostUrlInput)) {\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType)) {\r
- checkOcServer();\r
- }\r
- }\r
- return false; // always return false to grant that the software keyboard is hidden anyway\r
- }\r
-\r
-\r
- private abstract static class RightDrawableOnTouchListener implements OnTouchListener {\r
-\r
- private int fuzz = 75;\r
- \r
- /**\r
- * {@inheritDoc}\r
- */\r
- @Override\r
- public boolean onTouch(View view, MotionEvent event) {\r
- Drawable rightDrawable = null;\r
- if (view instanceof TextView) {\r
- Drawable[] drawables = ((TextView)view).getCompoundDrawables();\r
- if (drawables.length > 2) {\r
- rightDrawable = drawables[2];\r
- }\r
- }\r
- if (rightDrawable != null) {\r
- final int x = (int) event.getX();\r
- final int y = (int) event.getY();\r
- final Rect bounds = rightDrawable.getBounds();\r
- if (x >= (view.getRight() - bounds.width() - fuzz) && x <= (view.getRight() - view.getPaddingRight() + fuzz)\r
- && y >= (view.getPaddingTop() - fuzz) && y <= (view.getHeight() - view.getPaddingBottom()) + fuzz) {\r
- \r
- return onDrawableTouch(event);\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- public abstract boolean onDrawableTouch(final MotionEvent event);\r
- }\r
-\r
-\r
- public void onSamlDialogSuccess(String sessionCookie){\r
- mAuthToken = sessionCookie;\r
- \r
- if (sessionCookie != null && sessionCookie.length() > 0) {\r
- mAuthToken = sessionCookie;\r
- boolean success = false;\r
- if (mAction == ACTION_CREATE) {\r
- success = createAccount();\r
- \r
- } else {\r
- success = updateToken();\r
- }\r
- if (success) {\r
- finish();\r
- }\r
- }\r
-\r
- \r
- }\r
-\r
-\r
- @Override\r
- public void onSsoFinished(String sessionCookies) {\r
- //Toast.makeText(this, "got cookies: " + sessionCookie, Toast.LENGTH_LONG).show();\r
-\r
- if (sessionCookies != null && sessionCookies.length() > 0) {\r
- Log_OC.d(TAG, "Successful SSO - time to save the account");\r
- onSamlDialogSuccess(sessionCookies);\r
- Fragment fd = getSupportFragmentManager().findFragmentByTag(TAG_SAML_DIALOG);\r
- if (fd != null && fd instanceof SherlockDialogFragment) {\r
- Dialog d = ((SherlockDialogFragment)fd).getDialog();\r
- if (d != null && d.isShowing()) {\r
- d.dismiss();\r
- }\r
- }\r
-\r
- } else { \r
- // TODO - show fail\r
- Log_OC.d(TAG, "SSO failed");\r
- }\r
- \r
- }\r
- \r
- /** Show auth_message \r
- * \r
- * @param message\r
- */\r
- private void showAuthMessage(String message) {\r
- mAuthMessage.setVisibility(View.VISIBLE);\r
- mAuthMessage.setText(message);\r
- }\r
- \r
- private void hideAuthMessage() {\r
- mAuthMessage.setVisibility(View.GONE);\r
- }\r
-\r
- private void syncAccount(){\r
- /// immediately request for the synchronization of the new account\r
- Bundle bundle = new Bundle();\r
- bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);\r
- ContentResolver.requestSync(mAccount, MainApp.getAuthTokenType(), bundle);\r
- }\r
- \r
- @Override\r
- public boolean onTouchEvent(MotionEvent event) {\r
- if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(mAuthTokenType) &&\r
- mHostUrlInput.hasFocus() && event.getAction() == MotionEvent.ACTION_DOWN) {\r
- checkOcServer();\r
- }\r
- return super.onTouchEvent(event);\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.authentication;
-
-/**
- * Constant values for OAuth 2 protocol.
- *
- * Includes required and optional parameter NAMES used in the 'authorization code' grant type.
- *
- * @author David A. Velasco
- */
-
-public class OAuth2Constants {
-
- /// Parameters to send to the Authorization Endpoint
- public static final String KEY_RESPONSE_TYPE = "response_type";
- public static final String KEY_REDIRECT_URI = "redirect_uri";
- public static final String KEY_CLIENT_ID = "client_id";
- public static final String KEY_SCOPE = "scope";
- public static final String KEY_STATE = "state";
-
- /// Additional parameters to send to the Token Endpoint
- public static final String KEY_GRANT_TYPE = "grant_type";
- public static final String KEY_CODE = "code";
-
- /// Parameters received in an OK response from the Token Endpoint
- public static final String KEY_ACCESS_TOKEN = "access_token";
- public static final String KEY_TOKEN_TYPE = "token_type";
- public static final String KEY_EXPIRES_IN = "expires_in";
- public static final String KEY_REFRESH_TOKEN = "refresh_token";
-
- /// Parameters in an ERROR response
- public static final String KEY_ERROR = "error";
- public static final String KEY_ERROR_DESCRIPTION = "error_description";
- public static final String KEY_ERROR_URI = "error_uri";
- public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied";
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.authentication;
-
-import java.lang.ref.WeakReference;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.webkit.CookieManager;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-
-/**
- * Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process
- * running in the {@link WebView} that is attached to.
- *
- * Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
- * authentication process.
- *
- * @author David A. Velasco
- */
-public class SsoWebViewClient extends WebViewClient {
-
- private static final String TAG = SsoWebViewClient.class.getSimpleName();
-
- public interface SsoWebViewClientListener {
- public void onSsoFinished(String sessionCookie);
- }
-
- private Handler mListenerHandler;
- private WeakReference<SsoWebViewClientListener> mListenerRef;
- private String mTargetUrl;
- private String mLastReloadedUrlAtError;
-
- public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) {
- mListenerHandler = listenerHandler;
- mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
- mTargetUrl = "fake://url.to.be.set";
- mLastReloadedUrlAtError = null;
- }
-
- public String getTargetUrl() {
- return mTargetUrl;
- }
-
- public void setTargetUrl(String targetUrl) {
- mTargetUrl = targetUrl;
- }
-
- @Override
- public void onPageStarted (WebView view, String url, Bitmap favicon) {
- Log_OC.d(TAG, "onPageStarted : " + url);
- super.onPageStarted(view, url, favicon);
- }
-
- @Override
- public void onFormResubmission (WebView view, Message dontResend, Message resend) {
- Log_OC.d(TAG, "onFormResubMission ");
-
- // necessary to grant reload of last page when device orientation is changed after sending a form
- resend.sendToTarget();
- }
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- return false;
- }
-
- @Override
- public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
- Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
- if (!failingUrl.equals(mLastReloadedUrlAtError)) {
- view.reload();
- mLastReloadedUrlAtError = failingUrl;
- } else {
- mLastReloadedUrlAtError = null;
- super.onReceivedError(view, errorCode, description, failingUrl);
- }
- }
-
- @Override
- public void onPageFinished (WebView view, String url) {
- Log_OC.d(TAG, "onPageFinished : " + url);
- mLastReloadedUrlAtError = null;
- if (url.startsWith(mTargetUrl)) {
- view.setVisibility(View.GONE);
- CookieManager cookieManager = CookieManager.getInstance();
- final String cookies = cookieManager.getCookie(url);
- //Log_OC.d(TAG, "Cookies: " + cookies);
- if (mListenerHandler != null && mListenerRef != null) {
- // this is good idea because onPageFinished is not running in the UI thread
- mListenerHandler.post(new Runnable() {
- @Override
- public void run() {
- SsoWebViewClientListener listener = mListenerRef.get();
- if (listener != null) {
- listener.onSsoFinished(cookies);
- }
- }
- });
- }
- }
-
- }
-
- /*
- @Override
- public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
- Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
- }
-
- @Override
- public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
- Log_OC.d(TAG, "onReceivedSslError : " + error);
- }
-
- @Override
- public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
- Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
- }
-
- @Override
- public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
- Log_OC.d(TAG, "shouldInterceptRequest : " + url);
- return null;
- }
-
- @Override
- public void onLoadResource (WebView view, String url) {
- Log_OC.d(TAG, "onLoadResource : " + url);
- }
-
- @Override
- public void onReceivedLoginRequest (WebView view, String realm, String account, String args) {
- Log_OC.d(TAG, "onReceivedLoginRequest : " + realm + ", " + account + ", " + args);
- }
-
- @Override
- public void onScaleChanged (WebView view, float oldScale, float newScale) {
- Log_OC.d(TAG, "onScaleChanged : " + oldScale + " -> " + newScale);
- super.onScaleChanged(view, oldScale, newScale);
- }
-
- @Override
- public void onUnhandledKeyEvent (WebView view, KeyEvent event) {
- Log_OC.d(TAG, "onUnhandledKeyEvent : " + event);
- }
-
- @Override
- public boolean shouldOverrideKeyEvent (WebView view, KeyEvent event) {
- Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
- return false;
- }
- */
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.util.List;
-import java.util.Vector;
-
-public interface DataStorageManager {
-
- public static final int ROOT_PARENT_ID = 0;
-
- public OCFile getFileByPath(String path);
-
- public OCFile getFileById(long id);
-
- public boolean fileExists(String path);
-
- public boolean fileExists(long id);
-
- public boolean saveFile(OCFile file);
-
- public void saveFiles(List<OCFile> files);
-
- public Vector<OCFile> getDirectoryContent(OCFile f);
-
- public void removeFile(OCFile file, boolean removeLocalCopy);
-
- public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent);
-
- public void moveDirectory(OCFile dir, String newPath);
-
- public Vector<OCFile> getDirectoryImages(OCFile mParentFolder);
-
- public void calculateFolderSize(long id);
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import android.accounts.Account;
-import android.content.ContentProviderClient;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.RemoteException;
-
-public class FileDataStorageManager implements DataStorageManager {
-
- private ContentResolver mContentResolver;
- private ContentProviderClient mContentProvider;
- private Account mAccount;
-
- private static String TAG = "FileDataStorageManager";
-
- public FileDataStorageManager(Account account, ContentResolver cr) {
- mContentProvider = null;
- mContentResolver = cr;
- mAccount = account;
- }
-
- public FileDataStorageManager(Account account, ContentProviderClient cp) {
- mContentProvider = cp;
- mContentResolver = null;
- mAccount = account;
- }
-
- @Override
- public OCFile getFileByPath(String path) {
- Cursor c = getCursorForValue(ProviderTableMeta.FILE_PATH, path);
- OCFile file = null;
- if (c.moveToFirst()) {
- file = createFileInstance(c);
- }
- c.close();
- if (file == null && OCFile.PATH_SEPARATOR.equals(path)) {
- return createRootDir(); // root should always exist
- }
- return file;
- }
-
-
- private OCFile createRootDir() {
- OCFile file = new OCFile(OCFile.PATH_SEPARATOR);
- file.setMimetype("DIR");
- file.setParentId(DataStorageManager.ROOT_PARENT_ID);
- saveFile(file);
- return file;
- }
-
- @Override
- public OCFile getFileById(long id) {
- Cursor c = getCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
- OCFile file = null;
- if (c.moveToFirst()) {
- file = createFileInstance(c);
- }
- c.close();
- return file;
- }
-
- public OCFile getFileByLocalPath(String path) {
- Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
- OCFile file = null;
- if (c.moveToFirst()) {
- file = createFileInstance(c);
- }
- c.close();
- return file;
- }
-
- @Override
- public boolean fileExists(long id) {
- return fileExists(ProviderTableMeta._ID, String.valueOf(id));
- }
-
- @Override
- public boolean fileExists(String path) {
- return fileExists(ProviderTableMeta.FILE_PATH, path);
- }
-
- @Override
- public boolean saveFile(OCFile file) {
- boolean overriden = false;
- ContentValues cv = new ContentValues();
- cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
- cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
- cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
- cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
- cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
- cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
- if (file.getParentId() != 0)
- cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
- cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
- if (!file.isDirectory())
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
- cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
- cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
-
- boolean sameRemotePath = fileExists(file.getRemotePath());
- boolean changesSizeOfAncestors = false;
- if (sameRemotePath ||
- fileExists(file.getFileId()) ) { // for renamed files; no more delete and create
-
- OCFile oldFile = null;
- if (sameRemotePath) {
- oldFile = getFileByPath(file.getRemotePath());
- file.setFileId(oldFile.getFileId());
- } else {
- oldFile = getFileById(file.getFileId());
- }
- changesSizeOfAncestors = (oldFile.getFileLength() != file.getFileLength());
-
- overriden = true;
- if (getContentResolver() != null) {
- getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv,
- ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) });
- } else {
- try {
- getContentProvider().update(ProviderTableMeta.CONTENT_URI,
- cv, ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) });
- } catch (RemoteException e) {
- Log_OC.e(TAG,
- "Fail to insert insert file to database "
- + e.getMessage());
- }
- }
- } else {
- changesSizeOfAncestors = true;
- Uri result_uri = null;
- if (getContentResolver() != null) {
- result_uri = getContentResolver().insert(
- ProviderTableMeta.CONTENT_URI_FILE, cv);
- } else {
- try {
- result_uri = getContentProvider().insert(
- ProviderTableMeta.CONTENT_URI_FILE, cv);
- } catch (RemoteException e) {
- Log_OC.e(TAG,
- "Fail to insert insert file to database "
- + e.getMessage());
- }
- }
- if (result_uri != null) {
- long new_id = Long.parseLong(result_uri.getPathSegments()
- .get(1));
- file.setFileId(new_id);
- }
- }
-
- if (file.isDirectory()) {
- calculateFolderSize(file.getFileId());
- if (file.needsUpdatingWhileSaving()) {
- for (OCFile f : getDirectoryContent(file))
- saveFile(f);
- }
- }
-
- if (changesSizeOfAncestors || file.isDirectory()) {
- updateSizesToTheRoot(file.getParentId());
- }
-
- return overriden;
- }
-
-
- @Override
- public void saveFiles(List<OCFile> files) {
-
- Iterator<OCFile> filesIt = files.iterator();
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(files.size());
- OCFile file = null;
-
- // prepare operations to perform
- while (filesIt.hasNext()) {
- file = filesIt.next();
- ContentValues cv = new ContentValues();
- cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
- cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
- cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
- cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
- cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
- cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
- if (file.getParentId() != 0)
- cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
- cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
- if (!file.isDirectory())
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
- cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
- cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
- cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
-
- if (fileExists(file.getRemotePath())) {
- OCFile oldFile = getFileByPath(file.getRemotePath());
- file.setFileId(oldFile.getFileId());
-
- if (file.isDirectory()) {
- cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
- file.setFileLength(oldFile.getFileLength());
- }
-
- operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
- withValues(cv).
- withSelection( ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) })
- .build());
-
- } else if (fileExists(file.getFileId())) {
- OCFile oldFile = getFileById(file.getFileId());
- if (file.getStoragePath() == null && oldFile.getStoragePath() != null)
- file.setStoragePath(oldFile.getStoragePath());
-
- if (!file.isDirectory())
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
- else {
- cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, oldFile.getFileLength());
- file.setFileLength(oldFile.getFileLength());
- }
-
- operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
- withValues(cv).
- withSelection( ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(file.getFileId()) })
- .build());
-
- } else {
- operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
- }
- }
-
- // apply operations in batch
- ContentProviderResult[] results = null;
- try {
- if (getContentResolver() != null) {
- results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
-
- } else {
- results = getContentProvider().applyBatch(operations);
- }
-
- } catch (OperationApplicationException e) {
- Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
-
- } catch (RemoteException e) {
- Log_OC.e(TAG, "Fail to update/insert list of files to database " + e.getMessage());
- }
-
- // update new id in file objects for insertions
- if (results != null) {
- long newId;
- for (int i=0; i<results.length; i++) {
- if (results[i].uri != null) {
- newId = Long.parseLong(results[i].uri.getPathSegments().get(1));
- files.get(i).setFileId(newId);
- //Log_OC.v(TAG, "Found and added id in insertion for " + files.get(i).getRemotePath());
- }
- }
- }
-
- for (OCFile aFile : files) {
- if (aFile.isDirectory() && aFile.needsUpdatingWhileSaving())
- saveFiles(getDirectoryContent(aFile));
- }
-
- }
-
- public void setAccount(Account account) {
- mAccount = account;
- }
-
- public Account getAccount() {
- return mAccount;
- }
-
- public void setContentResolver(ContentResolver cr) {
- mContentResolver = cr;
- }
-
- public ContentResolver getContentResolver() {
- return mContentResolver;
- }
-
- public void setContentProvider(ContentProviderClient cp) {
- mContentProvider = cp;
- }
-
- public ContentProviderClient getContentProvider() {
- return mContentProvider;
- }
-
- @Override
- public Vector<OCFile> getDirectoryContent(OCFile f) {
- if (f != null && f.isDirectory() && f.getFileId() != -1) {
- return getDirectoryContent(f.getFileId());
-
- } else {
- return new Vector<OCFile>();
- }
- }
-
- private Vector<OCFile> getDirectoryContent(long parentId) {
-
- Vector<OCFile> ret = new Vector<OCFile>();
-
- Uri req_uri = Uri.withAppendedPath(
- ProviderTableMeta.CONTENT_URI_DIR,
- String.valueOf(parentId));
- Cursor c = null;
-
- if (getContentProvider() != null) {
- try {
- c = getContentProvider().query(req_uri, null,
- ProviderTableMeta.FILE_PARENT + "=?" ,
- new String[] { String.valueOf(parentId)}, null);
- } catch (RemoteException e) {
- Log_OC.e(TAG, e.getMessage());
- return ret;
- }
- } else {
- c = getContentResolver().query(req_uri, null,
- ProviderTableMeta.FILE_PARENT + "=?" ,
- new String[] { String.valueOf(parentId)}, null);
- }
-
- if (c.moveToFirst()) {
- do {
- OCFile child = createFileInstance(c);
- ret.add(child);
- } while (c.moveToNext());
- }
-
- c.close();
-
- Collections.sort(ret);
-
- return ret;
- }
-
-
-
- private boolean fileExists(String cmp_key, String value) {
- Cursor c;
- if (getContentResolver() != null) {
- c = getContentResolver()
- .query(ProviderTableMeta.CONTENT_URI,
- null,
- cmp_key + "=? AND "
- + ProviderTableMeta.FILE_ACCOUNT_OWNER
- + "=?",
- new String[] { value, mAccount.name }, null);
- } else {
- try {
- c = getContentProvider().query(
- ProviderTableMeta.CONTENT_URI,
- null,
- cmp_key + "=? AND "
- + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?",
- new String[] { value, mAccount.name }, null);
- } catch (RemoteException e) {
- Log_OC.e(TAG,
- "Couldn't determine file existance, assuming non existance: "
- + e.getMessage());
- return false;
- }
- }
- boolean retval = c.moveToFirst();
- c.close();
- return retval;
- }
-
- private Cursor getCursorForValue(String key, String value) {
- Cursor c = null;
- if (getContentResolver() != null) {
- c = getContentResolver()
- .query(ProviderTableMeta.CONTENT_URI,
- null,
- key + "=? AND "
- + ProviderTableMeta.FILE_ACCOUNT_OWNER
- + "=?",
- new String[] { value, mAccount.name }, null);
- } else {
- try {
- c = getContentProvider().query(
- ProviderTableMeta.CONTENT_URI,
- null,
- key + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER
- + "=?", new String[] { value, mAccount.name },
- null);
- } catch (RemoteException e) {
- Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
- c = null;
- }
- }
- return c;
- }
-
- private OCFile createFileInstance(Cursor c) {
- OCFile file = null;
- if (c != null) {
- file = new OCFile(c.getString(c
- .getColumnIndex(ProviderTableMeta.FILE_PATH)));
- file.setFileId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
- file.setParentId(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_PARENT)));
- file.setMimetype(c.getString(c
- .getColumnIndex(ProviderTableMeta.FILE_CONTENT_TYPE)));
- if (!file.isDirectory()) {
- file.setStoragePath(c.getString(c
- .getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH)));
- if (file.getStoragePath() == null) {
- // try to find existing file and bind it with current account; - with the current update of SynchronizeFolderOperation, this won't be necessary anymore after a full synchronization of the account
- File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
- if (f.exists()) {
- file.setStoragePath(f.getAbsolutePath());
- file.setLastSyncDateForData(f.lastModified());
- }
- }
- }
- file.setFileLength(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)));
- file.setCreationTimestamp(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_CREATION)));
- file.setModificationTimestamp(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_MODIFIED)));
- file.setModificationTimestampAtLastSyncForData(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
- file.setLastSyncDateForProperties(c.getLong(c
- .getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
- file.setLastSyncDateForData(c.getLong(c.
- getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
- file.setKeepInSync(c.getInt(
- c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
- }
- return file;
- }
-
- @Override
- public void removeFile(OCFile file, boolean removeLocalCopy) {
- Uri file_uri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, ""+file.getFileId());
- if (getContentProvider() != null) {
- try {
- getContentProvider().delete(file_uri,
- ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
- new String[]{mAccount.name});
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- } else {
- getContentResolver().delete(file_uri,
- ProviderTableMeta.FILE_ACCOUNT_OWNER+"=?",
- new String[]{mAccount.name});
- }
- if (file.isDown() && removeLocalCopy) {
- new File(file.getStoragePath()).delete();
- }
- if (file.isDirectory() && removeLocalCopy) {
- File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
- if (f.exists() && f.isDirectory() && (f.list() == null || f.list().length == 0)) {
- f.delete();
- }
- }
-
- if (file.getFileLength() > 0) {
- updateSizesToTheRoot(file.getParentId());
- }
- }
-
- @Override
- public void removeDirectory(OCFile dir, boolean removeDBData, boolean removeLocalContent) {
- // TODO consider possible failures
- if (dir != null && dir.isDirectory() && dir.getFileId() != -1) {
- Vector<OCFile> children = getDirectoryContent(dir);
- if (children.size() > 0) {
- OCFile child = null;
- for (int i=0; i<children.size(); i++) {
- child = children.get(i);
- if (child.isDirectory()) {
- removeDirectory(child, removeDBData, removeLocalContent);
- } else {
- if (removeDBData) {
- removeFile(child, removeLocalContent);
- } else if (removeLocalContent) {
- if (child.isDown()) {
- new File(child.getStoragePath()).delete();
- }
- }
- }
- }
- }
- if (removeDBData) {
- removeFile(dir, true);
- }
-
- if (dir.getFileLength() > 0) {
- updateSizesToTheRoot(dir.getParentId());
- }
- }
- }
-
-
- /**
- * Updates database for a folder that was moved to a different location.
- *
- * TODO explore better (faster) implementations
- * TODO throw exceptions up !
- */
- @Override
- public void moveDirectory(OCFile dir, String newPath) {
- // TODO check newPath
-
- if (dir != null && dir.isDirectory() && dir.fileExists() && !dir.getFileName().equals(OCFile.PATH_SEPARATOR)) {
- /// 1. get all the descendants of 'dir' in a single QUERY (including 'dir')
- Cursor c = null;
- if (getContentProvider() != null) {
- try {
- c = getContentProvider().query(ProviderTableMeta.CONTENT_URI,
- null,
- ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
- new String[] { mAccount.name, dir.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC ");
- } catch (RemoteException e) {
- Log_OC.e(TAG, e.getMessage());
- }
- } else {
- c = getContentResolver().query(ProviderTableMeta.CONTENT_URI,
- null,
- ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PATH + " LIKE ? ",
- new String[] { mAccount.name, dir.getRemotePath() + "%" }, ProviderTableMeta.FILE_PATH + " ASC ");
- }
-
- /// 2. prepare a batch of update operations to change all the descendants
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(c.getCount());
- int lengthOfOldPath = dir.getRemotePath().length();
- String defaultSavePath = FileStorageUtils.getSavePath(mAccount.name);
- int lengthOfOldStoragePath = defaultSavePath.length() + lengthOfOldPath;
- if (c.moveToFirst()) {
- do {
- ContentValues cv = new ContentValues(); // don't take the constructor out of the loop and clear the object
- OCFile child = createFileInstance(c);
- cv.put(ProviderTableMeta.FILE_PATH, newPath + child.getRemotePath().substring(lengthOfOldPath));
- if (child.getStoragePath() != null && child.getStoragePath().startsWith(defaultSavePath)) {
- cv.put(ProviderTableMeta.FILE_STORAGE_PATH, defaultSavePath + newPath + child.getStoragePath().substring(lengthOfOldStoragePath));
- }
- operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
- withValues(cv).
- withSelection( ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(child.getFileId()) })
- .build());
- } while (c.moveToNext());
- }
- c.close();
-
- /// 3. apply updates in batch
- try {
- if (getContentResolver() != null) {
- getContentResolver().applyBatch(MainApp.getAuthority(), operations);
-
- } else {
- getContentProvider().applyBatch(operations);
- }
-
- } catch (OperationApplicationException e) {
- Log_OC.e(TAG, "Fail to update descendants of " + dir.getFileId() + " in database", e);
-
- } catch (RemoteException e) {
- Log_OC.e(TAG, "Fail to update desendants of " + dir.getFileId() + " in database", e);
- }
-
- }
- }
-
- @Override
- public Vector<OCFile> getDirectoryImages(OCFile directory) {
- Vector<OCFile> ret = new Vector<OCFile>();
- if (directory != null) {
- // TODO better implementation, filtering in the access to database (if possible) instead of here
- Vector<OCFile> tmp = getDirectoryContent(directory);
- OCFile current = null;
- for (int i=0; i<tmp.size(); i++) {
- current = tmp.get(i);
- if (current.isImage()) {
- ret.add(current);
- }
- }
- }
- return ret;
- }
-
- /**
- * Calculate and save the folderSize on DB
- * @param id
- */
- @Override
- public void calculateFolderSize(long id) {
- long folderSize = 0;
-
- Vector<OCFile> files = getDirectoryContent(id);
-
- for (OCFile f: files)
- {
- folderSize = folderSize + f.getFileLength();
- }
-
- updateSize(id, folderSize);
- }
-
- /**
- * Update the size value of an OCFile in DB
- */
- private int updateSize(long id, long size) {
- ContentValues cv = new ContentValues();
- cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, size);
- int result = -1;
- if (getContentResolver() != null) {
- result = getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(id) });
- } else {
- try {
- result = getContentProvider().update(ProviderTableMeta.CONTENT_URI, cv, ProviderTableMeta._ID + "=?",
- new String[] { String.valueOf(id) });
- } catch (RemoteException e) {
- Log_OC.e(TAG,"Fail to update size column into database " + e.getMessage());
- }
- }
- return result;
- }
-
- /**
- * Update the size of a subtree of folder from a file to the root
- * @param parentId: parent of the file
- */
- private void updateSizesToTheRoot(long parentId) {
-
- OCFile file;
-
- while (parentId != 0) {
-
- // Update the size of the parent
- calculateFolderSize(parentId);
-
- // search the next parent
- file = getFileById(parentId);
- parentId = file.getParentId();
-
- }
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.datamodel;
-
-import java.io.File;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.webkit.MimeTypeMap;
-
-public class OCFile implements Parcelable, Comparable<OCFile> {
-
- public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
- @Override
- public OCFile createFromParcel(Parcel source) {
- return new OCFile(source);
- }
-
- @Override
- public OCFile[] newArray(int size) {
- return new OCFile[size];
- }
- };
-
- public static final String PATH_SEPARATOR = "/";
-
- private static final String TAG = OCFile.class.getSimpleName();
-
- private long mId;
- private long mParentId;
- private long mLength;
- private long mCreationTimestamp;
- private long mModifiedTimestamp;
- private long mModifiedTimestampAtLastSyncForData;
- private String mRemotePath;
- private String mLocalPath;
- private String mMimeType;
- private boolean mNeedsUpdating;
- private long mLastSyncDateForProperties;
- private long mLastSyncDateForData;
- private boolean mKeepInSync;
-
- private String mEtag;
-
- /**
- * Create new {@link OCFile} with given path.
- *
- * The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
- *
- * @param path The remote path of the file.
- */
- public OCFile(String path) {
- resetData();
- mNeedsUpdating = false;
- if (path == null || path.length() <= 0 || !path.startsWith(PATH_SEPARATOR)) {
- throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
- }
- mRemotePath = path;
- }
-
- /**
- * Reconstruct from parcel
- *
- * @param source The source parcel
- */
- private OCFile(Parcel source) {
- mId = source.readLong();
- mParentId = source.readLong();
- mLength = source.readLong();
- mCreationTimestamp = source.readLong();
- mModifiedTimestamp = source.readLong();
- mModifiedTimestampAtLastSyncForData = source.readLong();
- mRemotePath = source.readString();
- mLocalPath = source.readString();
- mMimeType = source.readString();
- mNeedsUpdating = source.readInt() == 0;
- mKeepInSync = source.readInt() == 1;
- mLastSyncDateForProperties = source.readLong();
- mLastSyncDateForData = source.readLong();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mId);
- dest.writeLong(mParentId);
- dest.writeLong(mLength);
- dest.writeLong(mCreationTimestamp);
- dest.writeLong(mModifiedTimestamp);
- dest.writeLong(mModifiedTimestampAtLastSyncForData);
- dest.writeString(mRemotePath);
- dest.writeString(mLocalPath);
- dest.writeString(mMimeType);
- dest.writeInt(mNeedsUpdating ? 1 : 0);
- dest.writeInt(mKeepInSync ? 1 : 0);
- dest.writeLong(mLastSyncDateForProperties);
- dest.writeLong(mLastSyncDateForData);
- }
-
- /**
- * Gets the ID of the file
- *
- * @return the file ID
- */
- public long getFileId() {
- return mId;
- }
-
- /**
- * Returns the remote path of the file on ownCloud
- *
- * @return The remote path to the file
- */
- public String getRemotePath() {
- return mRemotePath;
- }
-
- /**
- * Can be used to check, whether or not this file exists in the database
- * already
- *
- * @return true, if the file exists in the database
- */
- public boolean fileExists() {
- return mId != -1;
- }
-
- /**
- * Use this to find out if this file is a Directory
- *
- * @return true if it is a directory
- */
- public boolean isDirectory() {
- return mMimeType != null && mMimeType.equals("DIR");
- }
-
- /**
- * Use this to check if this file is available locally
- *
- * @return true if it is
- */
- public boolean isDown() {
- if (mLocalPath != null && mLocalPath.length() > 0) {
- File file = new File(mLocalPath);
- return (file.exists());
- }
- return false;
- }
-
- /**
- * The path, where the file is stored locally
- *
- * @return The local path to the file
- */
- public String getStoragePath() {
- return mLocalPath;
- }
-
- /**
- * Can be used to set the path where the file is stored
- *
- * @param storage_path to set
- */
- public void setStoragePath(String storage_path) {
- mLocalPath = storage_path;
- }
-
- /**
- * Get a UNIX timestamp of the file creation time
- *
- * @return A UNIX timestamp of the time that file was created
- */
- public long getCreationTimestamp() {
- return mCreationTimestamp;
- }
-
- /**
- * Set a UNIX timestamp of the time the file was created
- *
- * @param creation_timestamp to set
- */
- public void setCreationTimestamp(long creation_timestamp) {
- mCreationTimestamp = creation_timestamp;
- }
-
- /**
- * Get a UNIX timestamp of the file modification time.
- *
- * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
- * in the last synchronization of the properties of this file.
- */
- public long getModificationTimestamp() {
- return mModifiedTimestamp;
- }
-
- /**
- * Set a UNIX timestamp of the time the time the file was modified.
- *
- * To update with the value returned by the server in every synchronization of the properties
- * of this file.
- *
- * @param modification_timestamp to set
- */
- public void setModificationTimestamp(long modification_timestamp) {
- mModifiedTimestamp = modification_timestamp;
- }
-
-
- /**
- * Get a UNIX timestamp of the file modification time.
- *
- * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
- * in the last synchronization of THE CONTENTS of this file.
- */
- public long getModificationTimestampAtLastSyncForData() {
- return mModifiedTimestampAtLastSyncForData;
- }
-
- /**
- * Set a UNIX timestamp of the time the time the file was modified.
- *
- * To update with the value returned by the server in every synchronization of THE CONTENTS
- * of this file.
- *
- * @param modification_timestamp to set
- */
- public void setModificationTimestampAtLastSyncForData(long modificationTimestamp) {
- mModifiedTimestampAtLastSyncForData = modificationTimestamp;
- }
-
-
-
- /**
- * Returns the filename and "/" for the root directory
- *
- * @return The name of the file
- */
- public String getFileName() {
- File f = new File(getRemotePath());
- return f.getName().length() == 0 ? PATH_SEPARATOR : f.getName();
- }
-
- /**
- * Sets the name of the file
- *
- * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root directory
- */
- public void setFileName(String name) {
- Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
- if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) && !mRemotePath.equals(PATH_SEPARATOR)) {
- String parent = (new File(getRemotePath())).getParent();
- parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
- mRemotePath = parent + name;
- if (isDirectory()) {
- mRemotePath += PATH_SEPARATOR;
- }
- Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
- }
- }
-
- /**
- * Can be used to get the Mimetype
- *
- * @return the Mimetype as a String
- */
- public String getMimetype() {
- return mMimeType;
- }
-
- /**
- * Adds a file to this directory. If this file is not a directory, an
- * exception gets thrown.
- *
- * @param file to add
- * @throws IllegalStateException if you try to add a something and this is
- * not a directory
- */
- public void addFile(OCFile file) throws IllegalStateException {
- if (isDirectory()) {
- file.mParentId = mId;
- mNeedsUpdating = true;
- return;
- }
- throw new IllegalStateException(
- "This is not a directory where you can add stuff to!");
- }
-
- /**
- * Used internally. Reset all file properties
- */
- private void resetData() {
- mId = -1;
- mRemotePath = null;
- mParentId = 0;
- mLocalPath = null;
- mMimeType = null;
- mLength = 0;
- mCreationTimestamp = 0;
- mModifiedTimestamp = 0;
- mModifiedTimestampAtLastSyncForData = 0;
- mLastSyncDateForProperties = 0;
- mLastSyncDateForData = 0;
- mKeepInSync = false;
- mNeedsUpdating = false;
- }
-
- /**
- * Sets the ID of the file
- *
- * @param file_id to set
- */
- public void setFileId(long file_id) {
- mId = file_id;
- }
-
- /**
- * Sets the Mime-Type of the
- *
- * @param mimetype to set
- */
- public void setMimetype(String mimetype) {
- mMimeType = mimetype;
- }
-
- /**
- * Sets the ID of the parent folder
- *
- * @param parent_id to set
- */
- public void setParentId(long parent_id) {
- mParentId = parent_id;
- }
-
- /**
- * Sets the file size in bytes
- *
- * @param file_len to set
- */
- public void setFileLength(long file_len) {
- mLength = file_len;
- }
-
- /**
- * Returns the size of the file in bytes
- *
- * @return The filesize in bytes
- */
- public long getFileLength() {
- return mLength;
- }
-
- /**
- * Returns the ID of the parent Folder
- *
- * @return The ID
- */
- public long getParentId() {
- return mParentId;
- }
-
- /**
- * Check, if this file needs updating
- *
- * @return
- */
- public boolean needsUpdatingWhileSaving() {
- return mNeedsUpdating;
- }
-
- public long getLastSyncDateForProperties() {
- return mLastSyncDateForProperties;
- }
-
- public void setLastSyncDateForProperties(long lastSyncDate) {
- mLastSyncDateForProperties = lastSyncDate;
- }
-
- public long getLastSyncDateForData() {
- return mLastSyncDateForData;
- }
-
- public void setLastSyncDateForData(long lastSyncDate) {
- mLastSyncDateForData = lastSyncDate;
- }
-
- public void setKeepInSync(boolean keepInSync) {
- mKeepInSync = keepInSync;
- }
-
- public boolean keepInSync() {
- return mKeepInSync;
- }
-
- @Override
- public int describeContents() {
- return this.hashCode();
- }
-
- @Override
- public int compareTo(OCFile another) {
- if (isDirectory() && another.isDirectory()) {
- return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
- } else if (isDirectory()) {
- return -1;
- } else if (another.isDirectory()) {
- return 1;
- }
- return getRemotePath().toLowerCase().compareTo(another.getRemotePath().toLowerCase());
- }
-
- @Override
- public boolean equals(Object o) {
- if(o instanceof OCFile){
- OCFile that = (OCFile) o;
- if(that != null){
- return this.mId == that.mId;
- }
- }
-
- return false;
- }
-
- @Override
- public String toString() {
- String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s]";
- asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync));
- return asString;
- }
-
- public String getEtag() {
- return mEtag;
- }
-
- public long getLocalModificationTimestamp() {
- if (mLocalPath != null && mLocalPath.length() > 0) {
- File f = new File(mLocalPath);
- return f.lastModified();
- }
- return 0;
- }
-
- /** @return 'True' if the file contains audio */
- public boolean isAudio() {
- return (mMimeType != null && mMimeType.startsWith("audio/"));
- }
-
- /** @return 'True' if the file contains video */
- public boolean isVideo() {
- return (mMimeType != null && mMimeType.startsWith("video/"));
- }
-
- /** @return 'True' if the file contains an image */
- public boolean isImage() {
- return ((mMimeType != null && mMimeType.startsWith("image/")) ||
- getMimeTypeFromName().startsWith("image/"));
- }
-
- public String getMimeTypeFromName() {
- String extension = "";
- int pos = mRemotePath.lastIndexOf('.');
- if (pos >= 0) {
- extension = mRemotePath.substring(pos + 1);
- }
- String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
- return (result != null) ? result : "";
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011-2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.db;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-
-/**
- * Custom database helper for ownCloud
- *
- * @author Bartek Przybylski
- *
- */
-public class DbHandler {
- private SQLiteDatabase mDB;
- private OpenerHelper mHelper;
- private final String mDatabaseName; // = "ownCloud";
- private final int mDatabaseVersion = 3;
-
- private final String TABLE_INSTANT_UPLOAD = "instant_upload";
-
- public static final int UPLOAD_STATUS_UPLOAD_LATER = 0;
- public static final int UPLOAD_STATUS_UPLOAD_FAILED = 1;
-
- public DbHandler(Context context) {
- mHelper = new OpenerHelper(context);
- mDB = mHelper.getWritableDatabase();
- mDatabaseName = MainApp.getDBName();
- }
-
- public void close() {
- mDB.close();
- }
-
- public boolean putFileForLater(String filepath, String account, String message) {
- ContentValues cv = new ContentValues();
- cv.put("path", filepath);
- cv.put("account", account);
- cv.put("attempt", UPLOAD_STATUS_UPLOAD_LATER);
- cv.put("message", message);
- long result = mDB.insert(TABLE_INSTANT_UPLOAD, null, cv);
- Log_OC.d(TABLE_INSTANT_UPLOAD, "putFileForLater returns with: " + result + " for file: " + filepath);
- return result != -1;
- }
-
- public int updateFileState(String filepath, Integer status, String message) {
- ContentValues cv = new ContentValues();
- cv.put("attempt", status);
- cv.put("message", message);
- int result = mDB.update(TABLE_INSTANT_UPLOAD, cv, "path=?", new String[] { filepath });
- Log_OC.d(TABLE_INSTANT_UPLOAD, "updateFileState returns with: " + result + " for file: " + filepath);
- return result;
- }
-
- public Cursor getAwaitingFiles() {
- return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt=" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
- }
-
- public Cursor getFailedFiles() {
- return mDB.query(TABLE_INSTANT_UPLOAD, null, "attempt>" + UPLOAD_STATUS_UPLOAD_LATER, null, null, null, null);
- }
-
- public void clearFiles() {
- mDB.delete(TABLE_INSTANT_UPLOAD, null, null);
- }
-
- /**
- *
- * @param localPath
- * @return true when one or more pending files was removed
- */
- public boolean removeIUPendingFile(String localPath) {
- long result = mDB.delete(TABLE_INSTANT_UPLOAD, "path = ?", new String[] { localPath });
- Log_OC.d(TABLE_INSTANT_UPLOAD, "delete returns with: " + result + " for file: " + localPath);
- return result != 0;
-
- }
-
- private class OpenerHelper extends SQLiteOpenHelper {
- public OpenerHelper(Context context) {
- super(context, mDatabaseName, null, mDatabaseVersion);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_INSTANT_UPLOAD + " (" + " _id INTEGER PRIMARY KEY, " + " path TEXT,"
- + " account TEXT,attempt INTEGER,message TEXT);");
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion < 2) {
- db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN attempt INTEGER;");
- }
- db.execSQL("ALTER TABLE " + TABLE_INSTANT_UPLOAD + " ADD COLUMN message TEXT;");
-
- }
- }
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.db;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-\r
-import android.net.Uri;\r
-import android.provider.BaseColumns;\r
-\r
-/**\r
- * Meta-Class that holds various static field information\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class ProviderMeta {\r
-\r
- /* These constants are now in MainApp\r
- public static final String AUTHORITY_FILES = "org.owncloud";\r
- public static final String DB_FILE = "owncloud.db";\r
- */\r
- public static final String DB_NAME = "filelist";\r
- public static final int DB_VERSION = 4;\r
-\r
- private ProviderMeta() {\r
- }\r
-\r
- static public class ProviderTableMeta implements BaseColumns {\r
- public static final String DB_NAME = "filelist";\r
- public static final Uri CONTENT_URI = Uri.parse("content://"\r
- + MainApp.getAuthority() + "/");\r
- public static final Uri CONTENT_URI_FILE = Uri.parse("content://"\r
- + MainApp.getAuthority() + "/file");\r
- public static final Uri CONTENT_URI_DIR = Uri.parse("content://"\r
- + MainApp.getAuthority() + "/dir");\r
-\r
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";\r
- public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";\r
-\r
- public static final String FILE_PARENT = "parent";\r
- public static final String FILE_NAME = "filename";\r
- public static final String FILE_CREATION = "created";\r
- public static final String FILE_MODIFIED = "modified";\r
- public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data";\r
- public static final String FILE_CONTENT_LENGTH = "content_length";\r
- public static final String FILE_CONTENT_TYPE = "content_type";\r
- public static final String FILE_STORAGE_PATH = "media_path";\r
- public static final String FILE_PATH = "path";\r
- public static final String FILE_ACCOUNT_OWNER = "file_owner";\r
- public static final String FILE_LAST_SYNC_DATE = "last_sync_date"; // _for_properties, but let's keep it as it is\r
- public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
- public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
-\r
- public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
- + " collate nocase asc";\r
-\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import android.os.Bundle;
-import android.support.v4.app.FragmentManager;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-public class ExtensionsAvailableActivity extends SherlockFragmentActivity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FragmentManager fm = getSupportFragmentManager();
- ExtensionsAvailableDialog ead = new ExtensionsAvailableDialog();
- ead.show(fm, "extensions_available_dialog");
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-
-public class ExtensionsAvailableDialog extends DialogFragment implements
- OnClickListener {
-
- public ExtensionsAvailableDialog() {
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.extensions_available_dialog,
- container);
- CustomButton btnYes = (CustomButton) view.findViewById(R.id.buttonYes);
- CustomButton btnNo = (CustomButton) view.findViewById(R.id.buttonNo);
-
- btnYes.setOnClickListener(this);
- btnNo.setOnClickListener(this);
- getDialog().setTitle(R.string.extensions_avail_title);
- return view;
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.buttonYes: {
- Intent i = new Intent(getActivity(), ExtensionsListActivity.class);
- startActivity(i);
- getActivity().finish();
- }
- break;
- case R.id.buttonNo:
- getActivity().finish();
- break;
- default:
- Log_OC.e("EAD", "Button with unknown id clicked " + v.getId());
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.extensions;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-
-import android.R;
-import android.app.ListActivity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.widget.SimpleAdapter;
-
-public class ExtensionsListActivity extends ListActivity {
-
- private static final String packages_url = "http://alefzero.eu/a/packages.php";
-
- private Thread mGetterThread;
- private final Handler mHandler = new Handler();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mGetterThread = new Thread(new JsonGetter());
- mGetterThread.start();
- }
-
- public void done(JSONArray a) {
- LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
- for (int i = 0; i < a.length(); ++i) {
- try {
- ExtensionApplicationEntry ela = new ExtensionApplicationEntry(
- ((JSONObject) a.get(i)));
- HashMap<String, String> ss = new HashMap<String, String>();
- ss.put("NAME", ela.getName());
- ss.put("DESC", ela.getDescription());
- ll.add(ss);
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- setListAdapter(new SimpleAdapter(this, ll, R.layout.simple_list_item_2,
- new String[] { "NAME", "DESC" }, new int[] {
- android.R.id.text1, android.R.id.text2 }));
-
- }
-
- private class JsonGetter implements Runnable {
-
- @Override
- public void run() {
- HttpClient hc = new HttpClient();
- GetMethod gm = new GetMethod(packages_url);
- final JSONArray ar;
- try {
- hc.executeMethod(gm);
- Log_OC.e("ASD", gm.getResponseBodyAsString() + "");
- ar = new JSONObject(gm.getResponseBodyAsString())
- .getJSONArray("apps");
- } catch (Exception e) {
- e.printStackTrace();
- return;
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- done(ar);
- }
- });
-
- }
-
- }
-
- private class ExtensionApplicationEntry {
- private static final String APP_NAME = "name";
- private static final String APP_VERSION = "version";
- private static final String APP_DESC = "description";
- private static final String APP_ICON = "icon";
- private static final String APP_URL = "download";
- private static final String APP_PLAYID = "play_id";
-
- private String mName, mDescription, mIcon, mDownload, mPlayId;
- private OwnCloudVersion mVersion;
-
- public ExtensionApplicationEntry(JSONObject appentry) {
- try {
- mName = appentry.getString(APP_NAME);
- mDescription = appentry.getString(APP_DESC);
- mIcon = appentry.getString(APP_ICON);
- mDownload = appentry.getString(APP_URL);
- mPlayId = appentry.getString(APP_PLAYID);
- mVersion = new OwnCloudVersion(appentry.getString(APP_VERSION));
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
-
- public String getName() {
- return mName;
- }
-
- public String getDescription() {
- return mDescription;
- }
-
- @SuppressWarnings("unused")
- public String getIcon() {
- return mIcon;
- }
-
- @SuppressWarnings("unused")
- public String getDownload() {
- return mDownload;
- }
-
- @SuppressWarnings("unused")
- public String getPlayId() {
- return mPlayId;
- }
-
- @SuppressWarnings("unused")
- public OwnCloudVersion getVersion() {
- return mVersion;
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class BootupBroadcastReceiver extends BroadcastReceiver {
-
- private static String TAG = "BootupBroadcastReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
- Log_OC.wtf(TAG, "Incorrect action sent " + intent.getAction());
- return;
- }
- Log_OC.d(TAG, "Starting file observer service...");
- Intent i = new Intent(context, FileObserverService.class);
- i.putExtra(FileObserverService.KEY_FILE_CMD,
- FileObserverService.CMD_INIT_OBSERVED_LIST);
- context.startService(i);
- Log_OC.d(TAG, "DONE");
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files;
-
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-public interface FileHandler {
-
- /**
- * TODO
- */
- public void openFile(OCFile file);
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files;
-
-import java.io.File;
-
-
-import android.accounts.Account;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-//import android.content.IntentFilter;
-import android.database.Cursor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo.State;
-import android.preference.PreferenceManager;
-import android.provider.MediaStore.Images.Media;
-import android.webkit.MimeTypeMap;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-public class InstantUploadBroadcastReceiver extends BroadcastReceiver {
-
- private static String TAG = "InstantUploadBroadcastReceiver";
- private static final String[] CONTENT_PROJECTION = { Media.DATA, Media.DISPLAY_NAME, Media.MIME_TYPE, Media.SIZE };
- //Unofficial action, works for most devices but not HTC. See: https://github.com/owncloud/android/issues/6
- private static String NEW_PHOTO_ACTION_UNOFFICIAL = "com.android.camera.NEW_PICTURE";
- //Officially supported action since SDK 14: http://developer.android.com/reference/android/hardware/Camera.html#ACTION_NEW_PICTURE
- private static String NEW_PHOTO_ACTION = "android.hardware.action.NEW_PICTURE";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Log_OC.d(TAG, "Received: " + intent.getAction());
-
- FileUploader fileUploader = new FileUploader();
-
- if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) {
- handleConnectivityAction(context, intent);
- }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) {
- handleNewPhotoAction(context, intent);
- Log_OC.d(TAG, "UNOFFICIAL processed: com.android.camera.NEW_PICTURE");
- } else if (intent.getAction().equals(NEW_PHOTO_ACTION)) {
- handleNewPhotoAction(context, intent);
- Log_OC.d(TAG, "OFFICIAL processed: android.hardware.action.NEW_PICTURE");
- } else if (intent.getAction().equals(fileUploader.getUploadFinishMessage())) {
- handleUploadFinished(context, intent);
- } else {
- Log_OC.e(TAG, "Incorrect intent sent: " + intent.getAction());
- }
- }
-
- private void handleUploadFinished(Context context, Intent intent) {
- // remove successfull uploading, ignore rest for reupload on reconnect
- /*
- if (intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false)) {
- DbHandler db = new DbHandler(context);
- String localPath = intent.getStringExtra(FileUploader.EXTRA_OLD_FILE_PATH);
- if (!db.removeIUPendingFile(localPath)) {
- Log_OC.w(TAG, "Tried to remove non existing instant upload file " + localPath);
- }
- db.close();
- }
- */
- }
-
- private void handleNewPhotoAction(Context context, Intent intent) {
- if (!instantUploadEnabled(context)) {
- Log_OC.d(TAG, "Instant upload disabled, aborting uploading");
- return;
- }
-
- Account account = AccountUtils.getCurrentOwnCloudAccount(context);
- if (account == null) {
- Log_OC.w(TAG, "No owncloud account found for instant upload, aborting");
- return;
- }
-
- Cursor c = context.getContentResolver().query(intent.getData(), CONTENT_PROJECTION, null, null, null);
-
- if (!c.moveToFirst()) {
- Log_OC.e(TAG, "Couldn't resolve given uri: " + intent.getDataString());
- return;
- }
-
- String file_path = c.getString(c.getColumnIndex(Media.DATA));
- String file_name = c.getString(c.getColumnIndex(Media.DISPLAY_NAME));
- String mime_type = c.getString(c.getColumnIndex(Media.MIME_TYPE));
-
- c.close();
- Log_OC.e(TAG, file_path + "");
-
- // same always temporally the picture to upload
- DbHandler db = new DbHandler(context);
- db.putFileForLater(file_path, account.name, null);
- db.close();
-
- if (!isOnline(context) || (instantUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) {
- return;
- }
-
- // register for upload finishe message
- // there is a litte problem with android API, we can register for
- // particular
- // intent in registerReceiver but we cannot unregister from precise
- // intent
- // we can unregister from entire listenings but thats suck a bit.
- // On the other hand this might be only for dynamicly registered
- // broadcast receivers, needs investigation.
- /*IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
- context.getApplicationContext().registerReceiver(this, filter);*/
-
- Intent i = new Intent(context, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, account);
- i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, file_name));
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- i.putExtra(FileUploader.KEY_MIME_TYPE, mime_type);
- i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
- context.startService(i);
-
- }
-
- private void handleConnectivityAction(Context context, Intent intent) {
- if (!instantUploadEnabled(context)) {
- Log_OC.d(TAG, "Instant upload disabled, abording uploading");
- return;
- }
-
- if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY)
- && isOnline(context)
- && (!instantUploadViaWiFiOnly(context) || (instantUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) {
- DbHandler db = new DbHandler(context);
- Cursor c = db.getAwaitingFiles();
- if (c.moveToFirst()) {
- //IntentFilter filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE);
- //context.getApplicationContext().registerReceiver(this, filter);
- do {
- String account_name = c.getString(c.getColumnIndex("account"));
- String file_path = c.getString(c.getColumnIndex("path"));
- File f = new File(file_path);
- if (f.exists()) {
- Account account = new Account(account_name, MainApp.getAccountType());
-
- String mimeType = null;
- try {
- mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- f.getName().substring(f.getName().lastIndexOf('.') + 1));
-
- } catch (Throwable e) {
- Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + f.getName());
- }
- if (mimeType == null)
- mimeType = "application/octet-stream";
-
- Intent i = new Intent(context, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, account);
- i.putExtra(FileUploader.KEY_LOCAL_FILE, file_path);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, FileStorageUtils.getInstantUploadFilePath(context, f.getName()));
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- i.putExtra(FileUploader.KEY_INSTANT_UPLOAD, true);
- context.startService(i);
-
- } else {
- Log_OC.w(TAG, "Instant upload file " + f.getAbsolutePath() + " dont exist anymore");
- }
- } while (c.moveToNext());
- }
- c.close();
- db.close();
- }
-
- }
-
- public static boolean isOnline(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
- }
-
- public static boolean isConnectedViaWiFi(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm != null && cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
- && cm.getActiveNetworkInfo().getState() == State.CONNECTED;
- }
-
- public static boolean instantUploadEnabled(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false);
- }
-
- public static boolean instantUploadViaWiFiOnly(Context context) {
- return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_wifi", false);
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files;
-
-import java.io.File;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.ConflictsResolveActivity;
-
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-import android.os.FileObserver;
-
-public class OwnCloudFileObserver extends FileObserver {
-
- public static int CHANGES_ONLY = CLOSE_WRITE;
-
- private static String TAG = OwnCloudFileObserver.class.getSimpleName();
-
- private String mPath;
- private int mMask;
- private Account mOCAccount;
- //private OCFile mFile;
- private Context mContext;
-
-
- public OwnCloudFileObserver(String path, Account account, Context context, int mask) {
- super(path, mask);
- if (path == null)
- throw new IllegalArgumentException("NULL path argument received");
- /*if (file == null)
- throw new IllegalArgumentException("NULL file argument received");*/
- if (account == null)
- throw new IllegalArgumentException("NULL account argument received");
- if (context == null)
- throw new IllegalArgumentException("NULL context argument received");
- /*if (!path.equals(file.getStoragePath()) && !path.equals(FileStorageUtils.getDefaultSavePathFor(account.name, file)))
- throw new IllegalArgumentException("File argument is not linked to the local file set in path argument"); */
- mPath = path;
- //mFile = file;
- mOCAccount = account;
- mContext = context;
- mMask = mask;
- }
-
- @Override
- public void onEvent(int event, String path) {
- Log_OC.d(TAG, "Got file modified with event " + event + " and path " + mPath + ((path != null) ? File.separator + path : ""));
- if ((event & mMask) == 0) {
- Log_OC.wtf(TAG, "Incorrect event " + event + " sent for file " + mPath + ((path != null) ? File.separator + path : "") +
- " with registered for " + mMask + " and original path " +
- mPath);
- return;
- }
- FileDataStorageManager storageManager = new FileDataStorageManager(mOCAccount, mContext.getContentResolver());
- OCFile file = storageManager.getFileByLocalPath(mPath); // a fresh object is needed; many things could have occurred to the file since it was registered to observe
- // again, assuming that local files are linked to a remote file AT MOST, SOMETHING TO BE DONE;
- SynchronizeFileOperation sfo = new SynchronizeFileOperation(file,
- null,
- storageManager,
- mOCAccount,
- true,
- true,
- mContext);
- RemoteOperationResult result = sfo.execute(mOCAccount, mContext);
- if (result.getCode() == ResultCode.SYNC_CONFLICT) {
- // ISSUE 5: if the user is not running the app (this is a service!), this can be very intrusive; a notification should be preferred
- Intent i = new Intent(mContext, ConflictsResolveActivity.class);
- i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
- i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
- i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mOCAccount);
- mContext.startActivity(i);
- }
- // TODO save other errors in some point where the user can inspect them later;
- // or maybe just toast them;
- // or nothing, very strange fails
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files.managers;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.widget.RemoteViews;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-public class OCNotificationManager {
-
- enum NotificationType {
- NOTIFICATION_SIMPLE,
- NOTIFICATION_PROGRESS
- }
-
- static public class NotificationData {
- private String mText, mSubtitle;
- private int mPercent;
- private boolean mOngoing;
-
- public NotificationData(String text, String subtitle, boolean ongoing) {
- this(text, subtitle, -1, ongoing);
- }
-
- public NotificationData(int percent, boolean ongoing) {
- this(null, null, percent, ongoing);
- }
-
- public NotificationData(String text, int percent, boolean ongoing) {
- this(text, null, percent, ongoing);
- }
-
- public NotificationData(String text, String subtitle, int percent, boolean ongoing) {
- mText = text;
- mPercent = percent;
- mSubtitle = subtitle;
- mOngoing = ongoing;
- }
-
- public String getText() { return mText; }
- public int getPercent() { return mPercent; }
- public String getSubtitle() { return mSubtitle; }
- public boolean getOngoing() { return mOngoing; }
- }
-
- static private OCNotificationManager mInstance = null;
-
- private class NotificationTypePair {
- public Notification mNotificaiton;
- public NotificationType mType;
- public NotificationTypePair(Notification n, NotificationType type) {
- mNotificaiton = n;
- mType = type;
- }
- }
-
- private Context mContext;
- private Map<Integer, NotificationTypePair> mNotificationMap;
- private int mNotificationCounter;
- NotificationManager mNM;
-
- static OCNotificationManager getInstance(Context context) {
- if (mInstance == null)
- mInstance = new OCNotificationManager(context);
- return mInstance;
- }
-
- OCNotificationManager(Context context) {
- mContext = context;
- mNotificationMap = new HashMap<Integer, NotificationTypePair>();
- mNM = (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- mNotificationCounter = 0;
- }
-
- public int postNotification(NotificationType type, NotificationData data) {
- mNotificationCounter++;
- Notification notification = null;
-
- switch (type) {
- case NOTIFICATION_SIMPLE:
- notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
- break;
- case NOTIFICATION_PROGRESS:
- notification = new Notification();
- notification.contentView = new RemoteViews(mContext.getPackageName(), R.layout.progressbar_layout);
- notification.contentView.setTextViewText(R.id.status_text,
- data.getText());
- notification.contentView.setImageViewResource(R.id.status_icon,
- R.id.icon);
- notification.contentView.setProgressBar(R.id.status_progress,
- 100,
- data.getPercent(),
- false);
- break;
- default:
- return -1;
- }
- if (data.getOngoing()) {
- notification.flags |= notification.flags | Notification.FLAG_ONGOING_EVENT;
- }
-
- mNotificationMap.put(mNotificationCounter, new NotificationTypePair(notification, type));
- return mNotificationCounter;
- }
-
- public boolean updateNotification(int notification_id, NotificationData data) {
- if (!mNotificationMap.containsKey(notification_id)) {
- return false;
- }
- NotificationTypePair pair = mNotificationMap.get(notification_id);
- switch (pair.mType) {
- case NOTIFICATION_PROGRESS:
- pair.mNotificaiton.contentView.setProgressBar(R.id.status_text,
- 100,
- data.getPercent(),
- false);
- return true;
- case NOTIFICATION_SIMPLE:
- pair.mNotificaiton = new Notification(R.drawable.icon,
- data.getText(), System.currentTimeMillis());
- mNM.notify(notification_id, pair.mNotificaiton);
- return true;
- default:
- return false;
- }
- }
-
- public void discardNotification(int notification_id) {
- mNM.cancel(notification_id);
- mNotificationMap.remove(notification_id);
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files.services;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.AbstractList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-
-
-import android.accounts.Account;
-import android.accounts.AccountsException;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.widget.RemoteViews;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.DownloadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-import eu.alefzero.webdav.WebdavClient;
-
-public class FileDownloader extends Service implements OnDatatransferProgressListener {
-
- public static final String EXTRA_ACCOUNT = "ACCOUNT";
- public static final String EXTRA_FILE = "FILE";
-
- private static final String DOWNLOAD_ADDED_MESSAGE = "DOWNLOAD_ADDED";
- private static final String DOWNLOAD_FINISH_MESSAGE = "DOWNLOAD_FINISH";
- public static final String EXTRA_DOWNLOAD_RESULT = "RESULT";
- public static final String EXTRA_FILE_PATH = "FILE_PATH";
- public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
- public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
-
- private static final String TAG = "FileDownloader";
-
- private Looper mServiceLooper;
- private ServiceHandler mServiceHandler;
- private IBinder mBinder;
- private WebdavClient mDownloadClient = null;
- private Account mLastAccount = null;
- private FileDataStorageManager mStorageManager;
-
- private ConcurrentMap<String, DownloadFileOperation> mPendingDownloads = new ConcurrentHashMap<String, DownloadFileOperation>();
- private DownloadFileOperation mCurrentDownload = null;
-
- private NotificationManager mNotificationManager;
- private Notification mNotification;
- private int mLastPercent;
-
-
- public String getDownloadAddedMessage() {
- return getClass().getName().toString() + DOWNLOAD_ADDED_MESSAGE;
- }
-
- public String getDownloadFinishMessage() {
- return getClass().getName().toString() + DOWNLOAD_FINISH_MESSAGE;
- }
-
- /**
- * Builds a key for mPendingDownloads from the account and file to download
- *
- * @param account Account where the file to download is stored
- * @param file File to download
- */
- private String buildRemoteName(Account account, OCFile file) {
- return account.name + file.getRemotePath();
- }
-
-
- /**
- * Service initialization
- */
- @Override
- public void onCreate() {
- super.onCreate();
- mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- HandlerThread thread = new HandlerThread("FileDownloaderThread",
- Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mServiceLooper = thread.getLooper();
- mServiceHandler = new ServiceHandler(mServiceLooper, this);
- mBinder = new FileDownloaderBinder();
- }
-
- /**
- * Entry point to add one or several files to the queue of downloads.
- *
- * New downloads are added calling to startService(), resulting in a call to this method. This ensures the service will keep on working
- * although the caller activity goes away.
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if ( !intent.hasExtra(EXTRA_ACCOUNT) ||
- !intent.hasExtra(EXTRA_FILE)
- /*!intent.hasExtra(EXTRA_FILE_PATH) ||
- !intent.hasExtra(EXTRA_REMOTE_PATH)*/
- ) {
- Log_OC.e(TAG, "Not enough information provided in intent");
- return START_NOT_STICKY;
- }
- Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
- OCFile file = intent.getParcelableExtra(EXTRA_FILE);
-
- AbstractList<String> requestedDownloads = new Vector<String>(); // dvelasco: now this always contains just one element, but that can change in a near future (download of multiple selection)
- String downloadKey = buildRemoteName(account, file);
- try {
- DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
- mPendingDownloads.putIfAbsent(downloadKey, newDownload);
- newDownload.addDatatransferProgressListener(this);
- newDownload.addDatatransferProgressListener((FileDownloaderBinder)mBinder);
- requestedDownloads.add(downloadKey);
- sendBroadcastNewDownload(newDownload);
-
- } catch (IllegalArgumentException e) {
- Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
- return START_NOT_STICKY;
- }
-
- if (requestedDownloads.size() > 0) {
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- msg.obj = requestedDownloads;
- mServiceHandler.sendMessage(msg);
- }
-
- return START_NOT_STICKY;
- }
-
-
- /**
- * Provides a binder object that clients can use to perform operations on the queue of downloads, excepting the addition of new files.
- *
- * Implemented to perform cancellation, pause and resume of existing downloads.
- */
- @Override
- public IBinder onBind(Intent arg0) {
- return mBinder;
- }
-
-
- /**
- * Called when ALL the bound clients were onbound.
- */
- @Override
- public boolean onUnbind(Intent intent) {
- ((FileDownloaderBinder)mBinder).clearListeners();
- return false; // not accepting rebinding (default behaviour)
- }
-
-
- /**
- * Binder to let client components to perform operations on the queue of downloads.
- *
- * It provides by itself the available operations.
- */
- public class FileDownloaderBinder extends Binder implements OnDatatransferProgressListener {
-
- /**
- * Map of listeners that will be reported about progress of downloads from a {@link FileDownloaderBinder} instance
- */
- private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
-
-
- /**
- * Cancels a pending or current download of a remote file.
- *
- * @param account Owncloud account where the remote file is stored.
- * @param file A file in the queue of pending downloads
- */
- public void cancel(Account account, OCFile file) {
- DownloadFileOperation download = null;
- synchronized (mPendingDownloads) {
- download = mPendingDownloads.remove(buildRemoteName(account, file));
- }
- if (download != null) {
- download.cancel();
- }
- }
-
-
- public void clearListeners() {
- mBoundListeners.clear();
- }
-
-
- /**
- * Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or waiting to download.
- *
- * If 'file' is a directory, returns 'true' if some of its descendant files is downloading or waiting to download.
- *
- * @param account Owncloud account where the remote file is stored.
- * @param file A file that could be in the queue of downloads.
- */
- public boolean isDownloading(Account account, OCFile file) {
- if (account == null || file == null) return false;
- String targetKey = buildRemoteName(account, file);
- synchronized (mPendingDownloads) {
- if (file.isDirectory()) {
- // this can be slow if there are many downloads :(
- Iterator<String> it = mPendingDownloads.keySet().iterator();
- boolean found = false;
- while (it.hasNext() && !found) {
- found = it.next().startsWith(targetKey);
- }
- return found;
- } else {
- return (mPendingDownloads.containsKey(targetKey));
- }
- }
- }
-
-
- /**
- * Adds a listener interested in the progress of the download for a concrete file.
- *
- * @param listener Object to notify about progress of transfer.
- * @param account ownCloud account holding the file of interest.
- * @param file {@link OCfile} of interest for listener.
- */
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
- if (account == null || file == null || listener == null) return;
- String targetKey = buildRemoteName(account, file);
- mBoundListeners.put(targetKey, listener);
- }
-
-
- /**
- * Removes a listener interested in the progress of the download for a concrete file.
- *
- * @param listener Object to notify about progress of transfer.
- * @param account ownCloud account holding the file of interest.
- * @param file {@link OCfile} of interest for listener.
- */
- public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
- if (account == null || file == null || listener == null) return;
- String targetKey = buildRemoteName(account, file);
- if (mBoundListeners.get(targetKey) == listener) {
- mBoundListeners.remove(targetKey);
- }
- }
-
-
- @Override
- public void onTransferProgress(long progressRate) {
- // old way, should not be in use any more
- }
-
-
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
- String fileName) {
- String key = buildRemoteName(mCurrentDownload.getAccount(), mCurrentDownload.getFile());
- OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
- if (boundListener != null) {
- boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
- }
- }
-
- }
-
-
- /**
- * Download worker. Performs the pending downloads in the order they were requested.
- *
- * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}.
- */
- private static class ServiceHandler extends Handler {
- // don't make it a final class, and don't remove the static ; lint will warn about a possible memory leak
- FileDownloader mService;
- public ServiceHandler(Looper looper, FileDownloader service) {
- super(looper);
- if (service == null)
- throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
- mService = service;
- }
-
- @Override
- public void handleMessage(Message msg) {
- @SuppressWarnings("unchecked")
- AbstractList<String> requestedDownloads = (AbstractList<String>) msg.obj;
- if (msg.obj != null) {
- Iterator<String> it = requestedDownloads.iterator();
- while (it.hasNext()) {
- mService.downloadFile(it.next());
- }
- }
- mService.stopSelf(msg.arg1);
- }
- }
-
-
- /**
- * Core download method: requests a file to download and stores it.
- *
- * @param downloadKey Key to access the download to perform, contained in mPendingDownloads
- */
- private void downloadFile(String downloadKey) {
-
- synchronized(mPendingDownloads) {
- mCurrentDownload = mPendingDownloads.get(downloadKey);
- }
-
- if (mCurrentDownload != null) {
-
- notifyDownloadStart(mCurrentDownload);
-
- RemoteOperationResult downloadResult = null;
- try {
- /// prepare client object to send the request to the ownCloud server
- if (mDownloadClient == null || !mLastAccount.equals(mCurrentDownload.getAccount())) {
- mLastAccount = mCurrentDownload.getAccount();
- mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
- mDownloadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
- }
-
- /// perform the download
- downloadResult = mCurrentDownload.execute(mDownloadClient);
- if (downloadResult.isSuccess()) {
- saveDownloadedFile();
- }
-
- } catch (AccountsException e) {
- Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
- downloadResult = new RemoteOperationResult(e);
- } catch (IOException e) {
- Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
- downloadResult = new RemoteOperationResult(e);
-
- } finally {
- synchronized(mPendingDownloads) {
- mPendingDownloads.remove(downloadKey);
- }
- }
-
-
- /// notify result
- notifyDownloadResult(mCurrentDownload, downloadResult);
-
- sendBroadcastDownloadFinished(mCurrentDownload, downloadResult);
- }
- }
-
-
- /**
- * Updates the OC File after a successful download.
- */
- private void saveDownloadedFile() {
- OCFile file = mCurrentDownload.getFile();
- long syncDate = System.currentTimeMillis();
- file.setLastSyncDateForProperties(syncDate);
- file.setLastSyncDateForData(syncDate);
- file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp());
- file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp());
- // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available
- file.setMimetype(mCurrentDownload.getMimeType());
- file.setStoragePath(mCurrentDownload.getSavePath());
- file.setFileLength((new File(mCurrentDownload.getSavePath()).length()));
- mStorageManager.saveFile(file);
- }
-
-
- /**
- * Creates a status notification to show the download progress
- *
- * @param download Download operation starting.
- */
- private void notifyDownloadStart(DownloadFileOperation download) {
- /// create status notification with a progress bar
- mLastPercent = 0;
- mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
- mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
- mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
- mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
- mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
- mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
-
- /// includes a pending intent in the notification showing the details view of the file
- Intent showDetailsIntent = null;
- if (PreviewImageFragment.canBePreviewed(download.getFile())) {
- showDetailsIntent = new Intent(this, PreviewImageActivity.class);
- } else {
- showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- }
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
-
- mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
- }
-
-
- /**
- * Callback method to update the progress bar in the status notification.
- */
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
- int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
- if (percent != mLastPercent) {
- mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);
- String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
- mNotification.contentView.setTextViewText(R.id.status_text, text);
- mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
- }
- mLastPercent = percent;
- }
-
-
- /**
- * Callback method to update the progress bar in the status notification (old version)
- */
- @Override
- public void onTransferProgress(long progressRate) {
- // NOTHING TO DO HERE ANYMORE
- }
-
-
- /**
- * Updates the status notification with the result of a download operation.
- *
- * @param downloadResult Result of the download operation.
- * @param download Finished download operation
- */
- private void notifyDownloadResult(DownloadFileOperation download, RemoteOperationResult downloadResult) {
- mNotificationManager.cancel(R.string.downloader_download_in_progress_ticker);
- if (!downloadResult.isCancelled()) {
- int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
- int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
- Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
- finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
- boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
- // (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
- (downloadResult.isIdPRedirection()
- && MainApp.getAuthTokenTypeSamlSessionCookie().equals(mDownloadClient.getAuthTokenType())));
- if (needsToUpdateCredentials) {
- // let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, download.getAccount());
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
- finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
- finalNotification.setLatestEventInfo( getApplicationContext(),
- getString(tickerId),
- String.format(getString(contentId), new File(download.getSavePath()).getName()),
- finalNotification.contentIntent);
- mDownloadClient = null; // grant that future retries on the same account will get the fresh credentials
-
- } else {
- Intent showDetailsIntent = null;
- if (downloadResult.isSuccess()) {
- if (PreviewImageFragment.canBePreviewed(download.getFile())) {
- showDetailsIntent = new Intent(this, PreviewImageActivity.class);
- } else {
- showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- }
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- } else {
- // TODO put something smart in showDetailsIntent
- showDetailsIntent = new Intent();
- }
- finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), showDetailsIntent, 0);
- finalNotification.setLatestEventInfo(getApplicationContext(), getString(tickerId), String.format(getString(contentId), new File(download.getSavePath()).getName()), finalNotification.contentIntent);
- }
- mNotificationManager.notify(tickerId, finalNotification);
- }
- }
-
-
- /**
- * Sends a broadcast when a download finishes in order to the interested activities can update their view
- *
- * @param download Finished download operation
- * @param downloadResult Result of the download operation
- */
- private void sendBroadcastDownloadFinished(DownloadFileOperation download, RemoteOperationResult downloadResult) {
- Intent end = new Intent(getDownloadFinishMessage());
- end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess());
- end.putExtra(ACCOUNT_NAME, download.getAccount().name);
- end.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
- end.putExtra(EXTRA_FILE_PATH, download.getSavePath());
- sendStickyBroadcast(end);
- }
-
-
- /**
- * Sends a broadcast when a new download is added to the queue.
- *
- * @param download Added download operation
- */
- private void sendBroadcastNewDownload(DownloadFileOperation download) {
- Intent added = new Intent(getDownloadAddedMessage());
- added.putExtra(ACCOUNT_NAME, download.getAccount().name);
- added.putExtra(EXTRA_REMOTE_PATH, download.getRemotePath());
- added.putExtra(EXTRA_FILE_PATH, download.getSavePath());
- sendStickyBroadcast(added);
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files.services;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.files.OwnCloudFileObserver;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Service;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.database.Cursor;
-import android.os.Binder;
-import android.os.IBinder;
-
-public class FileObserverService extends Service {
-
- public final static int CMD_INIT_OBSERVED_LIST = 1;
- public final static int CMD_ADD_OBSERVED_FILE = 2;
- public final static int CMD_DEL_OBSERVED_FILE = 3;
-
- public final static String KEY_FILE_CMD = "KEY_FILE_CMD";
- public final static String KEY_CMD_ARG_FILE = "KEY_CMD_ARG_FILE";
- public final static String KEY_CMD_ARG_ACCOUNT = "KEY_CMD_ARG_ACCOUNT";
-
- private static String TAG = FileObserverService.class.getSimpleName();
-
- private static Map<String, OwnCloudFileObserver> mObserversMap;
- private static DownloadCompletedReceiverBis mDownloadReceiver;
- private IBinder mBinder = new LocalBinder();
-
- private String mDownloadAddedMessage;
- private String mDownloadFinishMessage;
-
- public class LocalBinder extends Binder {
- FileObserverService getService() {
- return FileObserverService.this;
- }
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- mDownloadReceiver = new DownloadCompletedReceiverBis();
-
- FileDownloader downloader = new FileDownloader();
- mDownloadAddedMessage = downloader.getDownloadAddedMessage();
- mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(mDownloadAddedMessage);
- filter.addAction(mDownloadFinishMessage);
- registerReceiver(mDownloadReceiver, filter);
-
- mObserversMap = new HashMap<String, OwnCloudFileObserver>();
- //initializeObservedList();
- }
-
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- unregisterReceiver(mDownloadReceiver);
- mObserversMap = null; // TODO study carefully the life cycle of Services to grant the best possible observance
- Log_OC.d(TAG, "Bye, bye");
- }
-
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // this occurs when system tries to restart
- // service, so we need to reinitialize observers
- if (intent == null) {
- initializeObservedList();
- return Service.START_STICKY;
- }
-
- if (!intent.hasExtra(KEY_FILE_CMD)) {
- Log_OC.e(TAG, "No KEY_FILE_CMD argument given");
- return Service.START_STICKY;
- }
-
- switch (intent.getIntExtra(KEY_FILE_CMD, -1)) {
- case CMD_INIT_OBSERVED_LIST:
- initializeObservedList();
- break;
- case CMD_ADD_OBSERVED_FILE:
- addObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
- (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
- break;
- case CMD_DEL_OBSERVED_FILE:
- removeObservedFile( (OCFile)intent.getParcelableExtra(KEY_CMD_ARG_FILE),
- (Account)intent.getParcelableExtra(KEY_CMD_ARG_ACCOUNT));
- break;
- default:
- Log_OC.wtf(TAG, "Incorrect key given");
- }
-
- return Service.START_STICKY;
- }
-
-
- /**
- * Read from the local database the list of files that must to be kept synchronized and
- * starts file observers to monitor local changes on them
- */
- private void initializeObservedList() {
- mObserversMap.clear();
- Cursor c = getContentResolver().query(
- ProviderTableMeta.CONTENT_URI,
- null,
- ProviderTableMeta.FILE_KEEP_IN_SYNC + " = ?",
- new String[] {String.valueOf(1)},
- null);
- if (c == null || !c.moveToFirst()) return;
- AccountManager acm = AccountManager.get(this);
- Account[] accounts = acm.getAccounts();
- do {
- Account account = null;
- for (Account a : accounts)
- if (a.name.equals(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ACCOUNT_OWNER)))) {
- account = a;
- break;
- }
-
- if (account == null) continue;
- FileDataStorageManager storage =
- new FileDataStorageManager(account, getContentResolver());
- if (!storage.fileExists(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PATH))))
- continue;
-
- String path = c.getString(c.getColumnIndex(ProviderTableMeta.FILE_STORAGE_PATH));
- if (path == null || path.length() <= 0)
- continue;
- OwnCloudFileObserver observer =
- new OwnCloudFileObserver( path,
- account,
- getApplicationContext(),
- OwnCloudFileObserver.CHANGES_ONLY);
- mObserversMap.put(path, observer);
- if (new File(path).exists()) {
- observer.startWatching();
- Log_OC.d(TAG, "Started watching file " + path);
- }
-
- } while (c.moveToNext());
- c.close();
- }
-
-
- /**
- * Registers the local copy of a remote file to be observed for local changes,
- * an automatically updated in the ownCloud server.
- *
- * This method does NOT perform a {@link SynchronizeFileOperation} over the file.
- *
- * TODO We are ignoring that, currently, a local file can be linked to different files
- * in ownCloud if it's uploaded several times. That's something pending to update: we
- * will avoid that the same local file is linked to different remote files.
- *
- * @param file Object representing a remote file which local copy must be observed.
- * @param account OwnCloud account containing file.
- */
- private void addObservedFile(OCFile file, Account account) {
- if (file == null) {
- Log_OC.e(TAG, "Trying to add a NULL file to observer");
- return;
- }
- String localPath = file.getStoragePath();
- if (localPath == null || localPath.length() <= 0) { // file downloading / to be download for the first time
- localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
- }
- OwnCloudFileObserver observer = mObserversMap.get(localPath);
- if (observer == null) {
- /// the local file was never registered to observe before
- observer = new OwnCloudFileObserver( localPath,
- account,
- getApplicationContext(),
- OwnCloudFileObserver.CHANGES_ONLY);
- mObserversMap.put(localPath, observer);
- Log_OC.d(TAG, "Observer added for path " + localPath);
-
- if (file.isDown()) {
- observer.startWatching();
- Log_OC.d(TAG, "Started watching " + localPath);
- } // else - the observance can't be started on a file not already down; mDownloadReceiver will get noticed when the download of the file finishes
- }
-
- }
-
-
- /**
- * Unregisters the local copy of a remote file to be observed for local changes.
- *
- * Starts to watch it, if the file has a local copy to watch.
- *
- * TODO We are ignoring that, currently, a local file can be linked to different files
- * in ownCloud if it's uploaded several times. That's something pending to update: we
- * will avoid that the same local file is linked to different remote files.
- *
- * @param file Object representing a remote file which local copy must be not observed longer.
- * @param account OwnCloud account containing file.
- */
- private void removeObservedFile(OCFile file, Account account) {
- if (file == null) {
- Log_OC.e(TAG, "Trying to remove a NULL file");
- return;
- }
- String localPath = file.getStoragePath();
- if (localPath == null || localPath.length() <= 0) {
- localPath = FileStorageUtils.getDefaultSavePathFor(account.name, file);
- }
-
- OwnCloudFileObserver observer = mObserversMap.get(localPath);
- if (observer != null) {
- observer.stopWatching();
- mObserversMap.remove(observer);
- Log_OC.d(TAG, "Stopped watching " + localPath);
- }
-
- }
-
-
- /**
- * Private receiver listening to events broadcast by the FileDownloader service.
- *
- * Starts and stops the observance on registered files when they are being download,
- * in order to avoid to start unnecessary synchronizations.
- */
- private class DownloadCompletedReceiverBis extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- String downloadPath = intent.getStringExtra(FileDownloader.EXTRA_FILE_PATH);
- OwnCloudFileObserver observer = mObserversMap.get(downloadPath);
- if (observer != null) {
- if (intent.getAction().equals(mDownloadFinishMessage) &&
- new File(downloadPath).exists()) { // the download could be successful. not; in both cases, the file could be down, due to a former download or upload
- observer.startWatching();
- Log_OC.d(TAG, "Watching again " + downloadPath);
-
- } else if (intent.getAction().equals(mDownloadAddedMessage)) {
- observer.stopWatching();
- Log_OC.d(TAG, "Disabling observance of " + downloadPath);
- }
- }
- }
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files.services;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.AbstractList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.webkit.MimeTypeMap;
-import android.widget.RemoteViews;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.ChunkedUploadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.CreateFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.ExistenceCheckOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.UploadFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.FailedUploadActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.InstantUploadActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-
-public class FileUploader extends Service implements OnDatatransferProgressListener {
-
- private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
- public static final String EXTRA_UPLOAD_RESULT = "RESULT";
- public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
- public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH";
- public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH";
- public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
-
- public static final String KEY_FILE = "FILE";
- public static final String KEY_LOCAL_FILE = "LOCAL_FILE";
- public static final String KEY_REMOTE_FILE = "REMOTE_FILE";
- public static final String KEY_MIME_TYPE = "MIME_TYPE";
-
- public static final String KEY_ACCOUNT = "ACCOUNT";
-
- public static final String KEY_UPLOAD_TYPE = "UPLOAD_TYPE";
- public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE";
- public static final String KEY_INSTANT_UPLOAD = "INSTANT_UPLOAD";
- public static final String KEY_LOCAL_BEHAVIOUR = "BEHAVIOUR";
-
- public static final int LOCAL_BEHAVIOUR_COPY = 0;
- public static final int LOCAL_BEHAVIOUR_MOVE = 1;
- public static final int LOCAL_BEHAVIOUR_FORGET = 2;
-
- public static final int UPLOAD_SINGLE_FILE = 0;
- public static final int UPLOAD_MULTIPLE_FILES = 1;
-
- private static final String TAG = FileUploader.class.getSimpleName();
-
- private Looper mServiceLooper;
- private ServiceHandler mServiceHandler;
- private IBinder mBinder;
- private WebdavClient mUploadClient = null;
- private Account mLastAccount = null;
- private FileDataStorageManager mStorageManager;
-
- private ConcurrentMap<String, UploadFileOperation> mPendingUploads = new ConcurrentHashMap<String, UploadFileOperation>();
- private UploadFileOperation mCurrentUpload = null;
-
- private NotificationManager mNotificationManager;
- private Notification mNotification;
- private int mLastPercent;
- private RemoteViews mDefaultNotificationContentView;
-
-
- public String getUploadFinishMessage() {
- return getClass().getName().toString() + UPLOAD_FINISH_MESSAGE;
- }
-
- /**
- * Builds a key for mPendingUploads from the account and file to upload
- *
- * @param account Account where the file to upload is stored
- * @param file File to upload
- */
- private String buildRemoteName(Account account, OCFile file) {
- return account.name + file.getRemotePath();
- }
-
- private String buildRemoteName(Account account, String remotePath) {
- return account.name + remotePath;
- }
-
- /**
- * Checks if an ownCloud server version should support chunked uploads.
- *
- * @param version OwnCloud version instance corresponding to an ownCloud
- * server.
- * @return 'True' if the ownCloud server with version supports chunked
- * uploads.
- */
- private static boolean chunkedUploadIsSupported(OwnCloudVersion version) {
- return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0);
- }
-
- /**
- * Service initialization
- */
- @Override
- public void onCreate() {
- super.onCreate();
- Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
- mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- HandlerThread thread = new HandlerThread("FileUploaderThread", Process.THREAD_PRIORITY_BACKGROUND);
- thread.start();
- mServiceLooper = thread.getLooper();
- mServiceHandler = new ServiceHandler(mServiceLooper, this);
- mBinder = new FileUploaderBinder();
- }
-
- /**
- * Entry point to add one or several files to the queue of uploads.
- *
- * New uploads are added calling to startService(), resulting in a call to
- * this method. This ensures the service will keep on working although the
- * caller activity goes away.
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (!intent.hasExtra(KEY_ACCOUNT) || !intent.hasExtra(KEY_UPLOAD_TYPE)
- || !(intent.hasExtra(KEY_LOCAL_FILE) || intent.hasExtra(KEY_FILE))) {
- Log_OC.e(TAG, "Not enough information provided in intent");
- return Service.START_NOT_STICKY;
- }
- int uploadType = intent.getIntExtra(KEY_UPLOAD_TYPE, -1);
- if (uploadType == -1) {
- Log_OC.e(TAG, "Incorrect upload type provided");
- return Service.START_NOT_STICKY;
- }
- Account account = intent.getParcelableExtra(KEY_ACCOUNT);
-
- String[] localPaths = null, remotePaths = null, mimeTypes = null;
- OCFile[] files = null;
- if (uploadType == UPLOAD_SINGLE_FILE) {
-
- if (intent.hasExtra(KEY_FILE)) {
- files = new OCFile[] { intent.getParcelableExtra(KEY_FILE) };
-
- } else {
- localPaths = new String[] { intent.getStringExtra(KEY_LOCAL_FILE) };
- remotePaths = new String[] { intent.getStringExtra(KEY_REMOTE_FILE) };
- mimeTypes = new String[] { intent.getStringExtra(KEY_MIME_TYPE) };
- }
-
- } else { // mUploadType == UPLOAD_MULTIPLE_FILES
-
- if (intent.hasExtra(KEY_FILE)) {
- files = (OCFile[]) intent.getParcelableArrayExtra(KEY_FILE); // TODO
- // will
- // this
- // casting
- // work
- // fine?
-
- } else {
- localPaths = intent.getStringArrayExtra(KEY_LOCAL_FILE);
- remotePaths = intent.getStringArrayExtra(KEY_REMOTE_FILE);
- mimeTypes = intent.getStringArrayExtra(KEY_MIME_TYPE);
- }
- }
-
- FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
-
- boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false);
- boolean isInstant = intent.getBooleanExtra(KEY_INSTANT_UPLOAD, false);
- int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_COPY);
-
- if (intent.hasExtra(KEY_FILE) && files == null) {
- Log_OC.e(TAG, "Incorrect array for OCFiles provided in upload intent");
- return Service.START_NOT_STICKY;
-
- } else if (!intent.hasExtra(KEY_FILE)) {
- if (localPaths == null) {
- Log_OC.e(TAG, "Incorrect array for local paths provided in upload intent");
- return Service.START_NOT_STICKY;
- }
- if (remotePaths == null) {
- Log_OC.e(TAG, "Incorrect array for remote paths provided in upload intent");
- return Service.START_NOT_STICKY;
- }
- if (localPaths.length != remotePaths.length) {
- Log_OC.e(TAG, "Different number of remote paths and local paths!");
- return Service.START_NOT_STICKY;
- }
-
- files = new OCFile[localPaths.length];
- for (int i = 0; i < localPaths.length; i++) {
- files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i]
- : (String) null), storageManager);
- if (files[i] == null) {
- // TODO @andomaex add failure Notiification
- return Service.START_NOT_STICKY;
- }
- }
- }
-
- OwnCloudVersion ocv = new OwnCloudVersion(AccountManager.get(this).getUserData(account,
- AccountAuthenticator.KEY_OC_VERSION));
- boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
- AbstractList<String> requestedUploads = new Vector<String>();
- String uploadKey = null;
- UploadFileOperation newUpload = null;
- try {
- for (int i = 0; i < files.length; i++) {
- uploadKey = buildRemoteName(account, files[i].getRemotePath());
- if (chunked) {
- newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
- localAction);
- } else {
- newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
- }
- if (isInstant) {
- newUpload.setRemoteFolderToBeCreated();
- }
- mPendingUploads.putIfAbsent(uploadKey, newUpload); // Grants that the file only upload once time
-
- newUpload.addDatatransferProgressListener(this);
- newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder);
- requestedUploads.add(uploadKey);
- }
-
- } catch (IllegalArgumentException e) {
- Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
- return START_NOT_STICKY;
-
- } catch (IllegalStateException e) {
- Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
- return START_NOT_STICKY;
-
- } catch (Exception e) {
- Log_OC.e(TAG, "Unexpected exception while processing upload intent", e);
- return START_NOT_STICKY;
-
- }
-
- if (requestedUploads.size() > 0) {
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- msg.obj = requestedUploads;
- mServiceHandler.sendMessage(msg);
- }
- Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
- return Service.START_NOT_STICKY;
- }
-
- /**
- * Provides a binder object that clients can use to perform operations on
- * the queue of uploads, excepting the addition of new files.
- *
- * Implemented to perform cancellation, pause and resume of existing
- * uploads.
- */
- @Override
- public IBinder onBind(Intent arg0) {
- return mBinder;
- }
-
- /**
- * Called when ALL the bound clients were onbound.
- */
- @Override
- public boolean onUnbind(Intent intent) {
- ((FileUploaderBinder)mBinder).clearListeners();
- return false; // not accepting rebinding (default behaviour)
- }
-
-
- /**
- * Binder to let client components to perform operations on the queue of
- * uploads.
- *
- * It provides by itself the available operations.
- */
- public class FileUploaderBinder extends Binder implements OnDatatransferProgressListener {
-
- /**
- * Map of listeners that will be reported about progress of uploads from a {@link FileUploaderBinder} instance
- */
- private Map<String, OnDatatransferProgressListener> mBoundListeners = new HashMap<String, OnDatatransferProgressListener>();
-
- /**
- * Cancels a pending or current upload of a remote file.
- *
- * @param account Owncloud account where the remote file will be stored.
- * @param file A file in the queue of pending uploads
- */
- public void cancel(Account account, OCFile file) {
- UploadFileOperation upload = null;
- synchronized (mPendingUploads) {
- upload = mPendingUploads.remove(buildRemoteName(account, file));
- }
- if (upload != null) {
- upload.cancel();
- }
- }
-
-
-
- public void clearListeners() {
- mBoundListeners.clear();
- }
-
-
-
-
- /**
- * Returns True when the file described by 'file' is being uploaded to
- * the ownCloud account 'account' or waiting for it
- *
- * If 'file' is a directory, returns 'true' if some of its descendant files is uploading or waiting to upload.
- *
- * @param account Owncloud account where the remote file will be stored.
- * @param file A file that could be in the queue of pending uploads
- */
- public boolean isUploading(Account account, OCFile file) {
- if (account == null || file == null)
- return false;
- String targetKey = buildRemoteName(account, file);
- synchronized (mPendingUploads) {
- if (file.isDirectory()) {
- // this can be slow if there are many uploads :(
- Iterator<String> it = mPendingUploads.keySet().iterator();
- boolean found = false;
- while (it.hasNext() && !found) {
- found = it.next().startsWith(targetKey);
- }
- return found;
- } else {
- return (mPendingUploads.containsKey(targetKey));
- }
- }
- }
-
-
- /**
- * Adds a listener interested in the progress of the upload for a concrete file.
- *
- * @param listener Object to notify about progress of transfer.
- * @param account ownCloud account holding the file of interest.
- * @param file {@link OCfile} of interest for listener.
- */
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
- if (account == null || file == null || listener == null) return;
- String targetKey = buildRemoteName(account, file);
- mBoundListeners.put(targetKey, listener);
- }
-
-
-
- /**
- * Removes a listener interested in the progress of the upload for a concrete file.
- *
- * @param listener Object to notify about progress of transfer.
- * @param account ownCloud account holding the file of interest.
- * @param file {@link OCfile} of interest for listener.
- */
- public void removeDatatransferProgressListener (OnDatatransferProgressListener listener, Account account, OCFile file) {
- if (account == null || file == null || listener == null) return;
- String targetKey = buildRemoteName(account, file);
- if (mBoundListeners.get(targetKey) == listener) {
- mBoundListeners.remove(targetKey);
- }
- }
-
-
- @Override
- public void onTransferProgress(long progressRate) {
- // old way, should not be in use any more
- }
-
-
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
- String fileName) {
- String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
- OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
- if (boundListener != null) {
- boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
- }
- }
-
- }
-
- /**
- * Upload worker. Performs the pending uploads in the order they were
- * requested.
- *
- * Created with the Looper of a new thread, started in
- * {@link FileUploader#onCreate()}.
- */
- private static class ServiceHandler extends Handler {
- // don't make it a final class, and don't remove the static ; lint will
- // warn about a possible memory leak
- FileUploader mService;
-
- public ServiceHandler(Looper looper, FileUploader service) {
- super(looper);
- if (service == null)
- throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
- mService = service;
- }
-
- @Override
- public void handleMessage(Message msg) {
- @SuppressWarnings("unchecked")
- AbstractList<String> requestedUploads = (AbstractList<String>) msg.obj;
- if (msg.obj != null) {
- Iterator<String> it = requestedUploads.iterator();
- while (it.hasNext()) {
- mService.uploadFile(it.next());
- }
- }
- mService.stopSelf(msg.arg1);
- }
- }
-
- /**
- * Core upload method: sends the file(s) to upload
- *
- * @param uploadKey Key to access the upload to perform, contained in
- * mPendingUploads
- */
- public void uploadFile(String uploadKey) {
-
- synchronized (mPendingUploads) {
- mCurrentUpload = mPendingUploads.get(uploadKey);
- }
-
- if (mCurrentUpload != null) {
-
- notifyUploadStart(mCurrentUpload);
-
- RemoteOperationResult uploadResult = null, grantResult = null;
-
- try {
- /// prepare client object to send requests to the ownCloud server
- if (mUploadClient == null || !mLastAccount.equals(mCurrentUpload.getAccount())) {
- mLastAccount = mCurrentUpload.getAccount();
- mStorageManager = new FileDataStorageManager(mLastAccount, getContentResolver());
- mUploadClient = OwnCloudClientUtils.createOwnCloudClient(mLastAccount, getApplicationContext());
- }
-
- /// check the existence of the parent folder for the file to upload
- String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent();
- remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR;
- grantResult = grantFolderExistence(remoteParentPath);
-
- /// perform the upload
- if (grantResult.isSuccess()) {
- OCFile parent = mStorageManager.getFileByPath(remoteParentPath);
- mCurrentUpload.getFile().setParentId(parent.getFileId());
- uploadResult = mCurrentUpload.execute(mUploadClient);
- if (uploadResult.isSuccess()) {
- saveUploadedFile();
- }
- } else {
- uploadResult = grantResult;
- }
-
- } catch (AccountsException e) {
- Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
- uploadResult = new RemoteOperationResult(e);
-
- } catch (IOException e) {
- Log_OC.e(TAG, "Error while trying to get autorization for " + mLastAccount.name, e);
- uploadResult = new RemoteOperationResult(e);
-
- } finally {
- synchronized (mPendingUploads) {
- mPendingUploads.remove(uploadKey);
- Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map.");
- }
- if (uploadResult.isException()) {
- // enforce the creation of a new client object for next uploads; this grant that a new socket will
- // be created in the future if the current exception is due to an abrupt lose of network connection
- mUploadClient = null;
- }
- }
-
- /// notify result
-
- notifyUploadResult(uploadResult, mCurrentUpload);
- sendFinalBroadcast(mCurrentUpload, uploadResult);
-
- }
-
- }
-
- /**
- * Checks the existence of the folder where the current file will be uploaded both in the remote server
- * and in the local database.
- *
- * If the upload is set to enforce the creation of the folder, the method tries to create it both remote
- * and locally.
- *
- * @param pathToGrant Full remote path whose existence will be granted.
- * @return An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
- */
- private RemoteOperationResult grantFolderExistence(String pathToGrant) {
- RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false);
- RemoteOperationResult result = operation.execute(mUploadClient);
- if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
- operation = new CreateFolderOperation( pathToGrant,
- true,
- mStorageManager );
- result = operation.execute(mUploadClient);
- }
- if (result.isSuccess()) {
- OCFile parentDir = mStorageManager.getFileByPath(pathToGrant);
- if (parentDir == null) {
- parentDir = createLocalFolder(pathToGrant);
- }
- if (parentDir != null) {
- result = new RemoteOperationResult(ResultCode.OK);
- } else {
- result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR);
- }
- }
- return result;
- }
-
-
- private OCFile createLocalFolder(String remotePath) {
- String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
- OCFile parent = mStorageManager.getFileByPath(parentPath);
- if (parent == null) {
- parent = createLocalFolder(parentPath);
- }
- if (parent != null) {
- OCFile createdFolder = new OCFile(remotePath);
- createdFolder.setMimetype("DIR");
- createdFolder.setParentId(parent.getFileId());
- mStorageManager.saveFile(createdFolder);
- return createdFolder;
- }
- return null;
- }
-
-
- /**
- * Saves a OC File after a successful upload.
- *
- * A PROPFIND is necessary to keep the props in the local database
- * synchronized with the server, specially the modification time and Etag
- * (where available)
- *
- * TODO refactor this ugly thing
- */
- private void saveUploadedFile() {
- OCFile file = mCurrentUpload.getFile();
- long syncDate = System.currentTimeMillis();
- file.setLastSyncDateForData(syncDate);
-
- // / new PROPFIND to keep data consistent with server in theory, should
- // return the same we already have
- PropFindMethod propfind = null;
- RemoteOperationResult result = null;
- try {
- propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_0);
- int status = mUploadClient.executeMethod(propfind);
- boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
- if (isMultiStatus) {
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0], mUploadClient.getBaseUri().getPath());
- updateOCFile(file, we);
- file.setLastSyncDateForProperties(syncDate);
-
- } else {
- mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
- }
-
- result = new RemoteOperationResult(isMultiStatus, status, propfind.getResponseHeaders());
- Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
- + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
- + result.getLogMessage(), e);
-
- } finally {
- if (propfind != null)
- propfind.releaseConnection();
- }
-
- // / maybe this would be better as part of UploadFileOperation... or
- // maybe all this method
- if (mCurrentUpload.wasRenamed()) {
- OCFile oldFile = mCurrentUpload.getOldFile();
- if (oldFile.fileExists()) {
- oldFile.setStoragePath(null);
- mStorageManager.saveFile(oldFile);
-
- } // else: it was just an automatic renaming due to a name
- // coincidence; nothing else is needed, the storagePath is right
- // in the instance returned by mCurrentUpload.getFile()
- }
-
- mStorageManager.saveFile(file);
- }
-
- private void updateOCFile(OCFile file, WebdavEntry we) {
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- file.setModificationTimestampAtLastSyncForData(we.modifiedTimestamp());
- // file.setEtag(mCurrentUpload.getEtag()); // TODO Etag, where available
- }
-
- private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
- FileDataStorageManager storageManager) {
- OCFile newFile = new OCFile(remotePath);
- newFile.setStoragePath(localPath);
- newFile.setLastSyncDateForProperties(0);
- newFile.setLastSyncDateForData(0);
-
- // size
- if (localPath != null && localPath.length() > 0) {
- File localFile = new File(localPath);
- newFile.setFileLength(localFile.length());
- newFile.setLastSyncDateForData(localFile.lastModified());
- } // don't worry about not assigning size, the problems with localPath
- // are checked when the UploadFileOperation instance is created
-
- // MIME type
- if (mimeType == null || mimeType.length() <= 0) {
- try {
- mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- remotePath.substring(remotePath.lastIndexOf('.') + 1));
- } catch (IndexOutOfBoundsException e) {
- Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + remotePath);
- }
- }
- if (mimeType == null) {
- mimeType = "application/octet-stream";
- }
- newFile.setMimetype(mimeType);
-
- return newFile;
- }
-
- /**
- * Creates a status notification to show the upload progress
- *
- * @param upload Upload operation starting.
- */
- @SuppressWarnings("deprecation")
- private void notifyUploadStart(UploadFileOperation upload) {
- // / create status notification with a progress bar
- mLastPercent = 0;
- mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker),
- System.currentTimeMillis());
- mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
- mDefaultNotificationContentView = mNotification.contentView;
- mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(),
- R.layout.progressbar_layout);
- mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
- mNotification.contentView.setTextViewText(R.id.status_text,
- String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
- mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
-
- /// includes a pending intent in the notification showing the details view of the file
- Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
- (int) System.currentTimeMillis(), showDetailsIntent, 0);
-
- mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
- }
-
- /**
- * Callback method to update the progress bar in the status notification
- */
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
- int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
- if (percent != mLastPercent) {
- mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, false);
- String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName);
- mNotification.contentView.setTextViewText(R.id.status_text, text);
- mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
- }
- mLastPercent = percent;
- }
-
- /**
- * Callback method to update the progress bar in the status notification
- * (old version)
- */
- @Override
- public void onTransferProgress(long progressRate) {
- // NOTHING TO DO HERE ANYMORE
- }
-
- /**
- * Updates the status notification with the result of an upload operation.
- *
- * @param uploadResult Result of the upload operation.
- * @param upload Finished upload operation
- */
- private void notifyUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) {
- Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
- if (uploadResult.isCancelled()) {
- // / cancelled operation -> silent removal of progress notification
- mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-
- } else if (uploadResult.isSuccess()) {
- // / success -> silent update of progress notification to success
- // message
- mNotification.flags ^= Notification.FLAG_ONGOING_EVENT; // remove
- // the
- // ongoing
- // flag
- mNotification.flags |= Notification.FLAG_AUTO_CANCEL;
- mNotification.contentView = mDefaultNotificationContentView;
-
- /// includes a pending intent in the notification showing the details view of the file
- Intent showDetailsIntent = null;
- if (PreviewImageFragment.canBePreviewed(upload.getFile())) {
- showDetailsIntent = new Intent(this, PreviewImageActivity.class);
- } else {
- showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- }
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
- (int) System.currentTimeMillis(), showDetailsIntent, 0);
-
- mNotification.setLatestEventInfo(getApplicationContext(),
- getString(R.string.uploader_upload_succeeded_ticker),
- String.format(getString(R.string.uploader_upload_succeeded_content_single), upload.getFileName()),
- mNotification.contentIntent);
-
- mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification); // NOT
- // AN
- DbHandler db = new DbHandler(this.getBaseContext());
- db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
- db.close();
-
- } else {
-
- // / fail -> explicit failure notification
- mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
- Notification finalNotification = new Notification(R.drawable.icon,
- getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
- finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
- String content = null;
-
- boolean needsToUpdateCredentials = (uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
- //(uploadResult.isTemporalRedirection() && uploadResult.isIdPRedirection() &&
- (uploadResult.isIdPRedirection() &&
- MainApp.getAuthTokenTypeSamlSessionCookie().equals(mUploadClient.getAuthTokenType())));
- if (needsToUpdateCredentials) {
- // let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount());
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
- finalNotification.contentIntent = PendingIntent.getActivity(this, (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
- content = String.format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
- finalNotification.setLatestEventInfo(getApplicationContext(),
- getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent);
- mUploadClient = null; // grant that future retries on the same account will get the fresh credentials
- } else {
- // TODO put something smart in the contentIntent below
- // finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
- //}
-
- if (uploadResult.getCode() == ResultCode.LOCAL_STORAGE_FULL
- || uploadResult.getCode() == ResultCode.LOCAL_STORAGE_NOT_COPIED) {
- // TODO we need a class to provide error messages for the users
- // from a RemoteOperationResult and a RemoteOperation
- content = String.format(getString(R.string.error__upload__local_file_not_copied), upload.getFileName(),
- getString(R.string.app_name));
- } else if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
- content = getString(R.string.failed_upload_quota_exceeded_text);
- } else {
- content = String
- .format(getString(R.string.uploader_upload_failed_content_single), upload.getFileName());
- }
-
- // we add only for instant-uploads the InstantUploadActivity and the
- // db entry
- Intent detailUploadIntent = null;
- if (upload.isInstant() && InstantUploadActivity.IS_ENABLED) {
- detailUploadIntent = new Intent(this, InstantUploadActivity.class);
- detailUploadIntent.putExtra(FileUploader.KEY_ACCOUNT, upload.getAccount());
- } else {
- detailUploadIntent = new Intent(this, FailedUploadActivity.class);
- detailUploadIntent.putExtra(FailedUploadActivity.MESSAGE, content);
- }
- finalNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
- (int) System.currentTimeMillis(), detailUploadIntent, PendingIntent.FLAG_UPDATE_CURRENT
- | PendingIntent.FLAG_ONE_SHOT);
-
- if (upload.isInstant()) {
- DbHandler db = null;
- try {
- db = new DbHandler(this.getBaseContext());
- String message = uploadResult.getLogMessage() + " errorCode: " + uploadResult.getCode();
- Log_OC.e(TAG, message + " Http-Code: " + uploadResult.getHttpCode());
- if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
- message = getString(R.string.failed_upload_quota_exceeded_text);
- if (db.updateFileState(upload.getOriginalStoragePath(), DbHandler.UPLOAD_STATUS_UPLOAD_FAILED,
- message) == 0) {
- db.putFileForLater(upload.getOriginalStoragePath(), upload.getAccount().name, message);
- }
- }
- } finally {
- if (db != null) {
- db.close();
- }
- }
- }
- }
- finalNotification.setLatestEventInfo(getApplicationContext(),
- getString(R.string.uploader_upload_failed_ticker), content, finalNotification.contentIntent);
-
- mNotificationManager.notify(R.string.uploader_upload_failed_ticker, finalNotification);
- }
-
- }
-
- /**
- * Sends a broadcast in order to the interested activities can update their
- * view
- *
- * @param upload Finished upload operation
- * @param uploadResult Result of the upload operation
- */
- private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) {
- Intent end = new Intent(getUploadFinishMessage());
- end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote
- // path, after
- // possible
- // automatic
- // renaming
- if (upload.wasRenamed()) {
- end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
- }
- end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath());
- end.putExtra(ACCOUNT_NAME, upload.getAccount().name);
- end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess());
- sendStickyBroadcast(end);
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.files.services;
-
-public interface OnUploadCompletedListener extends Runnable {
-
- public boolean getUploadResult();
-
- public void setUploadResult(boolean result);
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.location;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningServiceInfo;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
-public class LocationServiceLauncherReciever extends BroadcastReceiver {
-
- private final String TAG = getClass().getSimpleName();
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent deviceTrackingIntent = new Intent();
- deviceTrackingIntent
- .setAction("de.mobilcom.debitel.cloud.android.location.LocationUpdateService");
- SharedPreferences preferences = PreferenceManager
- .getDefaultSharedPreferences(context);
- boolean trackDevice = preferences.getBoolean("enable_devicetracking",
- true);
-
- // Used in Preferences activity so that tracking is disabled or
- // reenabled
- if (intent.hasExtra("TRACKING_SETTING")) {
- trackDevice = intent.getBooleanExtra("TRACKING_SETTING", true);
- }
-
- startOrStopDeviceTracking(context, trackDevice);
- }
-
- /**
- * Used internally. Starts or stops the device tracking service
- *
- * @param trackDevice true to start the service, false to stop it
- */
- private void startOrStopDeviceTracking(Context context, boolean trackDevice) {
- Intent deviceTrackingIntent = new Intent();
- deviceTrackingIntent
- .setAction("de.mobilcom.debitel.cloud.android.location.LocationUpdateService");
- if (!isDeviceTrackingServiceRunning(context) && trackDevice) {
- Log_OC.d(TAG, "Starting device tracker service");
- context.startService(deviceTrackingIntent);
- } else if (isDeviceTrackingServiceRunning(context) && !trackDevice) {
- Log_OC.d(TAG, "Stopping device tracker service");
- context.stopService(deviceTrackingIntent);
- }
- }
-
- /**
- * Checks to see whether or not the LocationUpdateService is running
- *
- * @return true, if it is. Otherwise false
- */
- private boolean isDeviceTrackingServiceRunning(Context context) {
- ActivityManager manager = (ActivityManager) context
- .getSystemService(Context.ACTIVITY_SERVICE);
- for (RunningServiceInfo service : manager
- .getRunningServices(Integer.MAX_VALUE)) {
- if (getClass().getName().equals(service.service.getClassName())) {
- return true;
- }
- }
- return false;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.location;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.widget.Toast;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-
-public class LocationUpdateService extends IntentService implements
- LocationListener {
-
- public static final String TAG = "LocationUpdateService";
-
- private LocationManager mLocationManager;
- private LocationProvider mLocationProvider;
- private SharedPreferences mPreferences;
-
- public LocationUpdateService() {
- super(TAG);
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
- // Determine, how we can track the device
- Criteria criteria = new Criteria();
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
- criteria.setPowerRequirement(Criteria.POWER_LOW);
- mLocationProvider = mLocationManager.getProvider(mLocationManager
- .getBestProvider(criteria, true));
-
- // Notify user if there is no way to track the device
- if (mLocationProvider == null) {
- String message = String.format(getString(R.string.location_no_provider), getString(R.string.app_name));
- Toast.makeText(this,
- message,
- Toast.LENGTH_LONG).show();
- stopSelf();
- return;
- }
-
- // Get preferences for device tracking
- mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- boolean trackDevice = mPreferences.getBoolean("enable_devicetracking",
- true);
- int updateIntervall = Integer.parseInt(mPreferences.getString(
- "devicetracking_update_intervall", "30")) * 60 * 1000;
- int distanceBetweenLocationChecks = 50;
-
- // If we do shall track the device -> Stop
- if (!trackDevice) {
- Log_OC.d(TAG, "Devicetracking is disabled");
- stopSelf();
- return;
- }
-
- mLocationManager.requestLocationUpdates(mLocationProvider.getName(),
- updateIntervall, distanceBetweenLocationChecks, this);
- }
-
- @Override
- public void onLocationChanged(Location location) {
- Log_OC.d(TAG, "Location changed: " + location);
-
- }
-
- @Override
- public void onProviderDisabled(String arg0) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onProviderEnabled(String arg0) {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
- // TODO Auto-generated method stub
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- *
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.media;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.MediaController.MediaPlayerControl;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
-
-import java.util.Formatter;
-import java.util.Locale;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * View containing controls for a {@link MediaPlayer}.
- *
- * Holds buttons "play / pause", "rewind", "fast forward"
- * and a progress slider.
- *
- * It synchronizes itself with the state of the
- * {@link MediaPlayer}.
- *
- * @author David A. Velasco
- */
-
-public class MediaControlView extends FrameLayout /* implements OnLayoutChangeListener, OnTouchListener */ implements OnClickListener, OnSeekBarChangeListener {
-
- private MediaPlayerControl mPlayer;
- private Context mContext;
- private View mRoot;
- private ProgressBar mProgress;
- private TextView mEndTime, mCurrentTime;
- private boolean mDragging;
- private static final int SHOW_PROGRESS = 1;
- StringBuilder mFormatBuilder;
- Formatter mFormatter;
- private ImageButton mPauseButton;
- private ImageButton mFfwdButton;
- private ImageButton mRewButton;
-
- public MediaControlView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
-
- FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT
- );
- LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mRoot = inflate.inflate(R.layout.media_control, null);
- initControllerView(mRoot);
- addView(mRoot, frameParams);
-
- setFocusable(true);
- setFocusableInTouchMode(true);
- setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- requestFocus();
- }
-
- @Override
- public void onFinishInflate() {
- /*
- if (mRoot != null)
- initControllerView(mRoot);
- */
- }
-
- /* TODO REMOVE
- public MediaControlView(Context context, boolean useFastForward) {
- super(context);
- mContext = context;
- mUseFastForward = useFastForward;
- initFloatingWindowLayout();
- //initFloatingWindow();
- }
- */
-
- /* TODO REMOVE
- public MediaControlView(Context context) {
- this(context, true);
- }
- */
-
- /* T
- private void initFloatingWindow() {
- mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mWindow = PolicyManager.makeNewWindow(mContext);
- mWindow.setWindowManager(mWindowManager, null, null);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
- mDecor = mWindow.getDecorView();
- mDecor.setOnTouchListener(mTouchListener);
- mWindow.setContentView(this);
- mWindow.setBackgroundDrawableResource(android.R.color.transparent);
-
- // While the media controller is up, the volume control keys should
- // affect the media stream type
- mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- setFocusable(true);
- setFocusableInTouchMode(true);
- setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
- requestFocus();
- }
- */
-
- /*
- // Allocate and initialize the static parts of mDecorLayoutParams. Must
- // also call updateFloatingWindowLayout() to fill in the dynamic parts
- // (y and width) before mDecorLayoutParams can be used.
- private void initFloatingWindowLayout() {
- mDecorLayoutParams = new WindowManager.LayoutParams();
- WindowManager.LayoutParams p = mDecorLayoutParams;
- p.gravity = Gravity.TOP;
- p.height = LayoutParams.WRAP_CONTENT;
- p.x = 0;
- p.format = PixelFormat.TRANSLUCENT;
- p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
- p.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
- p.token = null;
- p.windowAnimations = 0; // android.R.style.DropDownAnimationDown;
- }
- */
-
- // Update the dynamic parts of mDecorLayoutParams
- // Must be called with mAnchor != NULL.
- /*
- private void updateFloatingWindowLayout() {
- int [] anchorPos = new int[2];
- mAnchor.getLocationOnScreen(anchorPos);
-
- WindowManager.LayoutParams p = mDecorLayoutParams;
- p.width = mAnchor.getWidth();
- p.y = anchorPos[1] + mAnchor.getHeight();
- }
- */
-
- /*
- // This is called whenever mAnchor's layout bound changes
- public void onLayoutChange(View v, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight,
- int oldBottom) {
- //updateFloatingWindowLayout();
- if (mShowing) {
- mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);
- }
- }
- */
-
- /*
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (mShowing) {
- hide();
- }
- }
- return false;
- }
- */
-
-
- public void setMediaPlayer(MediaPlayerControl player) {
- mPlayer = player;
- mHandler.sendEmptyMessage(SHOW_PROGRESS);
- updatePausePlay();
- }
-
-
- private void initControllerView(View v) {
- mPauseButton = (ImageButton) v.findViewById(R.id.playBtn);
- if (mPauseButton != null) {
- mPauseButton.requestFocus();
- mPauseButton.setOnClickListener(this);
- }
-
- mFfwdButton = (ImageButton) v.findViewById(R.id.forwardBtn);
- if (mFfwdButton != null) {
- mFfwdButton.setOnClickListener(this);
- }
-
- mRewButton = (ImageButton) v.findViewById(R.id.rewindBtn);
- if (mRewButton != null) {
- mRewButton.setOnClickListener(this);
- }
-
- mProgress = (ProgressBar) v.findViewById(R.id.progressBar);
- if (mProgress != null) {
- if (mProgress instanceof SeekBar) {
- SeekBar seeker = (SeekBar) mProgress;
- seeker.setOnSeekBarChangeListener(this);
- }
- mProgress.setMax(1000);
- }
-
- mEndTime = (TextView) v.findViewById(R.id.totalTimeText);
- mCurrentTime = (TextView) v.findViewById(R.id.currentTimeText);
- mFormatBuilder = new StringBuilder();
- mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
-
- }
-
-
- /**
- * Disable pause or seek buttons if the stream cannot be paused or seeked.
- * This requires the control interface to be a MediaPlayerControlExt
- */
- private void disableUnsupportedButtons() {
- try {
- if (mPauseButton != null && !mPlayer.canPause()) {
- mPauseButton.setEnabled(false);
- }
- if (mRewButton != null && !mPlayer.canSeekBackward()) {
- mRewButton.setEnabled(false);
- }
- if (mFfwdButton != null && !mPlayer.canSeekForward()) {
- mFfwdButton.setEnabled(false);
- }
- } catch (IncompatibleClassChangeError ex) {
- // We were given an old version of the interface, that doesn't have
- // the canPause/canSeekXYZ methods. This is OK, it just means we
- // assume the media can be paused and seeked, and so we don't disable
- // the buttons.
- }
- }
-
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- int pos;
- switch (msg.what) {
- case SHOW_PROGRESS:
- pos = setProgress();
- if (!mDragging) {
- msg = obtainMessage(SHOW_PROGRESS);
- sendMessageDelayed(msg, 1000 - (pos % 1000));
- }
- break;
- }
- }
- };
-
- private String stringForTime(int timeMs) {
- int totalSeconds = timeMs / 1000;
-
- int seconds = totalSeconds % 60;
- int minutes = (totalSeconds / 60) % 60;
- int hours = totalSeconds / 3600;
-
- mFormatBuilder.setLength(0);
- if (hours > 0) {
- return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
- } else {
- return mFormatter.format("%02d:%02d", minutes, seconds).toString();
- }
- }
-
- private int setProgress() {
- if (mPlayer == null || mDragging) {
- return 0;
- }
- int position = mPlayer.getCurrentPosition();
- int duration = mPlayer.getDuration();
- if (mProgress != null) {
- if (duration > 0) {
- // use long to avoid overflow
- long pos = 1000L * position / duration;
- mProgress.setProgress( (int) pos);
- }
- int percent = mPlayer.getBufferPercentage();
- mProgress.setSecondaryProgress(percent * 10);
- }
-
- if (mEndTime != null)
- mEndTime.setText(stringForTime(duration));
- if (mCurrentTime != null)
- mCurrentTime.setText(stringForTime(position));
-
- return position;
- }
-
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- int keyCode = event.getKeyCode();
- final boolean uniqueDown = event.getRepeatCount() == 0
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
- || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
- || keyCode == KeyEvent.KEYCODE_SPACE) {
- if (uniqueDown) {
- doPauseResume();
- //show(sDefaultTimeout);
- if (mPauseButton != null) {
- mPauseButton.requestFocus();
- }
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
- if (uniqueDown && !mPlayer.isPlaying()) {
- mPlayer.start();
- updatePausePlay();
- //show(sDefaultTimeout);
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
- || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
- if (uniqueDown && mPlayer.isPlaying()) {
- mPlayer.pause();
- updatePausePlay();
- //show(sDefaultTimeout);
- }
- return true;
- }
-
- //show(sDefaultTimeout);
- return super.dispatchKeyEvent(event);
- }
-
- public void updatePausePlay() {
- if (mRoot == null || mPauseButton == null)
- return;
-
- if (mPlayer.isPlaying()) {
- mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
- } else {
- mPauseButton.setImageResource(android.R.drawable.ic_media_play);
- }
- }
-
- private void doPauseResume() {
- if (mPlayer.isPlaying()) {
- mPlayer.pause();
- } else {
- mPlayer.start();
- }
- updatePausePlay();
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- if (mPauseButton != null) {
- mPauseButton.setEnabled(enabled);
- }
- if (mFfwdButton != null) {
- mFfwdButton.setEnabled(enabled);
- }
- if (mRewButton != null) {
- mRewButton.setEnabled(enabled);
- }
- if (mProgress != null) {
- mProgress.setEnabled(enabled);
- }
- disableUnsupportedButtons();
- super.setEnabled(enabled);
- }
-
- @Override
- public void onClick(View v) {
- int pos;
- boolean playing = mPlayer.isPlaying();
- switch (v.getId()) {
-
- case R.id.playBtn:
- doPauseResume();
- break;
-
- case R.id.rewindBtn:
- pos = mPlayer.getCurrentPosition();
- pos -= 5000;
- mPlayer.seekTo(pos);
- if (!playing) mPlayer.pause(); // necessary in some 2.3.x devices
- setProgress();
- break;
-
- case R.id.forwardBtn:
- pos = mPlayer.getCurrentPosition();
- pos += 15000;
- mPlayer.seekTo(pos);
- if (!playing) mPlayer.pause(); // necessary in some 2.3.x devices
- setProgress();
- break;
-
- }
- }
-
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (!fromUser) {
- // We're not interested in programmatically generated changes to
- // the progress bar's position.
- return;
- }
-
- long duration = mPlayer.getDuration();
- long newposition = (duration * progress) / 1000L;
- mPlayer.seekTo( (int) newposition);
- if (mCurrentTime != null)
- mCurrentTime.setText(stringForTime( (int) newposition));
- }
-
- /**
- * Called in devices with touchpad when the user starts to adjust the
- * position of the seekbar's thumb.
- *
- * Will be followed by several onProgressChanged notifications.
- */
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- mDragging = true; // monitors the duration of dragging
- mHandler.removeMessages(SHOW_PROGRESS); // grants no more updates with media player progress while dragging
- }
-
-
- /**
- * Called in devices with touchpad when the user finishes the
- * adjusting of the seekbar.
- */
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- mDragging = false;
- setProgress();
- updatePausePlay();
- mHandler.sendEmptyMessage(SHOW_PROGRESS); // grants future updates with media player progress
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
- event.setClassName(MediaControlView.class.getName());
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
- info.setClassName(MediaControlView.class.getName());
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.media;
-
-import android.accounts.Account;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiManager.WifiLock;
-import android.os.IBinder;
-import android.os.PowerManager;
-import android.widget.Toast;
-
-import java.io.IOException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-
-/**
- * Service that handles media playback, both audio and video.
- *
- * Waits for Intents which signal the service to perform specific operations: Play, Pause,
- * Rewind, etc.
- *
- * @author David A. Velasco
- */
-public class MediaService extends Service implements OnCompletionListener, OnPreparedListener,
- OnErrorListener, AudioManager.OnAudioFocusChangeListener {
-
- private static final String TAG = MediaService.class.getSimpleName();
-
- private static final String MY_PACKAGE = MediaService.class.getPackage() != null ? MediaService.class.getPackage().getName() : "de.mobilcom.debitel.cloud.android.media";
-
- /// Intent actions that we are prepared to handle
- public static final String ACTION_PLAY_FILE = MY_PACKAGE + ".action.PLAY_FILE";
- public static final String ACTION_STOP_ALL = MY_PACKAGE + ".action.STOP_ALL";
-
- /// Keys to add extras to the action
- public static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
- public static final String EXTRA_ACCOUNT = MY_PACKAGE + ".extra.ACCOUNT";
- public static String EXTRA_START_POSITION = MY_PACKAGE + ".extra.START_POSITION";
- public static final String EXTRA_PLAY_ON_LOAD = MY_PACKAGE + ".extra.PLAY_ON_LOAD";
-
-
- /** Error code for specific messages - see regular error codes at {@link MediaPlayer} */
- public static final int OC_MEDIA_ERROR = 0;
-
- /** Time To keep the control panel visible when the user does not use it */
- public static final int MEDIA_CONTROL_SHORT_LIFE = 4000;
-
- /** Time To keep the control panel visible when the user does not use it */
- public static final int MEDIA_CONTROL_PERMANENT = 0;
-
- /** Volume to set when audio focus is lost and ducking is allowed */
- private static final float DUCK_VOLUME = 0.1f;
-
- /** Media player instance */
- private MediaPlayer mPlayer = null;
-
- /** Reference to the system AudioManager */
- private AudioManager mAudioManager = null;
-
-
- /** Values to indicate the state of the service */
- enum State {
- STOPPED,
- PREPARING,
- PLAYING,
- PAUSED
- };
-
-
- /** Current state */
- private State mState = State.STOPPED;
-
- /** Possible focus values */
- enum AudioFocus {
- NO_FOCUS,
- NO_FOCUS_CAN_DUCK,
- FOCUS
- }
-
- /** Current focus state */
- private AudioFocus mAudioFocus = AudioFocus.NO_FOCUS;
-
-
- /** 'True' when the current song is streaming from the network */
- private boolean mIsStreaming = false;
-
- /** Wifi lock kept to prevents the device from shutting off the radio when streaming a file. */
- private WifiLock mWifiLock;
-
- private static final String MEDIA_WIFI_LOCK_TAG = MY_PACKAGE + ".WIFI_LOCK";
-
- /** Notification to keep in the notification bar while a song is playing */
- private NotificationManager mNotificationManager;
- private Notification mNotification = null;
-
- /** File being played */
- private OCFile mFile;
-
- /** Account holding the file being played */
- private Account mAccount;
-
- /** Flag signaling if the audio should be played immediately when the file is prepared */
- protected boolean mPlayOnPrepared;
-
- /** Position, in miliseconds, where the audio should be started */
- private int mStartPosition;
-
- /** Interface to access the service through binding */
- private IBinder mBinder;
-
- /** Control panel shown to the user to control the playback, to register through binding */
- private MediaControlView mMediaController;
-
-
-
- /**
- * Helper method to get an error message suitable to show to users for errors occurred in media playback,
- *
- * @param context A context to access string resources.
- * @param what See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
- * @param extra See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
- * @return Message suitable to users.
- */
- public static String getMessageForMediaError(Context context, int what, int extra) {
- int messageId;
-
- if (what == OC_MEDIA_ERROR) {
- messageId = extra;
-
- } else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
- /* Added in API level 17
- Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
- Constant Value: -1010 (0xfffffc0e)
- */
- messageId = R.string.media_err_unsupported;
-
- } else if (extra == MediaPlayer.MEDIA_ERROR_IO) {
- /* Added in API level 17
- File or network related operation errors.
- Constant Value: -1004 (0xfffffc14)
- */
- messageId = R.string.media_err_io;
-
- } else if (extra == MediaPlayer.MEDIA_ERROR_MALFORMED) {
- /* Added in API level 17
- Bitstream is not conforming to the related coding standard or file spec.
- Constant Value: -1007 (0xfffffc11)
- */
- messageId = R.string.media_err_malformed;
-
- } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
- /* Added in API level 17
- Some operation takes too long to complete, usually more than 3-5 seconds.
- Constant Value: -110 (0xffffff92)
- */
- messageId = R.string.media_err_timeout;
-
- } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
- /* Added in API level 3
- The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
- Constant Value: 200 (0x000000c8)
- */
- messageId = R.string.media_err_invalid_progressive_playback;
-
- } else {
- /* MediaPlayer.MEDIA_ERROR_UNKNOWN
- Added in API level 1
- Unspecified media player error.
- Constant Value: 1 (0x00000001)
- */
- /* MediaPlayer.MEDIA_ERROR_SERVER_DIED)
- Added in API level 1
- Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
- Constant Value: 100 (0x00000064)
- */
- messageId = R.string.media_err_unknown;
- }
- return context.getString(messageId);
- }
-
-
-
- /**
- * Initialize a service instance
- *
- * {@inheritDoc}
- */
- @Override
- public void onCreate() {
- Log_OC.d(TAG, "Creating ownCloud media service");
-
- mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)).
- createWifiLock(WifiManager.WIFI_MODE_FULL, MEDIA_WIFI_LOCK_TAG);
-
- mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
- mBinder = new MediaServiceBinder(this);
- }
-
-
- /**
- * Entry point for Intents requesting actions, sent here via startService.
- *
- * {@inheritDoc}
- */
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- String action = intent.getAction();
- if (action.equals(ACTION_PLAY_FILE)) {
- processPlayFileRequest(intent);
-
- } else if (action.equals(ACTION_STOP_ALL)) {
- processStopRequest(true);
- }
-
- return START_NOT_STICKY; // don't want it to restart in case it's killed.
- }
-
-
- /**
- * Processes a request to play a media file received as a parameter
- *
- * TODO If a new request is received when a file is being prepared, it is ignored. Is this what we want?
- *
- * @param intent Intent received in the request with the data to identify the file to play.
- */
- private void processPlayFileRequest(Intent intent) {
- if (mState != State.PREPARING) {
- mFile = intent.getExtras().getParcelable(EXTRA_FILE);
- mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT);
- mPlayOnPrepared = intent.getExtras().getBoolean(EXTRA_PLAY_ON_LOAD, false);
- mStartPosition = intent.getExtras().getInt(EXTRA_START_POSITION, 0);
- tryToGetAudioFocus();
- playMedia();
- }
- }
-
-
- /**
- * Processes a request to play a media file.
- */
- protected void processPlayRequest() {
- // request audio focus
- tryToGetAudioFocus();
-
- // actually play the song
- if (mState == State.STOPPED) {
- // (re)start playback
- playMedia();
-
- } else if (mState == State.PAUSED) {
- // continue playback
- mState = State.PLAYING;
- setUpAsForeground(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
- configAndStartMediaPlayer();
-
- }
- }
-
-
- /**
- * Makes sure the media player exists and has been reset. This will create the media player
- * if needed. reset the existing media player if one already exists.
- */
- protected void createMediaPlayerIfNeeded() {
- if (mPlayer == null) {
- mPlayer = new MediaPlayer();
-
- // make sure the CPU won't go to sleep while media is playing
- mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
-
- // the media player will notify the service when it's ready preparing, and when it's done playing
- mPlayer.setOnPreparedListener(this);
- mPlayer.setOnCompletionListener(this);
- mPlayer.setOnErrorListener(this);
-
- } else {
- mPlayer.reset();
- }
- }
-
- /**
- * Processes a request to pause the current playback
- */
- protected void processPauseRequest() {
- if (mState == State.PLAYING) {
- mState = State.PAUSED;
- mPlayer.pause();
- releaseResources(false); // retain media player in pause
- // TODO polite audio focus, instead of keep it owned; or not?
- }
- }
-
-
- /**
- * Processes a request to stop the playback.
- *
- * @param force When 'true', the playback is stopped no matter the value of mState
- */
- protected void processStopRequest(boolean force) {
- if (mState != State.PREPARING || force) {
- mState = State.STOPPED;
- mFile = null;
- mAccount = null;
- releaseResources(true);
- giveUpAudioFocus();
- stopSelf(); // service is no longer necessary
- }
- }
-
-
- /**
- * Releases resources used by the service for playback. This includes the "foreground service"
- * status and notification, the wake locks and possibly the MediaPlayer.
- *
- * @param releaseMediaPlayer Indicates whether the Media Player should also be released or not
- */
- protected void releaseResources(boolean releaseMediaPlayer) {
- // stop being a foreground service
- stopForeground(true);
-
- // stop and release the Media Player, if it's available
- if (releaseMediaPlayer && mPlayer != null) {
- mPlayer.reset();
- mPlayer.release();
- mPlayer = null;
- }
-
- // release the Wifi lock, if holding it
- if (mWifiLock.isHeld()) {
- mWifiLock.release();
- }
- }
-
-
- /**
- * Fully releases the audio focus.
- */
- private void giveUpAudioFocus() {
- if (mAudioFocus == AudioFocus.FOCUS
- && mAudioManager != null
- && AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.abandonAudioFocus(this)) {
-
- mAudioFocus = AudioFocus.NO_FOCUS;
- }
- }
-
-
- /**
- * Reconfigures MediaPlayer according to audio focus settings and starts/restarts it.
- */
- protected void configAndStartMediaPlayer() {
- if (mPlayer == null) {
- throw new IllegalStateException("mPlayer is NULL");
- }
-
- if (mAudioFocus == AudioFocus.NO_FOCUS) {
- if (mPlayer.isPlaying()) {
- mPlayer.pause(); // have to be polite; but mState is not changed, to resume when focus is received again
- }
-
- } else {
- if (mAudioFocus == AudioFocus.NO_FOCUS_CAN_DUCK) {
- mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME);
-
- } else {
- mPlayer.setVolume(1.0f, 1.0f); // full volume
- }
-
- if (!mPlayer.isPlaying()) {
- mPlayer.start();
- }
- }
- }
-
-
- /**
- * Requests the audio focus to the Audio Manager
- */
- private void tryToGetAudioFocus() {
- if (mAudioFocus != AudioFocus.FOCUS
- && mAudioManager != null
- && (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAudioManager.requestAudioFocus( this,
- AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN))
- ) {
- mAudioFocus = AudioFocus.FOCUS;
- }
- }
-
-
- /**
- * Starts playing the current media file.
- */
- protected void playMedia() {
- mState = State.STOPPED;
- releaseResources(false); // release everything except MediaPlayer
-
- try {
- if (mFile == null) {
- Toast.makeText(this, R.string.media_err_nothing_to_play, Toast.LENGTH_LONG).show();
- processStopRequest(true);
- return;
-
- } else if (mAccount == null) {
- Toast.makeText(this, R.string.media_err_not_in_owncloud, Toast.LENGTH_LONG).show();
- processStopRequest(true);
- return;
- }
-
- createMediaPlayerIfNeeded();
- mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- String url = mFile.getStoragePath();
- /* Streaming is not possible right now
- if (url == null || url.length() <= 0) {
- url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
- }
- mIsStreaming = url.startsWith("http:") || url.startsWith("https:");
- */
- mIsStreaming = false;
-
- mPlayer.setDataSource(url);
-
- mState = State.PREPARING;
- setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName()));
-
- // starts preparing the media player in background
- mPlayer.prepareAsync();
-
- // prevent the Wifi from going to sleep when streaming
- if (mIsStreaming) {
- mWifiLock.acquire();
- } else if (mWifiLock.isHeld()) {
- mWifiLock.release();
- }
-
- } catch (SecurityException e) {
- Log_OC.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e);
- Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
- processStopRequest(true);
-
- } catch (IOException e) {
- Log_OC.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e);
- Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show();
- processStopRequest(true);
-
- } catch (IllegalStateException e) {
- Log_OC.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e);
- Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
- processStopRequest(true);
-
- } catch (IllegalArgumentException e) {
- Log_OC.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e);
- Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show();
- processStopRequest(true);
- }
- }
-
-
- /** Called when media player is done playing current song. */
- public void onCompletion(MediaPlayer player) {
- Toast.makeText(this, String.format(getString(R.string.media_event_done, mFile.getFileName())), Toast.LENGTH_LONG).show();
- if (mMediaController != null) {
- // somebody is still bound to the service
- player.seekTo(0);
- processPauseRequest();
- mMediaController.updatePausePlay();
- } else {
- // nobody is bound
- processStopRequest(true);
- }
- return;
- }
-
-
- /**
- * Called when media player is done preparing.
- *
- * Time to start.
- */
- public void onPrepared(MediaPlayer player) {
- mState = State.PLAYING;
- updateNotification(String.format(getString(R.string.media_state_playing), mFile.getFileName()));
- if (mMediaController != null) {
- mMediaController.setEnabled(true);
- }
- player.seekTo(mStartPosition);
- configAndStartMediaPlayer();
- if (!mPlayOnPrepared) {
- processPauseRequest();
- }
-
- if (mMediaController != null) {
- mMediaController.updatePausePlay();
- }
- }
-
-
- /**
- * Updates the status notification
- */
- @SuppressWarnings("deprecation")
- private void updateNotification(String content) {
- // TODO check if updating the Intent is really necessary
- Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
- (int)System.currentTimeMillis(),
- showDetailsIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- mNotification.when = System.currentTimeMillis();
- //mNotification.contentView.setTextViewText(R.id.status_text, content);
- String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
- mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
- mNotificationManager.notify(R.string.media_notif_ticker, mNotification);
- }
-
-
- /**
- * Configures the service as a foreground service.
- *
- * The system will avoid finishing the service as much as possible when resources as low.
- *
- * A notification must be created to keep the user aware of the existance of the service.
- */
- @SuppressWarnings("deprecation")
- private void setUpAsForeground(String content) {
- /// creates status notification
- // TODO put a progress bar to follow the playback progress
- mNotification = new Notification();
- mNotification.icon = android.R.drawable.ic_media_play;
- //mNotification.tickerText = text;
- mNotification.when = System.currentTimeMillis();
- mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
- //mNotification.contentView.setTextViewText(R.id.status_text, "ownCloud Music Player"); // NULL POINTER
- //mNotification.contentView.setTextViewText(R.id.status_text, getString(R.string.downloader_download_in_progress_content));
-
-
- /// includes a pending intent in the notification showing the details view of the file
- Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, mFile);
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
- showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
- (int)System.currentTimeMillis(),
- showDetailsIntent,
- PendingIntent.FLAG_UPDATE_CURRENT);
-
-
- //mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
- String ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name));
- mNotification.setLatestEventInfo(getApplicationContext(), ticker, content, mNotification.contentIntent);
- startForeground(R.string.media_notif_ticker, mNotification);
-
- }
-
- /**
- * Called when there's an error playing media.
- *
- * Warns the user about the error and resets the media player.
- */
- public boolean onError(MediaPlayer mp, int what, int extra) {
- Log_OC.e(TAG, "Error in audio playback, what = " + what + ", extra = " + extra);
-
- String message = getMessageForMediaError(this, what, extra);
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
-
- processStopRequest(true);
- return true;
- }
-
- /**
- * Called by the system when another app tries to play some sound.
- *
- * {@inheritDoc}
- */
- @Override
- public void onAudioFocusChange(int focusChange) {
- if (focusChange > 0) {
- // focus gain; check AudioManager.AUDIOFOCUS_* values
- mAudioFocus = AudioFocus.FOCUS;
- // restart media player with new focus settings
- if (mState == State.PLAYING)
- configAndStartMediaPlayer();
-
- } else if (focusChange < 0) {
- // focus loss; check AudioManager.AUDIOFOCUS_* values
- boolean canDuck = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK == focusChange;
- mAudioFocus = canDuck ? AudioFocus.NO_FOCUS_CAN_DUCK : AudioFocus.NO_FOCUS;
- // start/restart/pause media player with new focus settings
- if (mPlayer != null && mPlayer.isPlaying())
- configAndStartMediaPlayer();
- }
-
- }
-
- /**
- * Called when the service is finished for final clean-up.
- *
- * {@inheritDoc}
- */
- @Override
- public void onDestroy() {
- mState = State.STOPPED;
- releaseResources(true);
- giveUpAudioFocus();
- }
-
-
- /**
- * Provides a binder object that clients can use to perform operations on the MediaPlayer managed by the MediaService.
- */
- @Override
- public IBinder onBind(Intent arg) {
- return mBinder;
- }
-
-
- /**
- * Called when ALL the bound clients were onbound.
- *
- * The service is destroyed if playback stopped or paused
- */
- @Override
- public boolean onUnbind(Intent intent) {
- if (mState == State.PAUSED || mState == State.STOPPED) {
- processStopRequest(false);
- }
- return false; // not accepting rebinding (default behaviour)
- }
-
-
- /**
- * Accesses the current MediaPlayer instance in the service.
- *
- * To be handled carefully. Visibility is protected to be accessed only
- *
- * @return Current MediaPlayer instance handled by MediaService.
- */
- protected MediaPlayer getPlayer() {
- return mPlayer;
- }
-
-
- /**
- * Accesses the current OCFile loaded in the service.
- *
- * @return The current OCFile loaded in the service.
- */
- protected OCFile getCurrentFile() {
- return mFile;
- }
-
-
- /**
- * Accesses the current {@link State} of the MediaService.
- *
- * @return The current {@link State} of the MediaService.
- */
- protected State getState() {
- return mState;
- }
-
-
- protected void setMediaContoller(MediaControlView mediaController) {
- mMediaController = mediaController;
- }
-
- protected MediaControlView getMediaController() {
- return mMediaController;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.media;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.media.MediaService.State;
-
-import android.accounts.Account;
-import android.content.Intent;
-import android.media.MediaPlayer;
-import android.os.Binder;
-import android.widget.MediaController;
-
-
-/**
- * Binder allowing client components to perform operations on on the MediaPlayer managed by a MediaService instance.
- *
- * Provides the operations of {@link MediaController.MediaPlayerControl}, and an extra method to check if
- * an {@link OCFile} instance is handled by the MediaService.
- *
- * @author David A. Velasco
- */
-public class MediaServiceBinder extends Binder implements MediaController.MediaPlayerControl {
-
- private static final String TAG = MediaServiceBinder.class.getSimpleName();
- /**
- * {@link MediaService} instance to access with the binder
- */
- private MediaService mService = null;
-
- /**
- * Public constructor
- *
- * @param service A {@link MediaService} instance to access with the binder
- */
- public MediaServiceBinder(MediaService service) {
- if (service == null) {
- throw new IllegalArgumentException("Argument 'service' can not be null");
- }
- mService = service;
- }
-
-
- public boolean isPlaying(OCFile mFile) {
- return (mFile != null && mFile.equals(mService.getCurrentFile()));
- }
-
-
- @Override
- public boolean canPause() {
- return true;
- }
-
- @Override
- public boolean canSeekBackward() {
- return true;
- }
-
- @Override
- public boolean canSeekForward() {
- return true;
- }
-
- @Override
- public int getBufferPercentage() {
- MediaPlayer currentPlayer = mService.getPlayer();
- if (currentPlayer != null) {
- return 100;
- // TODO update for streamed playback; add OnBufferUpdateListener in MediaService
- } else {
- return 0;
- }
- }
-
- @Override
- public int getCurrentPosition() {
- MediaPlayer currentPlayer = mService.getPlayer();
- if (currentPlayer != null) {
- int pos = currentPlayer.getCurrentPosition();
- return pos;
- } else {
- return 0;
- }
- }
-
- @Override
- public int getDuration() {
- MediaPlayer currentPlayer = mService.getPlayer();
- if (currentPlayer != null) {
- int dur = currentPlayer.getDuration();
- return dur;
- } else {
- return 0;
- }
- }
-
-
- /**
- * Reports if the MediaService is playing a file or not.
- *
- * Considers that the file is being played when it is in preparation because the expected
- * client of this method is a {@link MediaController} , and we do not want that the 'play'
- * button is shown when the file is being prepared by the MediaService.
- */
- @Override
- public boolean isPlaying() {
- MediaService.State currentState = mService.getState();
- return (currentState == State.PLAYING || (currentState == State.PREPARING && mService.mPlayOnPrepared));
- }
-
-
- @Override
- public void pause() {
- Log_OC.d(TAG, "Pausing through binder...");
- mService.processPauseRequest();
- }
-
- @Override
- public void seekTo(int pos) {
- Log_OC.d(TAG, "Seeking " + pos + " through binder...");
- MediaPlayer currentPlayer = mService.getPlayer();
- MediaService.State currentState = mService.getState();
- if (currentPlayer != null && currentState != State.PREPARING && currentState != State.STOPPED) {
- currentPlayer.seekTo(pos);
- }
- }
-
- @Override
- public void start() {
- Log_OC.d(TAG, "Starting through binder...");
- mService.processPlayRequest(); // this will finish the service if there is no file preloaded to play
- }
-
- public void start(Account account, OCFile file, boolean playImmediately, int position) {
- Log_OC.d(TAG, "Loading and starting through binder...");
- Intent i = new Intent(mService, MediaService.class);
- i.putExtra(MediaService.EXTRA_ACCOUNT, account);
- i.putExtra(MediaService.EXTRA_FILE, file);
- i.putExtra(MediaService.EXTRA_PLAY_ON_LOAD, playImmediately);
- i.putExtra(MediaService.EXTRA_START_POSITION, position);
- i.setAction(MediaService.ACTION_PLAY_FILE);
- mService.startService(i);
- }
-
-
- public void registerMediaController(MediaControlView mediaController) {
- mService.setMediaContoller(mediaController);
- }
-
- public void unregisterMediaController(MediaControlView mediaController) {
- if (mediaController != null && mediaController == mService.getMediaController()) {
- mService.setMediaContoller(null);
- }
-
- }
-
- public boolean isInPlaybackState() {
- MediaService.State currentState = mService.getState();
- return (currentState == MediaService.State.PLAYING || currentState == MediaService.State.PAUSED);
- }
-
-}
-
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-import java.security.cert.X509Certificate;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.params.HttpConnectionParams;
-import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with
- * a custom SSLContext and an optional Hostname Verifier.
- *
- * @author David A. Velasco
- */
-
-public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
-
- private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
-
- private SSLContext mSslContext = null;
- private AdvancedX509TrustManager mTrustManager = null;
- private X509HostnameVerifier mHostnameVerifier = null;
-
- public SSLContext getSslContext() {
- return mSslContext;
- }
-
- /**
- * Constructor for AdvancedSSLProtocolSocketFactory.
- */
- public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) {
- if (sslContext == null)
- throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext");
- if (trustManager == null)
- throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager");
- mSslContext = sslContext;
- mTrustManager = trustManager;
- mHostnameVerifier = hostnameVerifier;
- }
-
- /**
- * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
- */
- public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
- Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
-
- /**
- * Attempts to get a new socket connection to the given host within the
- * given time limit.
- *
- * @param host the host name/IP
- * @param port the port on the host
- * @param clientHost the local host name/IP to bind the socket to
- * @param clientPort the port on the local machine
- * @param params {@link HttpConnectionParams Http connection parameters}
- *
- * @return Socket a new socket
- *
- * @throws IOException if an I/O error occurs while creating the socket
- * @throws UnknownHostException if the IP address of the host cannot be
- * determined
- */
- public Socket createSocket(final String host, final int port,
- final InetAddress localAddress, final int localPort,
- final HttpConnectionParams params) throws IOException,
- UnknownHostException, ConnectTimeoutException {
- Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
- if (params == null) {
- throw new IllegalArgumentException("Parameters may not be null");
- }
- int timeout = params.getConnectionTimeout();
- SocketFactory socketfactory = mSslContext.getSocketFactory();
- Log_OC.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
- Socket socket = socketfactory.createSocket();
- SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
- SocketAddress remoteaddr = new InetSocketAddress(host, port);
- socket.setSoTimeout(params.getSoTimeout());
- socket.bind(localaddr);
- socket.connect(remoteaddr, timeout);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
- /**
- * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
- */
- public Socket createSocket(String host, int port) throws IOException,
- UnknownHostException {
- Log_OC.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
- Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
- public boolean equals(Object obj) {
- return ((obj != null) && obj.getClass().equals(
- AdvancedSslSocketFactory.class));
- }
-
- public int hashCode() {
- return AdvancedSslSocketFactory.class.hashCode();
- }
-
-
- public X509HostnameVerifier getHostNameVerifier() {
- return mHostnameVerifier;
- }
-
-
- public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
- mHostnameVerifier = hostnameVerifier;
- }
-
- /**
- * Verifies the identity of the server.
- *
- * The server certificate is verified first.
- *
- * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any.
- * @param socket
- */
- private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException {
- try {
- CertificateCombinedException failInHandshake = null;
- /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager)
- try {
- SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect"
- sock.startHandshake();
-
- } catch (RuntimeException e) {
-
- if (e instanceof CertificateCombinedException) {
- failInHandshake = (CertificateCombinedException) e;
- } else {
- Throwable cause = e.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertificateCombinedException) {
- failInHandshake = (CertificateCombinedException)cause;
- }
- }
- if (failInHandshake == null) {
- throw e;
- }
- failInHandshake.setHostInUrl(host);
-
- }
-
- /// 2. VERIFY HOSTNAME
- SSLSession newSession = null;
- boolean verifiedHostname = true;
- if (mHostnameVerifier != null) {
- if (failInHandshake != null) {
- /// 2.1 : a new SSLSession instance was NOT created in the handshake
- X509Certificate serverCert = failInHandshake.getServerCertificate();
- try {
- mHostnameVerifier.verify(host, serverCert);
- } catch (SSLException e) {
- verifiedHostname = false;
- }
-
- } else {
- /// 2.2 : a new SSLSession instance was created in the handshake
- newSession = ((SSLSocket)socket).getSession();
- if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
- verifiedHostname = mHostnameVerifier.verify(host, newSession);
- }
- }
- }
-
- /// 3. Combine the exceptions to throw, if any
- if (!verifiedHostname) {
- SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL");
- if (failInHandshake == null) {
- failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
- failInHandshake.setHostInUrl(host);
- }
- failInHandshake.setSslPeerUnverifiedException(pue);
- pue.initCause(failInHandshake);
- throw pue;
-
- } else if (failInHandshake != null) {
- SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified");
- hse.initCause(failInHandshake);
- throw hse;
- }
-
- } catch (IOException io) {
- try {
- socket.close();
- } catch (Exception x) {
- // NOTHING - irrelevant exception for the caller
- }
- throw io;
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStoreException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * @author David A. Velasco
- */
-public class AdvancedX509TrustManager implements X509TrustManager {
-
- private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
-
- private X509TrustManager mStandardTrustManager = null;
- private KeyStore mKnownServersKeyStore;
-
- /**
- * Constructor for AdvancedX509TrustManager
- *
- * @param knownServersCertStore Local certificates store with server certificates explicitly trusted by the user.
- * @throws CertStoreException When no default X509TrustManager instance was found in the system.
- */
- public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
- throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
- super();
- TrustManagerFactory factory = TrustManagerFactory
- .getInstance(TrustManagerFactory.getDefaultAlgorithm());
- factory.init((KeyStore)null);
- mStandardTrustManager = findX509TrustManager(factory);
-
- mKnownServersKeyStore = knownServersKeyStore;
- }
-
-
- /**
- * Locates the first X509TrustManager provided by a given TrustManagerFactory
- * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager
- * @return The first X509TrustManager found in factory.
- * @throws CertStoreException When no X509TrustManager instance was found in factory
- */
- private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
- TrustManager tms[] = factory.getTrustManagers();
- for (int i = 0; i < tms.length; i++) {
- if (tms[i] instanceof X509TrustManager) {
- return (X509TrustManager) tms[i];
- }
- }
- return null;
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
- * String authType)
- */
- public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
- mStandardTrustManager.checkClientTrusted(certificates, authType);
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
- * String authType)
- */
- public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
- if (!isKnownServer(certificates[0])) {
- CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
- try {
- certificates[0].checkValidity();
- } catch (CertificateExpiredException c) {
- result.setCertificateExpiredException(c);
-
- } catch (CertificateNotYetValidException c) {
- result.setCertificateNotYetException(c);
- }
-
- try {
- mStandardTrustManager.checkServerTrusted(certificates, authType);
- } catch (CertificateException c) {
- Throwable cause = c.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertPathValidatorException) {
- result.setCertPathValidatorException((CertPathValidatorException)cause);
- } else {
- result.setOtherCertificateException(c);
- }
- }
-
- if (result.isException())
- throw result;
-
- }
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
- */
- public X509Certificate[] getAcceptedIssuers() {
- return mStandardTrustManager.getAcceptedIssuers();
- }
-
-
- public boolean isKnownServer(X509Certificate cert) {
- try {
- return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
- } catch (KeyStoreException e) {
- Log_OC.d(TAG, "Fail while checking certificate in the known-servers store");
- return false;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.util.Map;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.auth.AuthChallengeParser;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.AuthenticationException;
-import org.apache.commons.httpclient.auth.InvalidCredentialsException;
-import org.apache.commons.httpclient.auth.MalformedChallengeException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-/**
- * Bearer authentication scheme as defined in RFC 6750.
- *
- * @author David A. Velasco
- */
-
-public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
-
- private static final String TAG = BearerAuthScheme.class.getSimpleName();
-
- public static final String AUTH_POLICY = "Bearer";
-
- /** Whether the bearer authentication process is complete */
- private boolean mComplete;
-
- /** Authentication parameter map */
- private Map mParams = null;
-
-
- /**
- * Default constructor for the bearer authentication scheme.
- */
- public BearerAuthScheme() {
- mComplete = false;
- }
-
- /**
- * Constructor for the basic authentication scheme.
- *
- * @param challenge Authentication challenge
- *
- * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
- *
- * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
- */
- public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
- processChallenge(challenge);
- mComplete = true;
- }
-
- /**
- * Returns textual designation of the bearer authentication scheme.
- *
- * @return "Bearer"
- */
- public String getSchemeName() {
- return "bearer";
- }
-
- /**
- * Processes the Bearer challenge.
- *
- * @param challenge The challenge string
- *
- * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
- */
- public void processChallenge(String challenge) throws MalformedChallengeException {
- String s = AuthChallengeParser.extractScheme(challenge);
- if (!s.equalsIgnoreCase(getSchemeName())) {
- throw new MalformedChallengeException(
- "Invalid " + getSchemeName() + " challenge: " + challenge);
- }
- mParams = AuthChallengeParser.extractParams(challenge);
- mComplete = true;
- }
-
- /**
- * Tests if the Bearer authentication process has been completed.
- *
- * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
- */
- public boolean isComplete() {
- return this.mComplete;
- }
-
- /**
- * Produces bearer authorization string for the given set of
- * {@link Credentials}.
- *
- * @param credentials The set of credentials to be used for authentication
- * @param method Method name is ignored by the bearer authentication scheme
- * @param uri URI is ignored by the bearer authentication scheme
- * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable
- * for this authentication scheme
- * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure
- * @return A bearer authorization string
- *
- * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
- */
- public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
- Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
-
- BearerCredentials bearer = null;
- try {
- bearer = (BearerCredentials) credentials;
- } catch (ClassCastException e) {
- throw new InvalidCredentialsException(
- "Credentials cannot be used for bearer authentication: "
- + credentials.getClass().getName());
- }
- return BearerAuthScheme.authenticate(bearer);
- }
-
-
- /**
- * Returns 'false'. Bearer authentication scheme is request based.
- *
- * @return 'false'.
- */
- public boolean isConnectionBased() {
- return false;
- }
-
- /**
- * Produces bearer authorization string for the given set of {@link Credentials}.
- *
- * @param credentials The set of credentials to be used for authentication
- * @param method The method being authenticated
- * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication
- * scheme.
- * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure.
- *
- * @return a basic authorization string
- */
- public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
- Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
-
- if (method == null) {
- throw new IllegalArgumentException("Method may not be null");
- }
- BearerCredentials bearer = null;
- try {
- bearer = (BearerCredentials) credentials;
- } catch (ClassCastException e) {
- throw new InvalidCredentialsException(
- "Credentials cannot be used for bearer authentication: "
- + credentials.getClass().getName());
- }
- return BearerAuthScheme.authenticate(
- bearer,
- method.getParams().getCredentialCharset());
- }
-
- /**
- * @deprecated Use {@link #authenticate(BearerCredentials, String)}
- *
- * Returns a bearer Authorization header value for the given
- * {@link BearerCredentials}.
- *
- * @param credentials The credentials to encode.
- *
- * @return A bearer authorization string
- */
- public static String authenticate(BearerCredentials credentials) {
- return authenticate(credentials, "ISO-8859-1");
- }
-
- /**
- * Returns a bearer Authorization header value for the given
- * {@link BearerCredentials} and charset.
- *
- * @param credentials The credentials to encode.
- * @param charset The charset to use for encoding the credentials
- *
- * @return A bearer authorization string
- *
- * @since 3.0
- */
- public static String authenticate(BearerCredentials credentials, String charset) {
- Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
-
- if (credentials == null) {
- throw new IllegalArgumentException("Credentials may not be null");
- }
- if (charset == null || charset.length() == 0) {
- throw new IllegalArgumentException("charset may not be null or empty");
- }
- StringBuffer buffer = new StringBuffer();
- buffer.append(credentials.getAccessToken());
-
- //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
- return "Bearer " + buffer.toString();
- }
-
- /**
- * Returns a String identifying the authentication challenge. This is
- * used, in combination with the host and port to determine if
- * authorization has already been attempted or not. Schemes which
- * require multiple requests to complete the authentication should
- * return a different value for each stage in the request.
- *
- * Additionally, the ID should take into account any changes to the
- * authentication challenge and return a different value when appropriate.
- * For example when the realm changes in basic authentication it should be
- * considered a different authentication attempt and a different value should
- * be returned.
- *
- * This method simply returns the realm for the challenge.
- *
- * @return String a String identifying the authentication challenge.
- *
- * @deprecated no longer used
- */
- @Override
- public String getID() {
- return getRealm();
- }
-
- /**
- * Returns authentication parameter with the given name, if available.
- *
- * @param name The name of the parameter to be returned
- *
- * @return The parameter with the given name
- */
- @Override
- public String getParameter(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Parameter name may not be null");
- }
- if (mParams == null) {
- return null;
- }
- return (String) mParams.get(name.toLowerCase());
- }
-
- /**
- * Returns authentication realm. The realm may not be null.
- *
- * @return The authentication realm
- */
- @Override
- public String getRealm() {
- return getParameter("realm");
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.util.LangUtils;
-
-/**
- * Bearer token {@link Credentials}
- *
- * @author David A. Velasco
- */
-public class BearerCredentials implements Credentials {
-
-
- private String mAccessToken;
-
-
- /**
- * The constructor with the bearer token
- *
- * @param token The bearer token
- */
- public BearerCredentials(String token) {
- /*if (token == null) {
- throw new IllegalArgumentException("Bearer token may not be null");
- }*/
- mAccessToken = (token == null) ? "" : token;
- }
-
-
- /**
- * Returns the access token
- *
- * @return The access token
- */
- public String getAccessToken() {
- return mAccessToken;
- }
-
-
- /**
- * Get this object string.
- *
- * @return The access token
- */
- public String toString() {
- return mAccessToken;
- }
-
- /**
- * Does a hash of the access token.
- *
- * @return The hash code of the access token
- */
- public int hashCode() {
- int hash = LangUtils.HASH_SEED;
- hash = LangUtils.hashCode(hash, mAccessToken);
- return hash;
- }
-
- /**
- * These credentials are assumed equal if accessToken is the same.
- *
- * @param o The other object to compare with.
- *
- * @return 'True' if the object is equivalent.
- */
- public boolean equals(Object o) {
- if (o == null) return false;
- if (this == o) return true;
- if (this.getClass().equals(o.getClass())) {
- BearerCredentials that = (BearerCredentials) o;
- if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
- return true;
- }
- }
- return false;
- }
-
-}
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-/**
- * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
- * a certificate chain for a server.
- *
- * This was initially created as an extension of CertificateException, but some
- * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
- * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
- * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it.
- *
- * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException
- * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
- *
- * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
- * in client methods. Be sure to use it only when you know exactly where it will go.
- *
- * @author David A. Velasco
- */
-public class CertificateCombinedException extends RuntimeException {
-
- /** Generated - to refresh every time the class changes */
- private static final long serialVersionUID = -8875782030758554999L;
-
- private X509Certificate mServerCert = null;
- private String mHostInUrl;
-
- private CertificateExpiredException mCertificateExpiredException = null;
- private CertificateNotYetValidException mCertificateNotYetValidException = null;
- private CertPathValidatorException mCertPathValidatorException = null;
- private CertificateException mOtherCertificateException = null;
- private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
-
- public CertificateCombinedException(X509Certificate x509Certificate) {
- mServerCert = x509Certificate;
- }
-
- public X509Certificate getServerCertificate() {
- return mServerCert;
- }
-
- public String getHostInUrl() {
- return mHostInUrl;
- }
-
- public void setHostInUrl(String host) {
- mHostInUrl = host;
- }
-
- public CertificateExpiredException getCertificateExpiredException() {
- return mCertificateExpiredException;
- }
-
- public void setCertificateExpiredException(CertificateExpiredException c) {
- mCertificateExpiredException = c;
- }
-
- public CertificateNotYetValidException getCertificateNotYetValidException() {
- return mCertificateNotYetValidException;
- }
-
- public void setCertificateNotYetException(CertificateNotYetValidException c) {
- mCertificateNotYetValidException = c;
- }
-
- public CertPathValidatorException getCertPathValidatorException() {
- return mCertPathValidatorException;
- }
-
- public void setCertPathValidatorException(CertPathValidatorException c) {
- mCertPathValidatorException = c;
- }
-
- public CertificateException getOtherCertificateException() {
- return mOtherCertificateException;
- }
-
- public void setOtherCertificateException(CertificateException c) {
- mOtherCertificateException = c;
- }
-
- public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
- return mSslPeerUnverifiedException ;
- }
-
- public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
- mSslPeerUnverifiedException = s;
- }
-
- public boolean isException() {
- return (mCertificateExpiredException != null ||
- mCertificateNotYetValidException != null ||
- mCertPathValidatorException != null ||
- mOtherCertificateException != null ||
- mSslPeerUnverifiedException != null);
- }
-
- public boolean isRecoverable() {
- return (mCertificateExpiredException != null ||
- mCertificateNotYetValidException != null ||
- mCertPathValidatorException != null ||
- mSslPeerUnverifiedException != null);
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-
-import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
-import org.apache.commons.httpclient.protocol.Protocol;
-import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-
-import eu.alefzero.webdav.WebdavClient;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.app.Activity;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-
-public class OwnCloudClientUtils {
-
- final private static String TAG = OwnCloudClientUtils.class.getSimpleName();
-
- /** Default timeout for waiting data from the server */
- public static final int DEFAULT_DATA_TIMEOUT = 60000;
-
- /** Default timeout for establishing a connection */
- public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
-
- /** Connection manager for all the WebdavClients */
- private static MultiThreadedHttpConnectionManager mConnManager = null;
-
- private static Protocol mDefaultHttpsProtocol = null;
-
- private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null;
-
- private static X509HostnameVerifier mHostnameVerifier = null;
-
-
- /**
- * Creates a WebdavClient setup for an ownCloud account
- *
- * Do not call this method from the main thread.
- *
- * @param account The ownCloud account
- * @param appContext Android application context
- * @return A WebdavClient object ready to be used
- * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account.
- * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account.
- * @throws IOException If there was some I/O error while getting the authorization token for the account.
- * @throws AccountNotFoundException If 'account' is unknown for the AccountManager
- */
- public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
- //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
-
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
- AccountManager am = AccountManager.get(appContext);
- boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
- boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
- WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
- if (isOauth2) {
- String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), false);
- client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
-
- } else if (isSamlSso) { // TODO avoid a call to getUserData here
- String accessToken = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), false);
- client.setSsoSessionCookie(accessToken);
-
- } else {
- String username = account.name.substring(0, account.name.lastIndexOf('@'));
- //String password = am.getPassword(account);
- String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
- client.setBasicCredentials(username, password);
- }
-
- return client;
- }
-
-
- public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
- AccountManager am = AccountManager.get(appContext);
- boolean isOauth2 = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
- boolean isSamlSso = am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null;
- WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
-
- if (isOauth2) { // TODO avoid a call to getUserData here
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypeAccessToken(), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
- if (accessToken == null) throw new AuthenticatorException("WTF!");
- client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
-
- } else if (isSamlSso) { // TODO avoid a call to getUserData here
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypeSamlSessionCookie(), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
- if (accessToken == null) throw new AuthenticatorException("WTF!");
- client.setSsoSessionCookie(accessToken);
-
- } else {
- String username = account.name.substring(0, account.name.lastIndexOf('@'));
- //String password = am.getPassword(account);
- //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, MainApp.getAuthTokenTypePass(), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String password = result.getString(AccountManager.KEY_AUTHTOKEN);
- client.setBasicCredentials(username, password);
- }
-
- return client;
- }
-
- /**
- * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
- *
- * @param uri URL to the ownCloud server
- * @param context Android context where the WebdavClient is being created.
- * @return A WebdavClient object ready to be used
- */
- public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
- try {
- registerAdvancedSslContext(true, context);
- } catch (GeneralSecurityException e) {
- Log_OC.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e);
-
- } catch (IOException e) {
- Log_OC.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
- }
-
- WebdavClient client = new WebdavClient(getMultiThreadedConnManager());
-
- client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
- client.setBaseUri(uri);
- client.setFollowRedirects(followRedirects);
-
- return client;
- }
-
-
- /**
- * Registers or unregisters the proper components for advanced SSL handling.
- * @throws IOException
- */
- private static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException {
- Protocol pr = null;
- try {
- pr = Protocol.getProtocol("https");
- if (pr != null && mDefaultHttpsProtocol == null) {
- mDefaultHttpsProtocol = pr;
- }
- } catch (IllegalStateException e) {
- // nothing to do here; really
- }
- boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory);
- if (register && !isRegistered) {
- Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443));
-
- } else if (!register && isRegistered) {
- if (mDefaultHttpsProtocol != null) {
- Protocol.registerProtocol("https", mDefaultHttpsProtocol);
- }
- }
- }
-
- public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException {
- if (mAdvancedSslSocketFactory == null) {
- KeyStore trustStore = getKnownServersStore(context);
- AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore);
- TrustManager[] tms = new TrustManager[] { trustMgr };
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, tms, null);
-
- mHostnameVerifier = new BrowserCompatHostnameVerifier();
- mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier);
- }
- return mAdvancedSslSocketFactory;
- }
-
-
- private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
-
- private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
-
- private static KeyStore mKnownServersStore = null;
-
- /**
- * Returns the local store of reliable server certificates, explicitly accepted by the user.
- *
- * Returns a KeyStore instance with empty content if the local store was never created.
- *
- * Loads the store from the storage environment if needed.
- *
- * @param context Android context where the operation is being performed.
- * @return KeyStore instance with explicitly-accepted server certificates.
- * @throws KeyStoreException When the KeyStore instance could not be created.
- * @throws IOException When an existing local trust store could not be loaded.
- * @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
- * @throws CertificateException When an exception occurred while loading the certificates from the local trust store.
- */
- private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
- if (mKnownServersStore == null) {
- //mKnownServersStore = KeyStore.getInstance("BKS");
- mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
- File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
- Log_OC.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
- if (localTrustStoreFile.exists()) {
- InputStream in = new FileInputStream(localTrustStoreFile);
- try {
- mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
- } finally {
- in.close();
- }
- } else {
- mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance
- }
- }
- return mKnownServersStore;
- }
-
-
- public static void addCertToKnownServersStore(Certificate cert, Context context) throws KeyStoreException, NoSuchAlgorithmException,
- CertificateException, IOException {
- KeyStore knownServers = getKnownServersStore(context);
- knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
- FileOutputStream fos = null;
- try {
- fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
- knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
- } finally {
- fos.close();
- }
- }
-
-
- static private MultiThreadedHttpConnectionManager getMultiThreadedConnManager() {
- if (mConnManager == null) {
- mConnManager = new MultiThreadedHttpConnectionManager();
- mConnManager.getParams().setDefaultMaxConnectionsPerHost(5);
- mConnManager.getParams().setMaxTotalConnections(5);
- }
- return mConnManager;
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.network;
-
-import java.util.Collection;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-
-public interface ProgressiveDataTransferer {
-
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
-
- public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
-
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.util.Random;
-
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.PutMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
-
-import android.accounts.Account;
-
-import eu.alefzero.webdav.ChunkFromFileChannelRequestEntity;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-public class ChunkedUploadFileOperation extends UploadFileOperation {
-
- private static final long CHUNK_SIZE = 1024000;
- private static final String OC_CHUNKED_HEADER = "OC-Chunked";
- private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
-
- public ChunkedUploadFileOperation( Account account,
- OCFile file,
- boolean isInstant,
- boolean forceOverwrite,
- int localBehaviour) {
-
- super(account, file, isInstant, forceOverwrite, localBehaviour);
- }
-
- @Override
- protected int uploadFile(WebdavClient client) throws HttpException, IOException {
- int status = -1;
-
- FileChannel channel = null;
- RandomAccessFile raf = null;
- try {
- File file = new File(getStoragePath());
- raf = new RandomAccessFile(file, "r");
- channel = raf.getChannel();
- mEntity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file);
- ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners());
- long offset = 0;
- String uriPrefix = client.getBaseUri() + WebdavUtils.encodePath(getRemotePath()) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ;
- long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE);
- for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) {
- if (mPutMethod != null) {
- mPutMethod.releaseConnection(); // let the connection available for other methods
- }
- mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
- mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
- ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
- mPutMethod.setRequestEntity(mEntity);
- status = client.executeMethod(mPutMethod);
- client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
- Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
- if (!isSuccess(status))
- break;
- }
-
- } finally {
- if (channel != null)
- channel.close();
- if (raf != null)
- raf.close();
- if (mPutMethod != null)
- mPutMethod.releaseConnection(); // let the connection available for other methods
- }
- return status;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the creation of a new folder in the ownCloud server.
- *
- * @author David A. Velasco
- */
-public class CreateFolderOperation extends RemoteOperation {
-
- private static final String TAG = CreateFolderOperation.class.getSimpleName();
-
- private static final int READ_TIMEOUT = 10000;
- private static final int CONNECTION_TIMEOUT = 5000;
-
- protected String mRemotePath;
- protected boolean mCreateFullPath;
- protected DataStorageManager mStorageManager;
-
- /**
- * Constructor
- *
- * @param remotePath Full path to the new directory to create in the remote server.
- * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet.
- * @param storageManager Reference to the local database corresponding to the account where the file is contained.
- */
- public CreateFolderOperation(String remotePath, boolean createFullPath, DataStorageManager storageManager) {
- mRemotePath = remotePath;
- mCreateFullPath = createFullPath;
- mStorageManager = storageManager;
- }
-
-
- /**
- * Performs the operation
- *
- * @param client Client object to communicate with the remote ownCloud server.
- */
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- MkColMethod mkcol = null;
- try {
- mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
- int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
- if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) {
- result = createParentFolder(getParentPath(), client);
- status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); // second (and last) try
- }
- if (mkcol.succeeded()) {
- // Save new directory in local database
- OCFile newDir = new OCFile(mRemotePath);
- newDir.setMimetype("DIR");
- long parentId = mStorageManager.getFileByPath(getParentPath()).getFileId();
- newDir.setParentId(parentId);
- newDir.setModificationTimestamp(System.currentTimeMillis());
- mStorageManager.saveFile(newDir);
- }
- result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders());
- Log_OC.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
- client.exhaustResponse(mkcol.getResponseBodyAsStream());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
-
- } finally {
- if (mkcol != null)
- mkcol.releaseConnection();
- }
- return result;
- }
-
-
- private String getParentPath() {
- String parentPath = new File(mRemotePath).getParent();
- parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
- return parentPath;
- }
-
-
- private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) {
- RemoteOperation operation = new CreateFolderOperation( parentPath,
- mCreateFullPath,
- mStorageManager );
- return operation.execute(client);
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.http.HttpStatus;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-import android.accounts.Account;
-import android.webkit.MimeTypeMap;
-
-/**
- * Remote operation performing the download of a file to an ownCloud server
- *
- * @author David A. Velasco
- */
-public class DownloadFileOperation extends RemoteOperation {
-
- private static final String TAG = DownloadFileOperation.class.getSimpleName();
-
- private Account mAccount;
- private OCFile mFile;
- private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
- private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
- private long mModificationTimestamp = 0;
- private GetMethod mGet;
-
-
- public DownloadFileOperation(Account account, OCFile file) {
- if (account == null)
- throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
- if (file == null)
- throw new IllegalArgumentException("Illegal null file in DownloadFileOperation creation");
-
- mAccount = account;
- mFile = file;
- }
-
-
- public Account getAccount() {
- return mAccount;
- }
-
- public OCFile getFile() {
- return mFile;
- }
-
- public String getSavePath() {
- String path = mFile.getStoragePath(); // re-downloads should be done over the original file
- if (path != null && path.length() > 0) {
- return path;
- }
- return FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
- }
-
- public String getTmpPath() {
- return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
- }
-
- public String getRemotePath() {
- return mFile.getRemotePath();
- }
-
- public String getMimeType() {
- String mimeType = mFile.getMimetype();
- if (mimeType == null || mimeType.length() <= 0) {
- try {
- mimeType = MimeTypeMap.getSingleton()
- .getMimeTypeFromExtension(
- mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
- } catch (IndexOutOfBoundsException e) {
- Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
- }
- }
- if (mimeType == null) {
- mimeType = "application/octet-stream";
- }
- return mimeType;
- }
-
- public long getSize() {
- return mFile.getFileLength();
- }
-
- public long getModificationTimestamp() {
- return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
- }
-
-
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.add(listener);
- }
- }
-
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.remove(listener);
- }
- }
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- File newFile = null;
- boolean moved = true;
-
- /// download will be performed to a temporal file, then moved to the final location
- File tmpFile = new File(getTmpPath());
-
- /// perform the download
- try {
- tmpFile.getParentFile().mkdirs();
- int status = downloadFile(client, tmpFile);
- if (isSuccess(status)) {
- newFile = new File(getSavePath());
- newFile.getParentFile().mkdirs();
- moved = tmpFile.renameTo(newFile);
- }
- if (!moved)
- result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
- else
- result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null));
- Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
- }
-
- return result;
- }
-
-
- public boolean isSuccess(int status) {
- return (status == HttpStatus.SC_OK);
- }
-
-
- protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
- int status = -1;
- boolean savedFile = false;
- mGet = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
- Iterator<OnDatatransferProgressListener> it = null;
-
- FileOutputStream fos = null;
- try {
- status = client.executeMethod(mGet);
- if (isSuccess(status)) {
- targetFile.createNewFile();
- BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
- fos = new FileOutputStream(targetFile);
- long transferred = 0;
-
- byte[] bytes = new byte[4096];
- int readResult = 0;
- while ((readResult = bis.read(bytes)) != -1) {
- synchronized(mCancellationRequested) {
- if (mCancellationRequested.get()) {
- mGet.abort();
- throw new OperationCancelledException();
- }
- }
- fos.write(bytes, 0, readResult);
- transferred += readResult;
- synchronized (mDataTransferListeners) {
- it = mDataTransferListeners.iterator();
- while (it.hasNext()) {
- it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
- }
- }
- }
- savedFile = true;
- Header modificationTime = mGet.getResponseHeader("Last-Modified");
- if (modificationTime != null) {
- Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
- mModificationTimestamp = (d != null) ? d.getTime() : 0;
- }
-
- } else {
- client.exhaustResponse(mGet.getResponseBodyAsStream());
- }
-
- } finally {
- if (fos != null) fos.close();
- if (!savedFile && targetFile.exists()) {
- targetFile.delete();
- }
- mGet.releaseConnection(); // let the connection available for other methods
- }
- return status;
- }
-
-
- public void cancel() {
- mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.HeadMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-import android.content.Context;
-import android.net.ConnectivityManager;
-
-/**
- * Operation to check the existence or absence of a path in a remote server.
- *
- * @author David A. Velasco
- */
-public class ExistenceCheckOperation extends RemoteOperation {
-
- /** Maximum time to wait for a response from the server in MILLISECONDs. */
- public static final int TIMEOUT = 10000;
-
- private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
-
- private String mPath;
- private Context mContext;
- private boolean mSuccessIfAbsent;
-
-
- /**
- * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
- *
- * @param path Path to append to the URL owned by the client instance.
- * @param context Android application context.
- * @param successIfAbsent When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
- */
- public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
- mPath = (path != null) ? path : "";
- mContext = context;
- mSuccessIfAbsent = successIfAbsent;
- }
-
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- if (!isOnline()) {
- return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
- }
- RemoteOperationResult result = null;
- HeadMethod head = null;
- try {
- head = new HeadMethod(client.getBaseUri() + WebdavUtils.encodePath(mPath));
- int status = client.executeMethod(head, TIMEOUT, TIMEOUT);
- client.exhaustResponse(head.getResponseBodyAsStream());
- boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
- result = new RemoteOperationResult(success, status, head.getResponseHeaders());
- Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
-
- } finally {
- if (head != null)
- head.releaseConnection();
- }
- return result;
- }
-
- private boolean isOnline() {
- ConnectivityManager cm = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm != null && cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().isConnectedOrConnecting();
- }
-
-
-}
+++ /dev/null
-package de.mobilcom.debitel.cloud.android.operations;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.httpclient.methods.PostMethod;
-import org.apache.commons.httpclient.NameValuePair;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.OAuth2Constants;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import eu.alefzero.webdav.WebdavClient;
-
-public class OAuth2GetAccessToken extends RemoteOperation {
-
- private static final String TAG = OAuth2GetAccessToken.class.getSimpleName();
-
- private String mClientId;
- private String mRedirectUri;
- private String mGrantType;
-
- private String mOAuth2AuthorizationResponse;
- private Map<String, String> mOAuth2ParsedAuthorizationResponse;
- private Map<String, String> mResultTokenMap;
-
-
- public OAuth2GetAccessToken(String clientId, String redirectUri, String grantType, String oAuth2AuthorizationResponse) {
- mClientId = clientId;
- mRedirectUri = redirectUri;
- mGrantType = grantType;
- mOAuth2AuthorizationResponse = oAuth2AuthorizationResponse;
- mOAuth2ParsedAuthorizationResponse = new HashMap<String, String>();
- mResultTokenMap = null;
- }
-
-
- public Map<String, String> getOauth2AutorizationResponse() {
- return mOAuth2ParsedAuthorizationResponse;
- }
-
- public Map<String, String> getResultTokenMap() {
- return mResultTokenMap;
- }
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- PostMethod postMethod = null;
-
- try {
- parseAuthorizationResponse();
- if (mOAuth2ParsedAuthorizationResponse.keySet().contains(OAuth2Constants.KEY_ERROR)) {
- if (OAuth2Constants.VALUE_ERROR_ACCESS_DENIED.equals(mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_ERROR))) {
- result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR_ACCESS_DENIED);
- } else {
- result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
- }
- }
-
- if (result == null) {
- NameValuePair[] nameValuePairs = new NameValuePair[4];
- nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType);
- nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_CODE));
- nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri);
- nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
- //nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));
-
- postMethod = new PostMethod(client.getBaseUri().toString());
- postMethod.setRequestBody(nameValuePairs);
- int status = client.executeMethod(postMethod);
-
- String response = postMethod.getResponseBodyAsString();
- if (response != null && response.length() > 0) {
- JSONObject tokenJson = new JSONObject(response);
- parseAccessTokenResult(tokenJson);
- if (mResultTokenMap.get(OAuth2Constants.KEY_ERROR) != null || mResultTokenMap.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) {
- result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR);
-
- } else {
- result = new RemoteOperationResult(true, status, postMethod.getResponseHeaders());
- }
-
- } else {
- client.exhaustResponse(postMethod.getResponseBodyAsStream());
- result = new RemoteOperationResult(false, status, postMethod.getResponseHeaders());
- }
- }
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
-
- } finally {
- if (postMethod != null)
- postMethod.releaseConnection(); // let the connection available for other methods
-
- if (result.isSuccess()) {
- Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
-
- } else if (result.getException() != null) {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException());
-
- } else if (result.getCode() == ResultCode.OAUTH2_ERROR) {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
-
- } else {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
- }
- }
-
- return result;
- }
-
-
- private void parseAuthorizationResponse() {
- String[] pairs = mOAuth2AuthorizationResponse.split("&");
- int i = 0;
- String key = "";
- String value = "";
- StringBuilder sb = new StringBuilder();
- while (pairs.length > i) {
- int j = 0;
- String[] part = pairs[i].split("=");
- while (part.length > j) {
- String p = part[j];
- if (j == 0) {
- key = p;
- sb.append(key + " = ");
- } else if (j == 1) {
- value = p;
- mOAuth2ParsedAuthorizationResponse.put(key, value);
- sb.append(value + "\n");
- }
-
- Log_OC.v(TAG, "[" + i + "," + j + "] = " + p);
- j++;
- }
- i++;
- }
- }
-
-
- private void parseAccessTokenResult (JSONObject tokenJson) throws JSONException {
- mResultTokenMap = new HashMap<String, String>();
-
- if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) {
- mResultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson.getString(OAuth2Constants.KEY_ACCESS_TOKEN));
- }
- if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) {
- mResultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson.getString(OAuth2Constants.KEY_TOKEN_TYPE));
- }
- if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) {
- mResultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson.getString(OAuth2Constants.KEY_EXPIRES_IN));
- }
- if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) {
- mResultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson.getString(OAuth2Constants.KEY_REFRESH_TOKEN));
- }
- if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) {
- mResultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson.getString(OAuth2Constants.KEY_SCOPE));
- }
- if (tokenJson.has(OAuth2Constants.KEY_ERROR)) {
- mResultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson.getString(OAuth2Constants.KEY_ERROR));
- }
- if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) {
- mResultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson.getString(OAuth2Constants.KEY_ERROR_DESCRIPTION));
- }
- if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) {
- mResultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson.getString(OAuth2Constants.KEY_ERROR_URI));
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-public interface OnRemoteOperationListener {
-
- void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-public class OperationCancelledException extends Exception {
-
- /**
- * Generated serial version - to avoid Java warning
- */
- private static final long serialVersionUID = -6350981497740424983L;
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.Uri;
-
-public class OwnCloudServerCheckOperation extends RemoteOperation {
-
- /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */
- public static final int TRY_CONNECTION_TIMEOUT = 5000;
-
- private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
-
- private String mUrl;
- private RemoteOperationResult mLatestResult;
- private Context mContext;
- private OwnCloudVersion mOCVersion;
-
- public OwnCloudServerCheckOperation(String url, Context context) {
- mUrl = url;
- mContext = context;
- mOCVersion = null;
- }
-
- public OwnCloudVersion getDiscoveredVersion() {
- return mOCVersion;
- }
-
- private boolean tryConnection(WebdavClient wc, String urlSt) {
- boolean retval = false;
- GetMethod get = null;
- try {
- get = new GetMethod(urlSt);
- int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
- String response = get.getResponseBodyAsString();
- if (status == HttpStatus.SC_OK) {
- JSONObject json = new JSONObject(response);
- if (!json.getBoolean("installed")) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
- } else {
- mOCVersion = new OwnCloudVersion(json.getString("version"));
- if (!mOCVersion.isVersionValid()) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
-
- } else {
- mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ?
- RemoteOperationResult.ResultCode.OK_SSL :
- RemoteOperationResult.ResultCode.OK_NO_SSL
- );
-
- retval = true;
- }
- }
-
- } else {
- mLatestResult = new RemoteOperationResult(false, status, get.getResponseHeaders());
- }
-
- } catch (JSONException e) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-
- } catch (Exception e) {
- mLatestResult = new RemoteOperationResult(e);
-
- } finally {
- if (get != null)
- get.releaseConnection();
- }
-
- if (mLatestResult.isSuccess()) {
- Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
-
- } else if (mLatestResult.getException() != null) {
- Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
-
- } else {
- Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
- }
-
- return retval;
- }
-
- private boolean isOnline() {
- ConnectivityManager cm = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm != null && cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().isConnectedOrConnecting();
- }
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- if (!isOnline()) {
- return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
- }
- if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
- tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
-
- } else {
- client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
- boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH);
- if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
- Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection");
- client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
- tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
- }
- }
- return mLatestResult;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.IOException;
-
-import org.apache.commons.httpclient.Credentials;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.network.BearerCredentials;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Handler;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Operation which execution involves one or several interactions with an ownCloud server.
- *
- * Provides methods to execute the operation both synchronously or asynchronously.
- *
- * @author David A. Velasco
- */
-public abstract class RemoteOperation implements Runnable {
-
- private static final String TAG = RemoteOperation.class.getSimpleName();
-
- /** ownCloud account in the remote ownCloud server to operate */
- private Account mAccount = null;
-
- /** Android Application context */
- private Context mContext = null;
-
- /** Object to interact with the remote server */
- private WebdavClient mClient = null;
-
- /** Callback object to notify about the execution of the remote operation */
- private OnRemoteOperationListener mListener = null;
-
- /** Handler to the thread where mListener methods will be called */
- private Handler mListenerHandler = null;
-
- /** Activity */
- private Activity mCallerActivity;
-
-
- /**
- * Abstract method to implement the operation in derived classes.
- */
- protected abstract RemoteOperationResult run(WebdavClient client);
-
-
- /**
- * Synchronously executes the remote operation on the received ownCloud account.
- *
- * Do not call this method from the main thread.
- *
- * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
- *
- * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
- * @param context Android context for the component calling the method.
- * @return Result of the operation.
- */
- public final RemoteOperationResult execute(Account account, Context context) {
- if (account == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
- if (context == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
- mAccount = account;
- mContext = context.getApplicationContext();
- try {
- mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
- } catch (Exception e) {
- Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
- return new RemoteOperationResult(e);
- }
- return run(mClient);
- }
-
-
- /**
- * Synchronously executes the remote operation
- *
- * Do not call this method from the main thread.
- *
- * @param client Client object to reach an ownCloud server during the execution of the operation.
- * @return Result of the operation.
- */
- public final RemoteOperationResult execute(WebdavClient client) {
- if (client == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
- mClient = client;
- return run(client);
- }
-
-
- /**
- * Asynchronously executes the remote operation
- *
- * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
- *
- * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
- * @param context Android context for the component calling the method.
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
- if (account == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
- if (context == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
- mAccount = account;
- mContext = context.getApplicationContext();
- mCallerActivity = callerActivity;
- mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
-
- if (listener == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
- }
- mListener = listener;
-
- if (listenerHandler == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
- }
- mListenerHandler = listenerHandler;
-
- Thread runnerThread = new Thread(this);
- runnerThread.start();
- return runnerThread;
- }
-
-
- /**
- * Asynchronously executes the remote operation
- *
- * @param client Client object to reach an ownCloud server during the execution of the operation.
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
- if (client == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
- }
- mClient = client;
-
- if (listener == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
- }
- mListener = listener;
-
- if (listenerHandler == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
- }
- mListenerHandler = listenerHandler;
-
- Thread runnerThread = new Thread(this);
- runnerThread.start();
- return runnerThread;
- }
-
- /**
- * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
- *
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final RemoteOperationResult retry() {
- return execute(mClient);
- }
-
- /**
- * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
- *
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
- return execute(mClient, listener, listenerHandler);
- }
-
-
- /**
- * Asynchronous execution of the operation
- * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)},
- * and result posting.
- *
- * TODO refactor && clean the code; now it's a mess
- */
- @Override
- public final void run() {
- RemoteOperationResult result = null;
- boolean repeat = false;
- do {
- try{
- if (mClient == null) {
- if (mAccount != null && mContext != null) {
- if (mCallerActivity != null) {
- mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext, mCallerActivity);
- } else {
- mClient = OwnCloudClientUtils.createOwnCloudClient(mAccount, mContext);
- }
- } else {
- throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
- }
- }
-
- } catch (IOException e) {
- Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
- result = new RemoteOperationResult(e);
-
- } catch (AccountsException e) {
- Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
- result = new RemoteOperationResult(e);
- }
-
- if (result == null)
- result = run(mClient);
-
- repeat = false;
- if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
-// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
- (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
- /// possible fail due to lack of authorization in an operation performed in foreground
- Credentials cred = mClient.getCredentials();
- String ssoSessionCookie = mClient.getSsoSessionCookie();
- if (cred != null || ssoSessionCookie != null) {
- /// confirmed : unauthorized operation
- AccountManager am = AccountManager.get(mContext);
- boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
- boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
- if (bearerAuthorization) {
- am.invalidateAuthToken(MainApp.getAccountType(), ((BearerCredentials)cred).getAccessToken());
- } else if (samlBasedSsoAuthorization ) {
- am.invalidateAuthToken(MainApp.getAccountType(), ssoSessionCookie);
- } else {
- am.clearPassword(mAccount);
- }
- mClient = null;
- repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
- result = null;
- }
- }
- } while (repeat);
-
- final RemoteOperationResult resultToSend = result;
- if (mListenerHandler != null && mListener != null) {
- mListenerHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
- }
- });
- }
- }
-
-
- /**
- * Returns the current client instance to access the remote server.
- *
- * @return Current client instance to access the remote server.
- */
- public final WebdavClient getClient() {
- return mClient;
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-
-import javax.net.ssl.SSLException;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.DavException;
-
-import android.accounts.Account;
-import android.accounts.AccountsException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-import de.mobilcom.debitel.cloud.android.network.CertificateCombinedException;
-
-/**
- * The result of a remote operation required to an ownCloud server.
- *
- * Provides a common classification of remote operation results for all the
- * application.
- *
- * @author David A. Velasco
- */
-public class RemoteOperationResult implements Serializable {
-
- /** Generated - should be refreshed every time the class changes!! */
- private static final long serialVersionUID = -4415103901492836870L;
-
-
-
- private static final String TAG = "RemoteOperationResult";
-
- public enum ResultCode {
- OK,
- OK_SSL,
- OK_NO_SSL,
- UNHANDLED_HTTP_CODE,
- UNAUTHORIZED,
- FILE_NOT_FOUND,
- INSTANCE_NOT_CONFIGURED,
- UNKNOWN_ERROR,
- WRONG_CONNECTION,
- TIMEOUT,
- INCORRECT_ADDRESS,
- HOST_NOT_AVAILABLE,
- NO_NETWORK_CONNECTION,
- SSL_ERROR,
- SSL_RECOVERABLE_PEER_UNVERIFIED,
- BAD_OC_VERSION,
- CANCELLED,
- INVALID_LOCAL_FILE_NAME,
- INVALID_OVERWRITE,
- CONFLICT,
- OAUTH2_ERROR,
- SYNC_CONFLICT,
- LOCAL_STORAGE_FULL,
- LOCAL_STORAGE_NOT_MOVED,
- LOCAL_STORAGE_NOT_COPIED,
- OAUTH2_ERROR_ACCESS_DENIED,
- QUOTA_EXCEEDED,
- ACCOUNT_NOT_FOUND,
- ACCOUNT_EXCEPTION,
- ACCOUNT_NOT_NEW,
- ACCOUNT_NOT_THE_SAME
- }
-
- private boolean mSuccess = false;
- private int mHttpCode = -1;
- private Exception mException = null;
- private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
- private String mRedirectedLocation;
-
- public RemoteOperationResult(ResultCode code) {
- mCode = code;
- mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
- }
-
- private RemoteOperationResult(boolean success, int httpCode) {
- mSuccess = success;
- mHttpCode = httpCode;
-
- if (success) {
- mCode = ResultCode.OK;
-
- } else if (httpCode > 0) {
- switch (httpCode) {
- case HttpStatus.SC_UNAUTHORIZED:
- mCode = ResultCode.UNAUTHORIZED;
- break;
- case HttpStatus.SC_NOT_FOUND:
- mCode = ResultCode.FILE_NOT_FOUND;
- break;
- case HttpStatus.SC_INTERNAL_SERVER_ERROR:
- mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
- break;
- case HttpStatus.SC_CONFLICT:
- mCode = ResultCode.CONFLICT;
- break;
- case HttpStatus.SC_INSUFFICIENT_STORAGE:
- mCode = ResultCode.QUOTA_EXCEEDED;
- break;
- default:
- mCode = ResultCode.UNHANDLED_HTTP_CODE;
- Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
- }
- }
- }
-
- public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
- this(success, httpCode);
- if (headers != null) {
- Header current;
- for (int i=0; i<headers.length; i++) {
- current = headers[i];
- if ("Location".equals(current.getName())) {
- mRedirectedLocation = current.getValue();
- break;
- }
- }
- }
- }
-
- public RemoteOperationResult(Exception e) {
- mException = e;
-
- if (e instanceof OperationCancelledException) {
- mCode = ResultCode.CANCELLED;
-
- } else if (e instanceof SocketException) {
- mCode = ResultCode.WRONG_CONNECTION;
-
- } else if (e instanceof SocketTimeoutException) {
- mCode = ResultCode.TIMEOUT;
-
- } else if (e instanceof ConnectTimeoutException) {
- mCode = ResultCode.TIMEOUT;
-
- } else if (e instanceof MalformedURLException) {
- mCode = ResultCode.INCORRECT_ADDRESS;
-
- } else if (e instanceof UnknownHostException) {
- mCode = ResultCode.HOST_NOT_AVAILABLE;
-
- } else if (e instanceof AccountNotFoundException) {
- mCode = ResultCode.ACCOUNT_NOT_FOUND;
-
- } else if (e instanceof AccountsException) {
- mCode = ResultCode.ACCOUNT_EXCEPTION;
-
- } else if (e instanceof SSLException || e instanceof RuntimeException) {
- CertificateCombinedException se = getCertificateCombinedException(e);
- if (se != null) {
- mException = se;
- if (se.isRecoverable()) {
- mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
- }
- } else if (e instanceof RuntimeException) {
- mCode = ResultCode.HOST_NOT_AVAILABLE;
-
- } else {
- mCode = ResultCode.SSL_ERROR;
- }
-
- } else {
- mCode = ResultCode.UNKNOWN_ERROR;
- }
-
- }
-
- public boolean isSuccess() {
- return mSuccess;
- }
-
- public boolean isCancelled() {
- return mCode == ResultCode.CANCELLED;
- }
-
- public int getHttpCode() {
- return mHttpCode;
- }
-
- public ResultCode getCode() {
- return mCode;
- }
-
- public Exception getException() {
- return mException;
- }
-
- public boolean isSslRecoverableException() {
- return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
- }
-
- private CertificateCombinedException getCertificateCombinedException(Exception e) {
- CertificateCombinedException result = null;
- if (e instanceof CertificateCombinedException) {
- return (CertificateCombinedException) e;
- }
- Throwable cause = mException.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertificateCombinedException) {
- result = (CertificateCombinedException) cause;
- }
- return result;
- }
-
- public String getLogMessage() {
-
- if (mException != null) {
- if (mException instanceof OperationCancelledException) {
- return "Operation cancelled by the caller";
-
- } else if (mException instanceof SocketException) {
- return "Socket exception";
-
- } else if (mException instanceof SocketTimeoutException) {
- return "Socket timeout exception";
-
- } else if (mException instanceof ConnectTimeoutException) {
- return "Connect timeout exception";
-
- } else if (mException instanceof MalformedURLException) {
- return "Malformed URL exception";
-
- } else if (mException instanceof UnknownHostException) {
- return "Unknown host exception";
-
- } else if (mException instanceof CertificateCombinedException) {
- if (((CertificateCombinedException) mException).isRecoverable())
- return "SSL recoverable exception";
- else
- return "SSL exception";
-
- } else if (mException instanceof SSLException) {
- return "SSL exception";
-
- } else if (mException instanceof DavException) {
- return "Unexpected WebDAV exception";
-
- } else if (mException instanceof HttpException) {
- return "HTTP violation";
-
- } else if (mException instanceof IOException) {
- return "Unrecovered transport exception";
-
- } else if (mException instanceof AccountNotFoundException) {
- Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount();
- return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")";
-
- } else if (mException instanceof AccountsException) {
- return "Exception while using account";
-
- } else {
- return "Unexpected exception";
- }
- }
-
- if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
- return "The ownCloud server is not configured!";
-
- } else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
- return "No network connection";
-
- } else if (mCode == ResultCode.BAD_OC_VERSION) {
- return "No valid ownCloud version was found at the server";
-
- } else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
- return "Local storage full";
-
- } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
- return "Error while moving file to final directory";
-
- } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
- return "Account already existing when creating a new one";
-
- } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
- return "Authenticated with a different account than the one updating";
- }
-
- return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
-
- }
-
- public boolean isServerFail() {
- return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
- }
-
- public boolean isException() {
- return (mException != null);
- }
-
- public boolean isTemporalRedirection() {
- return (mHttpCode == 302 || mHttpCode == 307);
- }
-
- public String getRedirectedLocation() {
- return mRedirectedLocation;
- }
-
- public boolean isIdPRedirection() {
- return (mRedirectedLocation != null &&
- (mRedirectedLocation.toUpperCase().contains("SAML") ||
- mRedirectedLocation.toLowerCase().contains("wayf")));
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the removal of a remote file or folder in the ownCloud server.
- *
- * @author David A. Velasco
- */
-public class RemoveFileOperation extends RemoteOperation {
-
- private static final String TAG = RemoveFileOperation.class.getSimpleName();
-
- private static final int REMOVE_READ_TIMEOUT = 10000;
- private static final int REMOVE_CONNECTION_TIMEOUT = 5000;
-
- OCFile mFileToRemove;
- boolean mDeleteLocalCopy;
- DataStorageManager mDataStorageManager;
-
-
- /**
- * Constructor
- *
- * @param fileToRemove OCFile instance describing the remote file or folder to remove from the server
- * @param deleteLocalCopy When 'true', and a local copy of the file exists, it is also removed.
- * @param storageManager Reference to the local database corresponding to the account where the file is contained.
- */
- public RemoveFileOperation(OCFile fileToRemove, boolean deleteLocalCopy, DataStorageManager storageManager) {
- mFileToRemove = fileToRemove;
- mDeleteLocalCopy = deleteLocalCopy;
- mDataStorageManager = storageManager;
- }
-
-
- /**
- * Getter for the file to remove (or removed, if the operation was successfully performed).
- *
- * @return File to remove or already removed.
- */
- public OCFile getFile() {
- return mFileToRemove;
- }
-
-
- /**
- * Performs the remove operation
- *
- * @param client Client object to communicate with the remote ownCloud server.
- */
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- DeleteMethod delete = null;
- try {
- delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
- int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
- if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
- if (mFileToRemove.isDirectory()) {
- mDataStorageManager.removeDirectory(mFileToRemove, true, mDeleteLocalCopy);
- } else {
- mDataStorageManager.removeFile(mFileToRemove, mDeleteLocalCopy);
- }
- }
- delete.getResponseBodyAsString(); // exhaust the response, although not interesting
- result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
- Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
-
- } finally {
- if (delete != null)
- delete.releaseConnection();
- }
- return result;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.IOException;
-
-import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
-//import org.apache.jackrabbit.webdav.client.methods.MoveMethod;
-
-import android.accounts.Account;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
- *
- * @author David A. Velasco
- */
-public class RenameFileOperation extends RemoteOperation {
-
- private static final String TAG = RenameFileOperation.class.getSimpleName();
-
- private static final int RENAME_READ_TIMEOUT = 10000;
- private static final int RENAME_CONNECTION_TIMEOUT = 5000;
-
-
- private OCFile mFile;
- private Account mAccount;
- private String mNewName;
- private String mNewRemotePath;
- private DataStorageManager mStorageManager;
-
-
- /**
- * Constructor
- *
- * @param file OCFile instance describing the remote file or folder to rename
- * @param account OwnCloud account containing the remote file
- * @param newName New name to set as the name of file.
- * @param storageManager Reference to the local database corresponding to the account where the file is contained.
- */
- public RenameFileOperation(OCFile file, Account account, String newName, DataStorageManager storageManager) {
- mFile = file;
- mAccount = account;
- mNewName = newName;
- mNewRemotePath = null;
- mStorageManager = storageManager;
- }
-
- public OCFile getFile() {
- return mFile;
- }
-
-
- /**
- * Performs the rename operation.
- *
- * @param client Client object to communicate with the remote ownCloud server.
- */
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
-
- LocalMoveMethod move = null;
- mNewRemotePath = null;
- try {
- if (mNewName.equals(mFile.getFileName())) {
- return new RemoteOperationResult(ResultCode.OK);
- }
-
- String parent = (new File(mFile.getRemotePath())).getParent();
- parent = (parent.endsWith(OCFile.PATH_SEPARATOR)) ? parent : parent + OCFile.PATH_SEPARATOR;
- mNewRemotePath = parent + mNewName;
- if (mFile.isDirectory()) {
- mNewRemotePath += OCFile.PATH_SEPARATOR;
- }
-
- // check if the new name is valid in the local file system
- if (!isValidNewName()) {
- return new RemoteOperationResult(ResultCode.INVALID_LOCAL_FILE_NAME);
- }
-
- // check if a file with the new name already exists
- if (client.existsFile(mNewRemotePath) || // remote check could fail by network failure. by indeterminate behavior of HEAD for folders ...
- mStorageManager.getFileByPath(mNewRemotePath) != null) { // ... so local check is convenient
- return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
- }
- move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()),
- client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
- int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
- if (move.succeeded()) {
-
- if (mFile.isDirectory()) {
- saveLocalDirectory();
-
- } else {
- saveLocalFile();
-
- }
-
- /*
- *} else if (mFile.isDirectory() && (status == 207 || status >= 500)) {
- * // TODO
- * // if server fails in the rename of a folder, some children files could have been moved to a folder with the new name while some others
- * // stayed in the old folder;
- * //
- * // easiest and heaviest solution is synchronizing the parent folder (or the full account);
- * //
- * // a better solution is synchronizing the folders with the old and new names;
- *}
- */
-
- }
-
- move.getResponseBodyAsString(); // exhaust response, although not interesting
- result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders());
- Log_OC.i(TAG, "Rename " + mFile.getRemotePath() + " to " + mNewRemotePath + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Rename " + mFile.getRemotePath() + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
-
- } finally {
- if (move != null)
- move.releaseConnection();
- }
- return result;
- }
-
-
- private void saveLocalDirectory() {
- mStorageManager.moveDirectory(mFile, mNewRemotePath);
- String localPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile);
- File localDir = new File(localPath);
- if (localDir.exists()) {
- localDir.renameTo(new File(FileStorageUtils.getSavePath(mAccount.name) + mNewRemotePath));
- // TODO - if renameTo fails, children files that are already down will result unlinked
- }
- }
-
- private void saveLocalFile() {
- mFile.setFileName(mNewName);
-
- // try to rename the local copy of the file
- if (mFile.isDown()) {
- File f = new File(mFile.getStoragePath());
- String parentStoragePath = f.getParent();
- if (!parentStoragePath.endsWith(File.separator))
- parentStoragePath += File.separator;
- if (f.renameTo(new File(parentStoragePath + mNewName))) {
- mFile.setStoragePath(parentStoragePath + mNewName);
- }
- // else - NOTHING: the link to the local file is kept although the local name can't be updated
- // TODO - study conditions when this could be a problem
- }
-
- mStorageManager.saveFile(mFile);
- }
-
- /**
- * Checks if the new name to set is valid in the file system
- *
- * The only way to be sure is trying to create a file with that name. It's made in the temporal directory
- * for downloads, out of any account, and then removed.
- *
- * IMPORTANT: The test must be made in the same file system where files are download. The internal storage
- * could be formatted with a different file system.
- *
- * TODO move this method, and maybe FileDownload.get***Path(), to a class with utilities specific for the interactions with the file system
- *
- * @return 'True' if a temporal file named with the name to set could be created in the file system where
- * local files are stored.
- * @throws IOException When the temporal folder can not be created.
- */
- private boolean isValidNewName() throws IOException {
- // check tricky names
- if (mNewName == null || mNewName.length() <= 0 || mNewName.contains(File.separator) || mNewName.contains("%")) {
- return false;
- }
- // create a test file
- String tmpFolderName = FileStorageUtils.getTemporalPath("");
- File testFile = new File(tmpFolderName + mNewName);
- File tmpFolder = testFile.getParentFile();
- tmpFolder.mkdirs();
- if (!tmpFolder.isDirectory()) {
- throw new IOException("Unexpected error: temporal directory could not be created");
- }
- try {
- testFile.createNewFile(); // return value is ignored; it could be 'false' because the file already existed, that doesn't invalidate the name
- } catch (IOException e) {
- Log_OC.i(TAG, "Test for validity of name " + mNewName + " in the file system failed");
- return false;
- }
- boolean result = (testFile.exists() && testFile.isFile());
-
- // cleaning ; result is ignored, since there is not much we could do in case of failure, but repeat and repeat...
- testFile.delete();
-
- return result;
- }
-
-
- // move operation
- private class LocalMoveMethod extends DavMethodBase {
-
- public LocalMoveMethod(String uri, String dest) {
- super(uri);
- addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
- }
-
- @Override
- public String getName() {
- return "MOVE";
- }
-
- @Override
- protected boolean isSuccess(int status) {
- return status == 201 || status == 204;
- }
-
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-public class SynchronizeFileOperation extends RemoteOperation {
-
- private String TAG = SynchronizeFileOperation.class.getSimpleName();
- private static final int SYNC_READ_TIMEOUT = 10000;
- private static final int SYNC_CONNECTION_TIMEOUT = 5000;
-
- private OCFile mLocalFile;
- private OCFile mServerFile;
- private DataStorageManager mStorageManager;
- private Account mAccount;
- private boolean mSyncFileContents;
- private boolean mLocalChangeAlreadyKnown;
- private Context mContext;
-
- private boolean mTransferWasRequested = false;
-
- public SynchronizeFileOperation(
- OCFile localFile,
- OCFile serverFile, // make this null to let the operation checks the server; added to reuse info from SynchronizeFolderOperation
- DataStorageManager storageManager,
- Account account,
- boolean syncFileContents,
- boolean localChangeAlreadyKnown,
- Context context) {
-
- mLocalFile = localFile;
- mServerFile = serverFile;
- mStorageManager = storageManager;
- mAccount = account;
- mSyncFileContents = syncFileContents;
- mLocalChangeAlreadyKnown = localChangeAlreadyKnown;
- mContext = context;
- }
-
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
-
- PropFindMethod propfind = null;
- RemoteOperationResult result = null;
- mTransferWasRequested = false;
- try {
- if (!mLocalFile.isDown()) {
- /// easy decision
- requestForDownload(mLocalFile);
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else {
- /// local copy in the device -> need to think a bit more before do anything
-
- if (mServerFile == null) {
- /// take the duty of check the server for the current state of the file there
- propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_0);
- int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
- boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
- if (isMultiStatus) {
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
- client.getBaseUri().getPath());
- mServerFile = fillOCFile(we);
- mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
-
- } else {
- client.exhaustResponse(propfind.getResponseBodyAsStream());
- result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
- }
- }
-
- if (result == null) { // true if the server was not checked. nothing was wrong with the remote request
-
- /// check changes in server and local file
- boolean serverChanged = false;
- if (mServerFile.getEtag() != null) {
- serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
- } else {
- // server without etags
- serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
- }
- boolean localChanged = (mLocalChangeAlreadyKnown || mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
- // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
-
- /// decide action to perform depending upon changes
- if (localChanged && serverChanged) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
-
- } else if (localChanged) {
- if (mSyncFileContents) {
- requestForUpload(mLocalFile);
- // the local update of file properties will be done by the FileUploader service when the upload finishes
- } else {
- // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
- // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
- // that an upload is necessary (for instance, in FileObserverService).
- }
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else if (serverChanged) {
- if (mSyncFileContents) {
- requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
- // the update of local data will be done later by the FileUploader service when the upload finishes
- } else {
- // TODO CHECK: is this really useful in some point in the code?
- mServerFile.setKeepInSync(mLocalFile.keepInSync());
- mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
- mServerFile.setStoragePath(mLocalFile.getStoragePath());
- mServerFile.setParentId(mLocalFile.getParentId());
- mStorageManager.saveFile(mServerFile);
-
- }
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else {
- // nothing changed, nothing to do
- result = new RemoteOperationResult(ResultCode.OK);
- }
-
- }
-
- }
-
- Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
-
- } finally {
- if (propfind != null)
- propfind.releaseConnection();
- }
- return result;
- }
-
-
- /**
- * Requests for an upload to the FileUploader service
- *
- * @param file OCFile object representing the file to upload
- */
- private void requestForUpload(OCFile file) {
- Intent i = new Intent(mContext, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, mAccount);
- i.putExtra(FileUploader.KEY_FILE, file);
- /*i.putExtra(FileUploader.KEY_REMOTE_FILE, mRemotePath); // doing this we would lose the value of keepInSync in the road, and maybe it's not updated in the database when the FileUploader service gets it!
- i.putExtra(FileUploader.KEY_LOCAL_FILE, localFile.getStoragePath());*/
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
- mContext.startService(i);
- mTransferWasRequested = true;
- }
-
-
- /**
- * Requests for a download to the FileDownloader service
- *
- * @param file OCFile object representing the file to download
- */
- private void requestForDownload(OCFile file) {
- Intent i = new Intent(mContext, FileDownloader.class);
- i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount);
- i.putExtra(FileDownloader.EXTRA_FILE, file);
- mContext.startService(i);
- mTransferWasRequested = true;
- }
-
-
- /**
- * Creates and populates a new {@link OCFile} object with the data read from the server.
- *
- * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
- * @return New OCFile instance representing the remote resource described by we.
- */
- private OCFile fillOCFile(WebdavEntry we) {
- OCFile file = new OCFile(we.decodedPath());
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- return file;
- }
-
-
- public boolean transferWasRequested() {
- return mTransferWasRequested;
- }
-
-
- public OCFile getLocalFile() {
- return mLocalFile;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Vector;
-
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
-import android.accounts.Account;
-import android.content.Context;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavEntry;
-import eu.alefzero.webdav.WebdavUtils;
-
-
-/**
- * Remote operation performing the synchronization a the contents of a remote folder with the local database
- *
- * @author David A. Velasco
- */
-public class SynchronizeFolderOperation extends RemoteOperation {
-
- private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
-
- /** Remote folder to synchronize */
- private String mRemotePath;
-
- /** Timestamp for the synchronization in progress */
- private long mCurrentSyncTime;
-
- /** Id of the folder to synchronize in the local database */
- private long mParentId;
-
- /** Access to the local database */
- private DataStorageManager mStorageManager;
-
- /** Account where the file to synchronize belongs */
- private Account mAccount;
-
- /** Android context; necessary to send requests to the download service; maybe something to refactor */
- private Context mContext;
-
- /** Files and folders contained in the synchronized folder */
- private List<OCFile> mChildren;
-
- private int mConflictsFound;
-
- private int mFailsInFavouritesFound;
-
- private Map<String, String> mForgottenLocalFiles;
-
-
- public SynchronizeFolderOperation( String remotePath,
- long currentSyncTime,
- long parentId,
- DataStorageManager dataStorageManager,
- Account account,
- Context context ) {
- mRemotePath = remotePath;
- mCurrentSyncTime = currentSyncTime;
- mParentId = parentId;
- mStorageManager = dataStorageManager;
- mAccount = account;
- mContext = context;
- mForgottenLocalFiles = new HashMap<String, String>();
- }
-
-
- public int getConflictsFound() {
- return mConflictsFound;
- }
-
- public int getFailsInFavouritesFound() {
- return mFailsInFavouritesFound;
- }
-
- public Map<String, String> getForgottenLocalFiles() {
- return mForgottenLocalFiles;
- }
-
- /**
- * Returns the list of files and folders contained in the synchronized folder, if called after synchronization is complete.
- *
- * @return List of files and folders contained in the synchronized folder.
- */
- public List<OCFile> getChildren() {
- return mChildren;
- }
-
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- mFailsInFavouritesFound = 0;
- mConflictsFound = 0;
- mForgottenLocalFiles.clear();
-
- // code before in FileSyncAdapter.fetchData
- PropFindMethod query = null;
- try {
- Log_OC.d(TAG, "Synchronizing " + mAccount.name + ", fetching files in " + mRemotePath);
-
- // remote request
- query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_1);
- int status = client.executeMethod(query);
-
- // check and process response - /// TODO take into account all the possible status per child-resource
- if (isMultiStatus(status)) {
- MultiStatus resp = query.getResponseBodyAsMultiStatus();
-
- // synchronize properties of the parent folder, if necessary
- if (mParentId == DataStorageManager.ROOT_PARENT_ID) {
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0], client.getBaseUri().getPath());
- OCFile parent = fillOCFile(we);
- mStorageManager.saveFile(parent);
- mParentId = parent.getFileId();
- }
-
- // read contents in folder
- List<OCFile> updatedFiles = new Vector<OCFile>(resp.getResponses().length - 1);
- List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
- for (int i = 1; i < resp.getResponses().length; ++i) {
- /// new OCFile instance with the data from the server
- WebdavEntry we = new WebdavEntry(resp.getResponses()[i], client.getBaseUri().getPath());
- OCFile file = fillOCFile(we);
-
- /// set data about local state, keeping unchanged former data if existing
- file.setLastSyncDateForProperties(mCurrentSyncTime);
- OCFile oldFile = mStorageManager.getFileByPath(file.getRemotePath());
- if (oldFile != null) {
- file.setKeepInSync(oldFile.keepInSync());
- file.setLastSyncDateForData(oldFile.getLastSyncDateForData());
- file.setModificationTimestampAtLastSyncForData(oldFile.getModificationTimestampAtLastSyncForData()); // must be kept unchanged when the file contents are not updated
- checkAndFixForeignStoragePath(oldFile);
- file.setStoragePath(oldFile.getStoragePath());
- }
-
- /// scan default location if local copy of file is not linked in OCFile instance
- if (file.getStoragePath() == null && !file.isDirectory()) {
- File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file));
- if (f.exists()) {
- file.setStoragePath(f.getAbsolutePath());
- file.setLastSyncDateForData(f.lastModified());
- }
- }
-
- /// prepare content synchronization for kept-in-sync files
- if (file.keepInSync()) {
- SynchronizeFileOperation operation = new SynchronizeFileOperation( oldFile,
- file,
- mStorageManager,
- mAccount,
- true,
- false,
- mContext
- );
- filesToSyncContents.add(operation);
- }
-
- updatedFiles.add(file);
- }
-
- // save updated contents in local database; all at once, trying to get a best performance in database update (not a big deal, indeed)
- mStorageManager.saveFiles(updatedFiles);
-
- // request for the synchronization of files AFTER saving last properties
- SynchronizeFileOperation op = null;
- RemoteOperationResult contentsResult = null;
- for (int i=0; i < filesToSyncContents.size(); i++) {
- op = filesToSyncContents.get(i);
- contentsResult = op.execute(client); // returns without waiting for upload or download finishes
- if (!contentsResult.isSuccess()) {
- if (contentsResult.getCode() == ResultCode.SYNC_CONFLICT) {
- mConflictsFound++;
- } else {
- mFailsInFavouritesFound++;
- if (contentsResult.getException() != null) {
- Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage(), contentsResult.getException());
- } else {
- Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
- }
- }
- } // won't let these fails break the synchronization process
- }
-
-
- // removal of obsolete files
- mChildren = mStorageManager.getDirectoryContent(mStorageManager.getFileById(mParentId));
- OCFile file;
- String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
- for (int i=0; i < mChildren.size(); ) {
- file = mChildren.get(i);
- if (file.getLastSyncDateForProperties() != mCurrentSyncTime) {
- Log_OC.d(TAG, "removing file: " + file);
- mStorageManager.removeFile(file, (file.isDown() && file.getStoragePath().startsWith(currentSavePath)));
- mChildren.remove(i);
- } else {
- i++;
- }
- }
-
- } else {
- client.exhaustResponse(query.getResponseBodyAsStream());
- }
-
- // prepare result object
- if (isMultiStatus(status)) {
- if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
-
- } else {
- result = new RemoteOperationResult(true, status, query.getResponseHeaders());
- }
- } else {
- result = new RemoteOperationResult(false, status, query.getResponseHeaders());
- }
-
-
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
-
-
- } finally {
- if (query != null)
- query.releaseConnection(); // let the connection available for other methods
- if (result.isSuccess()) {
- Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
- } else {
- if (result.isException()) {
- Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
- } else {
- Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
- }
- }
- }
-
- return result;
- }
-
-
- public boolean isMultiStatus(int status) {
- return (status == HttpStatus.SC_MULTI_STATUS);
- }
-
-
- /**
- * Creates and populates a new {@link OCFile} object with the data read from the server.
- *
- * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
- * @return New OCFile instance representing the remote resource described by we.
- */
- private OCFile fillOCFile(WebdavEntry we) {
- OCFile file = new OCFile(we.decodedPath());
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- file.setParentId(mParentId);
- return file;
- }
-
-
- /**
- * Checks the storage path of the OCFile received as parameter. If it's out of the local ownCloud folder,
- * tries to copy the file inside it.
- *
- * If the copy fails, the link to the local file is nullified. The account of forgotten files is kept in
- * {@link #mForgottenLocalFiles}
- *
- * @param file File to check and fix.
- */
- private void checkAndFixForeignStoragePath(OCFile file) {
- String storagePath = file.getStoragePath();
- String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, file);
- if (storagePath != null && !storagePath.equals(expectedPath)) {
- /// fix storagePaths out of the local ownCloud folder
- File originalFile = new File(storagePath);
- if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
- mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
- file.setStoragePath(null);
-
- } else {
- InputStream in = null;
- OutputStream out = null;
- try {
- File expectedFile = new File(expectedPath);
- File expectedParent = expectedFile.getParentFile();
- expectedParent.mkdirs();
- if (!expectedParent.isDirectory()) {
- throw new IOException("Unexpected error: parent directory could not be created");
- }
- expectedFile.createNewFile();
- if (!expectedFile.isFile()) {
- throw new IOException("Unexpected error: target file could not be created");
- }
- in = new FileInputStream(originalFile);
- out = new FileOutputStream(expectedFile);
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0){
- out.write(buf, 0, len);
- }
- file.setStoragePath(expectedPath);
-
- } catch (Exception e) {
- Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e);
- mForgottenLocalFiles.put(file.getRemotePath(), storagePath);
- file.setStoragePath(null);
-
- } finally {
- try {
- if (in != null) in.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing input stream for " + storagePath + " (ignoring)", e);
- }
- try {
- if (out != null) out.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
- }
- }
- }
- }
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.Context;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Remote operation that checks the version of an ownCloud server and stores it locally
- *
- * @author David A. Velasco
- */
-public class UpdateOCVersionOperation extends RemoteOperation {
-
- private static final String TAG = UpdateOCVersionOperation.class.getSimpleName();
-
- private Account mAccount;
- private Context mContext;
-
-
- public UpdateOCVersionOperation(Account account, Context context) {
- mAccount = account;
- mContext = context;
- }
-
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- AccountManager accountMngr = AccountManager.get(mContext);
- String statUrl = accountMngr.getUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL);
- statUrl += AccountUtils.STATUS_PATH;
- RemoteOperationResult result = null;
- GetMethod get = null;
- try {
- get = new GetMethod(statUrl);
- int status = client.executeMethod(get);
- if (status != HttpStatus.SC_OK) {
- client.exhaustResponse(get.getResponseBodyAsStream());
- result = new RemoteOperationResult(false, status, get.getResponseHeaders());
-
- } else {
- String response = get.getResponseBodyAsString();
- if (response != null) {
- JSONObject json = new JSONObject(response);
- if (json != null && json.getString("version") != null) {
- OwnCloudVersion ocver = new OwnCloudVersion(json.getString("version"));
- if (ocver.isVersionValid()) {
- accountMngr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION, ocver.toString());
- Log_OC.d(TAG, "Got new OC version " + ocver.toString());
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else {
- Log_OC.w(TAG, "Invalid version number received from server: " + json.getString("version"));
- result = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
- }
- }
- }
- if (result == null) {
- result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
- }
- }
- Log_OC.i(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage());
-
- } catch (JSONException e) {
- result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
- Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
-
- } finally {
- if (get != null)
- get.releaseConnection();
- }
- return result;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.operations;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.PutMethod;
-import org.apache.commons.httpclient.methods.RequestEntity;
-import org.apache.http.HttpStatus;
-
-import android.accounts.Account;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-import eu.alefzero.webdav.FileRequestEntity;
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-import eu.alefzero.webdav.WebdavClient;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Remote operation performing the upload of a file to an ownCloud server
- *
- * @author David A. Velasco
- */
-public class UploadFileOperation extends RemoteOperation {
-
- private static final String TAG = UploadFileOperation.class.getSimpleName();
-
- private Account mAccount;
- private OCFile mFile;
- private OCFile mOldFile;
- private String mRemotePath = null;
- private boolean mIsInstant = false;
- private boolean mRemoteFolderToBeCreated = false;
- private boolean mForceOverwrite = false;
- private int mLocalBehaviour = FileUploader.LOCAL_BEHAVIOUR_COPY;
- private boolean mWasRenamed = false;
- private String mOriginalFileName = null;
- private String mOriginalStoragePath = null;
- PutMethod mPutMethod = null;
- private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
- private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
-
- protected RequestEntity mEntity = null;
-
-
- public UploadFileOperation( Account account,
- OCFile file,
- boolean isInstant,
- boolean forceOverwrite,
- int localBehaviour) {
- if (account == null)
- throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
- if (file == null)
- throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
- if (file.getStoragePath() == null || file.getStoragePath().length() <= 0
- || !(new File(file.getStoragePath()).exists())) {
- throw new IllegalArgumentException(
- "Illegal file in UploadFileOperation; storage path invalid or file not found: "
- + file.getStoragePath());
- }
-
- mAccount = account;
- mFile = file;
- mRemotePath = file.getRemotePath();
- mIsInstant = isInstant;
- mForceOverwrite = forceOverwrite;
- mLocalBehaviour = localBehaviour;
- mOriginalStoragePath = mFile.getStoragePath();
- mOriginalFileName = mFile.getFileName();
- }
-
- public Account getAccount() {
- return mAccount;
- }
-
- public String getFileName() {
- return mOriginalFileName;
- }
-
- public OCFile getFile() {
- return mFile;
- }
-
- public OCFile getOldFile() {
- return mOldFile;
- }
-
- public String getOriginalStoragePath() {
- return mOriginalStoragePath;
- }
-
- public String getStoragePath() {
- return mFile.getStoragePath();
- }
-
- public String getRemotePath() {
- return mFile.getRemotePath();
- }
-
- public String getMimeType() {
- return mFile.getMimetype();
- }
-
- public boolean isInstant() {
- return mIsInstant;
- }
-
- public boolean isRemoteFolderToBeCreated() {
- return mRemoteFolderToBeCreated;
- }
-
- public void setRemoteFolderToBeCreated() {
- mRemoteFolderToBeCreated = true;
- }
-
- public boolean getForceOverwrite() {
- return mForceOverwrite;
- }
-
- public boolean wasRenamed() {
- return mWasRenamed;
- }
-
- public Set<OnDatatransferProgressListener> getDataTransferListeners() {
- return mDataTransferListeners;
- }
-
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.add(listener);
- }
- if (mEntity != null) {
- ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListener(listener);
- }
- }
-
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.remove(listener);
- }
- if (mEntity != null) {
- ((ProgressiveDataTransferer)mEntity).removeDatatransferProgressListener(listener);
- }
- }
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- boolean localCopyPassed = false, nameCheckPassed = false;
- File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
- try {
- // / rename the file to upload, if necessary
- if (!mForceOverwrite) {
- String remotePath = getAvailableRemotePath(client, mRemotePath);
- mWasRenamed = !remotePath.equals(mRemotePath);
- if (mWasRenamed) {
- createNewOCFile(remotePath);
- }
- }
- nameCheckPassed = true;
-
- String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); // /
- // not
- // before
- // getAvailableRemotePath()
- // !!!
- expectedFile = new File(expectedPath);
-
- // / check location of local file; if not the expected, copy to a
- // temporal file before upload (if COPY is the expected behaviour)
- if (!mOriginalStoragePath.equals(expectedPath) && mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
-
- if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
- result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
- return result; // error condition when the file should be
- // copied
-
- } else {
- String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
- mFile.setStoragePath(temporalPath);
- temporalFile = new File(temporalPath);
- if (!mOriginalStoragePath.equals(temporalPath)) { // preventing
- // weird
- // but
- // possible
- // situation
- InputStream in = null;
- OutputStream out = null;
- try {
- File temporalParent = temporalFile.getParentFile();
- temporalParent.mkdirs();
- if (!temporalParent.isDirectory()) {
- throw new IOException("Unexpected error: parent directory could not be created");
- }
- temporalFile.createNewFile();
- if (!temporalFile.isFile()) {
- throw new IOException("Unexpected error: target file could not be created");
- }
- in = new FileInputStream(originalFile);
- out = new FileOutputStream(temporalFile);
- byte[] buf = new byte[1024];
- int len;
- while ((len = in.read(buf)) > 0) {
- out.write(buf, 0, len);
- }
-
- } catch (Exception e) {
- result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
- return result;
-
- } finally {
- try {
- if (in != null)
- in.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", e);
- }
- try {
- if (out != null)
- out.close();
- } catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing output stream for " + expectedPath + " (ignoring)", e);
- }
- }
- }
- }
- }
- localCopyPassed = true;
-
- // / perform the upload
- synchronized (mCancellationRequested) {
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- } else {
- mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
- }
- }
- int status = uploadFile(client);
-
- // / move local temporal file or original file to its corresponding
- // location in the ownCloud local folder
- if (isSuccess(status)) {
- if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
- mFile.setStoragePath(null);
-
- } else {
- mFile.setStoragePath(expectedPath);
- File fileToMove = null;
- if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY
- // ; see where temporalFile was
- // set
- fileToMove = temporalFile;
- } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE
- fileToMove = originalFile;
- }
- if (!expectedFile.equals(fileToMove)) {
- File expectedFolder = expectedFile.getParentFile();
- expectedFolder.mkdirs();
- if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) {
- mFile.setStoragePath(null); // forget the local file
- // by now, treat this as a success; the file was
- // uploaded; the user won't like that the local file
- // is not linked, but this should be a very rare
- // fail;
- // the best option could be show a warning message
- // (but not a fail)
- // result = new
- // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED);
- // return result;
- }
- }
- }
- }
-
- result = new RemoteOperationResult(isSuccess(status), status, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null));
-
- } catch (Exception e) {
- // TODO something cleaner with cancellations
- if (mCancellationRequested.get()) {
- result = new RemoteOperationResult(new OperationCancelledException());
- } else {
- result = new RemoteOperationResult(e);
- }
-
- } finally {
- if (temporalFile != null && !originalFile.equals(temporalFile)) {
- temporalFile.delete();
- }
- if (result.isSuccess()) {
- Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
- } else {
- if (result.getException() != null) {
- String complement = "";
- if (!nameCheckPassed) {
- complement = " (while checking file existence in server)";
- } else if (!localCopyPassed) {
- complement = " (while copying local file to " + FileStorageUtils.getSavePath(mAccount.name)
- + ")";
- }
- Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
- } else {
- Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
- }
- }
- }
-
- return result;
- }
-
- private void createNewOCFile(String newRemotePath) {
- // a new OCFile instance must be created for a new remote path
- OCFile newFile = new OCFile(newRemotePath);
- newFile.setCreationTimestamp(mFile.getCreationTimestamp());
- newFile.setFileLength(mFile.getFileLength());
- newFile.setMimetype(mFile.getMimetype());
- newFile.setModificationTimestamp(mFile.getModificationTimestamp());
- newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
- // newFile.setEtag(mFile.getEtag())
- newFile.setKeepInSync(mFile.keepInSync());
- newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
- newFile.setLastSyncDateForData(mFile.getLastSyncDateForData());
- newFile.setStoragePath(mFile.getStoragePath());
- newFile.setParentId(mFile.getParentId());
- mOldFile = mFile;
- mFile = newFile;
- }
-
- public boolean isSuccess(int status) {
- return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
- }
-
- protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
- int status = -1;
- try {
- File f = new File(mFile.getStoragePath());
- mEntity = new FileRequestEntity(f, getMimeType());
- synchronized (mDataTransferListeners) {
- ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners);
- }
- mPutMethod.setRequestEntity(mEntity);
- status = client.executeMethod(mPutMethod);
- client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-
- } finally {
- mPutMethod.releaseConnection(); // let the connection available for
- // other methods
- }
- return status;
- }
-
- /**
- * Checks if remotePath does not exist in the server and returns it, or adds
- * a suffix to it in order to avoid the server file is overwritten.
- *
- * @param string
- * @return
- */
- private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
- boolean check = wc.existsFile(remotePath);
- if (!check) {
- return remotePath;
- }
-
- int pos = remotePath.lastIndexOf(".");
- String suffix = "";
- String extension = "";
- if (pos >= 0) {
- extension = remotePath.substring(pos + 1);
- remotePath = remotePath.substring(0, pos);
- }
- int count = 2;
- do {
- suffix = " (" + count + ")";
- if (pos >= 0)
- check = wc.existsFile(remotePath + suffix + "." + extension);
- else
- check = wc.existsFile(remotePath + suffix);
- count++;
- } while (check);
-
- if (pos >= 0) {
- return remotePath + suffix + "." + extension;
- } else {
- return remotePath + suffix;
- }
- }
-
- public void cancel() {
- synchronized (mCancellationRequested) {
- mCancellationRequested.set(true);
- if (mPutMethod != null)
- mPutMethod.abort();
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.providers;
-
-import java.util.HashMap;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta;
-import de.mobilcom.debitel.cloud.android.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;
-
-/**
- * The ContentProvider for the ownCloud App.
- *
- * @author Bartek Przybylski
- *
- */
-public class FileContentProvider 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_MODIFIED_AT_LAST_SYNC_FOR_DATA,
- ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);
- 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);
- mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
- ProviderTableMeta.FILE_LAST_SYNC_DATE);
- mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
- ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);
- mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
- ProviderTableMeta.FILE_KEEP_IN_SYNC);
- mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
- ProviderTableMeta.FILE_ACCOUNT_OWNER);
- }
-
- private static final int SINGLE_FILE = 1;
- private static final int DIRECTORY = 2;
- private static final int ROOT_DIRECTORY = 3;
-
- private UriMatcher mUriMatcher;
-// static {
-// mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, null, ROOT_DIRECTORY);
-// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/", SINGLE_FILE);
-// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "file/#", SINGLE_FILE);
-// mUriMatcher.addURI(ProviderMeta.AUTHORITY_FILES, "dir/#", DIRECTORY);
-// }
-
-
- @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 &&
- mUriMatcher.match(uri) != ROOT_DIRECTORY) {
-
- throw new IllegalArgumentException("Unknown uri id: " + uri);
- }
-
- SQLiteDatabase db = mDbHelper.getWritableDatabase();
- long rowId = db.insert(ProviderTableMeta.DB_NAME, null, 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());
-
- String authority = getContext().getResources().getString(R.string.authority);
- mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mUriMatcher.addURI(authority, null, ROOT_DIRECTORY);
- mUriMatcher.addURI(authority, "file/", SINGLE_FILE);
- mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
- mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
-
- 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:
- 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();
- // DB case_sensitive
- db.execSQL("PRAGMA case_sensitive_like = true");
- 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) {
- // files table
- Log_OC.i("SQL", "Entering in onCreate");
- 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, "
- + ProviderTableMeta.FILE_LAST_SYNC_DATE + " INTEGER, "
- + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
- + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
- + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER );"
- );
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log_OC.i("SQL", "Entering in onUpgrade");
- boolean upgraded = false;
- if (oldVersion == 1 && newVersion >= 2) {
- Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
- db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
- " ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
- " DEFAULT 0");
- upgraded = true;
- }
- if (oldVersion < 3 && newVersion >= 3) {
- Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
- db.beginTransaction();
- try {
- db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
- " ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " +
- " DEFAULT 0");
-
- // assume there are not local changes pending to upload
- db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
- " SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() +
- " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
-
- upgraded = true;
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
- if (oldVersion < 4 && newVersion >= 4) {
- Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
- db.beginTransaction();
- try {
- db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
- " ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER " +
- " DEFAULT 0");
-
- db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
- " SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED +
- " WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
-
- upgraded = true;
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
- if (!upgraded)
- Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
- }
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android.syncadapter;\r
-\r
-import java.io.IOException;\r
-import java.util.Date;\r
-\r
-import org.apache.http.HttpRequest;\r
-import org.apache.http.HttpResponse;\r
-import org.apache.http.client.ClientProtocolException;\r
-import org.apache.http.conn.ConnectionKeepAliveStrategy;\r
-import org.apache.http.protocol.HttpContext;\r
-\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;\r
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;\r
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.accounts.AuthenticatorException;\r
-import android.accounts.OperationCanceledException;\r
-import android.content.AbstractThreadedSyncAdapter;\r
-import android.content.ContentProviderClient;\r
-import android.content.Context;\r
-import eu.alefzero.webdav.WebdavClient;\r
-\r
-/**\r
- * Base SyncAdapter for OwnCloud Designed to be subclassed for the concrete\r
- * SyncAdapter, like ConcatsSync, CalendarSync, FileSync etc..\r
- * \r
- * @author sassman\r
- * \r
- */\r
-public abstract class AbstractOwnCloudSyncAdapter extends\r
- AbstractThreadedSyncAdapter {\r
-\r
- private AccountManager accountManager;\r
- private Account account;\r
- private ContentProviderClient contentProvider;\r
- private Date lastUpdated;\r
- private DataStorageManager mStoreManager;\r
-\r
- private WebdavClient mClient = null;\r
-\r
- public AbstractOwnCloudSyncAdapter(Context context, boolean autoInitialize) {\r
- super(context, autoInitialize);\r
- this.setAccountManager(AccountManager.get(context));\r
- }\r
-\r
- public AccountManager getAccountManager() {\r
- return accountManager;\r
- }\r
-\r
- public void setAccountManager(AccountManager accountManager) {\r
- this.accountManager = accountManager;\r
- }\r
-\r
- public Account getAccount() {\r
- return account;\r
- }\r
-\r
- public void setAccount(Account account) {\r
- this.account = account;\r
- }\r
-\r
- public ContentProviderClient getContentProvider() {\r
- return contentProvider;\r
- }\r
-\r
- public void setContentProvider(ContentProviderClient contentProvider) {\r
- this.contentProvider = contentProvider;\r
- }\r
-\r
- public Date getLastUpdated() {\r
- return lastUpdated;\r
- }\r
-\r
- public void setLastUpdated(Date lastUpdated) {\r
- this.lastUpdated = lastUpdated;\r
- }\r
-\r
- public void setStorageManager(DataStorageManager storage_manager) {\r
- mStoreManager = storage_manager;\r
- }\r
-\r
- public DataStorageManager getStorageManager() {\r
- return mStoreManager;\r
- }\r
-\r
- protected ConnectionKeepAliveStrategy getKeepAliveStrategy() {\r
- return new ConnectionKeepAliveStrategy() {\r
- public long getKeepAliveDuration(HttpResponse response,\r
- HttpContext context) {\r
- // Change keep alive straategy basing on response: ie\r
- // forbidden/not found/etc\r
- // should have keep alive 0\r
- // default return: 5s\r
- int statusCode = response.getStatusLine().getStatusCode();\r
-\r
- // HTTP 400, 500 Errors as well as HTTP 118 - Connection timed\r
- // out\r
- if ((statusCode >= 400 && statusCode <= 418)\r
- || (statusCode >= 421 && statusCode <= 426)\r
- || (statusCode >= 500 && statusCode <= 510)\r
- || statusCode == 118) {\r
- return 0;\r
- }\r
-\r
- return 5 * 1000;\r
- }\r
- };\r
- }\r
-\r
- protected HttpResponse fireRawRequest(HttpRequest query)\r
- throws ClientProtocolException, OperationCanceledException,\r
- AuthenticatorException, IOException {\r
- /*\r
- * BasicHttpContext httpContext = new BasicHttpContext(); BasicScheme\r
- * basicAuth = new BasicScheme();\r
- * httpContext.setAttribute("preemptive-auth", basicAuth);\r
- * \r
- * HttpResponse response = getClient().execute(mHost, query,\r
- * httpContext);\r
- */\r
- return null;\r
- }\r
-\r
- protected void initClientForCurrentAccount() throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {\r
- AccountUtils.constructFullURLForAccount(getContext(), account);\r
- mClient = OwnCloudClientUtils.createOwnCloudClient(account, getContext());\r
- }\r
- \r
- protected WebdavClient getClient() {\r
- return mClient;\r
- }\r
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.syncadapter;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.entity.ByteArrayEntity;
-
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.content.ContentProviderClient;
-import android.content.Context;
-import android.content.SyncResult;
-import android.content.res.AssetFileDescriptor;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-
-public class ContactSyncAdapter extends AbstractOwnCloudSyncAdapter {
- private String mAddrBookUri;
-
- public ContactSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- mAddrBookUri = null;
- }
-
- @Override
- public void onPerformSync(Account account, Bundle extras, String authority,
- ContentProviderClient provider, SyncResult syncResult) {
- setAccount(account);
- setContentProvider(provider);
- Cursor c = getLocalContacts(false);
- if (c.moveToFirst()) {
- do {
- String lookup = c.getString(c
- .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
- String a = getAddressBookUri();
- String uri = a + lookup + ".vcf";
- FileInputStream f;
- try {
- f = getContactVcard(lookup);
- HttpPut query = new HttpPut(uri);
- byte[] b = new byte[f.available()];
- f.read(b);
- query.setEntity(new ByteArrayEntity(b));
- fireRawRequest(query);
- } catch (IOException e) {
- e.printStackTrace();
- return;
- } catch (OperationCanceledException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (AuthenticatorException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- } while (c.moveToNext());
- // } while (c.moveToNext());
- }
-
- }
-
- private String getAddressBookUri() {
- if (mAddrBookUri != null)
- return mAddrBookUri;
-
- AccountManager am = getAccountManager();
- @SuppressWarnings("deprecation")
- String uri = am.getUserData(getAccount(),
- AccountAuthenticator.KEY_OC_URL).replace(
- AccountUtils.WEBDAV_PATH_2_0, AccountUtils.CARDDAV_PATH_2_0);
- uri += "/addressbooks/"
- + getAccount().name.substring(0,
- getAccount().name.lastIndexOf('@')) + "/default/";
- mAddrBookUri = uri;
- return uri;
- }
-
- private FileInputStream getContactVcard(String lookupKey)
- throws IOException {
- Uri uri = Uri.withAppendedPath(
- ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
- AssetFileDescriptor fd = getContext().getContentResolver()
- .openAssetFileDescriptor(uri, "r");
- return fd.createInputStream();
- }
-
- private Cursor getLocalContacts(boolean include_hidden_contacts) {
- return getContext().getContentResolver().query(
- ContactsContract.Contacts.CONTENT_URI,
- new String[] { ContactsContract.Contacts._ID,
- ContactsContract.Contacts.LOOKUP_KEY },
- ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?",
- new String[] { (include_hidden_contacts ? "0" : "1") },
- ContactsContract.Contacts._ID + " DESC");
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.syncadapter;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-
-public class ContactSyncService extends Service {
- private static final Object syncAdapterLock = new Object();
- private static AbstractOwnCloudSyncAdapter mSyncAdapter = null;
-
- @Override
- public void onCreate() {
- synchronized (syncAdapterLock) {
- if (mSyncAdapter == null) {
- mSyncAdapter = new ContactSyncAdapter(getApplicationContext(),
- true);
- }
- }
- }
-
- @Override
- public IBinder onBind(Intent arg0) {
- return mSyncAdapter.getSyncAdapterBinder();
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.syncadapter;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.jackrabbit.webdav.DavException;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.UpdateOCVersionOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
-
-import android.accounts.Account;
-import android.accounts.AccountsException;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SyncResult;
-import android.os.Bundle;
-
-/**
- * SyncAdapter implementation for syncing sample SyncAdapter contacts to the
- * platform ContactOperations provider.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
-
- private final static String TAG = "FileSyncAdapter";
-
- /**
- * Maximum number of failed folder synchronizations that are supported before finishing the synchronization operation
- */
- private static final int MAX_FAILED_RESULTS = 3;
-
- private long mCurrentSyncTime;
- private boolean mCancellation;
- private boolean mIsManualSync;
- private int mFailedResultsCounter;
- private RemoteOperationResult mLastFailedResult;
- private SyncResult mSyncResult;
- private int mConflictsFound;
- private int mFailsInFavouritesFound;
- private Map<String, String> mForgottenLocalFiles;
-
-
- public FileSyncAdapter(Context context, boolean autoInitialize) {
- super(context, autoInitialize);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized void onPerformSync(Account account, Bundle extras,
- String authority, ContentProviderClient provider,
- SyncResult syncResult) {
-
- mCancellation = false;
- mIsManualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
- mFailedResultsCounter = 0;
- mLastFailedResult = null;
- mConflictsFound = 0;
- mFailsInFavouritesFound = 0;
- mForgottenLocalFiles = new HashMap<String, String>();
- mSyncResult = syncResult;
- mSyncResult.fullSyncRequested = false;
- mSyncResult.delayUntil = 60*60*24; // sync after 24h
-
- this.setAccount(account);
- this.setContentProvider(provider);
- this.setStorageManager(new FileDataStorageManager(account, getContentProvider()));
- try {
- this.initClientForCurrentAccount();
- } catch (IOException e) {
- /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
- mSyncResult.tooManyRetries = true;
- notifyFailedSynchronization();
- return;
- } catch (AccountsException e) {
- /// the account is unknown for the Synchronization Manager, or unreachable for this context; don't try this again
- mSyncResult.tooManyRetries = true;
- notifyFailedSynchronization();
- return;
- }
-
- Log_OC.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");
- sendStickyBroadcast(true, null, null); // message to signal the start of the synchronization to the UI
-
- try {
- updateOCVersion();
- mCurrentSyncTime = System.currentTimeMillis();
- if (!mCancellation) {
- fetchData(OCFile.PATH_SEPARATOR, DataStorageManager.ROOT_PARENT_ID);
-
- } else {
- Log_OC.d(TAG, "Leaving synchronization before any remote request due to cancellation was requested");
- }
-
-
- } finally {
- // it's important making this although very unexpected errors occur; that's the reason for the finally
-
- if (mFailedResultsCounter > 0 && mIsManualSync) {
- /// don't let the system synchronization manager retries MANUAL synchronizations
- // (be careful: "MANUAL" currently includes the synchronization requested when a new account is created and when the user changes the current account)
- mSyncResult.tooManyRetries = true;
-
- /// notify the user about the failure of MANUAL synchronization
- notifyFailedSynchronization();
-
- }
- if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
- notifyFailsInFavourites();
- }
- if (mForgottenLocalFiles.size() > 0) {
- notifyForgottenLocalFiles();
-
- }
- sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI
- }
-
- }
-
- /**
- * Called by system SyncManager when a synchronization is required to be cancelled.
- *
- * Sets the mCancellation flag to 'true'. THe synchronization will be stopped when before a new folder is fetched. Data of the last folder
- * fetched will be still saved in the database. See onPerformSync implementation.
- */
- @Override
- public void onSyncCanceled() {
- Log_OC.d(TAG, "Synchronization of " + getAccount().name + " has been requested to cancel");
- mCancellation = true;
- super.onSyncCanceled();
- }
-
-
- /**
- * Updates the locally stored version value of the ownCloud server
- */
- private void updateOCVersion() {
- UpdateOCVersionOperation update = new UpdateOCVersionOperation(getAccount(), getContext());
- RemoteOperationResult result = update.execute(getClient());
- if (!result.isSuccess()) {
- mLastFailedResult = result;
- }
- }
-
-
- /**
- * Synchronize the properties of files and folders contained in a remote folder given by remotePath.
- *
- * @param remotePath Remote path to the folder to synchronize.
- * @param parentId Database Id of the folder to synchronize.
- */
- private void fetchData(String remotePath, long parentId) {
-
- if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult))
- return;
-
- // perform folder synchronization
- SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( remotePath,
- mCurrentSyncTime,
- parentId,
- getStorageManager(),
- getAccount(),
- getContext()
- );
- RemoteOperationResult result = synchFolderOp.execute(getClient());
-
-
- // synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
- sendStickyBroadcast(true, remotePath, null);
-
- if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
-
- if (result.getCode() == ResultCode.SYNC_CONFLICT) {
- mConflictsFound += synchFolderOp.getConflictsFound();
- mFailsInFavouritesFound += synchFolderOp.getFailsInFavouritesFound();
- }
- if (synchFolderOp.getForgottenLocalFiles().size() > 0) {
- mForgottenLocalFiles.putAll(synchFolderOp.getForgottenLocalFiles());
- }
- // synchronize children folders
- List<OCFile> children = synchFolderOp.getChildren();
- fetchChildren(children); // beware of the 'hidden' recursion here!
-
- sendStickyBroadcast(true, remotePath, null);
-
- } else {
- if (result.getCode() == RemoteOperationResult.ResultCode.UNAUTHORIZED ||
- // (result.isTemporalRedirection() && result.isIdPRedirection() &&
- ( result.isIdPRedirection() &&
- MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))) {
- mSyncResult.stats.numAuthExceptions++;
-
- } else if (result.getException() instanceof DavException) {
- mSyncResult.stats.numParseExceptions++;
-
- } else if (result.getException() instanceof IOException) {
- mSyncResult.stats.numIoExceptions++;
- }
- mFailedResultsCounter++;
- mLastFailedResult = result;
- }
-
- }
-
- /**
- * Checks if a failed result should terminate the synchronization process immediately, according to
- * OUR OWN POLICY
- *
- * @param failedResult Remote operation result to check.
- * @return 'True' if the result should immediately finish the synchronization
- */
- private boolean isFinisher(RemoteOperationResult failedResult) {
- if (failedResult != null) {
- RemoteOperationResult.ResultCode code = failedResult.getCode();
- return (code.equals(RemoteOperationResult.ResultCode.SSL_ERROR) ||
- code.equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) ||
- code.equals(RemoteOperationResult.ResultCode.BAD_OC_VERSION) ||
- code.equals(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED));
- }
- return false;
- }
-
- /**
- * Synchronize data of folders in the list of received files
- *
- * @param files Files to recursively fetch
- */
- private void fetchChildren(List<OCFile> files) {
- int i;
- for (i=0; i < files.size() && !mCancellation; i++) {
- OCFile newFile = files.get(i);
- if (newFile.isDirectory()) {
- fetchData(newFile.getRemotePath(), newFile.getFileId());
-
- // Update folder size on DB
- getStorageManager().calculateFolderSize(newFile.getFileId());
- }
- }
-
- if (mCancellation && i <files.size()) Log_OC.d(TAG, "Leaving synchronization before synchronizing " + files.get(i).getRemotePath() + " because cancelation request");
- }
-
-
- /**
- * Sends a message to any application component interested in the progress of the synchronization.
- *
- * @param inProgress 'True' when the synchronization progress is not finished.
- * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
- */
- private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
- FileSyncService fileSyncService = new FileSyncService();
-
- Intent i = new Intent(fileSyncService.getSyncMessage());
- i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
- i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
- if (dirRemotePath != null) {
- i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
- }
- if (result != null) {
- i.putExtra(FileSyncService.SYNC_RESULT, result);
- }
- getContext().sendStickyBroadcast(i);
- }
-
-
-
- /**
- * Notifies the user about a failed synchronization through the status notification bar
- */
- private void notifyFailedSynchronization() {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- boolean needsToUpdateCredentials = (mLastFailedResult != null &&
- ( mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
- // (mLastFailedResult.isTemporalRedirection() && mLastFailedResult.isIdPRedirection() &&
- ( mLastFailedResult.isIdPRedirection() &&
- MainApp.getAuthTokenTypeSamlSessionCookie().equals(getClient().getAuthTokenType()))
- )
- );
- // TODO put something smart in the contentIntent below for all the possible errors
- notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
- if (needsToUpdateCredentials) {
- // let the user update credentials with one click
- Intent updateAccountCredentials = new Intent(getContext(), AuthenticatorActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, getAccount());
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
- notification.contentIntent = PendingIntent.getActivity(getContext(), (int)System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT);
- notification.setLatestEventInfo(getContext().getApplicationContext(),
- getContext().getString(R.string.sync_fail_ticker),
- String.format(getContext().getString(R.string.sync_fail_content_unauthorized), getAccount().name),
- notification.contentIntent);
- } else {
- notification.setLatestEventInfo(getContext().getApplicationContext(),
- getContext().getString(R.string.sync_fail_ticker),
- String.format(getContext().getString(R.string.sync_fail_content), getAccount().name),
- notification.contentIntent);
- }
- ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_ticker, notification);
- }
-
-
- /**
- * Notifies the user about conflicts and strange fails when trying to synchronize the contents of kept-in-sync files.
- *
- * By now, we won't consider a failed synchronization.
- */
- private void notifyFailsInFavourites() {
- if (mFailedResultsCounter > 0) {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- // TODO put something smart in the contentIntent below
- notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
- notification.setLatestEventInfo(getContext().getApplicationContext(),
- getContext().getString(R.string.sync_fail_in_favourites_ticker),
- String.format(getContext().getString(R.string.sync_fail_in_favourites_content), mFailedResultsCounter + mConflictsFound, mConflictsFound),
- notification.contentIntent);
- ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
-
- } else {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- // TODO put something smart in the contentIntent below
- notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
- notification.setLatestEventInfo(getContext().getApplicationContext(),
- getContext().getString(R.string.sync_conflicts_in_favourites_ticker),
- String.format(getContext().getString(R.string.sync_conflicts_in_favourites_content), mConflictsFound),
- notification.contentIntent);
- ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_conflicts_in_favourites_ticker, notification);
- }
- }
-
- /**
- * Notifies the user about local copies of files out of the ownCloud local directory that were 'forgotten' because
- * copying them inside the ownCloud local directory was not possible.
- *
- * We don't want links to files out of the ownCloud local directory (foreign files) anymore. It's easy to have
- * synchronization problems if a local file is linked to more than one remote file.
- *
- * We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
- */
- private void notifyForgottenLocalFiles() {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
-
- /// includes a pending intent in the notification showing a more detailed explanation
- Intent explanationIntent = new Intent(getContext(), ErrorsWhileCopyingHandlerActivity.class);
- explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_ACCOUNT, getAccount());
- ArrayList<String> remotePaths = new ArrayList<String>();
- ArrayList<String> localPaths = new ArrayList<String>();
- remotePaths.addAll(mForgottenLocalFiles.keySet());
- localPaths.addAll(mForgottenLocalFiles.values());
- explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_LOCAL_PATHS, localPaths);
- explanationIntent.putExtra(ErrorsWhileCopyingHandlerActivity.EXTRA_REMOTE_PATHS, remotePaths);
- explanationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
- notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), explanationIntent, 0);
- notification.setLatestEventInfo(getContext().getApplicationContext(),
- getContext().getString(R.string.sync_foreign_files_forgotten_ticker),
- String.format(getContext().getString(R.string.sync_foreign_files_forgotten_content), mForgottenLocalFiles.size(), getContext().getString(R.string.app_name)),
- notification.contentIntent);
- ((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_foreign_files_forgotten_ticker, notification);
-
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.syncadapter;\r
-\r
-import android.app.Service;\r
-import android.content.Intent;\r
-import android.os.IBinder;\r
-\r
-/**\r
- * Background service for syncing files to our local Database\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileSyncService extends Service {\r
- public static final String SYNC_MESSAGE = "ACCOUNT_SYNC";\r
- public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";\r
- public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";\r
- public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
- public static final String SYNC_RESULT = "SYNC_RESULT";\r
-\r
- public String getSyncMessage(){\r
- return getClass().getName().toString() + SYNC_MESSAGE;\r
- }\r
- /*\r
- * {@inheritDoc}\r
- */\r
- @Override\r
- public void onCreate() {\r
- }\r
-\r
- /*\r
- * {@inheritDoc}\r
- */\r
- @Override\r
- public IBinder onBind(Intent intent) {\r
- return new FileSyncAdapter(getApplicationContext(), true).getSyncAdapterBinder();\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui;\r
-\r
-import android.graphics.drawable.Drawable;\r
-import android.view.View.OnClickListener;\r
-\r
-/**\r
- * Represents an Item on the ActionBar.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class ActionItem {\r
- private Drawable mIcon;\r
- private String mTitle;\r
- private OnClickListener mClickListener;\r
-\r
- public ActionItem() {\r
- }\r
-\r
- public void setTitle(String title) {\r
- mTitle = title;\r
- }\r
-\r
- public String getTitle() {\r
- return mTitle;\r
- }\r
-\r
- public void setIcon(Drawable icon) {\r
- mIcon = icon;\r
- }\r
-\r
- public Drawable getIcon() {\r
- return mIcon;\r
- }\r
-\r
- public void setOnClickListener(OnClickListener listener) {\r
- mClickListener = listener;\r
- }\r
-\r
- public OnClickListener getOnClickListerner() {\r
- return mClickListener;\r
- }\r
-\r
-}\r
+++ /dev/null
-package de.mobilcom.debitel.cloud.android.ui;
-
-import de.mobilcom.debitel.cloud.android.R;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Button;
-/**
- * @author masensio
- *
- * Button for customizing the button background
- */
-
-public class CustomButton extends Button {
-
- public CustomButton(Context context) {
- super(context);
-
- boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
- if (customButtons)
- {
- this.setBackgroundResource(R.drawable.btn_default);
- }
- }
-
- public CustomButton(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
- if (customButtons)
- {
- this.setBackgroundResource(R.drawable.btn_default);
- }
- }
-
- public CustomButton(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- boolean customButtons = getResources().getBoolean(R.bool.custom_buttons);
- if (customButtons)
- {
- this.setBackgroundResource(R.drawable.btn_default);
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui;\r
-\r
-import android.content.Context;\r
-import android.graphics.Rect;\r
-import android.graphics.drawable.BitmapDrawable;\r
-import android.graphics.drawable.Drawable;\r
-import android.view.Gravity;\r
-import android.view.LayoutInflater;\r
-import android.view.MotionEvent;\r
-import android.view.View;\r
-import android.view.WindowManager;\r
-import android.view.View.OnTouchListener;\r
-import android.view.ViewGroup.LayoutParams;\r
-import android.widget.PopupWindow;\r
-\r
-/**\r
- * Represents a custom PopupWindows\r
- * \r
- * @author Lorensius. W. T\r
- * \r
- */\r
-public class CustomPopup {\r
- protected final View mAnchor;\r
- protected final PopupWindow mWindow;\r
- private View root;\r
- private Drawable background = null;\r
- protected final WindowManager mWManager;\r
-\r
- public CustomPopup(View anchor) {\r
- mAnchor = anchor;\r
- mWindow = new PopupWindow(anchor.getContext());\r
-\r
- mWindow.setTouchInterceptor(new OnTouchListener() {\r
-\r
- public boolean onTouch(View v, MotionEvent event) {\r
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {\r
- CustomPopup.this.dismiss();\r
- return true;\r
- }\r
- return false;\r
- }\r
- });\r
-\r
- mWManager = (WindowManager) anchor.getContext().getSystemService(\r
- Context.WINDOW_SERVICE);\r
- onCreate();\r
- }\r
-\r
- public void onCreate() {\r
- }\r
-\r
- public void onShow() {\r
- }\r
-\r
- public void preShow() {\r
- if (root == null) {\r
- throw new IllegalStateException(\r
- "setContentView called with a view to display");\r
- }\r
-\r
- onShow();\r
-\r
- if (background == null) {\r
- mWindow.setBackgroundDrawable(new BitmapDrawable());\r
- } else {\r
- mWindow.setBackgroundDrawable(background);\r
- }\r
-\r
- mWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);\r
- mWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);\r
- mWindow.setTouchable(true);\r
- mWindow.setFocusable(true);\r
- mWindow.setOutsideTouchable(true);\r
-\r
- mWindow.setContentView(root);\r
- }\r
-\r
- public void setBackgroundDrawable(Drawable background) {\r
- this.background = background;\r
- }\r
-\r
- public void setContentView(View root) {\r
- this.root = root;\r
- mWindow.setContentView(root);\r
- }\r
-\r
- public void setContentView(int layoutResId) {\r
- LayoutInflater inflater = (LayoutInflater) mAnchor.getContext()\r
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
- setContentView(inflater.inflate(layoutResId, null));\r
- }\r
-\r
- public void showDropDown() {\r
- showDropDown(0, 0);\r
- }\r
-\r
- public void showDropDown(int x, int y) {\r
- preShow();\r
- mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
- mWindow.showAsDropDown(mAnchor, x, y);\r
- }\r
-\r
- public void showLikeQuickAction() {\r
- showLikeQuickAction(0, 0);\r
- }\r
-\r
- public void showLikeQuickAction(int x, int y) {\r
- preShow();\r
-\r
- mWindow.setAnimationStyle(android.R.style.Animation_Dialog);\r
- int[] location = new int[2];\r
- mAnchor.getLocationOnScreen(location);\r
-\r
- Rect anchorRect = new Rect(location[0], location[1], location[0]\r
- + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
-\r
- root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
- LayoutParams.WRAP_CONTENT));\r
- root.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\r
-\r
- int rootW = root.getWidth(), rootH = root.getHeight();\r
- int screenW = mWManager.getDefaultDisplay().getWidth();\r
-\r
- int xpos = ((screenW - rootW) / 2) + x;\r
- int ypos = anchorRect.top - rootH + y;\r
-\r
- if (rootH > anchorRect.top) {\r
- ypos = anchorRect.bottom + y;\r
- }\r
- mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, xpos, ypos);\r
- }\r
-\r
- public void dismiss() {\r
- mWindow.dismiss();\r
- }\r
-\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.ListView;
-
-/**
- * ListView allowing to specify the position of an item that should be centered in the visible area, if possible.
- *
- * The cleanest way I found to overcome the problem due to getHeight() returns 0 until the view is really drawn.
- *
- * @author David A. Velasco
- */
-public class ExtendedListView extends ListView {
-
- private int mPositionToSetAndCenter;
-
- public ExtendedListView(Context context) {
- super(context);
- }
-
- public ExtendedListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ExtendedListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- /**
- * {@inheritDoc}
- *
- *
- */
- @Override
- protected void onDraw (Canvas canvas) {
- super.onDraw(canvas);
- if (mPositionToSetAndCenter > 0) {
- this.setSelectionFromTop(mPositionToSetAndCenter, getHeight() / 2);
- mPositionToSetAndCenter = 0;
- }
- }
-
- /**
- * Public method to set the position of the item that should be centered in the visible area of the view.
- *
- * The position is saved here and checked in onDraw().
- *
- * @param position Position (in the list of items) of the item to center in the visible area.
- */
- public void setAndCenterSelection(int position) {
- mPositionToSetAndCenter = position;
- }
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui;\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
-import de.mobilcom.debitel.cloud.android.R;\r
-\r
-/**\r
- * Popup window, shows action list as icon and text like the one in Gallery3D\r
- * 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\r
- .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\r
- * 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]\r
- + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\r
-\r
- createActionList();\r
-\r
- root.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\r
- 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),\r
- 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\r
- * TRUE if displayed on top of anchor view and vice versa\r
- */\r
- private void setAnimationStyle(int screenWidth, int requestedX,\r
- 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
- : R.style.Animations_PopDownMenu_Left);\r
- break;\r
-\r
- case ANIM_GROW_FROM_RIGHT:\r
- mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
- : R.style.Animations_PopDownMenu_Right);\r
- break;\r
-\r
- case ANIM_GROW_FROM_CENTER:\r
- mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
- : R.style.Animations_PopDownMenu_Center);\r
- break;\r
-\r
- case ANIM_REFLECT:\r
- mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Reflect\r
- : 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
- : R.style.Animations_PopDownMenu_Left);\r
- } else if (arrowPos > screenWidth / 4\r
- && arrowPos < 3 * (screenWidth / 4)) {\r
- mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Center\r
- : R.style.Animations_PopDownMenu_Center);\r
- } else {\r
- mWindow.setAnimationStyle((onTop) ? R.style.Animations_PopUpMenu_Right\r
- : 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,\r
- OnClickListener listener) {\r
- LinearLayout container = (LinearLayout) inflater.inflate(\r
- 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\r
- : mArrowDown;\r
- final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown\r
- : mArrowUp;\r
-\r
- final int arrowWidth = mArrowUp.getMeasuredWidth();\r
-\r
- showArrow.setVisibility(View.VISIBLE);\r
-\r
- ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow\r
- .getLayoutParams();\r
-\r
- param.leftMargin = requestedX - arrowWidth / 2;\r
-\r
- hideArrow.setVisibility(View.INVISIBLE);\r
- }\r
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.CheckedTextView;
-import android.widget.ListView;
-import android.widget.SimpleAdapter;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockListActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountAuthenticator;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AuthenticatorActivity;
-
-public class AccountSelectActivity extends SherlockListActivity implements
- AccountManagerCallback<Boolean> {
-
- private static final String TAG = "AccountSelectActivity";
-
- private static final String PREVIOUS_ACCOUNT_KEY = "ACCOUNT";
-
- private final Handler mHandler = new Handler();
- private Account mPreviousAccount = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null) {
- mPreviousAccount = savedInstanceState.getParcelable(PREVIOUS_ACCOUNT_KEY);
- } else {
- mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
- }
-
- ActionBar action_bar = getSupportActionBar();
- action_bar.setDisplayShowTitleEnabled(true);
- action_bar.setDisplayHomeAsUpEnabled(false);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- populateAccountList();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (this.isFinishing()) {
- Account current = AccountUtils.getCurrentOwnCloudAccount(this);
- if ((mPreviousAccount == null && current != null) ||
- (mPreviousAccount != null && !mPreviousAccount.equals(current))) {
- /// the account set as default changed since this activity was created
-
- // trigger synchronization
- ContentResolver.cancelSync(null, MainApp.getAuthTokenType());
- Bundle bundle = new Bundle();
- bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- ContentResolver.requestSync(AccountUtils.getCurrentOwnCloudAccount(this), MainApp.getAuthTokenType(), bundle);
-
- // restart the main activity
- Intent i = new Intent(this, FileDisplayActivity.class);
- i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(i);
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Show Create Account if Multiaccount is enabled
- if (getResources().getBoolean(R.bool.multiaccount_support)) {
- MenuInflater inflater = getSherlock().getMenuInflater();
- inflater.inflate(R.menu.account_picker, menu);
- }
- return true;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- getMenuInflater().inflate(R.menu.account_picker_long_click, menu);
- super.onCreateContextMenu(menu, v, menuInfo);
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- String accountName = ((TextView) v.findViewById(android.R.id.text1))
- .getText().toString();
- AccountUtils.setCurrentOwnCloudAccount(this, accountName);
- finish(); // immediate exit
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- if (item.getItemId() == R.id.createAccount) {
- /*Intent intent = new Intent(
- android.provider.Settings.ACTION_ADD_ACCOUNT);
- intent.putExtra("authorities",
- new String[] { MainApp.getAuthTokenType() });
- startActivity(intent);*/
- AccountManager am = AccountManager.get(getApplicationContext());
- am.addAccount(MainApp.getAccountType(),
- null,
- null,
- null,
- this,
- null,
- null);
- return true;
- }
- return false;
- }
-
- /**
- * Called when the user clicked on an item into the context menu created at
- * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} for every
- * ownCloud {@link Account} , containing 'secondary actions' for them.
- *
- * {@inheritDoc}}
- */
- @SuppressWarnings("unchecked")
- @Override
- public boolean onContextItemSelected(android.view.MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- int index = info.position;
- HashMap<String, String> map = null;
- try {
- map = (HashMap<String, String>) getListAdapter().getItem(index);
- } catch (ClassCastException e) {
- Log_OC.wtf(TAG, "getitem(index) from list adapter did not return hashmap, bailing out");
- return false;
- }
-
- String accountName = map.get("NAME");
- AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
- Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
- for (Account a : accounts) {
- if (a.name.equals(accountName)) {
- if (item.getItemId() == R.id.change_password) {
- Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a);
- updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
- startActivity(updateAccountCredentials);
-
- } else if (item.getItemId() == R.id.delete_account) {
- am.removeAccount(a, this, mHandler);
- }
- }
- }
-
- return true;
- }
-
- private void populateAccountList() {
- AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
- Account accounts[] = am
- .getAccountsByType(MainApp.getAccountType());
- if (am.getAccountsByType(MainApp.getAccountType()).length == 0) {
- // Show create account screen if there isn't any account
- am.addAccount(MainApp.getAccountType(),
- null,
- null,
- null,
- this,
- null,
- null);
- }
- else {
- LinkedList<HashMap<String, String>> ll = new LinkedList<HashMap<String, String>>();
- for (Account a : accounts) {
- HashMap<String, String> h = new HashMap<String, String>();
- h.put("NAME", a.name);
- h.put("VER",
- "ownCloud version: "
- + am.getUserData(a,
- AccountAuthenticator.KEY_OC_VERSION));
- ll.add(h);
- }
-
- setListAdapter(new AccountCheckedSimpleAdepter(this, ll,
- android.R.layout.simple_list_item_single_choice,
- new String[] { "NAME" }, new int[] { android.R.id.text1 }));
- registerForContextMenu(getListView());
- }
- }
-
- @Override
- public void run(AccountManagerFuture<Boolean> future) {
- if (future.isDone()) {
- Account a = AccountUtils.getCurrentOwnCloudAccount(this);
- String accountName = "";
- if (a == null) {
- Account[] accounts = AccountManager.get(this)
- .getAccountsByType(MainApp.getAccountType());
- if (accounts.length != 0)
- accountName = accounts[0].name;
- AccountUtils.setCurrentOwnCloudAccount(this, accountName);
- }
- populateAccountList();
- }
- }
-
- private class AccountCheckedSimpleAdepter extends SimpleAdapter {
- private Account mCurrentAccount;
- private List<? extends Map<String, ?>> mPrivateData;
-
- public AccountCheckedSimpleAdepter(Context context,
- List<? extends Map<String, ?>> data, int resource,
- String[] from, int[] to) {
- super(context, data, resource, from, to);
- mCurrentAccount = AccountUtils
- .getCurrentOwnCloudAccount(AccountSelectActivity.this);
- mPrivateData = data;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = super.getView(position, convertView, parent);
- CheckedTextView ctv = (CheckedTextView) v
- .findViewById(android.R.id.text1);
- if (mPrivateData.get(position).get("NAME")
- .equals(mCurrentAccount.name)) {
- ctv.setChecked(true);
- }
- return v;
- }
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog.Decision;
-import de.mobilcom.debitel.cloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-/**
- * Wrapper activity which will be launched if keep-in-sync file will be modified by external
- * application.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
-
- private String TAG = ConflictsResolveActivity.class.getSimpleName();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public void ConflictDecisionMade(Decision decision) {
- Intent i = new Intent(getApplicationContext(), FileUploader.class);
-
- switch (decision) {
- case CANCEL:
- finish();
- return;
- case OVERWRITE:
- i.putExtra(FileUploader.KEY_FORCE_OVERWRITE, true);
- break;
- case KEEP_BOTH:
- i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
- break;
- default:
- Log_OC.wtf(TAG, "Unhandled conflict decision " + decision);
- return;
- }
- i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
- i.putExtra(FileUploader.KEY_FILE, getFile());
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
-
- startService(i);
- finish();
- }
-
- @Override
- protected void onAccountSet(boolean stateWasRecovered) {
- if (getAccount() != null) {
- OCFile file = getFile();
- if (getFile() == null) {
- Log_OC.e(TAG, "No conflictive file received");
- finish();
- } else {
- /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
- DataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
- file = storageManager.getFileByPath(file.getRemotePath()); // file = null if not in the current Account
- if (file != null) {
- setFile(file);
- ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
- d.showDialog(this);
-
- } else {
- // account was changed to a different one - just finish
- finish();
- }
- }
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
- finish();
- }
-
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import android.accounts.Account;
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.DialogFragment;
-import android.text.method.ScrollingMovementMethod;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.dialog.IndeterminateProgressDialog;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-
-/**
- * Activity reporting errors occurred when local files uploaded to an ownCloud account with an app in
- * version under 1.3.16 where being copied to the ownCloud local folder.
- *
- * Allows the user move the files to the ownCloud local folder. let them unlinked to the remote
- * files.
- *
- * Shown when the error notification summarizing the list of errors is clicked by the user.
- *
- * @author David A. Velasco
- */
-public class ErrorsWhileCopyingHandlerActivity extends SherlockFragmentActivity implements OnClickListener {
-
- private static final String TAG = ErrorsWhileCopyingHandlerActivity.class.getSimpleName();
-
- public static final String EXTRA_ACCOUNT = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_ACCOUNT";
- public static final String EXTRA_LOCAL_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_LOCAL_PATHS";
- public static final String EXTRA_REMOTE_PATHS = ErrorsWhileCopyingHandlerActivity.class.getCanonicalName() + ".EXTRA_REMOTE_PATHS";
-
- private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
-
- protected Account mAccount;
- protected FileDataStorageManager mStorageManager;
- protected ArrayList<String> mLocalPaths;
- protected ArrayList<String> mRemotePaths;
- protected ArrayAdapter<String> mAdapter;
- protected Handler mHandler;
- private DialogFragment mCurrentDialog;
-
- /**
- * {@link}
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- /// read extra parameters in intent
- Intent intent = getIntent();
- mAccount = intent.getParcelableExtra(EXTRA_ACCOUNT);
- mRemotePaths = intent.getStringArrayListExtra(EXTRA_REMOTE_PATHS);
- mLocalPaths = intent.getStringArrayListExtra(EXTRA_LOCAL_PATHS);
- mStorageManager = new FileDataStorageManager(mAccount, getContentResolver());
- mHandler = new Handler();
- if (mCurrentDialog != null) {
- mCurrentDialog.dismiss();
- mCurrentDialog = null;
- }
-
- /// load generic layout
- setContentView(R.layout.generic_explanation);
-
- /// customize text message
- TextView textView = (TextView) findViewById(R.id.message);
- String appName = getString(R.string.app_name);
- String message = String.format(getString(R.string.sync_foreign_files_forgotten_explanation), appName, appName, appName, appName, mAccount.name);
- textView.setText(message);
- textView.setMovementMethod(new ScrollingMovementMethod());
-
- /// load the list of local and remote files that failed
- ListView listView = (ListView) findViewById(R.id.list);
- if (mLocalPaths != null && mLocalPaths.size() > 0) {
- mAdapter = new ErrorsWhileCopyingListAdapter();
- listView.setAdapter(mAdapter);
- } else {
- listView.setVisibility(View.GONE);
- mAdapter = null;
- }
-
- /// customize buttons
- CustomButton cancelBtn = (CustomButton) findViewById(R.id.cancel);
- CustomButton okBtn = (CustomButton) findViewById(R.id.ok);
-
- okBtn.setText(R.string.foreign_files_move);
- cancelBtn.setOnClickListener(this);
- okBtn.setOnClickListener(this);
- }
-
-
- /**
- * Customized adapter, showing the local files as main text in two-lines list item and the remote files
- * as the secondary text.
- *
- * @author David A. Velasco
- */
- public class ErrorsWhileCopyingListAdapter extends ArrayAdapter<String> {
-
- ErrorsWhileCopyingListAdapter() {
- super(ErrorsWhileCopyingHandlerActivity.this, android.R.layout.two_line_list_item, android.R.id.text1, mLocalPaths);
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View getView (int position, View convertView, ViewGroup parent) {
- View view = convertView;
- if (view == null) {
- LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = vi.inflate(android.R.layout.two_line_list_item, null);
- }
- if (view != null) {
- String localPath = getItem(position);
- if (localPath != null) {
- TextView text1 = (TextView) view.findViewById(android.R.id.text1);
- if (text1 != null) {
- text1.setText(String.format(getString(R.string.foreign_files_local_text), localPath));
- }
- }
- if (mRemotePaths != null && mRemotePaths.size() > 0 && position >= 0 && position < mRemotePaths.size()) {
- TextView text2 = (TextView) view.findViewById(android.R.id.text2);
- String remotePath = mRemotePaths.get(position);
- if (text2 != null && remotePath != null) {
- text2.setText(String.format(getString(R.string.foreign_files_remote_text), remotePath));
- }
- }
- }
- return view;
- }
- }
-
-
- /**
- * Listener method to perform the MOVE / CANCEL action available in this activity.
- *
- * @param v Clicked view (button MOVE or CANCEL)
- */
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.ok) {
- /// perform movement operation in background thread
- Log_OC.d(TAG, "Clicked MOVE, start movement");
- new MoveFilesTask().execute();
-
- } else if (v.getId() == R.id.cancel) {
- /// just finish
- Log_OC.d(TAG, "Clicked CANCEL, bye");
- finish();
-
- } else {
- Log_OC.e(TAG, "Clicked phantom button, id: " + v.getId());
- }
- }
-
-
- /**
- * Asynchronous task performing the move of all the local files to the ownCloud folder.
- *
- * @author David A. Velasco
- */
- private class MoveFilesTask extends AsyncTask<Void, Void, Boolean> {
-
- /**
- * Updates the UI before trying the movement
- */
- @Override
- protected void onPreExecute () {
- /// progress dialog and disable 'Move' button
- mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
- mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
- findViewById(R.id.ok).setEnabled(false);
- }
-
-
- /**
- * Performs the movement
- *
- * @return 'False' when the movement of any file fails.
- */
- @Override
- protected Boolean doInBackground(Void... params) {
- while (!mLocalPaths.isEmpty()) {
- String currentPath = mLocalPaths.get(0);
- File currentFile = new File(currentPath);
- String expectedPath = FileStorageUtils.getSavePath(mAccount.name) + mRemotePaths.get(0);
- File expectedFile = new File(expectedPath);
-
- if (expectedFile.equals(currentFile) || currentFile.renameTo(expectedFile)) {
- // SUCCESS
- OCFile file = mStorageManager.getFileByPath(mRemotePaths.get(0));
- file.setStoragePath(expectedPath);
- mStorageManager.saveFile(file);
- mRemotePaths.remove(0);
- mLocalPaths.remove(0);
-
- } else {
- // FAIL
- return false;
- }
- }
- return true;
- }
-
- /**
- * Updates the activity UI after the movement of local files is tried.
- *
- * If the movement was successful for all the files, finishes the activity immediately.
- *
- * In other case, the list of remaining files is still available to retry the movement.
- *
- * @param result 'True' when the movement was successful.
- */
- @Override
- protected void onPostExecute(Boolean result) {
- mAdapter.notifyDataSetChanged();
- mCurrentDialog.dismiss();
- mCurrentDialog = null;
- findViewById(R.id.ok).setEnabled(true);
-
- if (result) {
- // nothing else to do in this activity
- Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_success), Toast.LENGTH_LONG);
- t.show();
- finish();
-
- } else {
- Toast t = Toast.makeText(ErrorsWhileCopyingHandlerActivity.this, getString(R.string.foreign_files_fail), Toast.LENGTH_LONG);
- t.show();
- }
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android.ui.activity;\r
-\r
-import android.app.Activity;\r
-import android.os.Bundle;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-import android.widget.TextView;\r
-\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;\r
-\r
-/**\r
- * This Activity is used to display a detail message for failed uploads\r
- * \r
- * The entry-point for this activity is the 'Failed upload Notification"\r
- * \r
- * @author andomaex / Matthias Baumann\r
- */\r
-public class FailedUploadActivity extends Activity {\r
-\r
- public static final String MESSAGE = "message";\r
-\r
- @Override\r
- public void onCreate(Bundle savedInstanceState) {\r
- super.onCreate(savedInstanceState);\r
- setContentView(R.layout.failed_upload_message_view);\r
- String message = getIntent().getStringExtra(MESSAGE);\r
- TextView textView = (TextView) findViewById(R.id.faild_upload_message);\r
- textView.setText(message);\r
- CustomButton closeBtn = (CustomButton) findViewById(R.id.failed_uploadactivity_close_button);\r
- \r
- closeBtn.setOnClickListener(new OnClickListener() {\r
- @Override\r
- public void onClick(View v) {\r
- finish();\r
- }\r
- });\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.accounts.OperationCanceledException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.webkit.MimeTypeMap;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
- *
- * @author David A. Velasco
- */
-public abstract class FileActivity extends SherlockFragmentActivity {
-
- public static final String EXTRA_FILE = "de.mobilcom.debitel.cloud.android.ui.activity.FILE";
- public static final String EXTRA_ACCOUNT = "de.mobilcom.debitel.cloud.android.ui.activity.ACCOUNT";
- public static final String EXTRA_WAITING_TO_PREVIEW = "de.mobilcom.debitel.cloud.android.ui.activity.WAITING_TO_PREVIEW";
-
- public static final String TAG = FileActivity.class.getSimpleName();
-
-
- /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */
- private Account mAccount;
-
- /** Main {@link OCFile} handled by the activity.*/
- private OCFile mFile;
-
- /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account} */
- private boolean mRedirectingToSetupAccount = false;
-
- /** Flag to signal when the value of mAccount was set */
- private boolean mAccountWasSet;
-
- /** Flag to signal when the value of mAccount was restored from a saved state */
- private boolean mAccountWasRestored;
-
-
- /**
- * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of
- * the {@link FileActivity}.
- *
- * Grants that a valid ownCloud {@link Account} is associated to the instance, or that the user
- * is requested to create a new one.
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Account account;
- if(savedInstanceState != null) {
- account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
- mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
- } else {
- account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
- mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
- }
-
- setAccount(account, savedInstanceState != null);
- }
-
-
- /**
- * Since ownCloud {@link Account}s can be managed from the system setting menu,
- * the existence of the {@link Account} associated to the instance must be checked
- * every time it is restarted.
- */
- @Override
- protected void onRestart() {
- super.onRestart();
- boolean validAccount = (mAccount != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), mAccount.name));
- if (!validAccount) {
- swapToDefaultAccount();
- }
-
- }
-
-
- @Override
- protected void onStart() {
- super.onStart();
- if (mAccountWasSet) {
- onAccountSet(mAccountWasRestored);
- }
- }
-
-
- /**
- * Sets and validates the ownCloud {@link Account} associated to the Activity.
- *
- * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
- *
- * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
- *
- * @param account New {@link Account} to set.
- * @param savedAccount When 'true', account was retrieved from a saved instance state.
- */
- private void setAccount(Account account, boolean savedAccount) {
- Account oldAccount = mAccount;
- boolean validAccount = (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), account.name));
- if (validAccount) {
- mAccount = account;
- mAccountWasSet = true;
- mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
-
- } else {
- swapToDefaultAccount();
- }
- }
-
-
- /**
- * Tries to swap the current ownCloud {@link Account} for other valid and existing.
- *
- * If no valid ownCloud {@link Account} exists, the the user is requested
- * to create a new ownCloud {@link Account}.
- *
- * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
- *
- * @return 'True' if the checked {@link Account} was valid.
- */
- private void swapToDefaultAccount() {
- // default to the most recently used account
- Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
- if (newAccount == null) {
- /// no account available: force account creation
- createFirstAccount();
- mRedirectingToSetupAccount = true;
- mAccountWasSet = false;
- mAccountWasRestored = false;
-
- } else {
- mAccountWasSet = true;
- mAccountWasRestored = (newAccount.equals(mAccount));
- mAccount = newAccount;
- }
- }
-
-
- /**
- * Launches the account creation activity. To use when no ownCloud account is available
- */
- private void createFirstAccount() {
- AccountManager am = AccountManager.get(getApplicationContext());
- am.addAccount(MainApp.getAccountType(),
- null,
- null,
- null,
- this,
- new AccountCreationCallback(),
- null);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
- outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
- }
-
-
- /**
- * Getter for the main {@link OCFile} handled by the activity.
- *
- * @return Main {@link OCFile} handled by the activity.
- */
- public OCFile getFile() {
- return mFile;
- }
-
-
- /**
- * Setter for the main {@link OCFile} handled by the activity.
- *
- * @param file Main {@link OCFile} to be handled by the activity.
- */
- public void setFile(OCFile file) {
- mFile = file;
- }
-
-
- /**
- * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
- *
- * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
- */
- public Account getAccount() {
- return mAccount;
- }
-
-
- /**
- * @return 'True' when the Activity is finishing to enforce the setup of a new account.
- */
- protected boolean isRedirectingToSetupAccount() {
- return mRedirectingToSetupAccount;
- }
-
-
- /**
- * Helper class handling a callback from the {@link AccountManager} after the creation of
- * a new ownCloud {@link Account} finished, successfully or not.
- *
- * At this moment, only called after the creation of the first account.
- *
- * @author David A. Velasco
- */
- public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
-
- @Override
- public void run(AccountManagerFuture<Bundle> future) {
- FileActivity.this.mRedirectingToSetupAccount = false;
- boolean accountWasSet = false;
- if (future != null) {
- try {
- Bundle result;
- result = future.getResult();
- String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
- String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
- if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
- setAccount(new Account(name, type), false);
- accountWasSet = true;
- }
- } catch (OperationCanceledException e) {
- Log_OC.d(TAG, "Account creation canceled");
-
- } catch (Exception e) {
- Log_OC.e(TAG, "Account creation finished in exception: ", e);
- }
-
- } else {
- Log_OC.e(TAG, "Account creation callback with null bundle");
- }
- if (!accountWasSet) {
- moveTaskToBack(true);
- }
- }
-
- }
-
-
- /**
- * Called when the ownCloud {@link Account} associated to the Activity was just updated.
- *
- * Child classes must grant that state depending on the {@link Account} is updated.
- */
- protected abstract void onAccountSet(boolean stateWasRecovered);
-
-
-
- public void openFile(OCFile file) {
- if (file != null) {
- String storagePath = file.getStoragePath();
- String encodedStoragePath = WebdavUtils.encodePath(storagePath);
-
- Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
- intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
- intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
-
- Intent intentForGuessedMimeType = null;
- if (storagePath.lastIndexOf('.') >= 0) {
- String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
- if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
- intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
- intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
- intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
- }
-
- Intent chooserIntent = null;
- if (intentForGuessedMimeType != null) {
- chooserIntent = Intent.createChooser(intentForGuessedMimeType, getString(R.string.actionbar_open_with));
- } else {
- chooserIntent = Intent.createChooser(intentForSavedMimeType, getString(R.string.actionbar_open_with));
- }
-
- startActivity(chooserIntent);
-
- } else {
- Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-
-import android.accounts.Account;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.res.Resources.NotFoundException;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import android.provider.MediaStore;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-import com.actionbarsherlock.view.Window;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.CreateFolderOperation;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.syncadapter.FileSyncService;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.LoadingDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileDetailFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.OCFileListFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageActivity;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewMediaFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewVideoActivity;
-
-/**
- * Displays, what files the user has available in his ownCloud.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-
-public class FileDisplayActivity extends FileActivity implements
-OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
-
- private ArrayAdapter<String> mDirectories;
-
- /** Access point to the cached database for the current ownCloud {@link Account} */
- private DataStorageManager mStorageManager = null;
-
- private SyncBroadcastReceiver mSyncBroadcastReceiver;
- private UploadFinishReceiver mUploadFinishReceiver;
- private DownloadFinishReceiver mDownloadFinishReceiver;
- private FileDownloaderBinder mDownloaderBinder = null;
- private FileUploaderBinder mUploaderBinder = null;
- private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
- private RemoteOperationResult mLastSslUntrustedServerResult = null;
-
- private boolean mDualPane;
- private View mLeftFragmentContainer;
- private View mRightFragmentContainer;
-
- private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
-
- public static final int DIALOG_SHORT_WAIT = 0;
- private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
- private static final int DIALOG_SSL_VALIDATOR = 2;
- private static final int DIALOG_CERT_NOT_SAVED = 3;
-
- private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-
- public static final String ACTION_DETAILS = "de.mobilcom.debitel.cloud.android.ui.activity.action.DETAILS";
-
- private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
- private static final int ACTION_SELECT_MULTIPLE_FILES = 2;
-
- private static final String TAG = FileDisplayActivity.class.getSimpleName();
-
- private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
- private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
-
- private OCFile mWaitingToPreview;
- private Handler mHandler;
-
- private String mDownloadAddedMessage;
- private String mDownloadFinishMessage;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate() start");
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
-
- super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
-
- mHandler = new Handler();
-
- FileDownloader downloader = new FileDownloader();
- mDownloadAddedMessage = downloader.getDownloadAddedMessage();
- mDownloadFinishMessage= downloader.getDownloadFinishMessage();
-
- /// bindings to transference services
- mUploadConnection = new ListServiceConnection();
- mDownloadConnection = new ListServiceConnection();
- bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
- bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
-
- // PIN CODE request ; best location is to decide, let's try this first
- if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
- requestPinCode();
- }
-
- /// file observer
- Intent observer_intent = new Intent(this, FileObserverService.class);
- observer_intent.putExtra(FileObserverService.KEY_FILE_CMD, FileObserverService.CMD_INIT_OBSERVED_LIST);
- startService(observer_intent);
-
- /// Load of saved instance state
- if(savedInstanceState != null) {
- mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
-
- } else {
- mWaitingToPreview = null;
- }
-
- /// USER INTERFACE
-
- // Inflate and set the layout view
- setContentView(R.layout.files);
- mDualPane = getResources().getBoolean(R.bool.large_land_layout);
- mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
- mRightFragmentContainer = findViewById(R.id.right_fragment_container);
- if (savedInstanceState == null) {
- createMinFragments();
- }
-
- // Action bar setup
- mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
- getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
- setSupportProgressBarIndeterminateVisibility(false); // always AFTER setContentView(...) ; to work around bug in its implementation
-
-
-
- Log_OC.d(TAG, "onCreate() end");
- }
-
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mDownloadConnection != null)
- unbindService(mDownloadConnection);
- if (mUploadConnection != null)
- unbindService(mUploadConnection);
- }
-
-
- /**
- * Called when the ownCloud {@link Account} associated to the Activity was just updated.
- */
- @Override
- protected void onAccountSet(boolean stateWasRecovered) {
- if (getAccount() != null) {
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-
- /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
- OCFile file = getFile();
- // get parent from path
- String parentPath = "";
- if (file != null) {
- if (file.isDown() && file.getLastSyncDateForProperties() == 0) {
- // upload in progress - right now, files are not inserted in the local cache until the upload is successful
- // get parent from path
- parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
- if (mStorageManager.getFileByPath(parentPath) == null)
- file = null; // not able to know the directory where the file is uploading
- } else {
- file = mStorageManager.getFileByPath(file.getRemotePath()); // currentDir = null if not in the current Account
- }
- }
- if (file == null) {
- // fall back to root folder
- file = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); // never returns null
- }
- setFile(file);
- mDirectories.clear();
- OCFile fileIt = file;
- while(fileIt != null && fileIt.getFileName() != OCFile.PATH_SEPARATOR) {
- if (fileIt.isDirectory()) {
- mDirectories.add(fileIt.getFileName());
- }
- // get parent from path
- parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
- fileIt = mStorageManager.getFileByPath(parentPath);
- }
- mDirectories.add(OCFile.PATH_SEPARATOR);
- if (!stateWasRecovered) {
- Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
- initFragmentsWithFile();
-
- } else {
- updateFragmentsVisibility(!file.isDirectory());
- updateNavigationElementsInActionBar(file.isDirectory() ? null : file);
- }
-
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
- }
- }
-
-
- private void createMinFragments() {
- OCFileListFragment listOfFiles = new OCFileListFragment();
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
- transaction.commit();
- }
-
- private void initFragmentsWithFile() {
- if (getAccount() != null && getFile() != null) {
- /// First fragment
- OCFileListFragment listOfFiles = getListOfFilesFragment();
- if (listOfFiles != null) {
- listOfFiles.listDirectory(getCurrentDir());
- } else {
- Log.e(TAG, "Still have a chance to lose the initializacion of list fragment >(");
- }
-
- /// Second fragment
- OCFile file = getFile();
- Fragment secondFragment = chooseInitialSecondFragment(file);
- if (secondFragment != null) {
- setSecondFragment(secondFragment);
- updateFragmentsVisibility(true);
- updateNavigationElementsInActionBar(file);
-
- } else {
- cleanSecondFragment();
- }
-
- } else {
- Log.wtf(TAG, "initFragments() called with invalid NULLs!");
- if (getAccount() == null) {
- Log.wtf(TAG, "\t account is NULL");
- }
- if (getFile() == null) {
- Log.wtf(TAG, "\t file is NULL");
- }
- }
- }
-
- private Fragment chooseInitialSecondFragment(OCFile file) {
- Fragment secondFragment = null;
- if (file != null && !file.isDirectory()) {
- if (file.isDown() && PreviewMediaFragment.canBePreviewed(file)
- && file.getLastSyncDateForProperties() > 0 // temporal fix
- ) {
- int startPlaybackPosition = getIntent().getIntExtra(PreviewVideoActivity.EXTRA_START_POSITION, 0);
- boolean autoplay = getIntent().getBooleanExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, true);
- secondFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
-
- } else {
- secondFragment = new FileDetailFragment(file, getAccount());
- }
- }
- return secondFragment;
- }
-
-
- /**
- * Replaces the second fragment managed by the activity with the received as
- * a parameter.
- *
- * Assumes never will be more than two fragments managed at the same time.
- *
- * @param fragment New second Fragment to set.
- */
- private void setSecondFragment(Fragment fragment) {
- FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
- transaction.replace(R.id.right_fragment_container, fragment, TAG_SECOND_FRAGMENT);
- transaction.commit();
- }
-
-
- private void updateFragmentsVisibility(boolean existsSecondFragment) {
- if (mDualPane) {
- if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
- mLeftFragmentContainer.setVisibility(View.VISIBLE);
- }
- if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
- mRightFragmentContainer.setVisibility(View.VISIBLE);
- }
-
- } else if (existsSecondFragment) {
- if (mLeftFragmentContainer.getVisibility() != View.GONE) {
- mLeftFragmentContainer.setVisibility(View.GONE);
- }
- if (mRightFragmentContainer.getVisibility() != View.VISIBLE) {
- mRightFragmentContainer.setVisibility(View.VISIBLE);
- }
-
- } else {
- if (mLeftFragmentContainer.getVisibility() != View.VISIBLE) {
- mLeftFragmentContainer.setVisibility(View.VISIBLE);
- }
- if (mRightFragmentContainer.getVisibility() != View.GONE) {
- mRightFragmentContainer.setVisibility(View.GONE);
- }
- }
- }
-
-
- private OCFileListFragment getListOfFilesFragment() {
- Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
- if (listOfFiles != null) {
- return (OCFileListFragment)listOfFiles;
- }
- Log_OC.wtf(TAG, "Access to unexisting list of files fragment!!");
- return null;
- }
-
- protected FileFragment getSecondFragment() {
- Fragment second = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT);
- if (second != null) {
- return (FileFragment)second;
- }
- return null;
- }
-
- public void cleanSecondFragment() {
- Fragment second = getSecondFragment();
- if (second != null) {
- FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
- tr.remove(second);
- tr.commit();
- }
- updateFragmentsVisibility(false);
- updateNavigationElementsInActionBar(null);
- }
-
- protected void refeshListOfFilesFragment() {
- OCFileListFragment fileListFragment = getListOfFilesFragment();
- if (fileListFragment != null) {
- fileListFragment.listDirectory();
- }
- }
-
- protected void refreshSecondFragment(String downloadEvent, String downloadedRemotePath, boolean success) {
- FileFragment secondFragment = getSecondFragment();
- boolean waitedPreview = (mWaitingToPreview != null && mWaitingToPreview.getRemotePath().equals(downloadedRemotePath));
- if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
- FileDetailFragment detailsFragment = (FileDetailFragment) secondFragment;
- OCFile fileInFragment = detailsFragment.getFile();
- if (fileInFragment != null && !downloadedRemotePath.equals(fileInFragment.getRemotePath())) {
- // the user browsed to other file ; forget the automatic preview
- mWaitingToPreview = null;
-
- } else if (downloadEvent.equals(mDownloadAddedMessage)) {
- // grant that the right panel updates the progress bar
- detailsFragment.listenForTransferProgress();
- detailsFragment.updateFileDetails(true, false);
-
- } else if (downloadEvent.equals(mDownloadFinishMessage)) {
- // update the right panel
- boolean detailsFragmentChanged = false;
- if (waitedPreview) {
- if (success) {
- mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
- if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
- startMediaPreview(mWaitingToPreview, 0, true);
- detailsFragmentChanged = true;
- } else {
- openFile(mWaitingToPreview);
- }
- }
- mWaitingToPreview = null;
- }
- if (!detailsFragmentChanged) {
- detailsFragment.updateFileDetails(false, (success));
- }
- }
- }
- }
-
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getSherlock().getMenuInflater();
- inflater.inflate(R.menu.main_menu, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- boolean retval = true;
- switch (item.getItemId()) {
- case R.id.action_create_dir: {
- EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.uploader_info_dirname), "", -1, -1, this);
- dialog.show(getSupportFragmentManager(), "createdirdialog");
- break;
- }
- case R.id.action_sync_account: {
- startSynchronization();
- break;
- }
- case R.id.action_upload: {
- showDialog(DIALOG_CHOOSE_UPLOAD_SOURCE);
- break;
- }
- case R.id.action_settings: {
- Intent settingsIntent = new Intent(this, Preferences.class);
- startActivity(settingsIntent);
- break;
- }
- case android.R.id.home: {
- FileFragment second = getSecondFragment();
- OCFile currentDir = getCurrentDir();
- if((currentDir != null && currentDir.getParentId() != 0) ||
- (second != null && second.getFile() != null)) {
- onBackPressed();
- }
- break;
- }
- default:
- retval = super.onOptionsItemSelected(item);
- }
- return retval;
- }
-
- private void startSynchronization() {
- ContentResolver.cancelSync(null, MainApp.getAuthTokenType()); // cancel the current synchronizations of any ownCloud account
- Bundle bundle = new Bundle();
- bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
- ContentResolver.requestSync(
- getAccount(),
- MainApp.getAuthTokenType(), bundle);
- }
-
-
- @Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- int i = itemPosition;
- while (i-- != 0) {
- onBackPressed();
- }
- // the next operation triggers a new call to this method, but it's necessary to
- // ensure that the name exposed in the action bar is the current directory when the
- // user selected it in the navigation list
- if (itemPosition != 0)
- getSupportActionBar().setSelectedNavigationItem(0);
- return true;
- }
-
- /**
- * Called, when the user selected something for uploading
- */
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (requestCode == ACTION_SELECT_CONTENT_FROM_APPS && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
- requestSimpleUpload(data, resultCode);
-
- } else if (requestCode == ACTION_SELECT_MULTIPLE_FILES && (resultCode == RESULT_OK || resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)) {
- requestMultipleUpload(data, resultCode);
-
- }
- }
-
- private void requestMultipleUpload(Intent data, int resultCode) {
- String[] filePaths = data.getStringArrayExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES);
- if (filePaths != null) {
- String[] remotePaths = new String[filePaths.length];
- String remotePathBase = "";
- for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
- remotePathBase += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
- }
- if (!remotePathBase.endsWith(OCFile.PATH_SEPARATOR))
- remotePathBase += OCFile.PATH_SEPARATOR;
- for (int j = 0; j< remotePaths.length; j++) {
- remotePaths[j] = remotePathBase + (new File(filePaths[j])).getName();
- }
-
- Intent i = new Intent(this, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, getAccount());
- i.putExtra(FileUploader.KEY_LOCAL_FILE, filePaths);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths);
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_MULTIPLE_FILES);
- if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
- i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
- startService(i);
-
- } else {
- Log_OC.d(TAG, "User clicked on 'Update' with no selection");
- Toast t = Toast.makeText(this, getString(R.string.filedisplay_no_file_selected), Toast.LENGTH_LONG);
- t.show();
- return;
- }
- }
-
-
- private void requestSimpleUpload(Intent data, int resultCode) {
- String filepath = null;
- try {
- Uri selectedImageUri = data.getData();
-
- String filemanagerstring = selectedImageUri.getPath();
- String selectedImagePath = getPath(selectedImageUri);
-
- if (selectedImagePath != null)
- filepath = selectedImagePath;
- else
- filepath = filemanagerstring;
-
- } catch (Exception e) {
- Log_OC.e(TAG, "Unexpected exception when trying to read the result of Intent.ACTION_GET_CONTENT", e);
- e.printStackTrace();
-
- } finally {
- if (filepath == null) {
- Log_OC.e(TAG, "Couldnt resolve path to file");
- Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
- t.show();
- return;
- }
- }
-
- Intent i = new Intent(this, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT,
- getAccount());
- String remotepath = new String();
- for (int j = mDirectories.getCount() - 2; j >= 0; --j) {
- remotepath += OCFile.PATH_SEPARATOR + mDirectories.getItem(j);
- }
- if (!remotepath.endsWith(OCFile.PATH_SEPARATOR))
- remotepath += OCFile.PATH_SEPARATOR;
- remotepath += new File(filepath).getName();
-
- i.putExtra(FileUploader.KEY_LOCAL_FILE, filepath);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, remotepath);
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- if (resultCode == UploadFilesActivity.RESULT_OK_AND_MOVE)
- i.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, FileUploader.LOCAL_BEHAVIOUR_MOVE);
- startService(i);
- }
-
- @Override
- public void onBackPressed() {
- OCFileListFragment listOfFiles = getListOfFilesFragment();
- if (mDualPane || getSecondFragment() == null) {
- if (listOfFiles != null) { // should never be null, indeed
- if (mDirectories.getCount() <= 1) {
- finish();
- return;
- }
- popDirname();
- listOfFiles.onBrowseUp();
- }
- }
- if (listOfFiles != null) { // should never be null, indeed
- setFile(listOfFiles.getCurrentFile());
- }
- cleanSecondFragment();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
- Log_OC.e(TAG, "onSaveInstanceState() start");
- super.onSaveInstanceState(outState);
- outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
- Log_OC.d(TAG, "onSaveInstanceState() end");
- }
-
-
-
- @Override
- protected void onResume() {
- super.onResume();
- Log_OC.e(TAG, "onResume() start");
-
- FileUploader fileUploader = new FileUploader();
- FileSyncService fileSyncService = new FileSyncService();
-
- // Listen for sync messages
- IntentFilter syncIntentFilter = new IntentFilter(fileSyncService.getSyncMessage());
- mSyncBroadcastReceiver = new SyncBroadcastReceiver();
- registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
-
- // Listen for upload messages
- IntentFilter uploadIntentFilter = new IntentFilter(fileUploader.getUploadFinishMessage());
- mUploadFinishReceiver = new UploadFinishReceiver();
- registerReceiver(mUploadFinishReceiver, uploadIntentFilter);
-
- // Listen for download messages
- IntentFilter downloadIntentFilter = new IntentFilter(mDownloadAddedMessage);
- downloadIntentFilter.addAction(mDownloadFinishMessage);
- mDownloadFinishReceiver = new DownloadFinishReceiver();
- registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
-
- Log_OC.d(TAG, "onResume() end");
- }
-
-
- @Override
- protected void onPause() {
- super.onPause();
- Log_OC.e(TAG, "onPause() start");
- if (mSyncBroadcastReceiver != null) {
- unregisterReceiver(mSyncBroadcastReceiver);
- mSyncBroadcastReceiver = null;
- }
- if (mUploadFinishReceiver != null) {
- unregisterReceiver(mUploadFinishReceiver);
- mUploadFinishReceiver = null;
- }
- if (mDownloadFinishReceiver != null) {
- unregisterReceiver(mDownloadFinishReceiver);
- mDownloadFinishReceiver = null;
- }
-
- Log_OC.d(TAG, "onPause() end");
- }
-
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
- if (id == DIALOG_SSL_VALIDATOR && mLastSslUntrustedServerResult != null) {
- ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
- }
- }
-
-
- @Override
- protected Dialog onCreateDialog(int id) {
- Dialog dialog = null;
- AlertDialog.Builder builder;
- switch (id) {
- case DIALOG_SHORT_WAIT: {
- ProgressDialog working_dialog = new ProgressDialog(this);
- working_dialog.setMessage(getResources().getString(
- R.string.wait_a_moment));
- working_dialog.setIndeterminate(true);
- working_dialog.setCancelable(false);
- dialog = working_dialog;
- break;
- }
- case DIALOG_CHOOSE_UPLOAD_SOURCE: {
-
- String[] items = null;
-
- String[] allTheItems = { getString(R.string.actionbar_upload_files),
- getString(R.string.actionbar_upload_from_apps),
- getString(R.string.actionbar_failed_instant_upload) };
-
- String[] commonItems = { getString(R.string.actionbar_upload_files),
- getString(R.string.actionbar_upload_from_apps) };
-
- if (InstantUploadActivity.IS_ENABLED)
- items = allTheItems;
- else
- items = commonItems;
-
- builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.actionbar_upload);
- builder.setItems(items, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int item) {
- if (item == 0) {
- // if (!mDualPane) {
- Intent action = new Intent(FileDisplayActivity.this, UploadFilesActivity.class);
- action.putExtra(UploadFilesActivity.EXTRA_ACCOUNT, FileDisplayActivity.this.getAccount());
- startActivityForResult(action, ACTION_SELECT_MULTIPLE_FILES);
- // } else {
- // TODO create and handle new fragment
- // LocalFileListFragment
- // }
- } else if (item == 1) {
- Intent action = new Intent(Intent.ACTION_GET_CONTENT);
- action = action.setType("*/*").addCategory(Intent.CATEGORY_OPENABLE);
- startActivityForResult(Intent.createChooser(action, getString(R.string.upload_chooser_title)),
- ACTION_SELECT_CONTENT_FROM_APPS);
- } else if (item == 2 && InstantUploadActivity.IS_ENABLED) {
- Intent action = new Intent(FileDisplayActivity.this, InstantUploadActivity.class);
- action.putExtra(FileUploader.KEY_ACCOUNT, FileDisplayActivity.this.getAccount());
- startActivity(action);
- }
- }
- });
- dialog = builder.create();
- break;
- }
- case DIALOG_SSL_VALIDATOR: {
- dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
- break;
- }
- case DIALOG_CERT_NOT_SAVED: {
- builder = new AlertDialog.Builder(this);
- builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
- builder.setCancelable(false);
- builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- };
- });
- dialog = builder.create();
- break;
- }
- default:
- dialog = null;
- }
-
- return dialog;
- }
-
-
- /**
- * Show loading dialog
- */
- public void showLoadingDialog() {
- // Construct dialog
- LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction ft = fm.beginTransaction();
- loading.show(ft, DIALOG_WAIT_TAG);
-
- }
-
- /**
- * Dismiss loading dialog
- */
- public void dismissLoadingDialog(){
- Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
- if (frag != null) {
- LoadingDialog loading = (LoadingDialog) frag;
- loading.dismiss();
- }
- }
-
-
- /**
- * Translates a content URI of an image to a physical path
- * on the disk
- * @param uri The URI to resolve
- * @return The path to the image or null if it could not be found
- */
- public String getPath(Uri uri) {
- String[] projection = { MediaStore.Images.Media.DATA };
- Cursor cursor = managedQuery(uri, projection, null, null, null);
- if (cursor != null) {
- int column_index = cursor
- .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
- cursor.moveToFirst();
- return cursor.getString(column_index);
- }
- return null;
- }
-
- /**
- * Pushes a directory to the drop down list
- * @param directory to push
- * @throws IllegalArgumentException If the {@link OCFile#isDirectory()} returns false.
- */
- public void pushDirname(OCFile directory) {
- if(!directory.isDirectory()){
- throw new IllegalArgumentException("Only directories may be pushed!");
- }
- mDirectories.insert(directory.getFileName(), 0);
- setFile(directory);
- }
-
- /**
- * Pops a directory name from the drop down list
- * @return True, unless the stack is empty
- */
- public boolean popDirname() {
- mDirectories.remove(mDirectories.getItem(0));
- return !mDirectories.isEmpty();
- }
-
- // Custom array adapter to override text colors
- private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
-
- public CustomArrayAdapter(FileDisplayActivity ctx, int view) {
- super(ctx, view);
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = super.getView(position, convertView, parent);
-
- ((TextView) v).setTextColor(getResources().getColorStateList(
- android.R.color.white));
- return v;
- }
-
- public View getDropDownView(int position, View convertView,
- ViewGroup parent) {
- View v = super.getDropDownView(position, convertView, parent);
-
- ((TextView) v).setTextColor(getResources().getColorStateList(
- android.R.color.white));
-
- return v;
- }
-
- }
-
- private class SyncBroadcastReceiver extends BroadcastReceiver {
-
- /**
- * {@link BroadcastReceiver} to enable syncing feedback in UI
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
- String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
-
- Log_OC.d(TAG, "sync of account " + accountName + " is in_progress: " + inProgress);
-
- if (getAccount() != null && accountName.equals(getAccount().name)
- && mStorageManager != null
- ) {
-
- String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
-
- boolean fillBlankRoot = false;
- OCFile currentDir = getCurrentDir();
- if (currentDir == null) {
- currentDir = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
- fillBlankRoot = (currentDir != null);
- }
-
- if ((synchFolderRemotePath != null && currentDir != null && (currentDir.getRemotePath().equals(synchFolderRemotePath)))
- || fillBlankRoot ) {
- if (!fillBlankRoot)
- currentDir = mStorageManager.getFileByPath(synchFolderRemotePath);
- OCFileListFragment fileListFragment = getListOfFilesFragment();
- if (fileListFragment != null) {
- fileListFragment.listDirectory(currentDir);
- }
- if (getSecondFragment() == null)
- setFile(currentDir);
- }
-
- setSupportProgressBarIndeterminateVisibility(inProgress);
- removeStickyBroadcast(intent);
-
- }
-
- RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
- if (synchResult != null) {
- if (synchResult.getCode().equals(RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED)) {
- mLastSslUntrustedServerResult = synchResult;
- showDialog(DIALOG_SSL_VALIDATOR);
- }
- }
- }
- }
-
-
- private class UploadFinishReceiver extends BroadcastReceiver {
- /**
- * Once the file upload has finished -> update view
- * @author David A. Velasco
- * {@link BroadcastReceiver} to enable upload feedback in UI
- */
- @Override
- public void onReceive(Context context, Intent intent) {
- String uploadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
- String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
- boolean sameAccount = getAccount() != null && accountName.equals(getAccount().name);
- OCFile currentDir = getCurrentDir();
- boolean isDescendant = (currentDir != null) && (uploadedRemotePath != null) && (uploadedRemotePath.startsWith(currentDir.getRemotePath()));
- if (sameAccount && isDescendant) {
- refeshListOfFilesFragment();
- }
- }
-
- }
-
-
- /**
- * Class waiting for broadcast events from the {@link FielDownloader} service.
- *
- * Updates the UI when a download is started or finished, provided that it is relevant for the
- * current folder.
- */
- private class DownloadFinishReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- boolean sameAccount = isSameAccount(context, intent);
- String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
- boolean isDescendant = isDescendant(downloadedRemotePath);
-
- if (sameAccount && isDescendant) {
- refeshListOfFilesFragment();
- refreshSecondFragment(intent.getAction(), downloadedRemotePath, intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false));
- }
-
- removeStickyBroadcast(intent);
- }
-
- private boolean isDescendant(String downloadedRemotePath) {
- OCFile currentDir = getCurrentDir();
- return (currentDir != null && downloadedRemotePath != null && downloadedRemotePath.startsWith(currentDir.getRemotePath()));
- }
-
- private boolean isSameAccount(Context context, Intent intent) {
- String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
- return (accountName != null && getAccount() != null && accountName.equals(getAccount().name));
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public DataStorageManager getStorageManager() {
- return mStorageManager;
- }
-
-
- /**
- * {@inheritDoc}
- *
- * Updates action bar and second fragment, if in dual pane mode.
- */
- @Override
- public void onBrowsedDownTo(OCFile directory) {
- pushDirname(directory);
- cleanSecondFragment();
- }
-
- /**
- * Opens the image gallery showing the image {@link OCFile} received as parameter.
- *
- * @param file Image {@link OCFile} to show.
- */
- @Override
- public void startImagePreview(OCFile file) {
- Intent showDetailsIntent = new Intent(this, PreviewImageActivity.class);
- showDetailsIntent.putExtra(EXTRA_FILE, file);
- showDetailsIntent.putExtra(EXTRA_ACCOUNT, getAccount());
- startActivity(showDetailsIntent);
- }
-
- /**
- * Stars the preview of an already down media {@link OCFile}.
- *
- * @param file Media {@link OCFile} to preview.
- * @param startPlaybackPosition Media position where the playback will be started, in milliseconds.
- * @param autoplay When 'true', the playback will start without user interactions.
- */
- @Override
- public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay) {
- Fragment mediaFragment = new PreviewMediaFragment(file, getAccount(), startPlaybackPosition, autoplay);
- setSecondFragment(mediaFragment);
- updateFragmentsVisibility(true);
- updateNavigationElementsInActionBar(file);
- setFile(file);
- }
-
- /**
- * Requests the download of the received {@link OCFile} , updates the UI
- * to monitor the download progress and prepares the activity to preview
- * or open the file when the download finishes.
- *
- * @param file {@link OCFile} to download and preview.
- */
- @Override
- public void startDownloadForPreview(OCFile file) {
- Fragment detailFragment = new FileDetailFragment(file, getAccount());
- setSecondFragment(detailFragment);
- mWaitingToPreview = file;
- requestForDownload();
- updateFragmentsVisibility(true);
- updateNavigationElementsInActionBar(file);
- setFile(file);
- }
-
-
- /**
- * Shows the information of the {@link OCFile} received as a
- * parameter in the second fragment.
- *
- * @param file {@link OCFile} whose details will be shown
- */
- @Override
- public void showDetails(OCFile file) {
- Fragment detailFragment = new FileDetailFragment(file, getAccount());
- setSecondFragment(detailFragment);
- updateFragmentsVisibility(true);
- updateNavigationElementsInActionBar(file);
- setFile(file);
- }
-
-
- /**
- * TODO
- */
- private void updateNavigationElementsInActionBar(OCFile chosenFile) {
- ActionBar actionBar = getSupportActionBar();
- if (chosenFile == null || mDualPane) {
- // only list of files - set for browsing through folders
- OCFile currentDir = getCurrentDir();
- actionBar.setDisplayHomeAsUpEnabled(currentDir != null && currentDir.getParentId() != 0);
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
-
- } else {
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setTitle(chosenFile.getFileName());
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onFileStateChanged() {
- refeshListOfFilesFragment();
- updateNavigationElementsInActionBar(getSecondFragment().getFile());
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public FileDownloaderBinder getFileDownloaderBinder() {
- return mDownloaderBinder;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public FileUploaderBinder getFileUploaderBinder() {
- return mUploaderBinder;
- }
-
-
- /** Defines callbacks for service binding, passed to bindService() */
- private class ListServiceConnection implements ServiceConnection {
-
- @Override
- public void onServiceConnected(ComponentName component, IBinder service) {
- if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
- Log_OC.d(TAG, "Download service connected");
- mDownloaderBinder = (FileDownloaderBinder) service;
- if (mWaitingToPreview != null) {
- requestForDownload();
- }
-
- } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
- Log_OC.d(TAG, "Upload service connected");
- mUploaderBinder = (FileUploaderBinder) service;
- } else {
- return;
- }
- // a new chance to get the mDownloadBinder through getFileDownloadBinder() - THIS IS A MESS
- OCFileListFragment listOfFiles = getListOfFilesFragment();
- if (listOfFiles != null) {
- listOfFiles.listDirectory();
- }
- FileFragment secondFragment = getSecondFragment();
- if (secondFragment != null && secondFragment instanceof FileDetailFragment) {
- FileDetailFragment detailFragment = (FileDetailFragment)secondFragment;
- detailFragment.listenForTransferProgress();
- detailFragment.updateFileDetails(false, false);
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName component) {
- if (component.equals(new ComponentName(FileDisplayActivity.this, FileDownloader.class))) {
- Log_OC.d(TAG, "Download service disconnected");
- mDownloaderBinder = null;
- } else if (component.equals(new ComponentName(FileDisplayActivity.this, FileUploader.class))) {
- Log_OC.d(TAG, "Upload service disconnected");
- mUploaderBinder = null;
- }
- }
- };
-
-
-
- /**
- * Launch an intent to request the PIN code to the user before letting him use the app
- */
- private void requestPinCode() {
- boolean pinStart = false;
- SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- pinStart = appPrefs.getBoolean("set_pincode", false);
- if (pinStart) {
- Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
- i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "FileDisplayActivity");
- startActivity(i);
- }
- }
-
-
- @Override
- public void onSavedCertificate() {
- startSynchronization();
- }
-
-
- @Override
- public void onFailedSavingCertificate() {
- showDialog(DIALOG_CERT_NOT_SAVED);
- }
-
-
- /**
- * Updates the view associated to the activity after the finish of some operation over files
- * in the current account.
- *
- * @param operation Removal operation performed.
- * @param result Result of the removal.
- */
- @Override
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
- if (operation instanceof RemoveFileOperation) {
- onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
-
- } else if (operation instanceof RenameFileOperation) {
- onRenameFileOperationFinish((RenameFileOperation)operation, result);
-
- } else if (operation instanceof SynchronizeFileOperation) {
- onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
-
- } else if (operation instanceof CreateFolderOperation) {
- onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
- }
- }
-
-
- /**
- * Updates the view associated to the activity after the finish of an operation trying to remove a
- * file.
- *
- * @param operation Removal operation performed.
- * @param result Result of the removal.
- */
- private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
- dismissLoadingDialog();
- if (result.isSuccess()) {
- Toast msg = Toast.makeText(this, R.string.remove_success_msg, Toast.LENGTH_LONG);
- msg.show();
- OCFile removedFile = operation.getFile();
- getSecondFragment();
- FileFragment second = getSecondFragment();
- if (second != null && removedFile.equals(second.getFile())) {
- cleanSecondFragment();
- }
- if (mStorageManager.getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
- refeshListOfFilesFragment();
- }
-
- } else {
- Toast msg = Toast.makeText(this, R.string.remove_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- mLastSslUntrustedServerResult = result;
- showDialog(DIALOG_SSL_VALIDATOR);
- }
- }
- }
-
- /**
- * Updates the view associated to the activity after the finish of an operation trying create a new folder
- *
- * @param operation Creation operation performed.
- * @param result Result of the creation.
- */
- private void onCreateFolderOperationFinish(CreateFolderOperation operation, RemoteOperationResult result) {
- if (result.isSuccess()) {
- dismissLoadingDialog();
- refeshListOfFilesFragment();
-
- } else {
- //dismissDialog(DIALOG_SHORT_WAIT);
- dismissLoadingDialog();
- try {
- Toast msg = Toast.makeText(FileDisplayActivity.this, R.string.create_dir_fail_msg, Toast.LENGTH_LONG);
- msg.show();
-
- } catch (NotFoundException e) {
- Log_OC.e(TAG, "Error while trying to show fail message " , e);
- }
- }
- }
-
-
- /**
- * Updates the view associated to the activity after the finish of an operation trying to rename a
- * file.
- *
- * @param operation Renaming operation performed.
- * @param result Result of the renaming.
- */
- private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
- dismissLoadingDialog();
- OCFile renamedFile = operation.getFile();
- if (result.isSuccess()) {
- if (mDualPane) {
- FileFragment details = getSecondFragment();
- if (details != null && details instanceof FileDetailFragment && renamedFile.equals(details.getFile()) ) {
- ((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
- }
- }
- if (mStorageManager.getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
- refeshListOfFilesFragment();
- }
-
- } else {
- if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
- Toast msg = Toast.makeText(this, R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- // TODO throw again the new rename dialog
- } else {
- Toast msg = Toast.makeText(this, R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- mLastSslUntrustedServerResult = result;
- showDialog(DIALOG_SSL_VALIDATOR);
- }
- }
- }
- }
-
-
- private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
- dismissLoadingDialog();
- OCFile syncedFile = operation.getLocalFile();
- if (!result.isSuccess()) {
- if (result.getCode() == ResultCode.SYNC_CONFLICT) {
- Intent i = new Intent(this, ConflictsResolveActivity.class);
- i.putExtra(ConflictsResolveActivity.EXTRA_FILE, syncedFile);
- i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, getAccount());
- startActivity(i);
-
- } else {
- Toast msg = Toast.makeText(this, R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- }
-
- } else {
- if (operation.transferWasRequested()) {
- refeshListOfFilesFragment();
- onTransferStateChanged(syncedFile, true, true);
-
- } else {
- Toast msg = Toast.makeText(this, R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
- msg.show();
- }
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
- if (mDualPane) {
- FileFragment details = getSecondFragment();
- if (details != null && details instanceof FileDetailFragment && file.equals(details.getFile()) ) {
- if (downloading || uploading) {
- ((FileDetailFragment)details).updateFileDetails(file, getAccount());
- } else {
- ((FileDetailFragment)details).updateFileDetails(false, true);
- }
- }
- }
- }
-
-
- public void onDismiss(EditNameDialog dialog) {
- if (dialog.getResult()) {
- String newDirectoryName = dialog.getNewFilename().trim();
- Log_OC.d(TAG, "'create directory' dialog dismissed with new name " + newDirectoryName);
- if (newDirectoryName.length() > 0) {
- String path = getCurrentDir().getRemotePath();
-
- // Create directory
- path += newDirectoryName + OCFile.PATH_SEPARATOR;
- RemoteOperation operation = new CreateFolderOperation(path, false, mStorageManager);
- operation.execute( getAccount(),
- FileDisplayActivity.this,
- FileDisplayActivity.this,
- mHandler,
- FileDisplayActivity.this);
-
- showLoadingDialog();
- }
- }
- }
-
-
- private void requestForDownload() {
- Account account = getAccount();
- if (!mDownloaderBinder.isDownloading(account, mWaitingToPreview)) {
- Intent i = new Intent(this, FileDownloader.class);
- i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
- i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview);
- startService(i);
- }
- }
-
-
- private OCFile getCurrentDir() {
- OCFile file = getFile();
- if (file != null) {
- if (file.isDirectory()) {
- return file;
- } else if (mStorageManager != null) {
- String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
- return mStorageManager.getFileByPath(parentPath);
- }
- }
- return null;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.ArrayList;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.method.ScrollingMovementMethod;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Activity showing a text message and, optionally, a couple list of single or paired text strings.
- *
- * Added to show explanations for notifications when the user clicks on them, and there no place
- * better to show them.
- *
- * @author David A. Velasco
- */
-public class GenericExplanationActivity extends SherlockFragmentActivity {
-
- public static final String EXTRA_LIST = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST";
- public static final String EXTRA_LIST_2 = GenericExplanationActivity.class.getCanonicalName() + ".EXTRA_LIST_2";
- public static final String MESSAGE = GenericExplanationActivity.class.getCanonicalName() + ".MESSAGE";
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- Intent intent = getIntent();
- String message = intent.getStringExtra(MESSAGE);
- ArrayList<String> list = intent.getStringArrayListExtra(EXTRA_LIST);
- ArrayList<String> list2 = intent.getStringArrayListExtra(EXTRA_LIST_2);
-
- setContentView(R.layout.generic_explanation);
-
- if (message != null) {
- TextView textView = (TextView) findViewById(R.id.message);
- textView.setText(message);
- textView.setMovementMethod(new ScrollingMovementMethod());
- }
-
- ListView listView = (ListView) findViewById(R.id.list);
- if (list != null && list.size() > 0) {
- //ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
- ListAdapter adapter = new ExplanationListAdapterView(this, list, list2);
- listView.setAdapter(adapter);
- } else {
- listView.setVisibility(View.GONE);
- }
- }
-
- public class ExplanationListAdapterView extends ArrayAdapter<String> {
-
- ArrayList<String> mList;
- ArrayList<String> mList2;
-
- ExplanationListAdapterView(Context context, ArrayList<String> list, ArrayList<String> list2) {
- super(context, android.R.layout.two_line_list_item, android.R.id.text1, list);
- mList = list;
- mList2 = list2;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View getView (int position, View convertView, ViewGroup parent) {
- View view = super.getView(position, convertView, parent);
- if (view != null) {
- if (mList2 != null && mList2.size() > 0 && position >= 0 && position < mList2.size()) {
- TextView text2 = (TextView) view.findViewById(android.R.id.text2);
- if (text2 != null) {
- text2.setText(mList2.get(position));
- }
- }
- }
- return view;
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-import de.mobilcom.debitel.cloud.android.files.InstantUploadBroadcastReceiver;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-/**
- * This Activity is used to display a list with images they could not be
- * uploaded instantly. The images can be selected for delete or for a try again
- * upload
- *
- * The entry-point for this activity is the 'Failed upload Notification" and a
- * sub-menu underneath the 'Upload' menu-item
- *
- * @author andomaex / Matthias Baumann
- */
-public class InstantUploadActivity extends Activity {
-
- private static final String LOG_TAG = InstantUploadActivity.class.getSimpleName();
- private LinearLayout listView;
- private static final String retry_chexbox_tag = "retry_chexbox_tag";
- public static final boolean IS_ENABLED = false;
- private static int MAX_LOAD_IMAGES = 5;
- private int lastLoadImageIdx = 0;
-
- private SparseArray<String> fileList = null;
- CheckBox failed_upload_all_cb;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.failed_upload_files);
-
- CustomButton deleteAllBtn = (CustomButton) findViewById(R.id.failed_upload_delete_all_btn);
- deleteAllBtn.setOnClickListener(getDeleteListner());
- CustomButton retryAllBtn = (CustomButton) findViewById(R.id.failed_upload_retry_all_btn);
- retryAllBtn.setOnClickListener(getRetryListner());
- this.failed_upload_all_cb = (CheckBox) findViewById(R.id.failed_upload_headline_cb);
- failed_upload_all_cb.setOnCheckedChangeListener(getCheckAllListener());
- listView = (LinearLayout) findViewById(R.id.failed_upload_scrollviewlayout);
-
- loadListView(true);
-
- }
-
- /**
- * init the listview with ImageButtons, checkboxes and filename for every
- * Image that was not successfully uploaded
- *
- * this method is call at Activity creation and on delete one ore more
- * list-entry an on retry the upload by clicking the ImageButton or by click
- * to the 'retry all' button
- *
- */
- private void loadListView(boolean reset) {
- DbHandler db = new DbHandler(getApplicationContext());
- Cursor c = db.getFailedFiles();
-
- if (reset) {
- fileList = new SparseArray<String>();
- listView.removeAllViews();
- lastLoadImageIdx = 0;
- }
- if (c != null) {
- try {
- c.moveToPosition(lastLoadImageIdx);
-
- while (c.moveToNext()) {
-
- lastLoadImageIdx++;
- String imp_path = c.getString(1);
- String message = c.getString(4);
- fileList.put(lastLoadImageIdx, imp_path);
- LinearLayout rowLayout = getHorizontalLinearLayout(lastLoadImageIdx);
- rowLayout.addView(getFileCheckbox(lastLoadImageIdx));
- rowLayout.addView(getImageButton(imp_path, lastLoadImageIdx));
- rowLayout.addView(getFileButton(imp_path, message, lastLoadImageIdx));
- listView.addView(rowLayout);
- Log_OC.d(LOG_TAG, imp_path + " on idx: " + lastLoadImageIdx);
- if (lastLoadImageIdx % MAX_LOAD_IMAGES == 0) {
- break;
- }
- }
- if (lastLoadImageIdx > 0) {
- addLoadMoreButton(listView);
- }
- } finally {
- db.close();
- }
- }
- }
-
- private void addLoadMoreButton(LinearLayout listView) {
- if (listView != null) {
- Button loadmoreBtn = null;
- View oldButton = listView.findViewById(42);
- if (oldButton != null) {
- // remove existing button
- listView.removeView(oldButton);
- // to add the button at the end
- loadmoreBtn = (Button) oldButton;
- } else {
- // create a new button to add to the scoll view
- loadmoreBtn = new Button(this);
- loadmoreBtn.setId(42);
- loadmoreBtn.setText(getString(R.string.failed_upload_load_more_images));
- loadmoreBtn.setBackgroundResource(R.color.background_color);
- loadmoreBtn.setTextSize(12);
- loadmoreBtn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- loadListView(false);
- }
-
- });
- }
- listView.addView(loadmoreBtn);
- }
- }
-
- /**
- * provide a list of CheckBox instances, looked up from parent listview this
- * list ist used to select/deselect all checkboxes at the list
- *
- * @return List<CheckBox>
- */
- private List<CheckBox> getCheckboxList() {
- List<CheckBox> list = new ArrayList<CheckBox>();
- for (int i = 0; i < listView.getChildCount(); i++) {
- Log_OC.d(LOG_TAG, "ListView has Childs: " + listView.getChildCount());
- View childView = listView.getChildAt(i);
- if (childView != null && childView instanceof ViewGroup) {
- View checkboxView = getChildViews((ViewGroup) childView);
- if (checkboxView != null && checkboxView instanceof CheckBox) {
- Log_OC.d(LOG_TAG, "found Child: " + checkboxView.getId() + " " + checkboxView.getClass());
- list.add((CheckBox) checkboxView);
- }
- }
- }
- return list;
- }
-
- /**
- * recursive called method, used from getCheckboxList method
- *
- * @param View
- * @return View
- */
- private View getChildViews(ViewGroup view) {
- if (view != null) {
- for (int i = 0; i < view.getChildCount(); i++) {
- View cb = view.getChildAt(i);
- if (cb != null && cb instanceof ViewGroup) {
- return getChildViews((ViewGroup) cb);
- } else if (cb instanceof CheckBox) {
- return cb;
- }
- }
- }
- return null;
- }
-
- /**
- * create a new OnCheckedChangeListener for the 'check all' checkbox *
- *
- * @return OnCheckedChangeListener to select all checkboxes at the list
- */
- private OnCheckedChangeListener getCheckAllListener() {
- return new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- List<CheckBox> list = getCheckboxList();
- for (CheckBox checkbox : list) {
- ((CheckBox) checkbox).setChecked(isChecked);
- }
- }
-
- };
- }
-
- /**
- * Button click Listener for the retry button at the headline
- *
- * @return a Listener to perform a retry for all selected images
- */
- private OnClickListener getRetryListner() {
- return new OnClickListener() {
-
- @Override
- public void onClick(View v) {
-
- try {
-
- List<CheckBox> list = getCheckboxList();
- for (CheckBox checkbox : list) {
- boolean to_retry = checkbox.isChecked();
-
- Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_retry);
- String img_path = fileList.get(checkbox.getId());
- if (to_retry) {
-
- final String msg = "Image-Path " + checkbox.getId() + " was checked: " + img_path;
- Log_OC.d(LOG_TAG, msg);
- startUpload(img_path);
- }
-
- }
- } finally {
- // refresh the List
- listView.removeAllViews();
- loadListView(true);
- if (failed_upload_all_cb != null) {
- failed_upload_all_cb.setChecked(false);
- }
- }
-
- }
- };
- }
-
- /**
- * Button click Listener for the delete button at the headline
- *
- * @return a Listener to perform a delete for all selected images
- */
- private OnClickListener getDeleteListner() {
-
- return new OnClickListener() {
-
- @Override
- public void onClick(View v) {
-
- final DbHandler dbh = new DbHandler(getApplicationContext());
- try {
- List<CheckBox> list = getCheckboxList();
- for (CheckBox checkbox : list) {
- boolean to_be_delete = checkbox.isChecked();
-
- Log_OC.d(LOG_TAG, "Checkbox for " + checkbox.getId() + " was checked: " + to_be_delete);
- String img_path = fileList.get(checkbox.getId());
- Log_OC.d(LOG_TAG, "Image-Path " + checkbox.getId() + " was checked: " + img_path);
- if (to_be_delete) {
- boolean deleted = dbh.removeIUPendingFile(img_path);
- Log_OC.d(LOG_TAG, "removing " + checkbox.getId() + " was : " + deleted);
-
- }
-
- }
- } finally {
- dbh.close();
- // refresh the List
- listView.removeAllViews();
- loadListView(true);
- if (failed_upload_all_cb != null) {
- failed_upload_all_cb.setChecked(false);
- }
- }
-
- }
- };
- }
-
- private LinearLayout getHorizontalLinearLayout(int id) {
- LinearLayout linearLayout = new LinearLayout(getApplicationContext());
- linearLayout.setId(id);
- linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT));
- linearLayout.setGravity(Gravity.RIGHT);
- linearLayout.setOrientation(LinearLayout.HORIZONTAL);
- return linearLayout;
- }
-
- private LinearLayout getVerticalLinearLayout() {
- LinearLayout linearLayout = new LinearLayout(getApplicationContext());
- linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.MATCH_PARENT));
- linearLayout.setGravity(Gravity.TOP);
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- return linearLayout;
- }
-
- private View getFileButton(final String img_path, String message, int id) {
-
- TextView failureTextView = new TextView(this);
- failureTextView.setText(getString(R.string.failed_upload_failure_text) + message);
- failureTextView.setBackgroundResource(R.color.background_color);
- failureTextView.setTextSize(8);
- failureTextView.setOnLongClickListener(getOnLongClickListener(message));
- failureTextView.setPadding(5, 5, 5, 10);
- TextView retryButton = new TextView(this);
- retryButton.setId(id);
- retryButton.setText(img_path);
- retryButton.setBackgroundResource(R.color.background_color);
- retryButton.setTextSize(8);
- retryButton.setOnClickListener(getImageButtonOnClickListener(img_path));
- retryButton.setOnLongClickListener(getOnLongClickListener(message));
- retryButton.setPadding(5, 5, 5, 10);
- LinearLayout verticalLayout = getVerticalLinearLayout();
- verticalLayout.addView(retryButton);
- verticalLayout.addView(failureTextView);
-
- return verticalLayout;
- }
-
- private OnLongClickListener getOnLongClickListener(final String message) {
- return new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- Log_OC.d(LOG_TAG, message);
- Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
- + message, Toast.LENGTH_LONG);
- toast.show();
- return true;
- }
-
- };
- }
-
- private CheckBox getFileCheckbox(int id) {
- CheckBox retryCB = new CheckBox(this);
- retryCB.setId(id);
- retryCB.setBackgroundResource(R.color.background_color);
- retryCB.setTextSize(8);
- retryCB.setTag(retry_chexbox_tag);
- return retryCB;
- }
-
- private ImageButton getImageButton(String img_path, int id) {
- ImageButton imageButton = new ImageButton(this);
- imageButton.setId(id);
- imageButton.setClickable(true);
- imageButton.setOnClickListener(getImageButtonOnClickListener(img_path));
-
- // scale and add a thumbnail to the imagebutton
- int base_scale_size = 32;
- if (img_path != null) {
- Log_OC.d(LOG_TAG, "add " + img_path + " to Image Button");
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- Bitmap bitmap = BitmapFactory.decodeFile(img_path, options);
- int width_tpm = options.outWidth, height_tmp = options.outHeight;
- int scale = 3;
- while (true) {
- if (width_tpm / 2 < base_scale_size || height_tmp / 2 < base_scale_size) {
- break;
- }
- width_tpm /= 2;
- height_tmp /= 2;
- scale++;
- }
-
- Log_OC.d(LOG_TAG, "scale Imgae with: " + scale);
- BitmapFactory.Options options2 = new BitmapFactory.Options();
- options2.inSampleSize = scale;
- bitmap = BitmapFactory.decodeFile(img_path, options2);
-
- if (bitmap != null) {
- Log_OC.d(LOG_TAG, "loaded Bitmap Bytes: " + bitmap.getRowBytes());
- imageButton.setImageBitmap(bitmap);
- } else {
- Log_OC.d(LOG_TAG, "could not load imgage: " + img_path);
- }
- }
- return imageButton;
- }
-
- private OnClickListener getImageButtonOnClickListener(final String img_path) {
- return new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- startUpload(img_path);
- loadListView(true);
- }
-
- };
- }
-
- /**
- * start uploading a file to the INSTANT_UPLOD_DIR
- *
- * @param img_path
- */
- private void startUpload(String img_path) {
- // extract filename
- String filename = FileStorageUtils.getInstantUploadFilePath(this, img_path);
- if (canInstantUpload()) {
- Account account = AccountUtils.getCurrentOwnCloudAccount(InstantUploadActivity.this);
- // add file again to upload queue
- DbHandler db = new DbHandler(InstantUploadActivity.this);
- try {
- db.updateFileState(img_path, DbHandler.UPLOAD_STATUS_UPLOAD_LATER, null);
- } finally {
- db.close();
- }
-
- Intent i = new Intent(InstantUploadActivity.this, FileUploader.class);
- i.putExtra(FileUploader.KEY_ACCOUNT, account);
- i.putExtra(FileUploader.KEY_LOCAL_FILE, img_path);
- i.putExtra(FileUploader.KEY_REMOTE_FILE, filename);
- i.putExtra(FileUploader.KEY_UPLOAD_TYPE, FileUploader.UPLOAD_SINGLE_FILE);
- i.putExtra(de.mobilcom.debitel.cloud.android.files.services.FileUploader.KEY_INSTANT_UPLOAD, true);
-
- final String msg = "try to upload file with name :" + filename;
- Log_OC.d(LOG_TAG, msg);
- Toast toast = Toast.makeText(InstantUploadActivity.this, getString(R.string.failed_upload_retry_text)
- + filename, Toast.LENGTH_LONG);
- toast.show();
-
- startService(i);
- } else {
- Toast toast = Toast.makeText(InstantUploadActivity.this,
- getString(R.string.failed_upload_retry_do_nothing_text) + filename, Toast.LENGTH_LONG);
- toast.show();
- }
- }
-
- private boolean canInstantUpload() {
-
- if (!InstantUploadBroadcastReceiver.isOnline(this)
- || (InstantUploadBroadcastReceiver.instantUploadViaWiFiOnly(this) && !InstantUploadBroadcastReceiver
- .isConnectedViaWiFi(this))) {
- return false;
- } else {
- return true;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui.activity;\r
-\r
-import com.actionbarsherlock.app.SherlockFragmentActivity;\r
-\r
-import android.accounts.Account;\r
-import android.accounts.AccountManager;\r
-import android.app.AlertDialog;\r
-import android.app.Dialog;\r
-import android.content.DialogInterface;\r
-import android.content.DialogInterface.OnClickListener;\r
-import android.content.Intent;\r
-import android.os.Bundle;\r
-import android.view.View;\r
-import android.widget.AdapterView;\r
-import android.widget.AdapterView.OnItemClickListener;\r
-import android.widget.GridView;\r
-import android.widget.Toast;\r
-\r
-import de.mobilcom.debitel.cloud.android.MainApp;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.adapter.LandingScreenAdapter;\r
-\r
-/**\r
- * This activity is used as a landing page when the user first opens this app.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingActivity extends SherlockFragmentActivity implements\r
- OnClickListener, OnItemClickListener {\r
-\r
- public static final int DIALOG_SETUP_ACCOUNT = 1;\r
-\r
- @Override\r
- protected void onCreate(Bundle savedInstanceState) {\r
- super.onCreate(savedInstanceState);\r
- setContentView(R.layout.main);\r
-\r
- // Fill the grid view of the landing screen with icons\r
- GridView landingScreenItems = (GridView) findViewById(R.id.homeScreenGrid);\r
- landingScreenItems.setAdapter(new LandingScreenAdapter(this));\r
- landingScreenItems.setOnItemClickListener(this);\r
-\r
- // Check, if there are ownCloud accounts\r
- if (!accountsAreSetup()) {\r
- showDialog(DIALOG_SETUP_ACCOUNT);\r
- } else {\r
- // Start device tracking service\r
- Intent locationServiceIntent = new Intent();\r
- locationServiceIntent\r
- .setAction("de.mobilcom.debitel.cloud.android.location.LocationLauncher");\r
- sendBroadcast(locationServiceIntent);\r
- }\r
-\r
- }\r
-\r
- @Override\r
- protected void onRestart() {\r
- super.onRestart();\r
- // Check, if there are ownCloud accounts\r
- if (!accountsAreSetup()) {\r
- showDialog(DIALOG_SETUP_ACCOUNT);\r
- }\r
- }\r
-\r
- @Override\r
- protected void onRestoreInstanceState(Bundle savedInstanceState) {\r
- super.onRestoreInstanceState(savedInstanceState);\r
- // Check, if there are ownCloud accounts\r
- if (!accountsAreSetup()) {\r
- showDialog(DIALOG_SETUP_ACCOUNT);\r
- }\r
- }\r
-\r
- @Override\r
- protected Dialog onCreateDialog(int id) {\r
- Dialog dialog;\r
- switch (id) {\r
- case DIALOG_SETUP_ACCOUNT:\r
- AlertDialog.Builder builder = new AlertDialog.Builder(this);\r
- builder.setTitle(R.string.main_tit_accsetup);\r
- builder.setMessage(R.string.main_wrn_accsetup);\r
- builder.setCancelable(false);\r
- builder.setPositiveButton(R.string.common_ok, this);\r
- builder.setNegativeButton(R.string.common_cancel, this);\r
- dialog = builder.create();\r
- break;\r
- default:\r
- dialog = null;\r
- }\r
-\r
- return dialog;\r
- }\r
-\r
- public void onClick(DialogInterface dialog, int which) {\r
- // In any case - we won't need it anymore\r
- dialog.dismiss();\r
- switch (which) {\r
- case DialogInterface.BUTTON_POSITIVE:\r
- Intent intent = new Intent(android.provider.Settings.ACTION_ADD_ACCOUNT);\r
- intent.putExtra("authorities",\r
- new String[] { MainApp.getAuthTokenType() });\r
- startActivity(intent);\r
- break;\r
- case DialogInterface.BUTTON_NEGATIVE:\r
- finish();\r
- }\r
-\r
- }\r
-\r
- @Override\r
- /**\r
- * Start an activity based on the selection\r
- * the user made\r
- */\r
- public void onItemClick(AdapterView<?> parent, View view, int position,\r
- long id) {\r
- Intent intent;\r
- intent = (Intent) parent.getAdapter().getItem(position);\r
- if (intent != null) {\r
- startActivity(intent);\r
- } else {\r
- // TODO: Implement all of this and make this text go away ;-)\r
- Toast toast = Toast.makeText(this, "Not yet implemented!",\r
- Toast.LENGTH_SHORT);\r
- toast.show();\r
- }\r
- }\r
-\r
- /**\r
- * Checks, whether or not there are any ownCloud accounts setup.\r
- * \r
- * @return true, if there is at least one account.\r
- */\r
- private boolean accountsAreSetup() {\r
- AccountManager accMan = AccountManager.get(this);\r
- Account[] accounts = accMan\r
- .getAccountsByType(MainApp.getAccountType());\r
- return accounts.length > 0;\r
- }\r
-\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ListView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockPreferenceActivity;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.adapter.LogListAdapter;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-
-
-public class LogHistoryActivity extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
- String logpath = FileStorageUtils.getLogPath();
- File logDIR = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.log_send_file);
- setTitle("Log History");
- ActionBar actionBar = getSherlock().getActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- ListView listView = (ListView) findViewById(android.R.id.list);
- CustomButton deleteHistoryButton = (CustomButton) findViewById(R.id.deleteLogHistoryButton);
-
- deleteHistoryButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- File dir = new File(logpath);
- if (dir != null) {
- File[] files = dir.listFiles();
- if(files!=null) {
- for(File f: files) {
- f.delete();
- }
- }
- dir.delete();
- }
- Intent intent = new Intent(getBaseContext(), Preferences.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- }
-
- });
-
-
- if(logpath != null){
- logDIR = new File(logpath);
- }
-
- if(logDIR != null && logDIR.isDirectory()) {
- File[] files = logDIR.listFiles();
-
- if (files != null && files.length != 0) {
- ArrayList<String> logfiles_name = new ArrayList<String>();
- for (File file : files) {
- logfiles_name.add(file.getName());
- }
- String[] logFiles2Array = logfiles_name.toArray(new String[logfiles_name.size()]);
- LogListAdapter listadapter = new LogListAdapter(this,logFiles2Array);
- listView.setAdapter(listadapter);
- }
- }
- }
-
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- super.onMenuItemSelected(featureId, item);
- Intent intent;
-
- switch (item.getItemId()) {
-
- case android.R.id.home:
- intent = new Intent(getBaseContext(), Preferences.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- break;
- default:
- return false;
- }
- return true;
- }
- @Override
- public boolean onPreferenceChange(Preference arg0, Object arg1) {
- return false;
- }
-}
\ No newline at end of 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 version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.Arrays;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.preference.PreferenceManager;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.View.OnKeyListener;
-import android.widget.EditText;
-import android.widget.TextView;
-
-public class PinCodeActivity extends SherlockFragmentActivity {
-
-
- public final static String EXTRA_ACTIVITY = "de.mobilcom.debitel.cloud.android.ui.activity.PinCodeActivity.ACTIVITY";
- public final static String EXTRA_NEW_STATE = "de.mobilcom.debitel.cloud.android.ui.activity.PinCodeActivity.NEW_STATE";
-
- CustomButton bCancel;
- TextView mPinHdr;
- TextView mPinHdrExplanation;
- EditText mText1;
- EditText mText2;
- EditText mText3;
- EditText mText4;
-
- String [] tempText ={"","","",""};
-
- String activity;
-
- boolean confirmingPinCode = false;
- boolean pinCodeChecked = false;
- boolean newPasswordEntered = false;
- boolean bChange = true; // to control that only one blocks jump
- int tCounter ; // Count the number of attempts an user could introduce the PIN code
-
-
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pincodelock);
-
- Intent intent = getIntent();
- activity = intent.getStringExtra(EXTRA_ACTIVITY);
-
- bCancel = (CustomButton) findViewById(R.id.cancel);
- mPinHdr = (TextView) findViewById(R.id.pinHdr);
- mPinHdrExplanation = (TextView) findViewById(R.id.pinHdrExpl);
- mText1 = (EditText) findViewById(R.id.txt1);
- mText1.requestFocus();
- getWindow().setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- mText2 = (EditText) findViewById(R.id.txt2);
- mText3 = (EditText) findViewById(R.id.txt3);
- mText4 = (EditText) findViewById(R.id.txt4);
-
- SharedPreferences appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
-
- // Not PIN Code defined yet.
- // In a previous version settings is allow from start
- if ( (appPrefs.getString("PrefPinCode1", null) == null ) ){
- setChangePincodeView(true);
- pinCodeChecked = true;
- newPasswordEntered = true;
-
- }else{
-
- if (appPrefs.getBoolean("set_pincode", false)){
- // pincode activated
- if (activity.equals("preferences")){
- // PIN has been activated yet
- mPinHdr.setText(R.string.pincode_configure_your_pin);
- mPinHdrExplanation.setVisibility(View.VISIBLE);
- pinCodeChecked = true ; // No need to check it
- setChangePincodeView(true);
- }else{
- // PIN active
- bCancel.setVisibility(View.INVISIBLE);
- bCancel.setVisibility(View.GONE);
- mPinHdr.setText(R.string.pincode_enter_pin_code);
- mPinHdrExplanation.setVisibility(View.INVISIBLE);
- setChangePincodeView(false);
- }
-
- }else {
- // pincode removal
- mPinHdr.setText(R.string.pincode_remove_your_pincode);
- mPinHdrExplanation.setVisibility(View.INVISIBLE);
- pinCodeChecked = false;
- setChangePincodeView(true);
- }
-
- }
- setTextListeners();
-
-
- }
-
-
-
- protected void setInitVars(){
- confirmingPinCode = false;
- pinCodeChecked = false;
- newPasswordEntered = false;
-
- }
-
- protected void setInitView(){
- bCancel.setVisibility(View.INVISIBLE);
- bCancel.setVisibility(View.GONE);
- mPinHdr.setText(R.string.pincode_enter_pin_code);
- mPinHdrExplanation.setVisibility(View.INVISIBLE);
- }
-
-
- protected void setChangePincodeView(boolean state){
-
- if(state){
- bCancel.setVisibility(View.VISIBLE);
- bCancel.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
-
- SharedPreferences.Editor appPrefsE = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
-
- SharedPreferences appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
- boolean state = appPrefs.getBoolean("set_pincode", false);
- appPrefsE.putBoolean("set_pincode",!state);
- appPrefsE.commit();
- setInitVars();
- finish();
- }
- });
- }
-
- }
-
-
-
- /*
- *
- */
- protected void setTextListeners(){
-
- /*------------------------------------------------
- * FIRST BOX
- -------------------------------------------------*/
-
- mText1.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[0] = mText1.getText().toString();
-
- }
- mText2.requestFocus();
- }
- }
- });
-
-
-
- /*------------------------------------------------
- * SECOND BOX
- -------------------------------------------------*/
- mText2.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[1] = mText2.getText().toString();
- }
-
- mText3.requestFocus();
- }
- }
- });
-
- mText2.setOnKeyListener(new OnKeyListener() {
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
-
- mText1.setText("");
- mText1.requestFocus();
- if (!confirmingPinCode)
- tempText[0] = "";
- bChange= false;
-
- }else if(!bChange){
- bChange=true;
-
- }
- return false;
- }
- });
-
- mText2.setOnFocusChangeListener(new OnFocusChangeListener() {
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mText2.setCursorVisible(true);
- if (mText1.getText().toString().equals("")){
- mText2.setSelected(false);
- mText2.setCursorVisible(false);
- mText1.requestFocus();
- mText1.setSelected(true);
- mText1.setSelection(0);
- }
-
- }
- });
-
-
- /*------------------------------------------------
- * THIRD BOX
- -------------------------------------------------*/
- mText3.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[2] = mText3.getText().toString();
- }
- mText4.requestFocus();
- }
- }
- });
-
- mText3.setOnKeyListener(new OnKeyListener() {
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
- mText2.requestFocus();
- if (!confirmingPinCode)
- tempText[1] = "";
- mText2.setText("");
- bChange= false;
-
- }else if(!bChange){
- bChange=true;
-
- }
- return false;
- }
- });
-
- mText3.setOnFocusChangeListener(new OnFocusChangeListener() {
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mText3.setCursorVisible(true);
- if (mText1.getText().toString().equals("")){
- mText3.setSelected(false);
- mText3.setCursorVisible(false);
- mText1.requestFocus();
- mText1.setSelected(true);
- mText1.setSelection(0);
- }else if (mText2.getText().toString().equals("")){
- mText3.setSelected(false);
- mText3.setCursorVisible(false);
- mText2.requestFocus();
- mText2.setSelected(true);
- mText2.setSelection(0);
- }
-
- }
- });
-
- /*------------------------------------------------
- * FOURTH BOX
- -------------------------------------------------*/
- mText4.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (s.length() > 0) {
-
- if (!confirmingPinCode){
- tempText[3] = mText4.getText().toString();
- }
- mText1.requestFocus();
-
- if (!pinCodeChecked){
- pinCodeChecked = checkPincode();
- }
-
- if (pinCodeChecked && activity.equals("FileDisplayActivity")){
- finish();
- } else if (pinCodeChecked){
-
- Intent intent = getIntent();
- String newState = intent.getStringExtra(EXTRA_NEW_STATE);
-
- if (newState.equals("false")){
- SharedPreferences.Editor appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- appPrefs.putBoolean("set_pincode",false);
- appPrefs.commit();
-
- setInitVars();
- pinCodeEnd(false);
-
- }else{
-
- if (!confirmingPinCode){
- pinCodeChangeRequest();
-
- } else {
- confirmPincode();
- }
- }
-
-
- }
- }
- }
- });
-
-
-
- mText4.setOnKeyListener(new OnKeyListener() {
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
- mText3.requestFocus();
- if (!confirmingPinCode)
- tempText[2]="";
- mText3.setText("");
- bChange= false;
-
- }else if(!bChange){
- bChange=true;
- }
- return false;
- }
- });
-
- mText4.setOnFocusChangeListener(new OnFocusChangeListener() {
-
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- mText4.setCursorVisible(true);
-
- if (mText1.getText().toString().equals("")){
- mText4.setSelected(false);
- mText4.setCursorVisible(false);
- mText1.requestFocus();
- mText1.setSelected(true);
- mText1.setSelection(0);
- }else if (mText2.getText().toString().equals("")){
- mText4.setSelected(false);
- mText4.setCursorVisible(false);
- mText2.requestFocus();
- mText2.setSelected(true);
- mText2.setSelection(0);
- }else if (mText3.getText().toString().equals("")){
- mText4.setSelected(false);
- mText4.setCursorVisible(false);
- mText3.requestFocus();
- mText3.setSelected(true);
- mText3.setSelection(0);
- }
-
- }
- });
-
-
-
- } // end setTextListener
-
-
- protected void pinCodeChangeRequest(){
-
- clearBoxes();
- mPinHdr.setText(R.string.pincode_reenter_your_pincode);
- mPinHdrExplanation.setVisibility(View.INVISIBLE);
- confirmingPinCode =true;
-
- }
-
-
- protected boolean checkPincode(){
-
-
- SharedPreferences appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
- String pText1 = appPrefs.getString("PrefPinCode1", null);
- String pText2 = appPrefs.getString("PrefPinCode2", null);
- String pText3 = appPrefs.getString("PrefPinCode3", null);
- String pText4 = appPrefs.getString("PrefPinCode4", null);
-
- if ( tempText[0].equals(pText1) &&
- tempText[1].equals(pText2) &&
- tempText[2].equals(pText3) &&
- tempText[3].equals(pText4) ) {
-
- return true;
-
-
- }else {
- Arrays.fill(tempText, null);
- AlertDialog aDialog = new AlertDialog.Builder(this).create();
- CharSequence errorSeq = getString(R.string.common_error);
- aDialog.setTitle(errorSeq);
- CharSequence cseq = getString(R.string.pincode_wrong);
- aDialog.setMessage(cseq);
- CharSequence okSeq = getString(R.string.common_ok);
- aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- return;
- }
-
- });
- aDialog.show();
- clearBoxes();
- mPinHdr.setText(R.string.pincode_enter_pin_code);
- mPinHdrExplanation.setVisibility(View.INVISIBLE);
- newPasswordEntered = true;
- confirmingPinCode = false;
-
- }
-
-
- return false;
- }
-
- protected void confirmPincode(){
-
- confirmingPinCode = false;
-
- String rText1 = mText1.getText().toString();
- String rText2 = mText2.getText().toString();
- String rText3 = mText3.getText().toString();
- String rText4 = mText4.getText().toString();
-
- if ( tempText[0].equals(rText1) &&
- tempText[1].equals(rText2) &&
- tempText[2].equals(rText3) &&
- tempText[3].equals(rText4) ) {
-
- savePincodeAndExit();
-
- } else {
-
- Arrays.fill(tempText, null);
- AlertDialog aDialog = new AlertDialog.Builder(this).create();
- CharSequence errorSeq = getString(R.string.common_error);
- aDialog.setTitle(errorSeq);
- CharSequence cseq = getString(R.string.pincode_mismatch);
- aDialog.setMessage(cseq);
- CharSequence okSeq = getString(R.string.common_ok);
- aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- return;
- }
-
- });
- aDialog.show();
- mPinHdr.setText(R.string.pincode_configure_your_pin);
- mPinHdrExplanation.setVisibility(View.VISIBLE);
- clearBoxes();
- }
-
- }
-
-
- protected void pinCodeEnd(boolean state){
- AlertDialog aDialog = new AlertDialog.Builder(this).create();
-
- if (state){
- CharSequence saveSeq = getString(R.string.common_save_exit);
- aDialog.setTitle(saveSeq);
- CharSequence cseq = getString(R.string.pincode_stored);
- aDialog.setMessage(cseq);
-
- }else{
- CharSequence saveSeq = getString(R.string.common_save_exit);
- aDialog.setTitle(saveSeq);
- CharSequence cseq = getString(R.string.pincode_removed);
- aDialog.setMessage(cseq);
-
- }
- CharSequence okSeq = getString(R.string.common_ok);
- aDialog.setButton(okSeq, new DialogInterface.OnClickListener(){
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- return;
- }
-
- });
- aDialog.show();
- }
-
- protected void savePincodeAndExit(){
- SharedPreferences.Editor appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
-
- appPrefs.putString("PrefPinCode1", tempText[0]);
- appPrefs.putString("PrefPinCode2",tempText[1]);
- appPrefs.putString("PrefPinCode3", tempText[2]);
- appPrefs.putString("PrefPinCode4", tempText[3]);
- appPrefs.putBoolean("set_pincode",true);
- appPrefs.commit();
-
- pinCodeEnd(true);
-
-
-
- }
-
-
- protected void clearBoxes(){
-
- mText1.setText("");
- mText2.setText("");
- mText3.setText("");
- mText4.setText("");
- mText1.requestFocus();
- }
-
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event){
- if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount()== 0){
-
- if (activity.equals("preferences")){
- SharedPreferences.Editor appPrefsE = PreferenceManager
-
- .getDefaultSharedPreferences(getApplicationContext()).edit();
-
- SharedPreferences appPrefs = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext());
-
- boolean state = appPrefs.getBoolean("set_pincode", false);
- appPrefsE.putBoolean("set_pincode",!state);
- appPrefsE.commit();
- setInitVars();
- finish();
- }
- return true;
-
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
-
-
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.util.Vector;
-
-import android.accounts.Account;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.net.Uri;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceCategory;
-import android.preference.PreferenceManager;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockPreferenceActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.OwnCloudSession;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.db.DbHandler;
-
-/**
- * An Activity that allows the user to change the application's settings.
- *
- * @author Bartek Przybylski
- *
- */
-public class Preferences extends SherlockPreferenceActivity implements OnPreferenceChangeListener {
-
- private static final String TAG = "OwnCloudPreferences";
- private final int mNewSession = 47;
- private final int mEditSession = 48;
- private DbHandler mDbHandler;
- private Vector<OwnCloudSession> mSessions;
- private ListPreference mTrackingUpdateInterval;
- private CheckBoxPreference mDeviceTracking;
- private CheckBoxPreference pCode;
- //private CheckBoxPreference pLogging;
- //private Preference pLoggingHistory;
- private Preference pAboutApp;
- private int mSelectedMenuItem;
-
-
- @SuppressWarnings("deprecation")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mDbHandler = new DbHandler(getBaseContext());
- mSessions = new Vector<OwnCloudSession>();
- addPreferencesFromResource(R.xml.preferences);
- //populateAccountList();
- ActionBar actionBar = getSherlock().getActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
-
- Preference p = findPreference("manage_account");
- if (p != null)
- p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- Intent i = new Intent(getApplicationContext(), AccountSelectActivity.class);
- startActivity(i);
- return true;
- }
- });
-
- pCode = (CheckBoxPreference) findPreference("set_pincode");
- if (pCode != null){
- pCode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
- i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "preferences");
- i.putExtra(PinCodeActivity.EXTRA_NEW_STATE, newValue.toString());
- startActivity(i);
-
- return true;
- }
- });
-
- }
-
-
-
- PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
-
- boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
- Preference pHelp = findPreference("help");
- if (pHelp != null ){
- if (helpEnabled) {
- pHelp.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- String helpWeb =(String) getText(R.string.url_help);
- if (helpWeb != null && helpWeb.length() > 0) {
- Uri uriUrl = Uri.parse(helpWeb);
- Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
- startActivity(intent);
- }
- return true;
- }
- });
- } else {
- preferenceCategory.removePreference(pHelp);
- }
-
- }
-
-
- boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
- Preference pRecommend = findPreference("recommend");
- if (pRecommend != null){
- if (recommendEnabled) {
- pRecommend.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
-
- Intent intent = new Intent(Intent.ACTION_SENDTO);
- intent.setType("text/plain");
- Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
- String appName = getString(R.string.app_name);
- String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
- String recommendSubject = String.format(getString(R.string.recommend_subject), username, appName);
- //String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
- intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
- String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), username);
- //String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), getString(R.string.url_app_download));
- intent.putExtra(Intent.EXTRA_TEXT, recommendText);
-
- intent.setData(Uri.parse(getString(R.string.mail_recommend)));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
-
-
- return(true);
-
- }
- });
- } else {
- preferenceCategory.removePreference(pRecommend);
- }
-
- }
-
- boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
- Preference pFeedback = findPreference("feedback");
- if (pFeedback != null){
- if (feedbackEnabled) {
- pFeedback.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- String feedbackMail =(String) getText(R.string.mail_feedback);
- String feedback =(String) getText(R.string.prefs_feedback);
- Intent intent = new Intent(Intent.ACTION_SENDTO);
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
-
- intent.setData(Uri.parse(feedbackMail));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
-
- return true;
- }
- });
- } else {
- preferenceCategory.removePreference(pFeedback);
- }
-
- }
-
- boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled);
- Preference pImprint = findPreference("imprint");
- if (pImprint != null) {
- if (imprintEnabled) {
- pImprint.setOnPreferenceClickListener(new OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- String imprintWeb = (String) getText(R.string.url_imprint);
- if (imprintWeb != null && imprintWeb.length() > 0) {
- Uri uriUrl = Uri.parse(imprintWeb);
- Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
- startActivity(intent);
- }
- //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
- return true;
- }
- });
- } else {
- preferenceCategory.removePreference(pImprint);
- }
- }
-
- /* About App */
- pAboutApp = (Preference) findPreference("about_app");
- if (pAboutApp != null) {
- pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
- PackageInfo pkg;
- try {
- pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
- pAboutApp.setSummary(String.format(getString(R.string.about_version), pkg.versionName));
- } catch (NameNotFoundException e) {
- Log_OC.e(TAG, "Error while showing about dialog", e);
- }
- }
-
- /* DISABLED FOR RELEASE UNTIL FIXED
- pLogging = (CheckBoxPreference) findPreference("log_to_file");
- if (pLogging != null) {
- pLogging.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
-
- String logpath = Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log";
-
- if(!pLogging.isChecked()) {
- Log_OC.d("Debug", "start logging");
- Log_OC.v("PATH", logpath);
- Log_OC.startLogging(logpath);
- }
- else {
- Log_OC.d("Debug", "stop logging");
- Log_OC.stopLogging();
- }
- return true;
- }
- });
- }
-
- pLoggingHistory = (Preference) findPreference("log_history");
- if (pLoggingHistory != null) {
- pLoggingHistory.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- Intent intent = new Intent(getApplicationContext(),LogHistoryActivity.class);
- startActivity(intent);
- return true;
- }
- });
- }
- */
-
- }
-
- @Override
- protected void onResume() {
- SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- boolean state = appPrefs.getBoolean("set_pincode", false);
- pCode.setChecked(state);
- super.onResume();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- return true;
- }
-
- @Override
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- super.onMenuItemSelected(featureId, item);
- Intent intent;
-
- switch (item.getItemId()) {
- //case R.id.addSessionItem:
- case 1:
- 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 android.R.id.home:
- intent = new Intent(getBaseContext(), FileDisplayActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- break;
- default:
- Log_OC.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);
- }
-
- @Override
- protected void onDestroy() {
- mDbHandler.close();
- super.onDestroy();
- }
-
- @Override
- /**
- * Updates various summaries after updates. Also starts and stops
- * the
- */
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- // Update current account summary
- /*if (preference.equals(mAccountList)) {
- mAccountList.setSummary(newValue.toString());
- }
-
- // Update tracking interval summary
- else*/ if (preference.equals(mTrackingUpdateInterval)) {
- String trackingSummary = getResources().getString(
- R.string.prefs_trackmydevice_interval_summary);
- trackingSummary = String.format(trackingSummary,
- newValue.toString());
- mTrackingUpdateInterval.setSummary(trackingSummary);
- }
-
- // Start or stop tracking service
- else if (preference.equals(mDeviceTracking)) {
- Intent locationServiceIntent = new Intent();
- locationServiceIntent
- .setAction("de.mobilcom.debitel.cloud.android.location.LocationLauncher");
- locationServiceIntent.putExtra("TRACKING_SETTING",
- (Boolean) newValue);
- sendBroadcast(locationServiceIntent);
- }
- return true;
- }
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-\r
-package de.mobilcom.debitel.cloud.android.ui.activity;\r
-\r
-import android.accounts.AccountAuthenticatorActivity;\r
-import android.app.Activity;\r
-import android.os.Bundle;\r
-import android.view.View;\r
-import android.view.View.OnClickListener;\r
-\r
-public class PreferencesNewSession extends AccountAuthenticatorActivity\r
- implements OnClickListener {\r
- @Override\r
- public void onCreate(Bundle savedInstanceState) {\r
- super.onCreate(savedInstanceState);\r
- // setContentView(R.layout.add_new_session);\r
- /*\r
- * EditText et;// = (EditText)\r
- * findViewById(R.id.newSession_sessionName);\r
- * \r
- * et = (EditText) findViewById(R.id.newSession_URL); if\r
- * (getIntent().hasExtra("sessionURL")) { try { URI uri = new\r
- * URI(getIntent().getStringExtra("sessionURL")); String url =\r
- * uri.getHost(); if (uri.getPort() != -1) { url += ":" +\r
- * String.valueOf(uri.getPort()); } if (uri.getPath() != null) { url +=\r
- * uri.getPath(); } else { url += "/"; } et.setText(url); et =\r
- * (EditText) findViewById(R.id.newSession_username); if\r
- * (uri.getAuthority() != null) { if (uri.getUserInfo().indexOf(':') !=\r
- * -1) { et.setText(uri.getUserInfo().substring(0,\r
- * uri.getUserInfo().indexOf(':'))); et = (EditText)\r
- * findViewById(R.id.newSession_password);\r
- * et.setText(uri.getUserInfo().substring\r
- * (uri.getUserInfo().indexOf(':')+1)); } else {\r
- * et.setText(uri.getUserInfo()); } }\r
- * \r
- * } catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
- * e.getLocalizedMessage()); } }\r
- * \r
- * mReturnData = new Intent(); setResult(Activity.RESULT_OK,\r
- * mReturnData); ((Button)\r
- * findViewById(R.id.button1)).setOnClickListener(this); ((Button)\r
- * findViewById(R.id.button2)).setOnClickListener(this);\r
- */\r
- }\r
-\r
- @Override\r
- protected void onResume() {\r
- super.onResume();\r
- }\r
-\r
- public void onClick(View v) {\r
- /*\r
- * switch (v.getId()) { case R.id.button1: Intent intent = new Intent();\r
- * if (getIntent().hasExtra("sessionId")) { intent.putExtra("sessionId",\r
- * getIntent().getIntExtra("sessionId", -1)); } //String sessionName =\r
- * ((EditText)\r
- * findViewById(R.id.newSession_sessionName)).getText().toString(); //\r
- * if (sessionName.trim().equals("") || !isNameValid(sessionName)) { //\r
- * Toast.makeText(this, R.string.new_session_session_name_error,\r
- * Toast.LENGTH_LONG).show(); // break; // } URI uri = prepareURI(); if\r
- * (uri != null) { //intent.putExtra("sessionName", sessionName);\r
- * intent.putExtra("sessionURL", uri.toString());\r
- * setResult(Activity.RESULT_OK, intent); AccountManager accMgr =\r
- * AccountManager.get(this); Account a = new Account("OwnCloud",\r
- * AccountAuthenticatorService.ACCOUNT_TYPE);\r
- * accMgr.addAccountExplicitly(a, "asd", null); finish(); } break; case\r
- * R.id.button2: setResult(Activity.RESULT_CANCELED); finish(); break; }\r
- */\r
- }\r
-\r
- /*\r
- * private URI prepareURI() { URI uri = null; String url = ""; try { String\r
- * username = ((EditText)\r
- * findViewById(R.id.newSession_username)).getText().toString().trim();\r
- * String password = ((EditText)\r
- * findViewById(R.id.newSession_password)).getText().toString().trim();\r
- * String hostname = ((EditText)\r
- * findViewById(R.id.newSession_URL)).getText().toString().trim(); String\r
- * scheme; if (hostname.matches("[A-Za-z]://")) { scheme =\r
- * hostname.substring(0, hostname.indexOf("://")+3); hostname =\r
- * hostname.substring(hostname.indexOf("://")+3); } else { scheme =\r
- * "http://"; } if (!username.equals("")) { if (!password.equals("")) {\r
- * username += ":" + password + "@"; } else { username += "@"; } } url =\r
- * scheme + username + hostname; Log.i(TAG, url); uri = new URI(url); }\r
- * catch (URISyntaxException e) { Log.e(TAG, "Incorrect URI syntax " +\r
- * e.getLocalizedMessage()); Toast.makeText(this,\r
- * R.string.new_session_uri_error, Toast.LENGTH_LONG).show(); } return uri;\r
- * }\r
- * \r
- * private boolean isNameValid(String string) { return\r
- * string.matches("[A-Za-z0-9 _-]*"); }\r
- */\r
-\r
- @Override\r
- public void onBackPressed() {\r
- setResult(Activity.RESULT_CANCELED);\r
- super.onBackPressed();\r
- }\r
-\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-
-public interface TransferServiceGetter {
-
- /**
- * Callback method invoked when the parent activity is fully created to get a reference to the FileDownloader service API.
- *
- * @return Directory to list firstly. Can be NULL.
- */
- public FileDownloaderBinder getFileDownloaderBinder();
-
-
- /**
- * Callback method invoked when the parent activity is fully created to get a reference to the FileUploader service API.
- *
- * @return Directory to list firstly. Can be NULL.
- */
- public FileUploaderBinder getFileUploaderBinder();
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.activity;
-
-import java.io.File;
-
-import android.accounts.Account;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Environment;
-import android.support.v4.app.DialogFragment;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-import de.mobilcom.debitel.cloud.android.ui.dialog.IndeterminateProgressDialog;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.LocalFileListFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import de.mobilcom.debitel.cloud.android.utils.FileStorageUtils;
-
-/**
- * Displays local files and let the user choose what of them wants to upload
- * to the current ownCloud account
- *
- * @author David A. Velasco
- *
- */
-
-public class UploadFilesActivity extends FileActivity implements
- LocalFileListFragment.ContainerActivity, OnNavigationListener, OnClickListener, ConfirmationDialogFragmentListener {
-
- private ArrayAdapter<String> mDirectories;
- private File mCurrentDir = null;
- private LocalFileListFragment mFileListFragment;
- private CustomButton mCancelBtn;
- private CustomButton mUploadBtn;
- private Account mAccountOnCreation;
- private DialogFragment mCurrentDialog;
-
- public static final String EXTRA_CHOSEN_FILES = UploadFilesActivity.class.getCanonicalName() + ".EXTRA_CHOSEN_FILES";
-
- public static final int RESULT_OK_AND_MOVE = RESULT_FIRST_USER;
-
- private static final String KEY_DIRECTORY_PATH = UploadFilesActivity.class.getCanonicalName() + ".KEY_DIRECTORY_PATH";
- private static final String TAG = "UploadFilesActivity";
- private static final String WAIT_DIALOG_TAG = "WAIT";
- private static final String QUERY_TO_MOVE_DIALOG_TAG = "QUERY_TO_MOVE";
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate() start");
- super.onCreate(savedInstanceState);
-
- if(savedInstanceState != null) {
- mCurrentDir = new File(savedInstanceState.getString(UploadFilesActivity.KEY_DIRECTORY_PATH));
- } else {
- mCurrentDir = Environment.getExternalStorageDirectory();
- }
-
- mAccountOnCreation = getAccount();
-
- /// USER INTERFACE
-
- // Drop-down navigation
- mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
- File currDir = mCurrentDir;
- while(currDir != null && currDir.getParentFile() != null) {
- mDirectories.add(currDir.getName());
- currDir = currDir.getParentFile();
- }
- mDirectories.add(File.separator);
-
- // Inflate and set the layout view
- setContentView(R.layout.upload_files_layout);
- mFileListFragment = (LocalFileListFragment) getSupportFragmentManager().findFragmentById(R.id.local_files_list);
-
-
- // Set input controllers
- mCancelBtn = (CustomButton) findViewById(R.id.upload_files_btn_cancel);
- mCancelBtn.setOnClickListener(this);
- mUploadBtn = (CustomButton) findViewById(R.id.upload_files_btn_upload);
- mUploadBtn.setOnClickListener(this);
-
-
- // Action bar setup
- ActionBar actionBar = getSupportActionBar();
- actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
- actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- actionBar.setListNavigationCallbacks(mDirectories, this);
-
- // wait dialog
- if (mCurrentDialog != null) {
- mCurrentDialog.dismiss();
- mCurrentDialog = null;
- }
-
- Log_OC.d(TAG, "onCreate() end");
- }
-
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- boolean retval = true;
- switch (item.getItemId()) {
- case android.R.id.home: {
- if(mCurrentDir != null && mCurrentDir.getParentFile() != null){
- onBackPressed();
- }
- break;
- }
- default:
- retval = super.onOptionsItemSelected(item);
- }
- return retval;
- }
-
-
- @Override
- public boolean onNavigationItemSelected(int itemPosition, long itemId) {
- int i = itemPosition;
- while (i-- != 0) {
- onBackPressed();
- }
- // the next operation triggers a new call to this method, but it's necessary to
- // ensure that the name exposed in the action bar is the current directory when the
- // user selected it in the navigation list
- if (itemPosition != 0)
- getSupportActionBar().setSelectedNavigationItem(0);
- return true;
- }
-
-
- @Override
- public void onBackPressed() {
- if (mDirectories.getCount() <= 1) {
- finish();
- return;
- }
- popDirname();
- mFileListFragment.onNavigateUp();
- mCurrentDir = mFileListFragment.getCurrentDirectory();
-
- if(mCurrentDir.getParentFile() == null){
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayHomeAsUpEnabled(false);
- }
- }
-
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- // responsibility of restore is preferred in onCreate() before than in onRestoreInstanceState when there are Fragments involved
- Log_OC.d(TAG, "onSaveInstanceState() start");
- super.onSaveInstanceState(outState);
- outState.putString(UploadFilesActivity.KEY_DIRECTORY_PATH, mCurrentDir.getAbsolutePath());
- Log_OC.d(TAG, "onSaveInstanceState() end");
- }
-
-
- /**
- * Pushes a directory to the drop down list
- * @param directory to push
- * @throws IllegalArgumentException If the {@link File#isDirectory()} returns false.
- */
- public void pushDirname(File directory) {
- if(!directory.isDirectory()){
- throw new IllegalArgumentException("Only directories may be pushed!");
- }
- mDirectories.insert(directory.getName(), 0);
- mCurrentDir = directory;
- }
-
- /**
- * Pops a directory name from the drop down list
- * @return True, unless the stack is empty
- */
- public boolean popDirname() {
- mDirectories.remove(mDirectories.getItem(0));
- return !mDirectories.isEmpty();
- }
-
-
- // Custom array adapter to override text colors
- private class CustomArrayAdapter<T> extends ArrayAdapter<T> {
-
- public CustomArrayAdapter(UploadFilesActivity ctx, int view) {
- super(ctx, view);
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = super.getView(position, convertView, parent);
-
- ((TextView) v).setTextColor(getResources().getColorStateList(
- android.R.color.white));
- return v;
- }
-
- public View getDropDownView(int position, View convertView,
- ViewGroup parent) {
- View v = super.getDropDownView(position, convertView, parent);
-
- ((TextView) v).setTextColor(getResources().getColorStateList(
- android.R.color.white));
-
- return v;
- }
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onDirectoryClick(File directory) {
- pushDirname(directory);
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onFileClick(File file) {
- // nothing to do
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public File getInitialDirectory() {
- return mCurrentDir;
- }
-
-
- /**
- * Performs corresponding action when user presses 'Cancel' or 'Upload' button
- *
- * TODO Make here the real request to the Upload service ; will require to receive the account and
- * target folder where the upload must be done in the received intent.
- */
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.upload_files_btn_cancel) {
- setResult(RESULT_CANCELED);
- finish();
-
- } else if (v.getId() == R.id.upload_files_btn_upload) {
- new CheckAvailableSpaceTask().execute();
- }
- }
-
-
- /**
- * Asynchronous task checking if there is space enough to copy all the files chosen
- * to upload into the ownCloud local folder.
- *
- * Maybe an AsyncTask is not strictly necessary, but who really knows.
- *
- * @author David A. Velasco
- */
- private class CheckAvailableSpaceTask extends AsyncTask<Void, Void, Boolean> {
-
- /**
- * Updates the UI before trying the movement
- */
- @Override
- protected void onPreExecute () {
- /// progress dialog and disable 'Move' button
- mCurrentDialog = IndeterminateProgressDialog.newInstance(R.string.wait_a_moment, false);
- mCurrentDialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);
- }
-
-
- /**
- * Checks the available space
- *
- * @return 'True' if there is space enough.
- */
- @Override
- protected Boolean doInBackground(Void... params) {
- String[] checkedFilePaths = mFileListFragment.getCheckedFilePaths();
- long total = 0;
- for (int i=0; checkedFilePaths != null && i < checkedFilePaths.length ; i++) {
- String localPath = checkedFilePaths[i];
- File localFile = new File(localPath);
- total += localFile.length();
- }
- return (FileStorageUtils.getUsableSpace(mAccountOnCreation.name) >= total);
- }
-
- /**
- * Updates the activity UI after the check of space is done.
- *
- * If there is not space enough. shows a new dialog to query the user if wants to move the files instead
- * of copy them.
- *
- * @param result 'True' when there is space enough to copy all the selected files.
- */
- @Override
- protected void onPostExecute(Boolean result) {
- mCurrentDialog.dismiss();
- mCurrentDialog = null;
-
- if (result) {
- // return the list of selected files (success)
- Intent data = new Intent();
- data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
- setResult(RESULT_OK, data);
- finish();
-
- } else {
- // show a dialog to query the user if wants to move the selected files to the ownCloud folder instead of copying
- String[] args = {getString(R.string.app_name)};
- ConfirmationDialogFragment dialog = ConfirmationDialogFragment.newInstance(R.string.upload_query_move_foreign_files, args, R.string.common_yes, -1, R.string.common_no);
- dialog.setOnConfirmationListener(UploadFilesActivity.this);
- dialog.show(getSupportFragmentManager(), QUERY_TO_MOVE_DIALOG_TAG);
- }
- }
- }
-
- @Override
- public void onConfirmation(String callerTag) {
- Log_OC.d(TAG, "Positive button in dialog was clicked; dialog tag is " + callerTag);
- if (callerTag.equals(QUERY_TO_MOVE_DIALOG_TAG)) {
- // return the list of selected files to the caller activity (success), signaling that they should be moved to the ownCloud folder, instead of copied
- Intent data = new Intent();
- data.putExtra(EXTRA_CHOSEN_FILES, mFileListFragment.getCheckedFilePaths());
- setResult(RESULT_OK_AND_MOVE, data);
- finish();
- }
- }
-
-
- @Override
- public void onNeutral(String callerTag) {
- Log_OC.d(TAG, "Phantom neutral button in dialog was clicked; dialog tag is " + callerTag);
- }
-
-
- @Override
- public void onCancel(String callerTag) {
- /// nothing to do; don't finish, let the user change the selection
- Log_OC.d(TAG, "Negative button in dialog was clicked; dialog tag is " + callerTag);
- }
-
-
- @Override
- protected void onAccountSet(boolean stateWasRecovered) {
- if (getAccount() != null) {
- if (!mAccountOnCreation.equals(getAccount())) {
- setResult(RESULT_CANCELED);
- finish();
- }
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
- setResult(RESULT_CANCELED);
- finish();
- }
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui.adapter;\r
-\r
-import android.accounts.Account;\r
-import android.content.Context;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.BaseAdapter;\r
-import android.widget.ImageView;\r
-import android.widget.ListAdapter;\r
-import android.widget.ListView;\r
-import android.widget.TextView;\r
-\r
-import de.mobilcom.debitel.cloud.android.DisplayUtils;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;\r
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;\r
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;\r
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;\r
-\r
-import java.util.Vector;\r
-\r
-\r
-/**\r
- * This Adapter populates a ListView with all files and folders in an ownCloud\r
- * instance.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
- */\r
-public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
- private Context mContext;\r
- private OCFile mFile = null;\r
- private Vector<OCFile> mFiles = null;\r
- private DataStorageManager mStorageManager;\r
- private Account mAccount;\r
- private TransferServiceGetter mTransferServiceGetter;\r
- \r
- public FileListListAdapter(Context context, TransferServiceGetter transferServiceGetter) {\r
- mContext = context;\r
- mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
- mTransferServiceGetter = transferServiceGetter;\r
- }\r
-\r
- @Override\r
- public boolean areAllItemsEnabled() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public boolean isEnabled(int position) {\r
- return true;\r
- }\r
-\r
- @Override\r
- public int getCount() {\r
- return mFiles != null ? mFiles.size() : 0;\r
- }\r
-\r
- @Override\r
- public Object getItem(int position) {\r
- if (mFiles == null || mFiles.size() <= position)\r
- return null;\r
- return mFiles.get(position);\r
- }\r
-\r
- @Override\r
- public long getItemId(int position) {\r
- if (mFiles == null || mFiles.size() <= position)\r
- return 0;\r
- return mFiles.get(position).getFileId();\r
- }\r
-\r
- @Override\r
- public int getItemViewType(int position) {\r
- return 0;\r
- }\r
-\r
- @Override\r
- public View getView(int position, View convertView, ViewGroup parent) {\r
- View view = convertView;\r
- if (view == null) {\r
- LayoutInflater inflator = (LayoutInflater) mContext\r
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\r
- view = inflator.inflate(R.layout.list_item, null);\r
- }\r
- \r
- if (mFiles != null && mFiles.size() > position) {\r
- OCFile file = mFiles.get(position);\r
- TextView fileName = (TextView) view.findViewById(R.id.Filename);\r
- String name = file.getFileName();\r
-\r
- fileName.setText(name);\r
- ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);\r
- fileIcon.setImageResource(DisplayUtils.getResourceId(file.getMimetype()));\r
- ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
- FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
- FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
- if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {\r
- localStateView.setImageResource(R.drawable.downloading_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {\r
- localStateView.setImageResource(R.drawable.uploading_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else if (file.isDown()) {\r
- localStateView.setImageResource(R.drawable.local_file_indicator);\r
- localStateView.setVisibility(View.VISIBLE);\r
- } else {\r
- localStateView.setVisibility(View.INVISIBLE);\r
- }\r
- \r
- TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);\r
- TextView lastModV = (TextView) view.findViewById(R.id.last_mod);\r
- ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);\r
- \r
- if (!file.isDirectory()) {\r
- fileSizeV.setVisibility(View.VISIBLE);\r
- fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
- // this if-else is needed even thoe fav icon is visible by default\r
- // because android reuses views in listview\r
- if (!file.keepInSync()) {\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
- } else {\r
- view.findViewById(R.id.imageView3).setVisibility(View.VISIBLE);\r
- }\r
- \r
- ListView parentList = (ListView)parent;\r
- if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) { \r
- checkBoxV.setVisibility(View.GONE);\r
- } else {\r
- if (parentList.isItemChecked(position)) {\r
- checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);\r
- } else {\r
- checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);\r
- }\r
- checkBoxV.setVisibility(View.VISIBLE);\r
- }\r
- \r
- } \r
- else {\r
- \r
- fileSizeV.setVisibility(View.VISIBLE);\r
- fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
- checkBoxV.setVisibility(View.GONE);\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
- }\r
- }\r
-\r
- return view;\r
- }\r
-\r
- @Override\r
- public int getViewTypeCount() {\r
- return 1;\r
- }\r
-\r
- @Override\r
- public boolean hasStableIds() {\r
- return true;\r
- }\r
-\r
- @Override\r
- public boolean isEmpty() {\r
- return (mFiles == null || mFiles.isEmpty());\r
- }\r
-\r
- /**\r
- * Change the adapted directory for a new one\r
- * @param directory New file to adapt. Can be NULL, meaning "no content to adapt".\r
- * @param updatedStorageManager Optional updated storage manager; used to replace mStorageManager if is different (and not NULL)\r
- */\r
- public void swapDirectory(OCFile directory, DataStorageManager updatedStorageManager) {\r
- mFile = directory;\r
- if (updatedStorageManager != null && updatedStorageManager != mStorageManager) {\r
- mStorageManager = updatedStorageManager;\r
- mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
- }\r
- if (mStorageManager != null) {\r
- mFiles = mStorageManager.getDirectoryContent(mFile);\r
- } else {\r
- mFiles = null;\r
- }\r
- notifyDataSetChanged();\r
- }\r
- \r
-}\r
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui.adapter;\r
-\r
-\r
-import android.content.Context;\r
-import android.content.Intent;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.BaseAdapter;\r
-import android.widget.ImageView;\r
-import android.widget.TextView;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.Preferences;\r
-\r
-/**\r
- * Populates the landing screen icons.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingScreenAdapter extends BaseAdapter {\r
-\r
- private Context mContext;\r
-\r
- private final Integer[] mLandingScreenIcons = { R.drawable.home,\r
- R.drawable.music, R.drawable.contacts, R.drawable.calendar,\r
- android.R.drawable.ic_menu_agenda, R.drawable.settings };\r
-\r
- private final Integer[] mLandingScreenTexts = { R.string.main_files,\r
- R.string.main_music, R.string.main_contacts,\r
- R.string.main_calendar, R.string.main_bookmarks,\r
- R.string.main_settings };\r
-\r
- public LandingScreenAdapter(Context context) {\r
- mContext = context;\r
- }\r
-\r
- @Override\r
- public int getCount() {\r
- return mLandingScreenIcons.length;\r
- }\r
-\r
- @Override\r
- /**\r
- * Returns the Intent associated with this object\r
- * or null if the functionality is not yet implemented\r
- */\r
- public Object getItem(int position) {\r
- Intent intent = new Intent();\r
-\r
- switch (position) {\r
- case 0:\r
- /*\r
- * The FileDisplayActivity requires the ownCloud account as an\r
- * parcableExtra. We will put in the one that is selected in the\r
- * preferences\r
- */\r
- intent.setClass(mContext, FileDisplayActivity.class);\r
- intent.putExtra("ACCOUNT",\r
- AccountUtils.getCurrentOwnCloudAccount(mContext));\r
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r
- break;\r
- case 5:\r
- intent.setClass(mContext, Preferences.class);\r
- break;\r
- default:\r
- intent = null;\r
- }\r
- return intent;\r
- }\r
-\r
- @Override\r
- public long getItemId(int position) {\r
- return position;\r
- }\r
-\r
- @Override\r
- public View getView(int position, View convertView, ViewGroup parent) {\r
- if (convertView == null) {\r
- LayoutInflater inflator = LayoutInflater.from(mContext);\r
- convertView = inflator.inflate(R.layout.landing_page_item, null);\r
-\r
- ImageView icon = (ImageView) convertView\r
- .findViewById(R.id.gridImage);\r
- TextView iconText = (TextView) convertView\r
- .findViewById(R.id.gridText);\r
-\r
- icon.setImageResource(mLandingScreenIcons[position]);\r
- iconText.setText(mLandingScreenTexts[position]);\r
- }\r
- return convertView;\r
- }\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.adapter;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Comparator;
-
-import de.mobilcom.debitel.cloud.android.DisplayUtils;
-import de.mobilcom.debitel.cloud.android.R;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-/**
- * This Adapter populates a ListView with all files and directories contained
- * in a local directory
- *
- * @author David A. Velasco
- *
- */
-public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
-
- private Context mContext;
- private File mDirectory;
- private File[] mFiles = null;
-
- public LocalFileListAdapter(File directory, Context context) {
- mContext = context;
- swapDirectory(directory);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return true;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return true;
- }
-
- @Override
- public int getCount() {
- return mFiles != null ? mFiles.length : 0;
- }
-
- @Override
- public Object getItem(int position) {
- if (mFiles == null || mFiles.length <= position)
- return null;
- return mFiles[position];
- }
-
- @Override
- public long getItemId(int position) {
- return mFiles != null && mFiles.length <= position ? position : -1;
- }
-
- @Override
- public int getItemViewType(int position) {
- return 0;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = convertView;
- if (view == null) {
- LayoutInflater inflator = (LayoutInflater) mContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- view = inflator.inflate(R.layout.list_item, null);
- }
- if (mFiles != null && mFiles.length > position) {
- File file = mFiles[position];
-
- TextView fileName = (TextView) view.findViewById(R.id.Filename);
- String name = file.getName();
- fileName.setText(name);
-
- ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
- if (!file.isDirectory()) {
- fileIcon.setImageResource(R.drawable.file);
- } else {
- fileIcon.setImageResource(R.drawable.ic_menu_archive);
- }
-
- TextView fileSizeV = (TextView) view.findViewById(R.id.file_size);
- TextView lastModV = (TextView) view.findViewById(R.id.last_mod);
- ImageView checkBoxV = (ImageView) view.findViewById(R.id.custom_checkbox);
- if (!file.isDirectory()) {
- fileSizeV.setVisibility(View.VISIBLE);
- fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.length()));
- lastModV.setVisibility(View.VISIBLE);
- lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.lastModified()));
- ListView parentList = (ListView)parent;
- if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) {
- checkBoxV.setVisibility(View.GONE);
- } else {
- if (parentList.isItemChecked(position)) {
- checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
- } else {
- checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
- }
- checkBoxV.setVisibility(View.VISIBLE);
- }
-
- } else {
- fileSizeV.setVisibility(View.GONE);
- lastModV.setVisibility(View.GONE);
- checkBoxV.setVisibility(View.GONE);
- }
-
- view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE); // not GONE; the alignment changes; ugly way to keep it
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);
- }
-
- return view;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public boolean hasStableIds() {
- return false;
- }
-
- @Override
- public boolean isEmpty() {
- return (mFiles == null || mFiles.length == 0);
- }
-
- /**
- * Change the adapted directory for a new one
- * @param directory New file to adapt. Can be NULL, meaning "no content to adapt".
- */
- public void swapDirectory(File directory) {
- mDirectory = directory;
- mFiles = (mDirectory != null ? mDirectory.listFiles() : null);
- if (mFiles != null) {
- Arrays.sort(mFiles, new Comparator<File>() {
- @Override
- public int compare(File lhs, File rhs) {
- if (lhs.isDirectory() && !rhs.isDirectory()) {
- return -1;
- } else if (!lhs.isDirectory() && rhs.isDirectory()) {
- return 1;
- }
- return compareNames(lhs, rhs);
- }
-
- private int compareNames(File lhs, File rhs) {
- return lhs.getName().toLowerCase().compareTo(rhs.getName().toLowerCase());
- }
-
- });
- }
- notifyDataSetChanged();
- }
-}
+++ /dev/null
-package de.mobilcom.debitel.cloud.android.ui.adapter;
-
-import java.io.File;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Environment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-
-public class LogListAdapter extends ArrayAdapter<String> {
- private Context context = null;
- private String[] values;
- private Uri fileUri = null;
-
-
- public LogListAdapter(Context context, String[] values) {
- super(context, R.layout.log_item, values);
- this.context = context;
- this.values = values;
- }
-
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View rowView = inflater.inflate(R.layout.log_item, parent, false);
- TextView listText = (TextView) rowView.findViewById(R.id.log_item_single);
- listText.setText(values[position]);
- listText.setTextSize(15);
- fileUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory()+File.separator+"owncloud"+File.separator+"log"+File.separator+values[position]));
- listText.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
- emailIntent.setType("text/rtf");
- emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "OwnCloud Logfile");
- emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "This is a automatic E-mail send by owncloud/android");
- emailIntent.putExtra(android.content.Intent.EXTRA_STREAM, fileUri);
- emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
- }
- });
- return rowView;
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.webkit.WebView;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Dialog to show the contents of res/raw/CHANGELOG.txt
- */
-public class ChangelogDialog extends SherlockDialogFragment {
-
- private static final String ARG_CANCELABLE = ChangelogDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
-
-
- /**
- * Public factory method to get dialog instances.
- *
- * @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
- * @return New dialog instance, ready to show.
- */
- public static ChangelogDialog newInstance(boolean cancelable) {
- ChangelogDialog fragment = new ChangelogDialog();
- Bundle args = new Bundle();
- args.putBoolean(ARG_CANCELABLE, cancelable);
- fragment.setArguments(args);
- return fragment;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- /// load the custom view to insert in the dialog, between title and
- WebView webview = new WebView(getActivity());
- webview.loadUrl("file:///android_res/raw/" + getResources().getResourceEntryName(R.raw.changelog) + ".html");
-
- /// build the dialog
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- Dialog dialog = builder.setView(webview)
- .setIcon(R.drawable.icon)
- //.setTitle(R.string.whats_new)
- .setPositiveButton(R.string.common_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- }).create();
-
- dialog.setCancelable(getArguments().getBoolean(ARG_CANCELABLE));
- return dialog;
- }
-
- /**
- * {@inheritDoc}
- *-/
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- /// load the custom layout
- View view = inflater.inflate(R.layout.fragment_changelog, container);
- mEditText = (EditText) view.findViewById(R.id.txt_your_name);
- getDialog().setTitle(R.string.whats_new);
-
- /// read full contents of the change log file (don't make it too big)
- InputStream changeLogStream = getResources().openRawResource(R.raw.changelog);
- Scanner scanner = new java.util.Scanner(changeLogStream).useDelimiter("\\A");
- String text = scanner.hasNext() ? scanner.next() : "";
-
- /// make clickable the links in the change log file
- SpannableString sText = new SpannableString(text);
- Linkify.addLinks(sText, Linkify.ALL);
-
- return view;
- }
- */
-}
-
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-/**
- * Dialog which will be displayed to user upon keep-in-sync file conflict.
- *
- * @author Bartek Przybylski
- *
- */
-public class ConflictsResolveDialog extends SherlockDialogFragment {
-
- public static enum Decision {
- CANCEL,
- KEEP_BOTH,
- OVERWRITE
- }
-
- OnConflictDecisionMadeListener mListener;
-
- public static ConflictsResolveDialog newInstance(String path, OnConflictDecisionMadeListener listener) {
- ConflictsResolveDialog f = new ConflictsResolveDialog();
- Bundle args = new Bundle();
- args.putString("remotepath", path);
- f.setArguments(args);
- f.mListener = listener;
- return f;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- String remotepath = getArguments().getString("remotepath");
- return new AlertDialog.Builder(getSherlockActivity())
- .setIcon(R.drawable.icon)
- .setTitle(R.string.conflict_title)
- .setMessage(String.format(getString(R.string.conflict_message), remotepath))
- .setPositiveButton(R.string.conflict_overwrite,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (mListener != null)
- mListener.ConflictDecisionMade(Decision.OVERWRITE);
- }
- })
- .setNeutralButton(R.string.conflict_keep_both,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (mListener != null)
- mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
- }
- })
- .setNegativeButton(R.string.conflict_dont_upload,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (mListener != null)
- mListener.ConflictDecisionMade(Decision.CANCEL);
- }
- })
- .create();
- }
-
- public void showDialog(SherlockFragmentActivity activity) {
- Fragment prev = activity.getSupportFragmentManager().findFragmentByTag("dialog");
- FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
- if (prev != null) {
- ft.remove(prev);
- }
- ft.addToBackStack(null);
-
- this.show(ft, "dialog");
- }
-
- public void dismissDialog(SherlockFragmentActivity activity) {
- Fragment prev = activity.getSupportFragmentManager().findFragmentByTag(getTag());
- if (prev != null) {
- FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
- ft.remove(prev);
- ft.commit();
- }
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- mListener.ConflictDecisionMade(Decision.CANCEL);
- }
-
- public interface OnConflictDecisionMadeListener {
- public void ConflictDecisionMade(Decision decision);
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager.LayoutParams;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-
-/**
- * Dialog to request the user to input a name, optionally initialized with a former name.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class EditNameDialog extends SherlockDialogFragment implements DialogInterface.OnClickListener {
-
- public static final String TAG = EditNameDialog.class.getSimpleName();
-
- protected static final String ARG_TITLE = "TITLE";
- protected static final String ARG_NAME = "NAME";
- protected static final String ARG_SELECTION_START = "SELECTION_START";
- protected static final String ARG_SELECTION_END = "SELECTION_END";
-
- private String mNewFilename;
- private boolean mResult;
- private EditNameDialogListener mListener;
-
- /**
- * Public factory method to get dialog instances.
- *
- * @param title Text to show as title in the dialog.
- * @param name Optional text to include in the text input field when the dialog is shown.
- * @param listener Instance to notify when the dialog is dismissed.
- * @param selectionStart Index to the first character to be selected in the input field; negative value for none
- * @param selectionEnd Index to the last character to be selected in the input field; negative value for none
- * @return New dialog instance, ready to show.
- */
- static public EditNameDialog newInstance(String title, String name, int selectionStart, int selectionEnd, EditNameDialogListener listener) {
- EditNameDialog f = new EditNameDialog();
- Bundle args = new Bundle();
- args.putString(ARG_TITLE, title);
- args.putString(ARG_NAME, name);
- args.putInt(ARG_SELECTION_START, selectionStart);
- args.putInt(ARG_SELECTION_END, selectionEnd);
- f.setArguments(args);
- f.setOnDismissListener(listener);
- return f;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- String currentName = getArguments().getString(ARG_NAME);
- if (currentName == null)
- currentName = "";
- String title = getArguments().getString(ARG_TITLE);
-
- // Inflate the layout for the dialog
- LayoutInflater inflater = getSherlockActivity().getLayoutInflater();
- View v = inflater.inflate(R.layout.edit_box_dialog, null); // null parent view because it will go in the dialog layout
- EditText inputText = ((EditText)v.findViewById(R.id.user_input));
- inputText.setText(currentName);
-
- // Set it to the dialog
- AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
- builder.setView(v)
- .setPositiveButton(R.string.common_ok, this)
- .setNegativeButton(R.string.common_cancel, this);
-
- if (title != null) {
- builder.setTitle(title);
- }
-
- mResult = false;
-
- Dialog d = builder.create();
-
- inputText.requestFocus();
- int selectionStart = getArguments().getInt(ARG_SELECTION_START, -1);
- int selectionEnd = getArguments().getInt(ARG_SELECTION_END, -1);
- if (selectionStart >= 0 && selectionEnd >= 0) {
- inputText.setSelection(Math.min(selectionStart, selectionEnd), Math.max(selectionStart, selectionEnd));
- }
- d.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
- return d;
- }
-
-
- /**
- * Performs the corresponding action when a dialog button is clicked.
- *
- * Saves the text in the input field to be accessed through {@link #getNewFilename()} when the positive
- * button is clicked.
- *
- * Notify the current listener in any case.
- */
- @Override
- public void onClick(DialogInterface dialog, int which) {
- switch (which) {
- case AlertDialog.BUTTON_POSITIVE: {
- mNewFilename = ((TextView)(getDialog().findViewById(R.id.user_input))).getText().toString();
- mResult = true;
- }
- case AlertDialog.BUTTON_NEGATIVE: { // fall through
- dismiss();
- if (mListener != null)
- mListener.onDismiss(this);
- }
- }
- }
-
- protected void setOnDismissListener(EditNameDialogListener listener) {
- mListener = listener;
- }
-
- /**
- * Returns the text in the input field after the user clicked the positive button.
- *
- * @return Text in the input field.
- */
- public String getNewFilename() {
- return mNewFilename;
- }
-
- /**
- *
- * @return True when the user clicked the positive button.
- */
- public boolean getResult() {
- return mResult;
- }
-
-
- /**
- * Interface to receive a notification when any button in the dialog is clicked.
- */
- public interface EditNameDialogListener {
- public void onDismiss(EditNameDialog dialog);
- }
-
-
-}
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnKeyListener;
-import android.os.Bundle;
-import android.view.KeyEvent;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-public class IndeterminateProgressDialog extends SherlockDialogFragment {
-
- private static final String ARG_MESSAGE_ID = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_MESSAGE_ID";
- private static final String ARG_CANCELABLE = IndeterminateProgressDialog.class.getCanonicalName() + ".ARG_CANCELABLE";
-
-
- /**
- * Public factory method to get dialog instances.
- *
- * @param messageId Resource id for a message to show in the dialog.
- * @param cancelable If 'true', the dialog can be cancelled by the user input (BACK button, touch outside...)
- * @return New dialog instance, ready to show.
- */
- public static IndeterminateProgressDialog newInstance(int messageId, boolean cancelable) {
- IndeterminateProgressDialog fragment = new IndeterminateProgressDialog();
- Bundle args = new Bundle();
- args.putInt(ARG_MESSAGE_ID, messageId);
- args.putBoolean(ARG_CANCELABLE, cancelable);
- fragment.setArguments(args);
- return fragment;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- /// create indeterminate progress dialog
- final ProgressDialog dialog = new ProgressDialog(getActivity());
- dialog.setIndeterminate(true);
-
- /// set message
- int messageId = getArguments().getInt(ARG_MESSAGE_ID, R.string.placeholder_sentence);
- dialog.setMessage(getString(messageId));
-
- /// set cancellation behavior
- boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE, false);
- if (!cancelable) {
- dialog.setCancelable(false);
- // disable the back button
- OnKeyListener keyListener = new OnKeyListener() {
- @Override
- public boolean onKey(DialogInterface dialog, int keyCode,
- KeyEvent event) {
-
- if( keyCode == KeyEvent.KEYCODE_BACK){
- return true;
- }
- return false;
- }
-
- };
- dialog.setOnKeyListener(keyListener);
- }
-
- return dialog;
- }
-
-}
-
-
+++ /dev/null
-package de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import de.mobilcom.debitel.cloud.android.R;
-
-import android.app.Dialog;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.widget.TextView;
-
-public class LoadingDialog extends DialogFragment {
-
- private String mMessage;
-
- public LoadingDialog() {
- super();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setRetainInstance(true);
- setCancelable(false);
- }
-
- public LoadingDialog(String message) {
- this.mMessage = message;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- // Create a view by inflating desired layout
- View v = inflater.inflate(R.layout.loading_dialog, container, false);
-
- // set value
- TextView tv = (TextView) v.findViewById(R.id.loadingText);
- tv.setText(mMessage);
-
- return v;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- Dialog dialog = super.onCreateDialog(savedInstanceState);
- dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
- return dialog;
- }
-
- @Override
- public void onDestroyView() {
- if (getDialog() != null && getRetainInstance())
- getDialog().setDismissMessage(null);
- super.onDestroyView();
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.app.FragmentManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
-import android.webkit.WebBackForwardList;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient;
-import de.mobilcom.debitel.cloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
-
-import eu.alefzero.webdav.WebdavClient;
-
-/**
- * Dialog to show the WebView for SAML Authentication
- *
- * @author Maria Asensio
- * @author David A. Velasco
- */
-public class SamlWebViewDialog extends SherlockDialogFragment {
-
- public final String SAML_DIALOG_TAG = "SamlWebViewDialog";
-
- private final static String TAG = SamlWebViewDialog.class.getSimpleName();
-
- private static final String ARG_INITIAL_URL = "INITIAL_URL";
- private static final String ARG_TARGET_URL = "TARGET_URL";
- private static final String KEY_WEBVIEW_STATE = "WEBVIEW_STATE";
-
- private WebView mSsoWebView;
- private SsoWebViewClient mWebViewClient;
-
- private String mInitialUrl;
- private String mTargetUrl;
-
- private Handler mHandler;
-
- private SsoWebViewClientListener mSsoWebViewClientListener;
-
- //private View mSsoRootView;
-
-
- /**
- * Public factory method to get dialog instances.
- *
- * @param handler
- * @param Url Url to open at WebView
- * @param targetURL mHostBaseUrl + AccountUtils.getWebdavPath(mDiscoveredVersion, mCurrentAuthTokenType)
- * @return New dialog instance, ready to show.
- */
- public static SamlWebViewDialog newInstance(String url, String targetUrl) {
- Log_OC.d(TAG, "New instance");
- SamlWebViewDialog fragment = new SamlWebViewDialog();
- Bundle args = new Bundle();
- args.putString(ARG_INITIAL_URL, url);
- args.putString(ARG_TARGET_URL, targetUrl);
- fragment.setArguments(args);
- return fragment;
- }
-
-
- public SamlWebViewDialog() {
- super();
- Log_OC.d(TAG, "constructor");
- }
-
-
- @Override
- public void onAttach(Activity activity) {
- Log_OC.d(TAG, "onAttach");
- super.onAttach(activity);
- try {
- mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
- mHandler = new Handler();
- mWebViewClient = new SsoWebViewClient(mHandler, mSsoWebViewClientListener);
-
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement " + SsoWebViewClientListener.class.getSimpleName());
- }
- }
-
-
- @SuppressLint("SetJavaScriptEnabled")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate");
- super.onCreate(savedInstanceState);
-
- CookieSyncManager.createInstance(getActivity());
-
- if (savedInstanceState == null) {
- mInitialUrl = getArguments().getString(ARG_INITIAL_URL);
- mTargetUrl = getArguments().getString(ARG_TARGET_URL);
- } else {
- mInitialUrl = savedInstanceState.getString(ARG_INITIAL_URL);
- mTargetUrl = savedInstanceState.getString(ARG_TARGET_URL);
- }
-
- setStyle(SherlockDialogFragment.STYLE_NO_TITLE, R.style.Theme_ownCloud_Dialog);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreateDialog");
-
- /*
- // build the dialog
- AlertDialog.Builder builder = new AlertDialog.Builder(getSherlockActivity());
- if (mSsoRootView.getParent() != null) {
- ((ViewGroup)(mSsoRootView.getParent())).removeView(mSsoRootView);
- }
- builder.setView(mSsoRootView);
- //builder.setView(mSsoWebView);
- Dialog dialog = builder.create();
- */
-
- return super.onCreateDialog(savedInstanceState);
- }
-
- @SuppressLint("SetJavaScriptEnabled")
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreateView");
-
- // Inflate layout of the dialog
- View rootView = inflater.inflate(R.layout.sso_dialog, container, false); // null parent view because it will go in the dialog layout
- mSsoWebView = (WebView) rootView.findViewById(R.id.sso_webview);
-
- mWebViewClient.setTargetUrl(mTargetUrl);
- mSsoWebView.setWebViewClient(mWebViewClient);
-
- if (savedInstanceState == null) {
- Log_OC.d(TAG, " initWebView start");
- CookieManager cookieManager = CookieManager.getInstance();
- cookieManager.setAcceptCookie(true);
- cookieManager.removeAllCookie();
- mSsoWebView.loadUrl(mInitialUrl);
-
- } else {
- Log_OC.d(TAG, " restoreWebView start");
- WebBackForwardList history = mSsoWebView.restoreState(savedInstanceState.getBundle(KEY_WEBVIEW_STATE));
- if (history == null) {
- Log_OC.e(TAG, "Error restoring WebView state ; back to starting URL");
- mSsoWebView.loadUrl(mInitialUrl);
- }
- }
-
- WebSettings webSettings = mSsoWebView.getSettings();
- webSettings.setJavaScriptEnabled(true);
- webSettings.setBuiltInZoomControls(true);
- webSettings.setLoadWithOverviewMode(false);
- webSettings.setSavePassword(false);
- webSettings.setUserAgentString(WebdavClient.USER_AGENT);
- webSettings.setSaveFormData(false);
-
- return rootView;
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- Log_OC.d(SAML_DIALOG_TAG, "onSaveInstanceState being CALLED");
- super.onSaveInstanceState(outState);
-
- // save URLs
- outState.putString(ARG_INITIAL_URL, mInitialUrl);
- outState.putString(ARG_TARGET_URL, mTargetUrl);
-
- // Save the state of the WebView
- Bundle webviewState = new Bundle();
- mSsoWebView.saveState(webviewState);
- outState.putBundle(KEY_WEBVIEW_STATE, webviewState);
- }
-
- @Override
- public void onDestroyView() {
- Log_OC.d(TAG, "onDestroyView");
-
- mSsoWebView.setWebViewClient(null);
-
- // Work around bug: http://code.google.com/p/android/issues/detail?id=17423
- Dialog dialog = getDialog();
- if ((dialog != null)) {
- dialog.setOnDismissListener(null);
- //dialog.dismiss();
- //dialog.setDismissMessage(null);
- }
-
- super.onDestroyView();
- }
-
- @Override
- public void onDestroy() {
- Log_OC.d(TAG, "onDestroy");
- super.onDestroy();
- }
-
- @Override
- public void onDetach() {
- Log_OC.d(TAG, "onDetach");
- mSsoWebViewClientListener = null;
- mWebViewClient = null;
- super.onDetach();
- }
-
- @Override
- public void onCancel (DialogInterface dialog) {
- Log_OC.d(SAML_DIALOG_TAG, "onCancel");
- super.onCancel(dialog);
- }
-
- @Override
- public void onDismiss (DialogInterface dialog) {
- Log_OC.d(SAML_DIALOG_TAG, "onDismiss");
- super.onDismiss(dialog);
- }
-
- @Override
- public void onStart() {
- Log_OC.d(SAML_DIALOG_TAG, "onStart");
- super.onStart();
- }
-
- @Override
- public void onStop() {
- Log_OC.d(SAML_DIALOG_TAG, "onStop");
- super.onStop();
- }
-
- @Override
- public void onResume() {
- Log_OC.d(SAML_DIALOG_TAG, "onResume");
- super.onResume();
- }
-
- @Override
- public void onPause() {
- Log_OC.d(SAML_DIALOG_TAG, "onPause");
- super.onPause();
- }
-
- @Override
- public int show (FragmentTransaction transaction, String tag) {
- Log_OC.d(SAML_DIALOG_TAG, "show (transaction)");
- return super.show(transaction, tag);
- }
-
- @Override
- public void show (FragmentManager manager, String tag) {
- Log_OC.d(SAML_DIALOG_TAG, "show (manager)");
- super.show(manager, tag);
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.security.auth.x500.X500Principal;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.Window;
-import android.widget.TextView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.network.CertificateCombinedException;
-import de.mobilcom.debitel.cloud.android.network.OwnCloudClientUtils;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.ui.CustomButton;
-
-/**
- * Dialog to request the user about a certificate that could not be validated with the certificates store in the system.
- *
- * @author David A. Velasco
- */
-public class SslValidatorDialog extends Dialog {
-
- private final static String TAG = SslValidatorDialog.class.getSimpleName();
-
- private OnSslValidatorListener mListener;
- private CertificateCombinedException mException = null;
- private View mView;
-
-
- /**
- * Creates a new SslValidatorDialog to ask the user if an untrusted certificate from a server should
- * be trusted.
- *
- * @param context Android context where the dialog will live.
- * @param result Result of a failed remote operation.
- * @param listener Object to notice when the server certificate was added to the local certificates store.
- * @return A new SslValidatorDialog instance. NULL if the operation can not be recovered
- * by setting the certificate as reliable.
- */
- public static SslValidatorDialog newInstance(Context context, RemoteOperationResult result, OnSslValidatorListener listener) {
- if (result != null && result.isSslRecoverableException()) {
- SslValidatorDialog dialog = new SslValidatorDialog(context, listener);
- return dialog;
- } else {
- return null;
- }
- }
-
- /**
- * Private constructor.
- *
- * Instances have to be created through static {@link SslValidatorDialog#newInstance}.
- *
- * @param context Android context where the dialog will live
- * @param e Exception causing the need of prompt the user about the server certificate.
- * @param listener Object to notice when the server certificate was added to the local certificates store.
- */
- private SslValidatorDialog(Context context, OnSslValidatorListener listener) {
- super(context);
- mListener = listener;
- }
-
-
- /**
- * {@inheritDoc}
- */
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- mView = getLayoutInflater().inflate(R.layout.ssl_validator_layout, null);
- setContentView(mView);
-
- mView.findViewById(R.id.ok).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- saveServerCert();
- dismiss();
- if (mListener != null)
- mListener.onSavedCertificate();
- else
- Log_OC.d(TAG, "Nobody there to notify the certificate was saved");
-
- } catch (GeneralSecurityException e) {
- dismiss();
- if (mListener != null)
- mListener.onFailedSavingCertificate();
- Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
-
- } catch (IOException e) {
- dismiss();
- if (mListener != null)
- mListener.onFailedSavingCertificate();
- Log_OC.e(TAG, "Server certificate could not be saved in the known servers trust store ", e);
- }
- }
- });
-
- mView.findViewById(R.id.cancel).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancel();
- }
- });
-
- mView.findViewById(R.id.details_btn).setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- View detailsScroll = findViewById(R.id.details_scroll);
- if (detailsScroll.getVisibility() == View.VISIBLE) {
- detailsScroll.setVisibility(View.GONE);
- ((CustomButton)v).setText(R.string.ssl_validator_btn_details_see);
-
- } else {
- detailsScroll.setVisibility(View.VISIBLE);
- ((CustomButton)v).setText(R.string.ssl_validator_btn_details_hide);
- }
- }
- });
- }
-
-
- public void updateResult(RemoteOperationResult result) {
- if (result.isSslRecoverableException()) {
- mException = (CertificateCombinedException) result.getException();
-
- /// clean
- mView.findViewById(R.id.reason_cert_not_trusted).setVisibility(View.GONE);
- mView.findViewById(R.id.reason_cert_expired).setVisibility(View.GONE);
- mView.findViewById(R.id.reason_cert_not_yet_valid).setVisibility(View.GONE);
- mView.findViewById(R.id.reason_hostname_not_verified).setVisibility(View.GONE);
- mView.findViewById(R.id.details_scroll).setVisibility(View.GONE);
-
- /// refresh
- if (mException.getCertPathValidatorException() != null) {
- ((TextView)mView.findViewById(R.id.reason_cert_not_trusted)).setVisibility(View.VISIBLE);
- }
-
- if (mException.getCertificateExpiredException() != null) {
- ((TextView)mView.findViewById(R.id.reason_cert_expired)).setVisibility(View.VISIBLE);
- }
-
- if (mException.getCertificateNotYetValidException() != null) {
- ((TextView)mView.findViewById(R.id.reason_cert_not_yet_valid)).setVisibility(View.VISIBLE);
- }
-
- if (mException.getSslPeerUnverifiedException() != null ) {
- ((TextView)mView.findViewById(R.id.reason_hostname_not_verified)).setVisibility(View.VISIBLE);
- }
-
-
- showCertificateData(mException.getServerCertificate());
- }
-
- }
-
- private void showCertificateData(X509Certificate cert) {
-
- if (cert != null) {
- showSubject(cert.getSubjectX500Principal());
- showIssuer(cert.getIssuerX500Principal());
- showValidity(cert.getNotBefore(), cert.getNotAfter());
- showSignature(cert);
-
- } else {
- // this should not happen
- // TODO
- }
- }
-
- private void showSignature(X509Certificate cert) {
- TextView sigView = ((TextView)mView.findViewById(R.id.value_signature));
- TextView algorithmView = ((TextView)mView.findViewById(R.id.value_signature_algorithm));
- sigView.setText(getHex(cert.getSignature()));
- algorithmView.setText(cert.getSigAlgName());
- }
-
- public String getHex(final byte [] raw) {
- if (raw == null) {
- return null;
- }
- final StringBuilder hex = new StringBuilder(2 * raw.length);
- for (final byte b : raw) {
- final int hiVal = (b & 0xF0) >> 4;
- final int loVal = b & 0x0F;
- hex.append((char) ('0' + (hiVal + (hiVal / 10 * 7))));
- hex.append((char) ('0' + (loVal + (loVal / 10 * 7))));
- }
- return hex.toString();
- }
-
- private void showValidity(Date notBefore, Date notAfter) {
- TextView fromView = ((TextView)mView.findViewById(R.id.value_validity_from));
- TextView toView = ((TextView)mView.findViewById(R.id.value_validity_to));
- fromView.setText(notBefore.toLocaleString());
- toView.setText(notAfter.toLocaleString());
- }
-
- private void showSubject(X500Principal subject) {
- Map<String, String> s = parsePrincipal(subject);
- TextView cnView = ((TextView)mView.findViewById(R.id.value_subject_CN));
- TextView oView = ((TextView)mView.findViewById(R.id.value_subject_O));
- TextView ouView = ((TextView)mView.findViewById(R.id.value_subject_OU));
- TextView cView = ((TextView)mView.findViewById(R.id.value_subject_C));
- TextView stView = ((TextView)mView.findViewById(R.id.value_subject_ST));
- TextView lView = ((TextView)mView.findViewById(R.id.value_subject_L));
-
- if (s.get("CN") != null) {
- cnView.setText(s.get("CN"));
- cnView.setVisibility(View.VISIBLE);
- } else {
- cnView.setVisibility(View.GONE);
- }
- if (s.get("O") != null) {
- oView.setText(s.get("O"));
- oView.setVisibility(View.VISIBLE);
- } else {
- oView.setVisibility(View.GONE);
- }
- if (s.get("OU") != null) {
- ouView.setText(s.get("OU"));
- ouView.setVisibility(View.VISIBLE);
- } else {
- ouView.setVisibility(View.GONE);
- }
- if (s.get("C") != null) {
- cView.setText(s.get("C"));
- cView.setVisibility(View.VISIBLE);
- } else {
- cView.setVisibility(View.GONE);
- }
- if (s.get("ST") != null) {
- stView.setText(s.get("ST"));
- stView.setVisibility(View.VISIBLE);
- } else {
- stView.setVisibility(View.GONE);
- }
- if (s.get("L") != null) {
- lView.setText(s.get("L"));
- lView.setVisibility(View.VISIBLE);
- } else {
- lView.setVisibility(View.GONE);
- }
- }
-
- private void showIssuer(X500Principal issuer) {
- Map<String, String> s = parsePrincipal(issuer);
- TextView cnView = ((TextView)mView.findViewById(R.id.value_issuer_CN));
- TextView oView = ((TextView)mView.findViewById(R.id.value_issuer_O));
- TextView ouView = ((TextView)mView.findViewById(R.id.value_issuer_OU));
- TextView cView = ((TextView)mView.findViewById(R.id.value_issuer_C));
- TextView stView = ((TextView)mView.findViewById(R.id.value_issuer_ST));
- TextView lView = ((TextView)mView.findViewById(R.id.value_issuer_L));
-
- if (s.get("CN") != null) {
- cnView.setText(s.get("CN"));
- cnView.setVisibility(View.VISIBLE);
- } else {
- cnView.setVisibility(View.GONE);
- }
- if (s.get("O") != null) {
- oView.setText(s.get("O"));
- oView.setVisibility(View.VISIBLE);
- } else {
- oView.setVisibility(View.GONE);
- }
- if (s.get("OU") != null) {
- ouView.setText(s.get("OU"));
- ouView.setVisibility(View.VISIBLE);
- } else {
- ouView.setVisibility(View.GONE);
- }
- if (s.get("C") != null) {
- cView.setText(s.get("C"));
- cView.setVisibility(View.VISIBLE);
- } else {
- cView.setVisibility(View.GONE);
- }
- if (s.get("ST") != null) {
- stView.setText(s.get("ST"));
- stView.setVisibility(View.VISIBLE);
- } else {
- stView.setVisibility(View.GONE);
- }
- if (s.get("L") != null) {
- lView.setText(s.get("L"));
- lView.setVisibility(View.VISIBLE);
- } else {
- lView.setVisibility(View.GONE);
- }
- }
-
-
- private Map<String, String> parsePrincipal(X500Principal principal) {
- Map<String, String> result = new HashMap<String, String>();
- String toParse = principal.getName();
- String[] pieces = toParse.split(",");
- String[] tokens = {"CN", "O", "OU", "C", "ST", "L"};
- for (int i=0; i < pieces.length ; i++) {
- for (int j=0; j<tokens.length; j++) {
- if (pieces[i].startsWith(tokens[j] + "=")) {
- result.put(tokens[j], pieces[i].substring(tokens[j].length()+1));
- }
- }
- }
- return result;
- }
-
- private void saveServerCert() throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
- if (mException.getServerCertificate() != null) {
- // TODO make this asynchronously, it can take some time
- OwnCloudClientUtils.addCertToKnownServersStore(mException.getServerCertificate(), getContext());
- }
- }
-
-
- public interface OnSslValidatorListener {
- public void onSavedCertificate();
- public void onFailedSavingCertificate();
- }
-}
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.dialog;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.webkit.WebView;
-
-public class SsoWebView extends WebView {
-
- public SsoWebView(Context context) {
- super(context);
- }
-
- public SsoWebView(Context context, AttributeSet attr) {
- super(context, attr);
- }
-
- @Override
- public boolean onCheckIsTextEditor () {
- return false;
- }
-
-}
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-public class AuthenticatorAccountDetailsFragment extends SherlockFragment {
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-public class AuthenticatorGetStartedFragment extends SherlockFragment {
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockDialogFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-
-public class ConfirmationDialogFragment extends SherlockDialogFragment {
-
- public final static String ARG_CONF_RESOURCE_ID = "resource_id";
- public final static String ARG_CONF_ARGUMENTS = "string_array";
-
- public final static String ARG_POSITIVE_BTN_RES = "positive_btn_res";
- public final static String ARG_NEUTRAL_BTN_RES = "neutral_btn_res";
- public final static String ARG_NEGATIVE_BTN_RES = "negative_btn_res";
-
- public static final String FTAG_CONFIRMATION = "CONFIRMATION_FRAGMENT";
-
- private ConfirmationDialogFragmentListener mListener;
-
- /**
- * Public factory method to create new ConfirmationDialogFragment instances.
- *
- * @param string_id Resource id for a message to show in the dialog.
- * @param arguments Arguments to complete the message, if it's a format string.
- * @param posBtn Resource id for the text of the positive button.
- * @param neuBtn Resource id for the text of the neutral button.
- * @param negBtn Resource id for the text of the negative button.
- * @return Dialog ready to show.
- */
- public static ConfirmationDialogFragment newInstance(int string_id, String[] arguments, int posBtn, int neuBtn, int negBtn) {
- ConfirmationDialogFragment frag = new ConfirmationDialogFragment();
- Bundle args = new Bundle();
- args.putInt(ARG_CONF_RESOURCE_ID, string_id);
- args.putStringArray(ARG_CONF_ARGUMENTS, arguments);
- args.putInt(ARG_POSITIVE_BTN_RES, posBtn);
- args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn);
- args.putInt(ARG_NEGATIVE_BTN_RES, negBtn);
- frag.setArguments(args);
- return frag;
- }
-
- public void setOnConfirmationListener(ConfirmationDialogFragmentListener listener) {
- mListener = listener;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- Object[] confirmationTarget = getArguments().getStringArray(ARG_CONF_ARGUMENTS);
- int resourceId = getArguments().getInt(ARG_CONF_RESOURCE_ID, -1);
- int posBtn = getArguments().getInt(ARG_POSITIVE_BTN_RES, -1);
- int neuBtn = getArguments().getInt(ARG_NEUTRAL_BTN_RES, -1);
- int negBtn = getArguments().getInt(ARG_NEGATIVE_BTN_RES, -1);
-
- if (confirmationTarget == null || resourceId == -1) {
- Log_OC.wtf(getTag(), "Calling confirmation dialog without resource or arguments");
- return null;
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(String.format(getString(resourceId), confirmationTarget))
- .setTitle(android.R.string.dialog_alert_title);
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- builder.setIconAttribute(android.R.attr.alertDialogIcon);
- }
-
- if (posBtn != -1)
- builder.setPositiveButton(posBtn,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- mListener.onConfirmation(getTag());
- dialog.dismiss();
- }
- });
- if (neuBtn != -1)
- builder.setNeutralButton(neuBtn,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- mListener.onNeutral(getTag());
- dialog.dismiss();
- }
- });
- if (negBtn != -1)
- builder.setNegativeButton(negBtn,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mListener.onCancel(getTag());
- dialog.dismiss();
- }
- });
- return builder.create();
- }
-
-
- public interface ConfirmationDialogFragmentListener {
- public void onConfirmation(String callerTag);
- public void onNeutral(String callerTag);
- public void onCancel(String callerTag);
- }
-
-}
-
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.ExtendedListView;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ListAdapter;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ListView;
-
-/**
- * TODO extending SherlockListFragment instead of SherlockFragment
- */
-public class ExtendedListFragment extends SherlockFragment implements OnItemClickListener {
-
- private static final String TAG = ExtendedListFragment.class.getSimpleName();
-
- private static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION";
-
- protected ExtendedListView mList;
-
- public void setListAdapter(ListAdapter listAdapter) {
- mList.setAdapter(listAdapter);
- mList.invalidate();
- }
-
- public ListView getListView() {
- return mList;
- }
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log_OC.e(TAG, "onCreateView");
- //mList = new ExtendedListView(getActivity());
- View v = inflater.inflate(R.layout.list_fragment, null);
- mList = (ExtendedListView)(v.findViewById(R.id.list_root));
- mList.setOnItemClickListener(this);
- //mList.setEmptyView(v.findViewById(R.id.empty_list_view)); // looks like it's not a cool idea
- mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
- mList.setDividerHeight(1);
-
- if (savedInstanceState != null) {
- int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
- setReferencePosition(referencePosition);
- }
-
- return v;
- }
-
-
- @Override
- public void onSaveInstanceState(Bundle savedInstanceState) {
- super.onSaveInstanceState(savedInstanceState);
- Log_OC.e(TAG, "onSaveInstanceState()");
- savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
- }
-
-
- /**
- * Calculates the position of the item that will be used as a reference to reposition the visible items in the list when
- * the device is turned to other position.
- *
- * THe current policy is take as a reference the visible item in the center of the screen.
- *
- * @return The position in the list of the visible item in the center of the screen.
- */
- protected int getReferencePosition() {
- if (mList != null) {
- return (mList.getFirstVisiblePosition() + mList.getLastVisiblePosition()) / 2;
- } else {
- return 0;
- }
- }
-
-
- /**
- * Sets the visible part of the list from the reference position.
- *
- * @param position Reference position previously returned by {@link LocalFileListFragment#getReferencePosition()}
- */
- protected void setReferencePosition(int position) {
- if (mList != null) {
- mList.setAndCenterSelection(position);
- }
- }
-
- @Override
- public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
- // to be @overriden
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.DisplayUtils;
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileObserverService;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult.ResultCode;
-import de.mobilcom.debitel.cloud.android.ui.activity.ConflictsResolveActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-
-/**
- * This Fragment is used to display the details about a file.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- */
-public class FileDetailFragment extends FileFragment implements
- OnClickListener,
- ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener, EditNameDialogListener {
-
- private FileFragment.ContainerActivity mContainerActivity;
-
- private int mLayout;
- private View mView;
- private Account mAccount;
- private FileDataStorageManager mStorageManager;
-
- private UploadFinishReceiver mUploadFinishReceiver;
- public ProgressListener mProgressListener;
-
- private Handler mHandler;
- private RemoteOperation mLastRemoteOperation;
-
- private static final String TAG = FileDetailFragment.class.getSimpleName();
- public static final String FTAG_CONFIRMATION = "REMOVE_CONFIRMATION_FRAGMENT";
-
-
- /**
- * Creates an empty details fragment.
- *
- * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
- */
- public FileDetailFragment() {
- super();
- mAccount = null;
- mStorageManager = null;
- mLayout = R.layout.file_details_empty;
- mProgressListener = null;
- }
-
- /**
- * Creates a details fragment.
- *
- * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
- *
- * @param fileToDetail An {@link OCFile} to show in the fragment
- * @param ocAccount An ownCloud account; needed to start downloads
- */
- public FileDetailFragment(OCFile fileToDetail, Account ocAccount) {
- super(fileToDetail);
- mAccount = ocAccount;
- mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
- mLayout = R.layout.file_details_empty;
- mProgressListener = null;
- }
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mHandler = new Handler();
- setHasOptionsMenu(true);
- }
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- //super.onCreateView(inflater, container, savedInstanceState);
-
- if (savedInstanceState != null) {
- setFile((OCFile)savedInstanceState.getParcelable(FileActivity.EXTRA_FILE));
- mAccount = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
- }
-
- if(getFile() != null && mAccount != null) {
- mLayout = R.layout.file_details_fragment;
- }
-
- View view = null;
- //view = inflater.inflate(mLayout, container, false);
- view = inflater.inflate(mLayout, null);
- mView = view;
-
- if (mLayout == R.layout.file_details_fragment) {
- mView.findViewById(R.id.fdKeepInSync).setOnClickListener(this);
- ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar);
- mProgressListener = new ProgressListener(progressBar);
- mView.findViewById(R.id.fdCancelBtn).setOnClickListener(this);
- }
-
- updateFileDetails(false, false);
- return view;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mContainerActivity = (ContainerActivity) activity;
-
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName());
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (mAccount != null) {
- mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
- }
- }
-
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(FileActivity.EXTRA_FILE, getFile());
- outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- listenForTransferProgress();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mUploadFinishReceiver = new UploadFinishReceiver();
- FileUploader fileUploader = new FileUploader();
- IntentFilter filter = new IntentFilter(fileUploader.getUploadFinishMessage());
- getActivity().registerReceiver(mUploadFinishReceiver, filter);
-
- }
-
-
- @Override
- public void onPause() {
- super.onPause();
- if (mUploadFinishReceiver != null) {
- getActivity().unregisterReceiver(mUploadFinishReceiver);
- mUploadFinishReceiver = null;
- }
- }
-
-
- @Override
- public void onStop() {
- super.onStop();
- leaveTransferProgress();
- }
-
-
- @Override
- public View getView() {
- return super.getView() == null ? mView : super.getView();
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.file_actions_menu, menu);
- MenuItem item = menu.findItem(R.id.action_see_details);
- if (item != null) {
- item.setVisible(false);
- item.setEnabled(false);
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onPrepareOptionsMenu (Menu menu) {
- super.onPrepareOptionsMenu(menu);
-
- List<Integer> toHide = new ArrayList<Integer>();
- List<Integer> toShow = new ArrayList<Integer>();
- OCFile file = getFile();
-
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- boolean downloading = downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file);
- FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
- boolean uploading = uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile());
-
- if (downloading || uploading) {
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_rename_file);
- toHide.add(R.id.action_remove_file);
- toHide.add(R.id.action_open_file_with);
- if (!downloading) {
- toHide.add(R.id.action_cancel_download);
- toShow.add(R.id.action_cancel_upload);
- } else {
- toHide.add(R.id.action_cancel_upload);
- toShow.add(R.id.action_cancel_download);
- }
-
- } else if (file != null && file.isDown()) {
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
-
- toShow.add(R.id.action_rename_file);
- toShow.add(R.id.action_remove_file);
- toShow.add(R.id.action_open_file_with);
- toShow.add(R.id.action_sync_file);
-
- } else if (file != null) {
- toHide.add(R.id.action_open_file_with);
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- toHide.add(R.id.action_sync_file);
-
- toShow.add(R.id.action_rename_file);
- toShow.add(R.id.action_remove_file);
- toShow.add(R.id.action_download_file);
-
- } else {
- toHide.add(R.id.action_open_file_with);
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- toHide.add(R.id.action_sync_file);
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_rename_file);
- toHide.add(R.id.action_remove_file);
-
- }
-
- MenuItem item = null;
- for (int i : toHide) {
- item = menu.findItem(i);
- if (item != null) {
- item.setVisible(false);
- item.setEnabled(false);
- }
- }
- for (int i : toShow) {
- item = menu.findItem(i);
- if (item != null) {
- item.setVisible(true);
- item.setEnabled(true);
- }
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_open_file_with: {
- mContainerActivity.openFile(getFile());
- return true;
- }
- case R.id.action_remove_file: {
- removeFile();
- return true;
- }
- case R.id.action_rename_file: {
- renameFile();
- return true;
- }
- case R.id.action_download_file:
- case R.id.action_cancel_download:
- case R.id.action_cancel_upload:
- case R.id.action_sync_file: {
- synchronizeFile();
- return true;
- }
- default:
- return false;
- }
- }
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.fdKeepInSync: {
- toggleKeepInSync();
- break;
- }
- case R.id.fdCancelBtn: {
- synchronizeFile();
- break;
- }
- default:
- Log_OC.e(TAG, "Incorrect view clicked!");
- }
- }
-
-
- private void toggleKeepInSync() {
- CheckBox cb = (CheckBox) getView().findViewById(R.id.fdKeepInSync);
- OCFile file = getFile();
- file.setKeepInSync(cb.isChecked());
- mStorageManager.saveFile(file);
-
- /// register the OCFile instance in the observer service to monitor local updates;
- /// if necessary, the file is download
- Intent intent = new Intent(getActivity().getApplicationContext(),
- FileObserverService.class);
- intent.putExtra(FileObserverService.KEY_FILE_CMD,
- (cb.isChecked()?
- FileObserverService.CMD_ADD_OBSERVED_FILE:
- FileObserverService.CMD_DEL_OBSERVED_FILE));
- intent.putExtra(FileObserverService.KEY_CMD_ARG_FILE, file);
- intent.putExtra(FileObserverService.KEY_CMD_ARG_ACCOUNT, mAccount);
- getActivity().startService(intent);
-
- if (file.keepInSync()) {
- synchronizeFile(); // force an immediate synchronization
- }
- }
-
-
- private void removeFile() {
- OCFile file = getFile();
- ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
- R.string.confirmation_remove_alert,
- new String[]{file.getFileName()},
- file.isDown() ? R.string.confirmation_remove_remote_and_local : R.string.confirmation_remove_remote,
- file.isDown() ? R.string.confirmation_remove_local : -1,
- R.string.common_cancel);
- confDialog.setOnConfirmationListener(this);
- confDialog.show(getFragmentManager(), FTAG_CONFIRMATION);
- }
-
-
- private void renameFile() {
- OCFile file = getFile();
- String fileName = file.getFileName();
- int extensionStart = file.isDirectory() ? -1 : fileName.lastIndexOf(".");
- int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
- EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
- dialog.show(getFragmentManager(), "nameeditdialog");
- }
-
- private void synchronizeFile() {
- OCFile file = getFile();
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
- if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) {
- downloaderBinder.cancel(mAccount, file);
- if (file.isDown()) {
- setButtonsForDown();
- } else {
- setButtonsForRemote();
- }
-
- } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file)) {
- uploaderBinder.cancel(mAccount, file);
- if (!file.fileExists()) {
- // TODO make something better
- ((FileDisplayActivity)getActivity()).cleanSecondFragment();
-
- } else if (file.isDown()) {
- setButtonsForDown();
- } else {
- setButtonsForRemote();
- }
-
- } else {
- mLastRemoteOperation = new SynchronizeFileOperation(file, null, mStorageManager, mAccount, true, false, getActivity());
- mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-
- // update ui
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
-
- }
- }
-
- @Override
- public void onConfirmation(String callerTag) {
- OCFile file = getFile();
- if (callerTag.equals(FTAG_CONFIRMATION)) {
- if (mStorageManager.getFileById(file.getFileId()) != null) {
- mLastRemoteOperation = new RemoveFileOperation( file,
- true,
- mStorageManager);
- mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
- }
- }
- }
-
- @Override
- public void onNeutral(String callerTag) {
- File f = null;
- OCFile file = getFile();
- if (file.isDown() && (f = new File(file.getStoragePath())).exists()) {
- f.delete();
- file.setStoragePath(null);
- mStorageManager.saveFile(file);
- updateFileDetails(file, mAccount);
- }
- }
-
- @Override
- public void onCancel(String callerTag) {
- Log_OC.d(TAG, "REMOVAL CANCELED");
- }
-
-
- /**
- * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced.
- *
- * @return True when the fragment was created with the empty layout.
- */
- public boolean isEmpty() {
- return (mLayout == R.layout.file_details_empty || getFile() == null || mAccount == null);
- }
-
-
- /**
- * Use this method to signal this Activity that it shall update its view.
- *
- * @param file : An {@link OCFile}
- */
- public void updateFileDetails(OCFile file, Account ocAccount) {
- setFile(file);
- if (ocAccount != null && (
- mStorageManager == null ||
- (mAccount != null && !mAccount.equals(ocAccount))
- )) {
- mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver());
- }
- mAccount = ocAccount;
- updateFileDetails(false, false);
- }
-
- /**
- * Updates the view with all relevant details about that file.
- *
- * TODO Remove parameter when the transferring state of files is kept in database.
- *
- * TODO REFACTORING! this method called 5 times before every time the fragment is shown!
- *
- * @param transferring Flag signaling if the file should be considered as downloading or uploading,
- * although {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and
- * {@link FileUploaderBinder#isUploading(Account, OCFile)} return false.
- *
- * @param refresh If 'true', try to refresh the hold file from the database
- */
- public void updateFileDetails(boolean transferring, boolean refresh) {
-
- if (readyToShow()) {
-
- if (refresh && mStorageManager != null) {
- setFile(mStorageManager.getFileByPath(getFile().getRemotePath()));
- }
- OCFile file = getFile();
-
- // set file details
- setFilename(file.getFileName());
- setFiletype(file.getMimetype());
- setFilesize(file.getFileLength());
- if(ocVersionSupportsTimeCreated()){
- setTimeCreated(file.getCreationTimestamp());
- }
-
- setTimeModified(file.getModificationTimestamp());
-
- CheckBox cb = (CheckBox)getView().findViewById(R.id.fdKeepInSync);
- cb.setChecked(file.keepInSync());
-
- // configure UI for depending upon local state of the file
- //if (FileDownloader.isDownloading(mAccount, mFile.getRemotePath()) || FileUploader.isUploading(mAccount, mFile.getRemotePath())) {
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
- if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file)) || (uploaderBinder != null && uploaderBinder.isUploading(mAccount, file))) {
- setButtonsForTransferring();
-
- } else if (file.isDown()) {
-
- setButtonsForDown();
-
- } else {
- // TODO load default preview image; when the local file is removed, the preview remains there
- setButtonsForRemote();
- }
- }
- getView().invalidate();
- }
-
- /**
- * Checks if the fragment is ready to show details of a OCFile
- *
- * @return 'True' when the fragment is ready to show details of a file
- */
- private boolean readyToShow() {
- return (getFile() != null && mAccount != null && mLayout == R.layout.file_details_fragment);
- }
-
-
- /**
- * Updates the filename in view
- * @param filename to set
- */
- private void setFilename(String filename) {
- TextView tv = (TextView) getView().findViewById(R.id.fdFilename);
- if (tv != null)
- tv.setText(filename);
- }
-
- /**
- * Updates the MIME type in view
- * @param mimetype to set
- */
- private void setFiletype(String mimetype) {
- TextView tv = (TextView) getView().findViewById(R.id.fdType);
- if (tv != null) {
- String printableMimetype = DisplayUtils.convertMIMEtoPrettyPrint(mimetype);;
- tv.setText(printableMimetype);
- }
- ImageView iv = (ImageView) getView().findViewById(R.id.fdIcon);
- if (iv != null) {
- iv.setImageResource(DisplayUtils.getResourceId(mimetype));
- }
- }
-
- /**
- * Updates the file size in view
- * @param filesize in bytes to set
- */
- private void setFilesize(long filesize) {
- TextView tv = (TextView) getView().findViewById(R.id.fdSize);
- if (tv != null)
- tv.setText(DisplayUtils.bytesToHumanReadable(filesize));
- }
-
- /**
- * Updates the time that the file was created in view
- * @param milliseconds Unix time to set
- */
- private void setTimeCreated(long milliseconds){
- TextView tv = (TextView) getView().findViewById(R.id.fdCreated);
- TextView tvLabel = (TextView) getView().findViewById(R.id.fdCreatedLabel);
- if(tv != null){
- tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
- tv.setVisibility(View.VISIBLE);
- tvLabel.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Updates the time that the file was last modified
- * @param milliseconds Unix time to set
- */
- private void setTimeModified(long milliseconds){
- TextView tv = (TextView) getView().findViewById(R.id.fdModified);
- if(tv != null){
- tv.setText(DisplayUtils.unixTimeToHumanReadable(milliseconds));
- }
- }
-
- /**
- * Enables or disables buttons for a file being downloaded
- */
- private void setButtonsForTransferring() {
- if (!isEmpty()) {
- // let's protect the user from himself ;)
- getView().findViewById(R.id.fdKeepInSync).setEnabled(false);
-
- // show the progress bar for the transfer
- getView().findViewById(R.id.fdProgressBlock).setVisibility(View.VISIBLE);
- TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
- progressText.setVisibility(View.VISIBLE);
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
- if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
- progressText.setText(R.string.downloader_download_in_progress_ticker);
- } else if (uploaderBinder != null && uploaderBinder.isUploading(mAccount, getFile())) {
- progressText.setText(R.string.uploader_upload_in_progress_ticker);
- }
- }
- }
-
- /**
- * Enables or disables buttons for a file locally available
- */
- private void setButtonsForDown() {
- if (!isEmpty()) {
- getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
-
- // hides the progress bar
- getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
- TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
- progressText.setVisibility(View.GONE);
- }
- }
-
- /**
- * Enables or disables buttons for a file not locally available
- */
- private void setButtonsForRemote() {
- if (!isEmpty()) {
- getView().findViewById(R.id.fdKeepInSync).setEnabled(true);
-
- // hides the progress bar
- getView().findViewById(R.id.fdProgressBlock).setVisibility(View.GONE);
- TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText);
- progressText.setVisibility(View.GONE);
- }
- }
-
-
- /**
- * In ownCloud 3.X.X and 4.X.X there is a bug that SabreDAV does not return
- * the time that the file was created. There is a chance that this will
- * be fixed in future versions. Use this method to check if this version of
- * ownCloud has this fix.
- * @return True, if ownCloud the ownCloud version is supporting creation time
- */
- private boolean ocVersionSupportsTimeCreated(){
- /*if(mAccount != null){
- AccountManager accManager = (AccountManager) getActivity().getSystemService(Context.ACCOUNT_SERVICE);
- OwnCloudVersion ocVersion = new OwnCloudVersion(accManager
- .getUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION));
- if(ocVersion.compareTo(new OwnCloudVersion(0x030000)) < 0) {
- return true;
- }
- }*/
- return false;
- }
-
-
- /**
- * Once the file upload has finished -> update view
- *
- * Being notified about the finish of an upload is necessary for the next sequence:
- * 1. Upload a big file.
- * 2. Force a synchronization; if it finished before the upload, the file in transfer will be included in the local database and in the file list
- * of its containing folder; the the server includes it in the PROPFIND requests although it's not fully upload.
- * 3. Click the file in the list to see its details.
- * 4. Wait for the upload finishes; at this moment, the details view must be refreshed to enable the action buttons.
- */
- private class UploadFinishReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String accountName = intent.getStringExtra(FileUploader.ACCOUNT_NAME);
-
- if (!isEmpty() && accountName.equals(mAccount.name)) {
- boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, false);
- String uploadRemotePath = intent.getStringExtra(FileUploader.EXTRA_REMOTE_PATH);
- boolean renamedInUpload = getFile().getRemotePath().equals(intent.getStringExtra(FileUploader.EXTRA_OLD_REMOTE_PATH));
- if (getFile().getRemotePath().equals(uploadRemotePath) ||
- renamedInUpload) {
- if (uploadWasFine) {
- setFile(mStorageManager.getFileByPath(uploadRemotePath));
- }
- if (renamedInUpload) {
- String newName = (new File(uploadRemotePath)).getName();
- Toast msg = Toast.makeText(getActivity().getApplicationContext(), String.format(getString(R.string.filedetails_renamed_in_upload_msg), newName), Toast.LENGTH_LONG);
- msg.show();
- }
- getSherlockActivity().removeStickyBroadcast(intent); // not the best place to do this; a small refactorization of BroadcastReceivers should be done
-
- updateFileDetails(false, false); // it updates the buttons; must be called although !uploadWasFine; interrupted uploads still leave an incomplete file in the server
-
- // Force the preview if the file is an image
- if (uploadWasFine && PreviewImageFragment.canBePreviewed(getFile())) {
- ((FileDisplayActivity) mContainerActivity).startImagePreview(getFile());
- }
- }
- }
- }
- }
-
-
- public void onDismiss(EditNameDialog dialog) {
- if (dialog.getResult()) {
- String newFilename = dialog.getNewFilename();
- Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
- mLastRemoteOperation = new RenameFileOperation( getFile(),
- mAccount,
- newFilename,
- new FileDataStorageManager(mAccount, getActivity().getContentResolver()));
- mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
- if (operation.equals(mLastRemoteOperation)) {
- if (operation instanceof RemoveFileOperation) {
- onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
-
- } else if (operation instanceof RenameFileOperation) {
- onRenameFileOperationFinish((RenameFileOperation)operation, result);
-
- } else if (operation instanceof SynchronizeFileOperation) {
- onSynchronizeFileOperationFinish((SynchronizeFileOperation)operation, result);
- }
- }
- }
-
-
- private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
- ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
- if (result.isSuccess()) {
- Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
- msg.show();
- ((FileDisplayActivity)getActivity()).cleanSecondFragment();
-
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- // TODO show the SSL warning dialog
- }
- }
- }
-
- private void onRenameFileOperationFinish(RenameFileOperation operation, RemoteOperationResult result) {
- ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
-
- if (result.isSuccess()) {
- updateFileDetails(((RenameFileOperation)operation).getFile(), mAccount);
- mContainerActivity.onFileStateChanged();
-
- } else {
- if (result.getCode().equals(ResultCode.INVALID_LOCAL_FILE_NAME)) {
- Toast msg = Toast.makeText(getActivity(), R.string.rename_local_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- // TODO throw again the new rename dialog
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.rename_server_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- // TODO show the SSL warning dialog
- }
- }
- }
- }
-
- private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) {
- ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
- OCFile file = getFile();
- if (!result.isSuccess()) {
- if (result.getCode() == ResultCode.SYNC_CONFLICT) {
- Intent i = new Intent(getActivity(), ConflictsResolveActivity.class);
- i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file);
- i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, mAccount);
- startActivity(i);
-
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.sync_file_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- }
-
- if (file.isDown()) {
- setButtonsForDown();
-
- } else {
- setButtonsForRemote();
- }
-
- } else {
- if (operation.transferWasRequested()) {
- setButtonsForTransferring();
- mContainerActivity.onFileStateChanged(); // this is not working; FileDownloader won't do NOTHING at all until this method finishes, so
- // checking the service to see if the file is downloading results in FALSE
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.sync_file_nothing_to_do_msg, Toast.LENGTH_LONG);
- msg.show();
- if (file.isDown()) {
- setButtonsForDown();
-
- } else {
- setButtonsForRemote();
- }
- }
- }
- }
-
-
- public void listenForTransferProgress() {
- if (mProgressListener != null) {
- if (mContainerActivity.getFileDownloaderBinder() != null) {
- mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
- }
- if (mContainerActivity.getFileUploaderBinder() != null) {
- mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
- }
- }
- }
-
-
- public void leaveTransferProgress() {
- if (mProgressListener != null) {
- if (mContainerActivity.getFileDownloaderBinder() != null) {
- mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
- }
- if (mContainerActivity.getFileUploaderBinder() != null) {
- mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
- }
- }
- }
-
-
-
- /**
- * Helper class responsible for updating the progress bar shown for file uploading or downloading
- *
- * @author David A. Velasco
- */
- private class ProgressListener implements OnDatatransferProgressListener {
- int mLastPercent = 0;
- WeakReference<ProgressBar> mProgressBar = null;
-
- ProgressListener(ProgressBar progressBar) {
- mProgressBar = new WeakReference<ProgressBar>(progressBar);
- }
-
- @Override
- public void onTransferProgress(long progressRate) {
- // old method, nothing here
- };
-
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
- int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
- if (percent != mLastPercent) {
- ProgressBar pb = mProgressBar.get();
- if (pb != null) {
- pb.setProgress(percent);
- pb.postInvalidate();
- }
- }
- mLastPercent = percent;
- }
-
- };
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import android.support.v4.app.Fragment;
-
-import com.actionbarsherlock.app.SherlockFragment;
-
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.FileHandler;
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;
-
-/**
- * Common methods for {@link Fragment}s containing {@link OCFile}s
- *
- * @author David A. Velasco
- *
- */
-public class FileFragment extends SherlockFragment {
-
- private OCFile mFile;
-
-
- /**
- * Creates an empty fragment.
- *
- * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
- */
- public FileFragment() {
- mFile = null;
- }
-
- /**
- * Creates an instance for a given {@OCFile}.
- *
- * @param file
- */
- public FileFragment(OCFile file) {
- mFile = file;
- }
-
- /**
- * Getter for the hold {@link OCFile}
- *
- * @return The {@link OCFile} hold
- */
- public OCFile getFile() {
- return mFile;
- }
-
-
- protected void setFile(OCFile file) {
- mFile = file;
- }
-
- /**
- * Interface to implement by any Activity that includes some instance of FileFragment
- *
- * @author David A. Velasco
- */
- public interface ContainerActivity extends TransferServiceGetter, FileHandler {
-
- /**
- * Callback method invoked when the detail fragment wants to notice its container
- * activity about a relevant state the file shown by the fragment.
- *
- * Added to notify to FileDisplayActivity about the need of refresh the files list.
- *
- * Currently called when:
- * - a download is started;
- * - a rename is completed;
- * - a deletion is completed;
- * - the 'inSync' flag is changed;
- */
- public void onFileStateChanged();
-
- /**
- * Request the parent activity to show the details of an {@link OCFile}.
- *
- * @param file File to show details
- */
- public void showDetails(OCFile file);
-
-
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application\r
- * Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
- *\r
- * This program is free software: you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License version 2,\r
- * as published by the Free Software Foundation.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program. If not, see <http://www.gnu.org/licenses/>.\r
- *\r
- */\r
-package de.mobilcom.debitel.cloud.android.ui.fragment;\r
-\r
-import com.actionbarsherlock.app.SherlockFragment;\r
-\r
-import android.os.Bundle;\r
-import android.view.LayoutInflater;\r
-import android.view.View;\r
-import android.view.ViewGroup;\r
-import android.widget.ListView;\r
-import de.mobilcom.debitel.cloud.android.R;\r
-import de.mobilcom.debitel.cloud.android.ui.activity.LandingActivity;\r
-import de.mobilcom.debitel.cloud.android.ui.adapter.LandingScreenAdapter;\r
-\r
-/**\r
- * Used on the Landing page to display what Components of the ownCloud there\r
- * are. Like Files, Music, Contacts, etc.\r
- * \r
- * @author Lennart Rosam\r
- * \r
- */\r
-public class LandingPageFragment extends SherlockFragment {\r
-\r
- @Override\r
- public View onCreateView(LayoutInflater inflater, ViewGroup container,\r
- Bundle savedInstanceState) {\r
- View root = inflater.inflate(R.layout.landing_page_fragment, container);\r
- return root;\r
- }\r
-\r
- @Override\r
- public void onActivityCreated(Bundle savedInstanceState) {\r
- super.onActivityCreated(savedInstanceState);\r
-\r
- ListView landingScreenItems = (ListView) getView().findViewById(\r
- R.id.homeScreenList);\r
- landingScreenItems.setAdapter(new LandingScreenAdapter(getActivity()));\r
- landingScreenItems\r
- .setOnItemClickListener((LandingActivity) getActivity());\r
- }\r
-\r
-}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Environment;
-import android.util.SparseBooleanArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.ui.adapter.LocalFileListAdapter;
-
-/**
- * A Fragment that lists all files and folders in a given LOCAL path.
- *
- * @author David A. Velasco
- *
- */
-public class LocalFileListFragment extends ExtendedListFragment {
- private static final String TAG = "LocalFileListFragment";
-
- /** Reference to the Activity which this fragment is attached to. For callbacks */
- private LocalFileListFragment.ContainerActivity mContainerActivity;
-
- /** Directory to show */
- private File mDirectory = null;
-
- /** Adapter to connect the data from the directory with the View object */
- private LocalFileListAdapter mAdapter = null;
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mContainerActivity = (ContainerActivity) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement " + LocalFileListFragment.ContainerActivity.class.getSimpleName());
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log_OC.i(TAG, "onCreateView() start");
- View v = super.onCreateView(inflater, container, savedInstanceState);
- getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- Log_OC.i(TAG, "onCreateView() end");
- return v;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- Log_OC.i(TAG, "onActivityCreated() start");
-
- super.onCreate(savedInstanceState);
- mAdapter = new LocalFileListAdapter(mContainerActivity.getInitialDirectory(), getActivity());
- setListAdapter(mAdapter);
-
- Log_OC.i(TAG, "onActivityCreated() stop");
- }
-
-
- /**
- * Checks the file clicked over. Browses inside if it is a directory. Notifies the container activity in any case.
- */
- @Override
- public void onItemClick(AdapterView<?> l, View v, int position, long id) {
- File file = (File) mAdapter.getItem(position);
- if (file != null) {
- /// Click on a directory
- if (file.isDirectory()) {
- // just local updates
- listDirectory(file);
- // notify the click to container Activity
- mContainerActivity.onDirectoryClick(file);
-
- } else { /// Click on a file
- ImageView checkBoxV = (ImageView) v.findViewById(R.id.custom_checkbox);
- if (checkBoxV != null) {
- if (getListView().isItemChecked(position)) {
- checkBoxV.setImageResource(android.R.drawable.checkbox_on_background);
- } else {
- checkBoxV.setImageResource(android.R.drawable.checkbox_off_background);
- }
- }
- // notify the change to the container Activity
- mContainerActivity.onFileClick(file);
- }
-
- } else {
- Log_OC.w(TAG, "Null object in ListAdapter!!");
- }
- }
-
-
- /**
- * Call this, when the user presses the up button
- */
- public void onNavigateUp() {
- File parentDir = null;
- if(mDirectory != null) {
- parentDir = mDirectory.getParentFile(); // can be null
- }
- listDirectory(parentDir);
- }
-
-
- /**
- * Use this to query the {@link File} object for the directory
- * that is currently being displayed by this fragment
- *
- * @return File The currently displayed directory
- */
- public File getCurrentDirectory(){
- return mDirectory;
- }
-
-
- /**
- * Calls {@link LocalFileListFragment#listDirectory(File)} with a null parameter
- * to refresh the current directory.
- */
- public void listDirectory(){
- listDirectory(null);
- }
-
-
- /**
- * Lists the given directory on the view. When the input parameter is null,
- * it will either refresh the last known directory. list the root
- * if there never was a directory.
- *
- * @param directory Directory to be listed
- */
- public void listDirectory(File directory) {
-
- // Check input parameters for null
- if(directory == null) {
- if(mDirectory != null){
- directory = mDirectory;
- } else {
- directory = Environment.getExternalStorageDirectory(); // TODO be careful with the state of the storage; could not be available
- if (directory == null) return; // no files to show
- }
- }
-
-
- // if that's not a directory -> List its parent
- if(!directory.isDirectory()){
- Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
- directory = directory.getParentFile();
- }
-
- mList.clearChoices(); // by now, only files in the same directory will be kept as selected
- mAdapter.swapDirectory(directory);
- if (mDirectory == null || !mDirectory.equals(directory)) {
- mList.setSelectionFromTop(0, 0);
- }
- mDirectory = directory;
- }
-
-
- /**
- * Returns the fule paths to the files checked by the user
- *
- * @return File paths to the files checked by the user.
- */
- public String[] getCheckedFilePaths() {
- String [] result = null;
- SparseBooleanArray positions = mList.getCheckedItemPositions();
- if (positions.size() > 0) {
- Log_OC.d(TAG, "Returning " + positions.size() + " selected files");
- result = new String[positions.size()];
- for (int i=0; i<positions.size(); i++) {
- result[i] = ((File) mList.getItemAtPosition(positions.keyAt(i))).getAbsolutePath();
- }
- }
- return result;
- }
-
-
- /**
- * Interface to implement by any Activity that includes some instance of LocalFileListFragment
- *
- * @author David A. Velasco
- */
- public interface ContainerActivity {
-
- /**
- * Callback method invoked when a directory is clicked by the user on the files list
- *
- * @param file
- */
- public void onDirectoryClick(File directory);
-
- /**
- * Callback method invoked when a file (non directory) is clicked by the user on the files list
- *
- * @param file
- */
- public void onFileClick(File file);
-
-
- /**
- * Callback method invoked when the parent activity is fully created to get the directory to list firstly.
- *
- * @return Directory to list firstly. Can be NULL.
- */
- public File getInitialDirectory();
-
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.fragment;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.FileHandler;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.RenameFileOperation;
-import de.mobilcom.debitel.cloud.android.operations.SynchronizeFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.TransferServiceGetter;
-import de.mobilcom.debitel.cloud.android.ui.adapter.FileListListAdapter;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog;
-import de.mobilcom.debitel.cloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewImageFragment;
-import de.mobilcom.debitel.cloud.android.ui.preview.PreviewMediaFragment;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextMenu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-
-/**
- * A Fragment that lists all files and folders in a given path.
- *
- * @author Bartek Przybylski
- *
- */
-public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener {
-
- private static final String TAG = OCFileListFragment.class.getSimpleName();
-
- private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "de.mobilcom.debitel.cloud.android.ui.fragment";
- private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE";
-
- private OCFileListFragment.ContainerActivity mContainerActivity;
-
- private OCFile mFile = null;
- private FileListListAdapter mAdapter;
-
- private Handler mHandler;
- private OCFile mTargetFile;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- Log_OC.e(TAG, "onAttach");
- try {
- mContainerActivity = (ContainerActivity) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName());
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- Log_OC.e(TAG, "onActivityCreated() start");
- mAdapter = new FileListListAdapter(getActivity(), mContainerActivity);
- if (savedInstanceState != null) {
- mFile = savedInstanceState.getParcelable(EXTRA_FILE);
- }
- setListAdapter(mAdapter);
-
- registerForContextMenu(getListView());
- getListView().setOnCreateContextMenuListener(this);
-
- mHandler = new Handler();
-
- }
-
- /**
- * Saves the current listed folder.
- */
- @Override
- public void onSaveInstanceState (Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(EXTRA_FILE, mFile);
- }
-
-
- /**
- * Call this, when the user presses the up button
- */
- public void onBrowseUp() {
- OCFile parentDir = null;
-
- if(mFile != null){
- DataStorageManager storageManager = mContainerActivity.getStorageManager();
- parentDir = storageManager.getFileById(mFile.getParentId());
- mFile = parentDir;
- }
- listDirectory(parentDir);
- }
-
- @Override
- public void onItemClick(AdapterView<?> l, View v, int position, long id) {
- OCFile file = (OCFile) mAdapter.getItem(position);
- if (file != null) {
- if (file.isDirectory()) {
- // update state and view of this fragment
- listDirectory(file);
- // then, notify parent activity to let it update its state and view, and other fragments
- mContainerActivity.onBrowsedDownTo(file);
-
- } else { /// Click on a file
- if (PreviewImageFragment.canBePreviewed(file)) {
- // preview image - it handles the download, if needed
- mContainerActivity.startImagePreview(file);
-
- } else if (file.isDown()) {
- if (PreviewMediaFragment.canBePreviewed(file)) {
- // media preview
- mContainerActivity.startMediaPreview(file, 0, true);
- } else {
- // open with
- mContainerActivity.openFile(file);
- }
-
- } else {
- // automatic download, preview on finish
- mContainerActivity.startDownloadForPreview(file);
- }
-
- }
-
- } else {
- Log_OC.d(TAG, "Null object in ListAdapter!!");
- }
-
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.file_actions_menu, menu);
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
- OCFile targetFile = (OCFile) mAdapter.getItem(info.position);
- List<Integer> toHide = new ArrayList<Integer>();
- List<Integer> toDisable = new ArrayList<Integer>();
-
- MenuItem item = null;
- if (targetFile.isDirectory()) {
- // contextual menu for folders
- toHide.add(R.id.action_open_file_with);
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- toHide.add(R.id.action_sync_file);
- toHide.add(R.id.action_see_details);
- if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ||
- mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) {
- toDisable.add(R.id.action_rename_file);
- toDisable.add(R.id.action_remove_file);
-
- }
-
- } else {
- // contextual menu for regular files
-
- // new design: 'download' and 'open with' won't be available anymore in context menu
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_open_file_with);
-
- if (targetFile.isDown()) {
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
-
- } else {
- toHide.add(R.id.action_sync_file);
- }
- if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
- toHide.add(R.id.action_cancel_upload);
- toDisable.add(R.id.action_rename_file);
- toDisable.add(R.id.action_remove_file);
-
- } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) {
- toHide.add(R.id.action_cancel_download);
- toDisable.add(R.id.action_rename_file);
- toDisable.add(R.id.action_remove_file);
-
- } else {
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- }
- }
-
- for (int i : toHide) {
- item = menu.findItem(i);
- if (item != null) {
- item.setVisible(false);
- item.setEnabled(false);
- }
- }
-
- for (int i : toDisable) {
- item = menu.findItem(i);
- if (item != null) {
- item.setEnabled(false);
- }
- }
- }
-
-
- /**
- * {@inhericDoc}
- */
- @Override
- public boolean onContextItemSelected (MenuItem item) {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
- mTargetFile = (OCFile) mAdapter.getItem(info.position);
- switch (item.getItemId()) {
- case R.id.action_rename_file: {
- String fileName = mTargetFile.getFileName();
- int extensionStart = mTargetFile.isDirectory() ? -1 : fileName.lastIndexOf(".");
- int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length();
- EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this);
- dialog.show(getFragmentManager(), EditNameDialog.TAG);
- return true;
- }
- case R.id.action_remove_file: {
- int messageStringId = R.string.confirmation_remove_alert;
- int posBtnStringId = R.string.confirmation_remove_remote;
- int neuBtnStringId = -1;
- if (mTargetFile.isDirectory()) {
- messageStringId = R.string.confirmation_remove_folder_alert;
- posBtnStringId = R.string.confirmation_remove_remote_and_local;
- neuBtnStringId = R.string.confirmation_remove_folder_local;
- } else if (mTargetFile.isDown()) {
- posBtnStringId = R.string.confirmation_remove_remote_and_local;
- neuBtnStringId = R.string.confirmation_remove_local;
- }
- ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
- messageStringId,
- new String[]{mTargetFile.getFileName()},
- posBtnStringId,
- neuBtnStringId,
- R.string.common_cancel);
- confDialog.setOnConfirmationListener(this);
- confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION);
- return true;
- }
- case R.id.action_sync_file: {
- Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity());
- RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, false, getSherlockActivity());
- operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
- ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog();
- return true;
- }
- case R.id.action_cancel_download: {
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
- if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) {
- downloaderBinder.cancel(account, mTargetFile);
- listDirectory();
- mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
- }
- return true;
- }
- case R.id.action_cancel_upload: {
- FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder();
- Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity());
- if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) {
- uploaderBinder.cancel(account, mTargetFile);
- listDirectory();
- mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
- }
- return true;
- }
- case R.id.action_see_details: {
- ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile);
- return true;
- }
- default:
- return super.onContextItemSelected(item);
- }
- }
-
-
- /**
- * Use this to query the {@link OCFile} that is currently
- * being displayed by this fragment
- * @return The currently viewed OCFile
- */
- public OCFile getCurrentFile(){
- return mFile;
- }
-
- /**
- * Calls {@link OCFileListFragment#listDirectory(OCFile)} with a null parameter
- */
- public void listDirectory(){
- listDirectory(null);
- }
-
- /**
- * Lists the given directory on the view. When the input parameter is null,
- * it will either refresh the last known directory. list the root
- * if there never was a directory.
- *
- * @param directory File to be listed
- */
- public void listDirectory(OCFile directory) {
- DataStorageManager storageManager = mContainerActivity.getStorageManager();
- if (storageManager != null) {
-
- // Check input parameters for null
- if(directory == null){
- if(mFile != null){
- directory = mFile;
- } else {
- directory = storageManager.getFileByPath("/");
- if (directory == null) return; // no files, wait for sync
- }
- }
-
-
- // If that's not a directory -> List its parent
- if(!directory.isDirectory()){
- Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
- directory = storageManager.getFileById(directory.getParentId());
- }
-
- mAdapter.swapDirectory(directory, storageManager);
- if (mFile == null || !mFile.equals(directory)) {
- mList.setSelectionFromTop(0, 0);
- }
- mFile = directory;
-
- }
- }
-
-
-
- /**
- * Interface to implement by any Activity that includes some instance of FileListFragment
- *
- * @author David A. Velasco
- */
- public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
-
- /**
- * Callback method invoked when a the user browsed into a different folder through the list of files
- *
- * @param file
- */
- public void onBrowsedDownTo(OCFile folder);
-
- public void startDownloadForPreview(OCFile file);
-
- public void startMediaPreview(OCFile file, int i, boolean b);
-
- public void startImagePreview(OCFile file);
-
- /**
- * Getter for the current DataStorageManager in the container activity
- */
- public DataStorageManager getStorageManager();
-
-
- /**
- * Callback method invoked when a the 'transfer state' of a file changes.
- *
- * This happens when a download or upload is started or ended for a file.
- *
- * This method is necessary by now to update the user interface of the double-pane layout in tablets
- * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)}
- * won't provide the needed response before the method where this is called finishes.
- *
- * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO)
- *
- * @param file OCFile which state changed.
- * @param downloading Flag signaling if the file is now downloading.
- * @param uploading Flag signaling if the file is now uploading.
- */
- public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading);
-
- }
-
-
- @Override
- public void onDismiss(EditNameDialog dialog) {
- if (dialog.getResult()) {
- String newFilename = dialog.getNewFilename();
- Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename);
- RemoteOperation operation = new RenameFileOperation(mTargetFile,
- AccountUtils.getCurrentOwnCloudAccount(getActivity()),
- newFilename,
- mContainerActivity.getStorageManager());
- operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
- }
- }
-
-
- @Override
- public void onConfirmation(String callerTag) {
- if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) {
- if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) {
- RemoteOperation operation = new RemoveFileOperation( mTargetFile,
- true,
- mContainerActivity.getStorageManager());
- operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity());
-
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
- }
- }
- }
-
- @Override
- public void onNeutral(String callerTag) {
- File f = null;
- if (mTargetFile.isDirectory()) {
- // TODO run in a secondary thread?
- mContainerActivity.getStorageManager().removeDirectory(mTargetFile, false, true);
-
- } else if (mTargetFile.isDown() && (f = new File(mTargetFile.getStoragePath())).exists()) {
- f.delete();
- mTargetFile.setStoragePath(null);
- mContainerActivity.getStorageManager().saveFile(mTargetFile);
- }
- listDirectory();
- mContainerActivity.onTransferStateChanged(mTargetFile, false, false);
- }
-
- @Override
- public void onCancel(String callerTag) {
- Log_OC.d(TAG, "REMOVAL CANCELED");
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- *
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import java.lang.ref.WeakReference;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-
-import eu.alefzero.webdav.OnDatatransferProgressListener;
-
-/**
- * This Fragment is used to monitor the progress of a file downloading.
- *
- * @author David A. Velasco
- */
-public class FileDownloadFragment extends FileFragment implements OnClickListener {
-
- public static final String EXTRA_FILE = "FILE";
- public static final String EXTRA_ACCOUNT = "ACCOUNT";
- private static final String EXTRA_ERROR = "ERROR";
-
- private FileFragment.ContainerActivity mContainerActivity;
-
- private View mView;
- private Account mAccount;
-
- public ProgressListener mProgressListener;
- private boolean mListening;
-
- private static final String TAG = FileDownloadFragment.class.getSimpleName();
-
- private boolean mIgnoreFirstSavedState;
- private boolean mError;
-
-
- /**
- * Creates an empty details fragment.
- *
- * It's necessary to keep a public constructor without parameters; the system uses it when tries to reinstantiate a fragment automatically.
- */
- public FileDownloadFragment() {
- super();
- mAccount = null;
- mProgressListener = null;
- mListening = false;
- mIgnoreFirstSavedState = false;
- mError = false;
- }
-
-
- /**
- * Creates a details fragment.
- *
- * When 'fileToDetail' or 'ocAccount' are null, creates a dummy layout (to use when a file wasn't tapped before).
- *
- * @param fileToDetail An {@link OCFile} to show in the fragment
- * @param ocAccount An ownCloud account; needed to start downloads
- * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution
- */
- public FileDownloadFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
- super(fileToDetail);
- mAccount = ocAccount;
- mProgressListener = null;
- mListening = false;
- mIgnoreFirstSavedState = ignoreFirstSavedState;
- mError = false;
- }
-
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
-
- if (savedInstanceState != null) {
- if (!mIgnoreFirstSavedState) {
- setFile((OCFile)savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_FILE));
- mAccount = savedInstanceState.getParcelable(FileDownloadFragment.EXTRA_ACCOUNT);
- mError = savedInstanceState.getBoolean(FileDownloadFragment.EXTRA_ERROR);
- } else {
- mIgnoreFirstSavedState = false;
- }
- }
-
- View view = null;
- view = inflater.inflate(R.layout.file_download_fragment, container, false);
- mView = view;
-
- ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.progressBar);
- mProgressListener = new ProgressListener(progressBar);
-
- ((ImageButton)mView.findViewById(R.id.cancelBtn)).setOnClickListener(this);
-
- if (mError) {
- setButtonsForRemote();
- } else {
- setButtonsForTransferring();
- }
-
- return view;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mContainerActivity = (ContainerActivity) activity;
-
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- if (mAccount != null) {
- //mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());;
- }
- }
-
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(FileDownloadFragment.EXTRA_FILE, getFile());
- outState.putParcelable(FileDownloadFragment.EXTRA_ACCOUNT, mAccount);
- outState.putBoolean(FileDownloadFragment.EXTRA_ERROR, mError);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- listenForTransferProgress();
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
-
- @Override
- public void onPause() {
- super.onPause();
- }
-
-
- @Override
- public void onStop() {
- super.onStop();
- leaveTransferProgress();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
-
- @Override
- public View getView() {
- if (!mListening) {
- listenForTransferProgress();
- }
- return super.getView() == null ? mView : super.getView();
- }
-
-
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.cancelBtn: {
- FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder();
- if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile())) {
- downloaderBinder.cancel(mAccount, getFile());
- getActivity().finish(); // :)
- /*
- leaveTransferProgress();
- if (mFile.isDown()) {
- setButtonsForDown();
- } else {
- setButtonsForRemote();
- }
- */
- }
- break;
- }
- default:
- Log_OC.e(TAG, "Incorrect view clicked!");
- }
- }
-
-
- /**
- * Updates the view depending upon the state of the downloading file.
- *
- * @param transferring When true, the view must be updated assuming that the holded file is
- * downloading, no matter what the downloaderBinder says.
- */
- public void updateView(boolean transferring) {
- // configure UI for depending upon local state of the file
- FileDownloaderBinder downloaderBinder = (mContainerActivity == null) ? null : mContainerActivity.getFileDownloaderBinder();
- if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, getFile()))) {
- setButtonsForTransferring();
-
- } else if (getFile().isDown()) {
-
- setButtonsForDown();
-
- } else {
- setButtonsForRemote();
- }
- getView().invalidate();
-
- }
-
-
- /**
- * Enables or disables buttons for a file being downloaded
- */
- private void setButtonsForTransferring() {
- getView().findViewById(R.id.cancelBtn).setVisibility(View.VISIBLE);
-
- // show the progress bar for the transfer
- getView().findViewById(R.id.progressBar).setVisibility(View.VISIBLE);
- TextView progressText = (TextView)getView().findViewById(R.id.progressText);
- progressText.setText(R.string.downloader_download_in_progress_ticker);
- progressText.setVisibility(View.VISIBLE);
-
- // hides the error icon
- getView().findViewById(R.id.errorText).setVisibility(View.GONE);
- getView().findViewById(R.id.error_image).setVisibility(View.GONE);
- }
-
-
- /**
- * Enables or disables buttons for a file locally available
- */
- private void setButtonsForDown() {
- getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
-
- // hides the progress bar
- getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
-
- // updates the text message
- TextView progressText = (TextView)getView().findViewById(R.id.progressText);
- progressText.setText(R.string.common_loading);
- progressText.setVisibility(View.VISIBLE);
-
- // hides the error icon
- getView().findViewById(R.id.errorText).setVisibility(View.GONE);
- getView().findViewById(R.id.error_image).setVisibility(View.GONE);
- }
-
-
- /**
- * Enables or disables buttons for a file not locally available
- *
- * Currently, this is only used when a download was failed
- */
- private void setButtonsForRemote() {
- getView().findViewById(R.id.cancelBtn).setVisibility(View.GONE);
-
- // hides the progress bar and message
- getView().findViewById(R.id.progressBar).setVisibility(View.GONE);
- getView().findViewById(R.id.progressText).setVisibility(View.GONE);
-
- // shows the error icon and message
- getView().findViewById(R.id.errorText).setVisibility(View.VISIBLE);
- getView().findViewById(R.id.error_image).setVisibility(View.VISIBLE);
- }
-
-
- public void listenForTransferProgress() {
- if (mProgressListener != null && !mListening) {
- if (mContainerActivity.getFileDownloaderBinder() != null) {
- mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, getFile());
- mListening = true;
- setButtonsForTransferring();
- }
- }
- }
-
-
- public void leaveTransferProgress() {
- if (mProgressListener != null) {
- if (mContainerActivity.getFileDownloaderBinder() != null) {
- mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, getFile());
- mListening = false;
- }
- }
- }
-
-
- /**
- * Helper class responsible for updating the progress bar shown for file uploading or downloading
- *
- * @author David A. Velasco
- */
- private class ProgressListener implements OnDatatransferProgressListener {
- int mLastPercent = 0;
- WeakReference<ProgressBar> mProgressBar = null;
-
- ProgressListener(ProgressBar progressBar) {
- mProgressBar = new WeakReference<ProgressBar>(progressBar);
- }
-
- @Override
- public void onTransferProgress(long progressRate) {
- // old method, nothing here
- };
-
- @Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
- int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
- if (percent != mLastPercent) {
- ProgressBar pb = mProgressBar.get();
- if (pb != null) {
- pb.setProgress(percent);
- pb.postInvalidate();
- }
- }
- mLastPercent = percent;
- }
-
- }
-
-
- public void setError(boolean error) {
- mError = error;
- };
-
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.ViewPager;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.view.MenuItem;
-import com.actionbarsherlock.view.Window;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader;
-import de.mobilcom.debitel.cloud.android.files.services.FileDownloader.FileDownloaderBinder;
-import de.mobilcom.debitel.cloud.android.files.services.FileUploader.FileUploaderBinder;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.dialog.LoadingDialog;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-
-/**
- * Holds a swiping galley where image files contained in an ownCloud directory are shown
- *
- * @author David A. Velasco
- */
-public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener {
-
- public static final int DIALOG_SHORT_WAIT = 0;
-
- public static final String TAG = PreviewImageActivity.class.getSimpleName();
-
- public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
- private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER";
-
- private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-
- private DataStorageManager mStorageManager;
-
- private ViewPager mViewPager;
- private PreviewImagePagerAdapter mPreviewImagePagerAdapter;
-
- private FileDownloaderBinder mDownloaderBinder = null;
- private ServiceConnection mDownloadConnection, mUploadConnection = null;
- private FileUploaderBinder mUploaderBinder = null;
-
- private boolean mRequestWaitingForBinder;
-
- private DownloadFinishReceiver mDownloadFinishReceiver;
-
- private boolean mFullScreen;
-
- private String mDownloadAddedMessage;
- private String mDownloadFinishMessage;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- setContentView(R.layout.preview_image_activity);
-
- ActionBar actionBar = getSupportActionBar();
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.hide();
-
- mFullScreen = true;
- if (savedInstanceState != null) {
- mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
- } else {
- mRequestWaitingForBinder = false;
- }
-
- FileDownloader downloader = new FileDownloader();
- mDownloadAddedMessage = downloader.getDownloadAddedMessage();
- mDownloadFinishMessage= downloader.getDownloadFinishMessage();
- }
-
- private void initViewPager() {
- // get parent from path
- String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
- OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
- //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
- if (parentFolder == null) {
- // should not be necessary
- parentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR);
- }
- mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
- mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
- int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
- position = (position >= 0) ? position : 0;
- mViewPager.setAdapter(mPreviewImagePagerAdapter);
- mViewPager.setOnPageChangeListener(this);
- mViewPager.setCurrentItem(position);
- if (position == 0 && !getFile().isDown()) {
- // this is necessary because mViewPager.setCurrentItem(0) just after setting the adapter does not result in a call to #onPageSelected(0)
- mRequestWaitingForBinder = true;
- }
- }
-
-
- @Override
- public void onStart() {
- super.onStart();
- mDownloadConnection = new PreviewImageServiceConnection();
- bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE);
- mUploadConnection = new PreviewImageServiceConnection();
- bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder);
- }
-
-
- /** Defines callbacks for service binding, passed to bindService() */
- private class PreviewImageServiceConnection implements ServiceConnection {
-
- @Override
- public void onServiceConnected(ComponentName component, IBinder service) {
-
- if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
- mDownloaderBinder = (FileDownloaderBinder) service;
- if (mRequestWaitingForBinder) {
- mRequestWaitingForBinder = false;
- Log_OC.d(TAG, "Simulating reselection of current page after connection of download binder");
- onPageSelected(mViewPager.getCurrentItem());
- }
-
- } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
- Log_OC.d(TAG, "Upload service connected");
- mUploaderBinder = (FileUploaderBinder) service;
- } else {
- return;
- }
-
- }
-
- @Override
- public void onServiceDisconnected(ComponentName component) {
- if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) {
- Log_OC.d(TAG, "Download service suddenly disconnected");
- mDownloaderBinder = null;
- } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) {
- Log_OC.d(TAG, "Upload service suddenly disconnected");
- mUploaderBinder = null;
- }
- }
- };
-
-
- @Override
- public void onStop() {
- super.onStop();
- if (mDownloadConnection != null) {
- unbindService(mDownloadConnection);
- mDownloadConnection = null;
- }
- if (mUploadConnection != null) {
- unbindService(mUploadConnection);
- mUploadConnection = null;
- }
- }
-
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- boolean returnValue = false;
-
- switch(item.getItemId()){
- case android.R.id.home:
- backToDisplayActivity();
- returnValue = true;
- break;
- default:
- returnValue = super.onOptionsItemSelected(item);
- }
-
- return returnValue;
- }
-
-
- @Override
- protected void onResume() {
- super.onResume();
- //Log.e(TAG, "ACTIVITY, ONRESUME");
- mDownloadFinishReceiver = new DownloadFinishReceiver();
-
- IntentFilter filter = new IntentFilter(mDownloadFinishMessage);
- filter.addAction(mDownloadAddedMessage);
- registerReceiver(mDownloadFinishReceiver, filter);
- }
-
- @Override
- protected void onPostResume() {
- //Log.e(TAG, "ACTIVITY, ONPOSTRESUME");
- super.onPostResume();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- unregisterReceiver(mDownloadFinishReceiver);
- mDownloadFinishReceiver = null;
- }
-
-
- private void backToDisplayActivity() {
- finish();
- }
-
- /**
- * Show loading dialog
- */
- public void showLoadingDialog() {
- // Construct dialog
- LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction ft = fm.beginTransaction();
- loading.show(ft, DIALOG_WAIT_TAG);
-
- }
-
- /**
- * Dismiss loading dialog
- */
- public void dismissLoadingDialog(){
- Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
- if (frag != null) {
- LoadingDialog loading = (LoadingDialog) frag;
- loading.dismiss();
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onFileStateChanged() {
- // nothing to do here!
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public FileDownloaderBinder getFileDownloaderBinder() {
- return mDownloaderBinder;
- }
-
-
- @Override
- public FileUploaderBinder getFileUploaderBinder() {
- return mUploaderBinder;
- }
-
-
- @Override
- public void showDetails(OCFile file) {
- Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
- showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS);
- showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file);
- showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this));
- startActivity(showDetailsIntent);
- int pos = mPreviewImagePagerAdapter.getFilePosition(file);
- file = mPreviewImagePagerAdapter.getFileAt(pos);
-
- }
-
-
- private void requestForDownload(OCFile file) {
- if (mDownloaderBinder == null) {
- Log_OC.d(TAG, "requestForDownload called without binder to download service");
-
- } else if (!mDownloaderBinder.isDownloading(getAccount(), file)) {
- Intent i = new Intent(this, FileDownloader.class);
- i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount());
- i.putExtra(FileDownloader.EXTRA_FILE, file);
- startService(i);
- }
- }
-
- /**
- * This method will be invoked when a new page becomes selected. Animation is not necessarily complete.
- *
- * @param Position Position index of the new selected page
- */
- @Override
- public void onPageSelected(int position) {
- if (mDownloaderBinder == null) {
- mRequestWaitingForBinder = true;
-
- } else {
- OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position);
- getSupportActionBar().setTitle(currentFile.getFileName());
- if (!currentFile.isDown()) {
- if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) {
- requestForDownload(currentFile);
- }
- }
- }
- }
-
- /**
- * Called when the scroll state changes. Useful for discovering when the user begins dragging,
- * when the pager is automatically settling to the current page. when it is fully stopped/idle.
- *
- * @param State The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING
- */
- @Override
- public void onPageScrollStateChanged(int state) {
- }
-
- /**
- * This method will be invoked when the current page is scrolled, either as part of a programmatically
- * initiated smooth scroll or a user initiated touch scroll.
- *
- * @param position Position index of the first page currently being displayed.
- * Page position+1 will be visible if positionOffset is nonzero.
- *
- * @param positionOffset Value from [0, 1) indicating the offset from the page at position.
- * @param positionOffsetPixels Value in pixels indicating the offset from position.
- */
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- }
-
-
- /**
- * Class waiting for broadcast events from the {@link FielDownloader} service.
- *
- * Updates the UI when a download is started or finished, provided that it is relevant for the
- * folder displayed in the gallery.
- */
- private class DownloadFinishReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME);
- String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH);
- if (getAccount().name.equals(accountName) &&
- downloadedRemotePath != null) {
-
- OCFile file = mStorageManager.getFileByPath(downloadedRemotePath);
- int position = mPreviewImagePagerAdapter.getFilePosition(file);
- boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
- //boolean isOffscreen = Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
-
- if (position >= 0 && intent.getAction().equals(mDownloadFinishMessage)) {
- if (downloadWasFine) {
- mPreviewImagePagerAdapter.updateFile(position, file);
-
- } else {
- mPreviewImagePagerAdapter.updateWithDownloadError(position);
- }
- mPreviewImagePagerAdapter.notifyDataSetChanged(); // will trigger the creation of new fragments
-
- } else {
- Log_OC.d(TAG, "Download finished, but the fragment is offscreen");
- }
-
- }
- removeStickyBroadcast(intent);
- }
-
- }
-
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_UP) {
- toggleFullScreen();
- }
- return true;
- }
-
-
- private void toggleFullScreen() {
- ActionBar actionBar = getSupportActionBar();
- if (mFullScreen) {
- actionBar.show();
-
- } else {
- actionBar.hide();
-
- }
- mFullScreen = !mFullScreen;
- }
-
- @Override
- protected void onAccountSet(boolean stateWasRecovered) {
- if (getAccount() != null) {
- OCFile file = getFile();
- /// Validate handled file (first image to preview)
- if (file == null) {
- throw new IllegalStateException("Instanced with a NULL OCFile");
- }
- if (!file.isImage()) {
- throw new IllegalArgumentException("Non-image file passed as argument");
- }
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-
- // Update file according to DB file, if it is possible
- if (file.getFileId() > DataStorageManager.ROOT_PARENT_ID)
- file = mStorageManager.getFileById(file.getFileId());
-
- if (file != null) {
- /// Refresh the activity according to the Account and OCFile set
- setFile(file); // reset after getting it fresh from mStorageManager
- getSupportActionBar().setTitle(getFile().getFileName());
- //if (!stateWasRecovered) {
- initViewPager();
- //}
-
- } else {
- // handled file not in the current Account
- finish();
- }
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
- }
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-
-import android.accounts.Account;
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapFactory.Options;
-import android.graphics.Point;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.webkit.MimeTypeMap;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-import eu.alefzero.webdav.WebdavUtils;
-
-
-/**
- * This fragment shows a preview of a downloaded image.
- *
- * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
- *
- * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
- *
- * @author David A. Velasco
- */
-public class PreviewImageFragment extends FileFragment implements OnRemoteOperationListener,
- ConfirmationDialogFragment.ConfirmationDialogFragmentListener {
- public static final String EXTRA_FILE = "FILE";
- public static final String EXTRA_ACCOUNT = "ACCOUNT";
-
- private View mView;
- private Account mAccount;
- private FileDataStorageManager mStorageManager;
- private ImageView mImageView;
- private TextView mMessageView;
- private ProgressBar mProgressWheel;
-
- public Bitmap mBitmap = null;
-
- private Handler mHandler;
- private RemoteOperation mLastRemoteOperation;
-
- private static final String TAG = PreviewImageFragment.class.getSimpleName();
-
- private boolean mIgnoreFirstSavedState;
-
-
- /**
- * Creates a fragment to preview an image.
- *
- * When 'imageFile' or 'ocAccount' are null
- *
- * @param imageFile An {@link OCFile} to preview as an image in the fragment
- * @param ocAccount An ownCloud account; needed to start downloads
- * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of {@link FragmentStatePagerAdapter}; TODO better solution
- */
- public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) {
- super(fileToDetail);
- mAccount = ocAccount;
- mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
- mIgnoreFirstSavedState = ignoreFirstSavedState;
- }
-
-
- /**
- * Creates an empty fragment for image previews.
- *
- * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
- *
- * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction
- */
- public PreviewImageFragment() {
- super();
- mAccount = null;
- mStorageManager = null;
- mIgnoreFirstSavedState = false;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mHandler = new Handler();
- setHasOptionsMenu(true);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
- mView = inflater.inflate(R.layout.preview_image_fragment, container, false);
- mImageView = (ImageView)mView.findViewById(R.id.image);
- mImageView.setVisibility(View.GONE);
- mView.setOnTouchListener((OnTouchListener)getActivity()); // WATCH OUT THAT CAST
- mMessageView = (TextView)mView.findViewById(R.id.message);
- mMessageView.setVisibility(View.GONE);
- mProgressWheel = (ProgressBar)mView.findViewById(R.id.progressWheel);
- mProgressWheel.setVisibility(View.VISIBLE);
- return mView;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (!(activity instanceof FileFragment.ContainerActivity))
- throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
- if (savedInstanceState != null) {
- if (!mIgnoreFirstSavedState) {
- setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
- mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
- } else {
- mIgnoreFirstSavedState = false;
- }
- }
- if (getFile() == null) {
- throw new IllegalStateException("Instanced with a NULL OCFile");
- }
- if (mAccount == null) {
- throw new IllegalStateException("Instanced with a NULL ownCloud Account");
- }
- if (!getFile().isDown()) {
- throw new IllegalStateException("There is no local file to preview");
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(PreviewImageFragment.EXTRA_FILE, getFile());
- outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount);
- }
-
-
- @Override
- public void onStart() {
- super.onStart();
- if (getFile() != null) {
- BitmapLoader bl = new BitmapLoader(mImageView, mMessageView, mProgressWheel);
- bl.execute(new String[]{getFile().getStoragePath()});
- }
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
-
- inflater.inflate(R.menu.file_actions_menu, menu);
- List<Integer> toHide = new ArrayList<Integer>();
-
- MenuItem item = null;
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_rename_file); // by now
-
- for (int i : toHide) {
- item = menu.findItem(i);
- if (item != null) {
- item.setVisible(false);
- item.setEnabled(false);
- }
- }
-
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_open_file_with: {
- openFile();
- return true;
- }
- case R.id.action_remove_file: {
- removeFile();
- return true;
- }
- case R.id.action_see_details: {
- seeDetails();
- return true;
- }
-
- default:
- return false;
- }
- }
-
-
- private void seeDetails() {
- ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
- }
-
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
-
- @Override
- public void onPause() {
- super.onPause();
- }
-
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (mBitmap != null) {
- mBitmap.recycle();
- }
- }
-
-
- /**
- * Opens the previewed image with an external application.
- *
- * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,
- * we should get a list of available apps for MIME tpye in the server and join it with the list of
- * available apps for the MIME type known from the file extension, to let the user choose
- */
- private void openFile() {
- OCFile file = getFile();
- String storagePath = file.getStoragePath();
- String encodedStoragePath = WebdavUtils.encodePath(storagePath);
- try {
- Intent i = new Intent(Intent.ACTION_VIEW);
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
- i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivity(i);
-
- } catch (Throwable t) {
- Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.getMimetype());
- boolean toastIt = true;
- String mimeType = "";
- try {
- Intent i = new Intent(Intent.ACTION_VIEW);
- mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
- if (mimeType == null || !mimeType.equals(file.getMimetype())) {
- if (mimeType != null) {
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
- } else {
- // desperate try
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
- }
- i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivity(i);
- toastIt = false;
- }
-
- } catch (IndexOutOfBoundsException e) {
- Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
-
- } catch (ActivityNotFoundException e) {
- Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
-
- } catch (Throwable th) {
- Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
-
- } finally {
- if (toastIt) {
- Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show();
- }
- }
-
- }
- finish();
- }
-
-
- /**
- * Starts a the removal of the previewed file.
- *
- * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
- * depending upon the user selection in the dialog.
- */
- private void removeFile() {
- ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
- R.string.confirmation_remove_alert,
- new String[]{getFile().getFileName()},
- R.string.confirmation_remove_remote_and_local,
- R.string.confirmation_remove_local,
- R.string.common_cancel);
- confDialog.setOnConfirmationListener(this);
- confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
- }
-
-
- /**
- * Performs the removal of the previewed file, both locally and in the server.
- */
- @Override
- public void onConfirmation(String callerTag) {
- if (mStorageManager.getFileById(getFile().getFileId()) != null) { // check that the file is still there;
- mLastRemoteOperation = new RemoveFileOperation( getFile(), // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
- true,
- mStorageManager);
- mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-
- ((PreviewImageActivity) getActivity()).showLoadingDialog();
- }
- }
-
-
- /**
- * Removes the file from local storage
- */
- @Override
- public void onNeutral(String callerTag) {
- // TODO this code should be made in a secondary thread,
- OCFile file = getFile();
- if (file.isDown()) { // checks it is still there
- File f = new File(file.getStoragePath());
- f.delete();
- file.setStoragePath(null);
- mStorageManager.saveFile(file);
- finish();
- }
- }
-
- /**
- * User cancelled the removal action.
- */
- @Override
- public void onCancel(String callerTag) {
- // nothing to do here
- }
-
-
- private class BitmapLoader extends AsyncTask<String, Void, Bitmap> {
-
- /**
- * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
- *
- * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
- */
- private final WeakReference<ImageView> mImageViewRef;
-
- /**
- * Weak reference to the target {@link TextView} where error messages will be written.
- *
- * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
- */
- private final WeakReference<TextView> mMessageViewRef;
-
-
- /**
- * Weak reference to the target {@link Progressbar} shown while the load is in progress.
- *
- * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes.
- */
- private final WeakReference<ProgressBar> mProgressWheelRef;
-
-
- /**
- * Error message to show when a load fails
- */
- private int mErrorMessageId;
-
-
- /**
- * Constructor.
- *
- * @param imageView Target {@link ImageView} where the bitmap will be loaded into.
- */
- public BitmapLoader(ImageView imageView, TextView messageView, ProgressBar progressWheel) {
- mImageViewRef = new WeakReference<ImageView>(imageView);
- mMessageViewRef = new WeakReference<TextView>(messageView);
- mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
- }
-
-
- @SuppressWarnings("deprecation")
- @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20
- @Override
- protected Bitmap doInBackground(String... params) {
- Bitmap result = null;
- if (params.length != 1) return result;
- String storagePath = params[0];
- try {
- // set desired options that will affect the size of the bitmap
- BitmapFactory.Options options = new Options();
- options.inScaled = true;
- options.inPurgeable = true;
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- options.inPreferQualityOverSpeed = false;
- }
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- options.inMutable = false;
- }
- // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType
- options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(storagePath, options);
-
- int width = options.outWidth;
- int height = options.outHeight;
- int scale = 1;
-
- Display display = getActivity().getWindowManager().getDefaultDisplay();
- Point size = new Point();
- int screenWidth;
- int screenHeight;
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
- display.getSize(size);
- screenWidth = size.x;
- screenHeight = size.y;
- } else {
- screenWidth = display.getWidth();
- screenHeight = display.getHeight();
- }
-
- if (width > screenWidth) {
- // second try to scale down the image , this time depending upon the screen size
- scale = (int) Math.floor((float)width / screenWidth);
- }
- if (height > screenHeight) {
- scale = Math.max(scale, (int) Math.floor((float)height / screenHeight));
- }
- options.inSampleSize = scale;
-
- // really load the bitmap
- options.inJustDecodeBounds = false; // the next decodeFile call will be real
- result = BitmapFactory.decodeFile(storagePath, options);
- //Log_OC.d(TAG, "Image loaded - width: " + options.outWidth + ", loaded height: " + options.outHeight);
-
- if (result == null) {
- mErrorMessageId = R.string.preview_image_error_unknown_format;
- Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
- }
-
- } catch (OutOfMemoryError e) {
- mErrorMessageId = R.string.preview_image_error_unknown_format;
- Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e);
-
- } catch (NoSuchFieldError e) {
- mErrorMessageId = R.string.common_error_unknown;
- Log_OC.e(TAG, "Error from access to unexisting field despite protection; file " + storagePath, e);
-
- } catch (Throwable t) {
- mErrorMessageId = R.string.common_error_unknown;
- Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
-
- }
- return result;
- }
-
- @Override
- protected void onPostExecute(Bitmap result) {
- hideProgressWheel();
- if (result != null) {
- showLoadedImage(result);
- } else {
- showErrorMessage();
- }
- }
-
- private void showLoadedImage(Bitmap result) {
- if (mImageViewRef != null) {
- final ImageView imageView = mImageViewRef.get();
- if (imageView != null) {
- imageView.setImageBitmap(result);
- imageView.setVisibility(View.VISIBLE);
- mBitmap = result;
- } // else , silently finish, the fragment was destroyed
- }
- if (mMessageViewRef != null) {
- final TextView messageView = mMessageViewRef.get();
- if (messageView != null) {
- messageView.setVisibility(View.GONE);
- } // else , silently finish, the fragment was destroyed
- }
- }
-
- private void showErrorMessage() {
- if (mImageViewRef != null) {
- final ImageView imageView = mImageViewRef.get();
- if (imageView != null) {
- // shows the default error icon
- imageView.setVisibility(View.VISIBLE);
- } // else , silently finish, the fragment was destroyed
- }
- if (mMessageViewRef != null) {
- final TextView messageView = mMessageViewRef.get();
- if (messageView != null) {
- messageView.setText(mErrorMessageId);
- messageView.setVisibility(View.VISIBLE);
- } // else , silently finish, the fragment was destroyed
- }
- }
-
- private void hideProgressWheel() {
- if (mProgressWheelRef != null) {
- final ProgressBar progressWheel = mProgressWheelRef.get();
- if (progressWheel != null) {
- progressWheel.setVisibility(View.GONE);
- }
- }
- }
-
- }
-
- /**
- * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} to be previewed.
- *
- * @param file File to test if can be previewed.
- * @return 'True' if the file can be handled by the fragment.
- */
- public static boolean canBePreviewed(OCFile file) {
- return (file != null && file.isImage());
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
- if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) {
- onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
- }
- }
-
- private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
- ((PreviewImageActivity) getActivity()).dismissLoadingDialog();
-
- if (result.isSuccess()) {
- Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
- msg.show();
- finish();
-
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- // TODO show the SSL warning dialog
- }
- }
- }
-
- /**
- * Finishes the preview
- */
- private void finish() {
- Activity container = getActivity();
- container.finish();
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
-
-import android.accounts.Account;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.view.ViewGroup;
-
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-
-/**
- * Adapter class that provides Fragment instances
- *
- * @author David A. Velasco
- */
-//public class PreviewImagePagerAdapter extends PagerAdapter {
-public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
-
- private Vector<OCFile> mImageFiles;
- private Account mAccount;
- private Set<Object> mObsoleteFragments;
- private Set<Integer> mObsoletePositions;
- private Set<Integer> mDownloadErrors;
- private DataStorageManager mStorageManager;
-
- private Map<Integer, FileFragment> mCachedFragments;
-
- /**
- * Constructor.
- *
- * @param fragmentManager {@link FragmentManager} instance that will handle the {@link Fragment}s provided by the adapter.
- * @param parentFolder Folder where images will be searched for.
- * @param storageManager Bridge to database.
- */
- public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) {
- super(fragmentManager);
-
- if (fragmentManager == null) {
- throw new IllegalArgumentException("NULL FragmentManager instance");
- }
- if (parentFolder == null) {
- throw new IllegalArgumentException("NULL parent folder");
- }
- if (storageManager == null) {
- throw new IllegalArgumentException("NULL storage manager");
- }
-
- mAccount = account;
- mStorageManager = storageManager;
- mImageFiles = mStorageManager.getDirectoryImages(parentFolder);
- mObsoleteFragments = new HashSet<Object>();
- mObsoletePositions = new HashSet<Integer>();
- mDownloadErrors = new HashSet<Integer>();
- //mFragmentManager = fragmentManager;
- mCachedFragments = new HashMap<Integer, FileFragment>();
- }
-
-
- /**
- * Returns the image files handled by the adapter.
- *
- * @return A vector with the image files handled by the adapter.
- */
- protected OCFile getFileAt(int position) {
- return mImageFiles.get(position);
- }
-
-
- public Fragment getItem(int i) {
- OCFile file = mImageFiles.get(i);
- Fragment fragment = null;
- if (file.isDown()) {
- fragment = new PreviewImageFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
-
- } else if (mDownloadErrors.contains(Integer.valueOf(i))) {
- fragment = new FileDownloadFragment(file, mAccount, true);
- ((FileDownloadFragment)fragment).setError(true);
- mDownloadErrors.remove(Integer.valueOf(i));
-
- } else {
- fragment = new FileDownloadFragment(file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)));
- }
- mObsoletePositions.remove(Integer.valueOf(i));
- return fragment;
- }
-
- public int getFilePosition(OCFile file) {
- return mImageFiles.indexOf(file);
- }
-
- @Override
- public int getCount() {
- return mImageFiles.size();
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
- return mImageFiles.get(position).getFileName();
- }
-
-
- public void updateFile(int position, OCFile file) {
- FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
- if (fragmentToUpdate != null) {
- mObsoleteFragments.add(fragmentToUpdate);
- }
- mObsoletePositions.add(Integer.valueOf(position));
- mImageFiles.set(position, file);
- }
-
-
- public void updateWithDownloadError(int position) {
- FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
- if (fragmentToUpdate != null) {
- mObsoleteFragments.add(fragmentToUpdate);
- }
- mDownloadErrors.add(Integer.valueOf(position));
- }
-
- public void clearErrorAt(int position) {
- FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position));
- if (fragmentToUpdate != null) {
- mObsoleteFragments.add(fragmentToUpdate);
- }
- mDownloadErrors.remove(Integer.valueOf(position));
- }
-
-
- @Override
- public int getItemPosition(Object object) {
- if (mObsoleteFragments.contains(object)) {
- mObsoleteFragments.remove(object);
- return POSITION_NONE;
- }
- return super.getItemPosition(object);
- }
-
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- Object fragment = super.instantiateItem(container, position);
- mCachedFragments.put(Integer.valueOf(position), (FileFragment)fragment);
- return fragment;
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- mCachedFragments.remove(Integer.valueOf(position));
- super.destroyItem(container, position, object);
- }
-
-
- public boolean pendingErrorAt(int position) {
- return mDownloadErrors.contains(Integer.valueOf(position));
- }
-
- /* -*
- * Called when a change in the shown pages is going to start being made.
- *
- * @param container The containing View which is displaying this adapter's page views.
- *- /
- @Override
- public void startUpdate(ViewGroup container) {
- Log.e(TAG, "** startUpdate");
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- Log.e(TAG, "** instantiateItem " + position);
-
- if (mFragments.size() > position) {
- Fragment fragment = mFragments.get(position);
- if (fragment != null) {
- Log.e(TAG, "** \t returning cached item");
- return fragment;
- }
- }
-
- if (mCurTransaction == null) {
- mCurTransaction = mFragmentManager.beginTransaction();
- }
-
- Fragment fragment = getItem(position);
- if (mSavedState.size() > position) {
- Fragment.SavedState savedState = mSavedState.get(position);
- if (savedState != null) {
- // TODO WATCH OUT:
- // * The Fragment must currently be attached to the FragmentManager.
- // * A new Fragment created using this saved state must be the same class type as the Fragment it was created from.
- // * The saved state can not contain dependencies on other fragments -- that is it can't use putFragment(Bundle, String, Fragment)
- // to store a fragment reference
- fragment.setInitialSavedState(savedState);
- }
- }
- while (mFragments.size() <= position) {
- mFragments.add(null);
- }
- fragment.setMenuVisibility(false);
- mFragments.set(position, fragment);
- //Log.e(TAG, "** \t adding fragment at position " + position + ", containerId " + container.getId());
- mCurTransaction.add(container.getId(), fragment);
-
- return fragment;
- }
-
- @Override
- public void destroyItem(ViewGroup container, int position, Object object) {
- Log.e(TAG, "** destroyItem " + position);
- Fragment fragment = (Fragment)object;
-
- if (mCurTransaction == null) {
- mCurTransaction = mFragmentManager.beginTransaction();
- }
- Log.e(TAG, "** \t removing fragment at position " + position);
- while (mSavedState.size() <= position) {
- mSavedState.add(null);
- }
- mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
- mFragments.set(position, null);
-
- mCurTransaction.remove(fragment);
- }
-
- @Override
- public void setPrimaryItem(ViewGroup container, int position, Object object) {
- Fragment fragment = (Fragment)object;
- if (fragment != mCurrentPrimaryItem) {
- if (mCurrentPrimaryItem != null) {
- mCurrentPrimaryItem.setMenuVisibility(false);
- }
- if (fragment != null) {
- fragment.setMenuVisibility(true);
- }
- mCurrentPrimaryItem = fragment;
- }
- }
-
- @Override
- public void finishUpdate(ViewGroup container) {
- Log.e(TAG, "** finishUpdate (start)");
- if (mCurTransaction != null) {
- mCurTransaction.commitAllowingStateLoss();
- mCurTransaction = null;
- mFragmentManager.executePendingTransactions();
- }
- Log.e(TAG, "** finishUpdate (end)");
- }
-
- @Override
- public boolean isViewFromObject(View view, Object object) {
- return ((Fragment)object).getView() == view;
- }
-
- @Override
- public Parcelable saveState() {
- Bundle state = null;
- if (mSavedState.size() > 0) {
- state = new Bundle();
- Fragment.SavedState[] savedStates = new Fragment.SavedState[mSavedState.size()];
- mSavedState.toArray(savedStates);
- state.putParcelableArray("states", savedStates);
- }
- for (int i=0; i<mFragments.size(); i++) {
- Fragment fragment = mFragments.get(i);
- if (fragment != null) {
- if (state == null) {
- state = new Bundle();
- }
- String key = "f" + i;
- mFragmentManager.putFragment(state, key, fragment);
- }
- }
- return state;
- }
-
- @Override
- public void restoreState(Parcelable state, ClassLoader loader) {
- if (state != null) {
- Bundle bundle = (Bundle)state;
- bundle.setClassLoader(loader);
- Parcelable[] states = bundle.getParcelableArray("states");
- mSavedState.clear();
- mFragments.clear();
- if (states != null) {
- for (int i=0; i<states.length; i++) {
- mSavedState.add((Fragment.SavedState)states[i]);
- }
- }
- Iterable<String> keys = bundle.keySet();
- for (String key: keys) {
- if (key.startsWith("f")) {
- int index = Integer.parseInt(key.substring(1));
- Fragment f = mFragmentManager.getFragment(bundle, key);
- if (f != null) {
- while (mFragments.size() <= index) {
- mFragments.add(null);
- }
- f.setMenuVisibility(false);
- mFragments.set(index, f);
- } else {
- Log.w(TAG, "Bad fragment at key " + key);
- }
- }
- }
- }
- }
- */
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.res.Configuration;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.webkit.MimeTypeMap;
-import android.widget.ImageView;
-import android.widget.Toast;
-import android.widget.VideoView;
-
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.media.MediaControlView;
-import de.mobilcom.debitel.cloud.android.media.MediaService;
-import de.mobilcom.debitel.cloud.android.media.MediaServiceBinder;
-import de.mobilcom.debitel.cloud.android.operations.OnRemoteOperationListener;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperation;
-import de.mobilcom.debitel.cloud.android.operations.RemoteOperationResult;
-import de.mobilcom.debitel.cloud.android.operations.RemoveFileOperation;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileDisplayActivity;
-import de.mobilcom.debitel.cloud.android.ui.fragment.ConfirmationDialogFragment;
-import de.mobilcom.debitel.cloud.android.ui.fragment.FileFragment;
-import eu.alefzero.webdav.WebdavUtils;
-
-/**
- * This fragment shows a preview of a downloaded media file (audio or video).
- *
- * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}.
- *
- * By now, if the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too.
- *
- * @author David A. Velasco
- */
-public class PreviewMediaFragment extends FileFragment implements
- OnTouchListener,
- ConfirmationDialogFragment.ConfirmationDialogFragmentListener, OnRemoteOperationListener {
-
- public static final String EXTRA_FILE = "FILE";
- public static final String EXTRA_ACCOUNT = "ACCOUNT";
- private static final String EXTRA_PLAY_POSITION = "PLAY_POSITION";
- private static final String EXTRA_PLAYING = "PLAYING";
-
- private View mView;
- private Account mAccount;
- private FileDataStorageManager mStorageManager;
- private ImageView mImagePreview;
- private VideoView mVideoPreview;
- private int mSavedPlaybackPosition;
-
- private Handler mHandler;
- private RemoteOperation mLastRemoteOperation;
-
- private MediaServiceBinder mMediaServiceBinder = null;
- private MediaControlView mMediaController = null;
- private MediaServiceConnection mMediaServiceConnection = null;
- private VideoHelper mVideoHelper;
- private boolean mAutoplay;
- public boolean mPrepared;
-
- private static final String TAG = PreviewMediaFragment.class.getSimpleName();
-
-
- /**
- * Creates a fragment to preview a file.
- *
- * When 'fileToDetail' or 'ocAccount' are null
- *
- * @param fileToDetail An {@link OCFile} to preview in the fragment
- * @param ocAccount An ownCloud account; needed to start downloads
- */
- public PreviewMediaFragment(OCFile fileToDetail, Account ocAccount, int startPlaybackPosition, boolean autoplay) {
- super(fileToDetail);
- mAccount = ocAccount;
- mSavedPlaybackPosition = startPlaybackPosition;
- mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment
- mAutoplay = autoplay;
- }
-
-
- /**
- * Creates an empty fragment for previews.
- *
- * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically (for instance, when the device is turned a aside).
- *
- * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful construction
- */
- public PreviewMediaFragment() {
- super();
- mAccount = null;
- mSavedPlaybackPosition = 0;
- mStorageManager = null;
- mAutoplay = true;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mHandler = new Handler();
- setHasOptionsMenu(true);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- super.onCreateView(inflater, container, savedInstanceState);
- Log_OC.e(TAG, "onCreateView");
-
-
- mView = inflater.inflate(R.layout.file_preview, container, false);
-
- mImagePreview = (ImageView)mView.findViewById(R.id.image_preview);
- mVideoPreview = (VideoView)mView.findViewById(R.id.video_preview);
- mVideoPreview.setOnTouchListener(this);
-
- mMediaController = (MediaControlView)mView.findViewById(R.id.media_controller);
-
- return mView;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- Log_OC.e(TAG, "onAttach");
-
- if (!(activity instanceof FileFragment.ContainerActivity))
- throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName());
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- Log_OC.e(TAG, "onActivityCreated");
-
- mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
- if (savedInstanceState != null) {
- setFile((OCFile)savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE));
- mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT);
- mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION);
- mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING);
-
- }
- OCFile file = getFile();
- if (file == null) {
- throw new IllegalStateException("Instanced with a NULL OCFile");
- }
- if (mAccount == null) {
- throw new IllegalStateException("Instanced with a NULL ownCloud Account");
- }
- if (!file.isDown()) {
- throw new IllegalStateException("There is no local file to preview");
- }
- if (file.isVideo()) {
- mVideoPreview.setVisibility(View.VISIBLE);
- mImagePreview.setVisibility(View.GONE);
- prepareVideo();
-
- } else {
- mVideoPreview.setVisibility(View.GONE);
- mImagePreview.setVisibility(View.VISIBLE);
- }
-
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- Log_OC.e(TAG, "onSaveInstanceState");
-
- outState.putParcelable(PreviewMediaFragment.EXTRA_FILE, getFile());
- outState.putParcelable(PreviewMediaFragment.EXTRA_ACCOUNT, mAccount);
-
- if (getFile().isVideo()) {
- mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
- mAutoplay = mVideoPreview.isPlaying();
- outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mSavedPlaybackPosition);
- outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mAutoplay);
- } else {
- outState.putInt(PreviewMediaFragment.EXTRA_PLAY_POSITION , mMediaServiceBinder.getCurrentPosition());
- outState.putBoolean(PreviewMediaFragment.EXTRA_PLAYING , mMediaServiceBinder.isPlaying());
- }
- }
-
-
- @Override
- public void onStart() {
- super.onStart();
- Log_OC.e(TAG, "onStart");
-
- OCFile file = getFile();
- if (file != null) {
- if (file.isAudio()) {
- bindMediaService();
-
- } else if (file.isVideo()) {
- stopAudio();
- playVideo();
- }
- }
- }
-
-
- private void stopAudio() {
- Intent i = new Intent(getSherlockActivity(), MediaService.class);
- i.setAction(MediaService.ACTION_STOP_ALL);
- getSherlockActivity().startService(i);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- super.onCreateOptionsMenu(menu, inflater);
-
- inflater.inflate(R.menu.file_actions_menu, menu);
- List<Integer> toHide = new ArrayList<Integer>();
-
- MenuItem item = null;
- toHide.add(R.id.action_cancel_download);
- toHide.add(R.id.action_cancel_upload);
- toHide.add(R.id.action_download_file);
- toHide.add(R.id.action_sync_file);
- toHide.add(R.id.action_rename_file); // by now
-
- for (int i : toHide) {
- item = menu.findItem(i);
- if (item != null) {
- item.setVisible(false);
- item.setEnabled(false);
- }
- }
-
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_open_file_with: {
- openFile();
- return true;
- }
- case R.id.action_remove_file: {
- removeFile();
- return true;
- }
- case R.id.action_see_details: {
- seeDetails();
- return true;
- }
-
- default:
- return false;
- }
- }
-
-
- private void seeDetails() {
- stopPreview(false);
- ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
- }
-
-
- private void prepareVideo() {
- // create helper to get more control on the playback
- mVideoHelper = new VideoHelper();
- mVideoPreview.setOnPreparedListener(mVideoHelper);
- mVideoPreview.setOnCompletionListener(mVideoHelper);
- mVideoPreview.setOnErrorListener(mVideoHelper);
- }
-
- private void playVideo() {
- // create and prepare control panel for the user
- mMediaController.setMediaPlayer(mVideoPreview);
-
- // load the video file in the video player ; when done, VideoHelper#onPrepared() will be called
- mVideoPreview.setVideoPath(getFile().getStoragePath());
- }
-
-
- private class VideoHelper implements OnCompletionListener, OnPreparedListener, OnErrorListener {
-
- /**
- * Called when the file is ready to be played.
- *
- * Just starts the playback.
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- */
- @Override
- public void onPrepared(MediaPlayer vp) {
- Log_OC.e(TAG, "onPrepared");
- mVideoPreview.seekTo(mSavedPlaybackPosition);
- if (mAutoplay) {
- mVideoPreview.start();
- }
- mMediaController.setEnabled(true);
- mMediaController.updatePausePlay();
- mPrepared = true;
- }
-
-
- /**
- * Called when the file is finished playing.
- *
- * Finishes the activity.
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- */
- @Override
- public void onCompletion(MediaPlayer mp) {
- Log_OC.e(TAG, "completed");
- if (mp != null) {
- mVideoPreview.seekTo(0);
- // next lines are necessary to work around undesired video loops
- if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD) {
- mVideoPreview.pause();
-
- } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.GINGERBREAD_MR1) {
- // mVideePreview.pause() is not enough
-
- mMediaController.setEnabled(false);
- mVideoPreview.stopPlayback();
- mAutoplay = false;
- mSavedPlaybackPosition = 0;
- mVideoPreview.setVideoPath(getFile().getStoragePath());
- }
- } // else : called from onError()
- mMediaController.updatePausePlay();
- }
-
-
- /**
- * Called when an error in playback occurs.
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- * @param what Type of error
- * @param extra Extra code specific to the error
- */
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- if (mVideoPreview.getWindowToken() != null) {
- String message = MediaService.getMessageForMediaError(getActivity(), what, extra);
- new AlertDialog.Builder(getActivity())
- .setMessage(message)
- .setPositiveButton(android.R.string.VideoView_error_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- dialog.dismiss();
- VideoHelper.this.onCompletion(null);
- }
- })
- .setCancelable(false)
- .show();
- }
- return true;
- }
-
- }
-
-
- @Override
- public void onPause() {
- super.onPause();
- Log_OC.e(TAG, "onPause");
- }
-
- @Override
- public void onResume() {
- super.onResume();
- Log_OC.e(TAG, "onResume");
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log_OC.e(TAG, "onDestroy");
- }
-
- @Override
- public void onStop() {
- Log_OC.e(TAG, "onStop");
- super.onStop();
-
- mPrepared = false;
- if (mMediaServiceConnection != null) {
- Log_OC.d(TAG, "Unbinding from MediaService ...");
- if (mMediaServiceBinder != null && mMediaController != null) {
- mMediaServiceBinder.unregisterMediaController(mMediaController);
- }
- getActivity().unbindService(mMediaServiceConnection);
- mMediaServiceConnection = null;
- mMediaServiceBinder = null;
- }
- }
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN && v == mVideoPreview) {
- startFullScreenVideo();
- return true;
- }
- return false;
- }
-
-
- private void startFullScreenVideo() {
- Intent i = new Intent(getActivity(), PreviewVideoActivity.class);
- i.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount);
- i.putExtra(FileActivity.EXTRA_FILE, getFile());
- i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying());
- mVideoPreview.pause();
- i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition());
- startActivityForResult(i, 0);
- }
-
- @Override
- public void onConfigurationChanged (Configuration newConfig) {
- Log_OC.e(TAG, "onConfigurationChanged " + this);
- }
-
- @Override
- public void onActivityResult (int requestCode, int resultCode, Intent data) {
- Log_OC.e(TAG, "onActivityResult " + this);
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == Activity.RESULT_OK) {
- mSavedPlaybackPosition = data.getExtras().getInt(PreviewVideoActivity.EXTRA_START_POSITION);
- mAutoplay = data.getExtras().getBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY);
- }
- }
-
-
- private void playAudio() {
- OCFile file = getFile();
- if (!mMediaServiceBinder.isPlaying(file)) {
- Log_OC.d(TAG, "starting playback of " + file.getStoragePath());
- mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition);
-
- } else {
- if (!mMediaServiceBinder.isPlaying() && mAutoplay) {
- mMediaServiceBinder.start();
- mMediaController.updatePausePlay();
- }
- }
- }
-
-
- private void bindMediaService() {
- Log_OC.d(TAG, "Binding to MediaService...");
- if (mMediaServiceConnection == null) {
- mMediaServiceConnection = new MediaServiceConnection();
- }
- getActivity().bindService( new Intent(getActivity(),
- MediaService.class),
- mMediaServiceConnection,
- Context.BIND_AUTO_CREATE);
- // follow the flow in MediaServiceConnection#onServiceConnected(...)
- }
-
- /** Defines callbacks for service binding, passed to bindService() */
- private class MediaServiceConnection implements ServiceConnection {
-
- @Override
- public void onServiceConnected(ComponentName component, IBinder service) {
- if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
- Log_OC.d(TAG, "Media service connected");
- mMediaServiceBinder = (MediaServiceBinder) service;
- if (mMediaServiceBinder != null) {
- prepareMediaController();
- playAudio(); // do not wait for the touch of nobody to play audio
-
- Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
-
- } else {
- Log_OC.e(TAG, "Unexpected response from MediaService while binding");
- }
- }
- }
-
- private void prepareMediaController() {
- mMediaServiceBinder.registerMediaController(mMediaController);
- if (mMediaController != null) {
- mMediaController.setMediaPlayer(mMediaServiceBinder);
- mMediaController.setEnabled(true);
- mMediaController.updatePausePlay();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName component) {
- if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
- Log_OC.e(TAG, "Media service suddenly disconnected");
- if (mMediaController != null) {
- mMediaController.setMediaPlayer(null);
- } else {
- Toast.makeText(getActivity(), "No media controller to release when disconnected from media service", Toast.LENGTH_SHORT).show();
- }
- mMediaServiceBinder = null;
- mMediaServiceConnection = null;
- }
- }
- }
-
-
-
- /**
- * Opens the previewed file with an external application.
- *
- * TODO - improve this; instead of prioritize the actions available for the MIME type in the server,
- * we should get a list of available apps for MIME tpye in the server and join it with the list of
- * available apps for the MIME type known from the file extension, to let the user choose
- */
- private void openFile() {
- OCFile file = getFile();
- stopPreview(true);
- String storagePath = file.getStoragePath();
- String encodedStoragePath = WebdavUtils.encodePath(storagePath);
- try {
- Intent i = new Intent(Intent.ACTION_VIEW);
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
- i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivity(i);
-
- } catch (Throwable t) {
- Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.getMimetype());
- boolean toastIt = true;
- String mimeType = "";
- try {
- Intent i = new Intent(Intent.ACTION_VIEW);
- mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
- if (mimeType == null || !mimeType.equals(file.getMimetype())) {
- if (mimeType != null) {
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType);
- } else {
- // desperate try
- i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*");
- }
- i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- startActivity(i);
- toastIt = false;
- }
-
- } catch (IndexOutOfBoundsException e) {
- Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath);
-
- } catch (ActivityNotFoundException e) {
- Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension");
-
- } catch (Throwable th) {
- Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th);
-
- } finally {
- if (toastIt) {
- Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show();
- }
- }
-
- }
- finish();
- }
-
- /**
- * Starts a the removal of the previewed file.
- *
- * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)},
- * depending upon the user selection in the dialog.
- */
- private void removeFile() {
- ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
- R.string.confirmation_remove_alert,
- new String[]{getFile().getFileName()},
- R.string.confirmation_remove_remote_and_local,
- R.string.confirmation_remove_local,
- R.string.common_cancel);
- confDialog.setOnConfirmationListener(this);
- confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
- }
-
-
- /**
- * Performs the removal of the previewed file, both locally and in the server.
- */
- @Override
- public void onConfirmation(String callerTag) {
- OCFile file = getFile();
- if (mStorageManager.getFileById(file.getFileId()) != null) { // check that the file is still there;
- stopPreview(true);
- mLastRemoteOperation = new RemoveFileOperation( file, // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters
- true,
- mStorageManager);
- mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity());
-
- ((FileDisplayActivity) getActivity()).showLoadingDialog();
- }
- }
-
-
- /**
- * Removes the file from local storage
- */
- @Override
- public void onNeutral(String callerTag) {
- // TODO this code should be made in a secondary thread,
- OCFile file = getFile();
- if (file.isDown()) { // checks it is still there
- stopPreview(true);
- File f = new File(file.getStoragePath());
- f.delete();
- file.setStoragePath(null);
- mStorageManager.saveFile(file);
- finish();
- }
- }
-
- /**
- * User cancelled the removal action.
- */
- @Override
- public void onCancel(String callerTag) {
- // nothing to do here
- }
-
-
- /**
- * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewMediaFragment} to be previewed.
- *
- * @param file File to test if can be previewed.
- * @return 'True' if the file can be handled by the fragment.
- */
- public static boolean canBePreviewed(OCFile file) {
- return (file != null && (file.isAudio() || file.isVideo()));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
- if (operation.equals(mLastRemoteOperation)) {
- if (operation instanceof RemoveFileOperation) {
- onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
- }
- }
- }
-
- private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) {
- ((FileDisplayActivity) getActivity()).dismissLoadingDialog();
- if (result.isSuccess()) {
- Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG);
- msg.show();
- finish();
-
- } else {
- Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG);
- msg.show();
- if (result.isSslRecoverableException()) {
- // TODO show the SSL warning dialog
- }
- }
- }
-
- private void stopPreview(boolean stopAudio) {
- OCFile file = getFile();
- if (file.isAudio() && stopAudio) {
- mMediaServiceBinder.pause();
-
- } else if (file.isVideo()) {
- mVideoPreview.stopPlayback();
- }
- }
-
-
-
- /**
- * Finishes the preview
- */
- private void finish() {
- getActivity().onBackPressed();
- }
-
-
- public int getPosition() {
- if (mPrepared) {
- mSavedPlaybackPosition = mVideoPreview.getCurrentPosition();
- }
- Log_OC.e(TAG, "getting position: " + mSavedPlaybackPosition);
- return mSavedPlaybackPosition;
- }
-
- public boolean isPlaying() {
- if (mPrepared) {
- mAutoplay = mVideoPreview.isPlaying();
- }
- return mAutoplay;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.ui.preview;
-
-import android.accounts.Account;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.media.MediaPlayer.OnErrorListener;
-import android.media.MediaPlayer.OnPreparedListener;
-import android.net.Uri;
-import android.os.Bundle;
-import android.widget.MediaController;
-import android.widget.VideoView;
-
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils.AccountNotFoundException;
-import de.mobilcom.debitel.cloud.android.datamodel.DataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.FileDataStorageManager;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-import de.mobilcom.debitel.cloud.android.media.MediaService;
-import de.mobilcom.debitel.cloud.android.ui.activity.FileActivity;
-
-/**
- * Activity implementing a basic video player.
- *
- * Used as an utility to preview video files contained in an ownCloud account.
- *
- * Currently, it always plays in landscape mode, full screen. When the playback ends,
- * the activity is finished.
- *
- * @author David A. Velasco
- */
-public class PreviewVideoActivity extends FileActivity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
-
- /** Key to receive a flag signaling if the video should be started immediately */
- public static final String EXTRA_AUTOPLAY = "AUTOPLAY";
-
- /** Key to receive the position of the playback where the video should be put at start */
- public static final String EXTRA_START_POSITION = "START_POSITION";
-
- private static final String TAG = PreviewVideoActivity.class.getSimpleName();
-
- private DataStorageManager mStorageManager;
-
- private int mSavedPlaybackPosition; // in the unit time handled by MediaPlayer.getCurrentPosition()
- private boolean mAutoplay; // when 'true', the playback starts immediately with the activity
- private VideoView mVideoPlayer; // view to play the file; both performs and show the playback
- private MediaController mMediaController; // panel control used by the user to control the playback
-
- /**
- * Called when the activity is first created.
- *
- * Searches for an {@link OCFile} and ownCloud {@link Account} holding it in the starting {@link Intent}.
- *
- * The {@link Account} is unnecessary if the file is downloaded; else, the {@link Account} is used to
- * try to stream the remote file - TODO get the streaming works
- *
- * {@inheritDoc}
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log_OC.e(TAG, "ACTIVITY\t\tonCreate");
-
- setContentView(R.layout.video_layout);
-
- if (savedInstanceState == null) {
- Bundle extras = getIntent().getExtras();
- mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION);
- mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY);
-
- } else {
- mSavedPlaybackPosition = savedInstanceState.getInt(EXTRA_START_POSITION);
- mAutoplay = savedInstanceState.getBoolean(EXTRA_AUTOPLAY);
- }
-
- mVideoPlayer = (VideoView) findViewById(R.id.videoPlayer);
-
- // set listeners to get more contol on the playback
- mVideoPlayer.setOnPreparedListener(this);
- mVideoPlayer.setOnCompletionListener(this);
- mVideoPlayer.setOnErrorListener(this);
-
- // keep the screen on while the playback is performed (prevents screen off by battery save)
- mVideoPlayer.setKeepScreenOn(true);
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- Log_OC.e(TAG, "ACTIVITY\t\tonSaveInstanceState");
- outState.putInt(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
- outState.putBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY , mVideoPlayer.isPlaying());
- }
-
-
- @Override
- public void onBackPressed() {
- Log_OC.e(TAG, "ACTIVTIY\t\tonBackPressed");
- Intent i = new Intent();
- i.putExtra(EXTRA_AUTOPLAY, mVideoPlayer.isPlaying());
- i.putExtra(EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition());
- setResult(RESULT_OK, i);
- super.onBackPressed();
- }
-
-
- /**
- * Called when the file is ready to be played.
- *
- * Just starts the playback.
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- */
- @Override
- public void onPrepared(MediaPlayer mp) {
- Log_OC.e(TAG, "ACTIVITY\t\tonPrepare");
- mVideoPlayer.seekTo(mSavedPlaybackPosition);
- if (mAutoplay) {
- mVideoPlayer.start();
- }
- mMediaController.show(5000);
- }
-
-
- /**
- * Called when the file is finished playing.
- *
- * Rewinds the video
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- */
- @Override
- public void onCompletion(MediaPlayer mp) {
- mVideoPlayer.seekTo(0);
- }
-
-
- /**
- * Called when an error in playback occurs.
- *
- * @param mp {@link MediaPlayer} instance performing the playback.
- * @param what Type of error
- * @param extra Extra code specific to the error
- */
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- Log_OC.e(TAG, "Error in video playback, what = " + what + ", extra = " + extra);
-
- if (mMediaController != null) {
- mMediaController.hide();
- }
-
- if (mVideoPlayer.getWindowToken() != null) {
- String message = MediaService.getMessageForMediaError(this, what, extra);
- new AlertDialog.Builder(this)
- .setMessage(message)
- .setPositiveButton(android.R.string.VideoView_error_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- PreviewVideoActivity.this.onCompletion(null);
- }
- })
- .setCancelable(false)
- .show();
- }
- return true;
- }
-
-
- @Override
- protected void onAccountSet(boolean stateWasRecovered) {
- if (getAccount() != null) {
- OCFile file = getFile();
- /// Validate handled file (first image to preview)
- if (file == null) {
- throw new IllegalStateException("Instanced with a NULL OCFile");
- }
- if (!file.isVideo()) {
- throw new IllegalArgumentException("Non-video file passed as argument");
- }
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
- file = mStorageManager.getFileById(file.getFileId());
- if (file != null) {
- if (file.isDown()) {
- mVideoPlayer.setVideoPath(file.getStoragePath());
-
- } else {
- // not working yet
- String url;
- try {
- url = AccountUtils.constructFullURLForAccount(this, getAccount()) + file.getRemotePath();
- mVideoPlayer.setVideoURI(Uri.parse(url));
- } catch (AccountNotFoundException e) {
- onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_no_account);
- }
- }
-
- // create and prepare control panel for the user
- mMediaController = new MediaController(this);
- mMediaController.setMediaPlayer(mVideoPlayer);
- mMediaController.setAnchorView(mVideoPlayer);
- mVideoPlayer.setMediaController(mMediaController);
-
- } else {
- finish();
- }
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
- finish();
- }
- }
-
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.utils;
-
-import java.io.File;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.StatFs;
-
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.R;
-import de.mobilcom.debitel.cloud.android.datamodel.OCFile;
-
-/**
- * Static methods to help in access to local file system.
- *
- * @author David A. Velasco
- */
-public class FileStorageUtils {
- //private static final String LOG_TAG = "FileStorageUtils";
-
- public static final String getSavePath(String accountName) {
- File sdCard = Environment.getExternalStorageDirectory();
- return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
- // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
- }
-
- public static final String getDefaultSavePathFor(String accountName, OCFile file) {
- return getSavePath(accountName) + file.getRemotePath();
- }
-
- public static final String getTemporalPath(String accountName) {
- File sdCard = Environment.getExternalStorageDirectory();
- return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/tmp/" + Uri.encode(accountName, "@");
- // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
- }
-
- @SuppressLint("NewApi")
- public static final long getUsableSpace(String accountName) {
- File savePath = Environment.getExternalStorageDirectory();
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
- return savePath.getUsableSpace();
-
- } else {
- StatFs stats = new StatFs(savePath.getAbsolutePath());
- return stats.getAvailableBlocks() * stats.getBlockSize();
- }
-
- }
-
- public static final String getLogPath() {
- return Environment.getExternalStorageDirectory() + File.separator + MainApp.getDataFolder() + File.separator + "log";
- }
-
- public static String getInstantUploadFilePath(Context context, String fileName) {
- String uploadPath = context.getString(R.string.instant_upload_path);
- String value = uploadPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
- return value;
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.utils;
-
-public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
- public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion(
- 0x010000);
- public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion(
- 0x020000);
- public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion(
- 0x030000);
- public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion(
- 0x040000);
- public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion(
- 0x040500);
-
- // format is in version
- // 0xAABBCC
- // for version AA.BB.CC
- // ie version 2.0.3 will be stored as 0x030003
- private int mVersion;
- private boolean mIsValid;
-
- public OwnCloudVersion(int version) {
- mVersion = version;
- mIsValid = true;
- }
-
- public OwnCloudVersion(String version) {
- mVersion = 0;
- mIsValid = false;
- parseVersionString(version);
- }
-
- public String toString() {
- return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "."
- + ((mVersion) % 256);
- }
-
- public boolean isVersionValid() {
- return mIsValid;
- }
-
- @Override
- public int compareTo(OwnCloudVersion another) {
- return another.mVersion == mVersion ? 0
- : another.mVersion < mVersion ? 1 : -1;
- }
-
- private void parseVersionString(String version) {
- try {
- String[] nums = version.split("\\.");
- if (nums.length > 0) {
- mVersion += Integer.parseInt(nums[0]);
- }
- mVersion = mVersion << 8;
- if (nums.length > 1) {
- mVersion += Integer.parseInt(nums[1]);
- }
- mVersion = mVersion << 8;
- if (nums.length > 2) {
- mVersion += Integer.parseInt(nums[2]);
- }
- mIsValid = true;
- } catch (Exception e) {
- mIsValid = false;
- }
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.utils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-import android.os.FileObserver;
-
-public class RecursiveFileObserver extends FileObserver {
-
- public static int CHANGES_ONLY = CLOSE_WRITE | MOVE_SELF | MOVED_FROM;
-
- List<SingleFileObserver> mObservers;
- String mPath;
- int mMask;
-
- public RecursiveFileObserver(String path) {
- this(path, ALL_EVENTS);
- }
-
- public RecursiveFileObserver(String path, int mask) {
- super(path, mask);
- mPath = path;
- mMask = mask;
- }
-
- @Override
- public void startWatching() {
- if (mObservers != null) return;
- mObservers = new ArrayList<SingleFileObserver>();
- Stack<String> stack = new Stack<String>();
- stack.push(mPath);
-
- while (!stack.empty()) {
- String parent = stack.pop();
- mObservers.add(new SingleFileObserver(parent, mMask));
- File path = new File(parent);
- File[] files = path.listFiles();
- if (files == null) continue;
- for (int i = 0; i < files.length; ++i) {
- if (files[i].isDirectory() && !files[i].getName().equals(".")
- && !files[i].getName().equals("..")) {
- stack.push(files[i].getPath());
- }
- }
- }
- for (int i = 0; i < mObservers.size(); i++)
- mObservers.get(i).startWatching();
- }
-
- @Override
- public void stopWatching() {
- if (mObservers == null) return;
-
- for (int i = 0; i < mObservers.size(); ++i)
- mObservers.get(i).stopWatching();
-
- mObservers.clear();
- mObservers = null;
- }
-
- @Override
- public void onEvent(int event, String path) {
-
- }
-
- private class SingleFileObserver extends FileObserver {
- private String mPath;
-
- public SingleFileObserver(String path, int mask) {
- super(path, mask);
- mPath = path;
- }
-
- @Override
- public void onEvent(int event, String path) {
- String newPath = mPath + "/" + path;
- RecursiveFileObserver.this.onEvent(event, newPath);
- }
-
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.widgets;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-import de.mobilcom.debitel.cloud.android.R;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.EditText;
-
-public class ActionEditText extends EditText {
- private String s;
- private String optionOneString;
- private int optionOneColor;
- private String optionTwoString;
- private int optionTwoColor;
- private Rect mTextBounds, mButtonRect;
-
- private String badgeClickCallback;
- private Rect btn_rect;
-
- public ActionEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- getAttrs(attrs);
- s = optionOneString;
- mTextBounds = new Rect();
- mButtonRect = new Rect();
- }
-
- public ActionEditText(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- getAttrs(attrs);
- s = optionOneString;
- mTextBounds = new Rect();
- mButtonRect = new Rect();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- Paint p = getPaint();
-
- p.getTextBounds(s, 0, s.length(), mTextBounds);
-
- getDrawingRect(mButtonRect);
- mButtonRect.top += 10;
- mButtonRect.bottom -= 10;
- mButtonRect.left = (int) (getWidth() - mTextBounds.width() - 18);
- mButtonRect.right = getWidth() - 10;
- btn_rect = mButtonRect;
-
- if (s.equals(optionOneString))
- p.setColor(optionOneColor);
- else
- p.setColor(optionTwoColor);
- canvas.drawRect(mButtonRect, p);
- p.setColor(Color.GRAY);
-
- canvas.drawText(s, mButtonRect.left + 3, mButtonRect.bottom
- - (mTextBounds.height() / 2), p);
-
- invalidate();
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int touchX = (int) event.getX();
- int touchY = (int) event.getY();
- boolean r = super.onTouchEvent(event);
- if (event.getAction() == MotionEvent.ACTION_UP) {
- if (btn_rect.contains(touchX, touchY)) {
- if (s.equals(optionTwoString))
- s = optionOneString;
- else
- s = optionTwoString;
- if (badgeClickCallback != null) {
- @SuppressWarnings("rawtypes")
- Class[] paramtypes = new Class[2];
- paramtypes[0] = android.view.View.class;
- paramtypes[1] = String.class;
- Method method;
- try {
-
- method = getContext().getClass().getMethod(
- badgeClickCallback, paramtypes);
- method.invoke(getContext(), this, s);
-
- } catch (NoSuchMethodException e) {
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
-
- invalidate();
- }
- }
- }
- return r;
- }
-
- private void getAttrs(AttributeSet attr) {
- TypedArray a = getContext().obtainStyledAttributes(attr,
- R.styleable.ActionEditText);
- optionOneString = a
- .getString(R.styleable.ActionEditText_optionOneString);
- optionTwoString = a
- .getString(R.styleable.ActionEditText_optionTwoString);
- optionOneColor = a.getColor(R.styleable.ActionEditText_optionOneColor,
- 0x00ff00);
- optionTwoColor = a.getColor(R.styleable.ActionEditText_optionTwoColor,
- 0xff0000);
- badgeClickCallback = a
- .getString(R.styleable.ActionEditText_onBadgeClick);
- }
-
-}
import org.apache.commons.httpclient.methods.RequestEntity;
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+
import eu.alefzero.webdav.OnDatatransferProgressListener;
import org.apache.commons.httpclient.methods.RequestEntity;
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.network.ProgressiveDataTransferer;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.network.ProgressiveDataTransferer;
+
import eu.alefzero.webdav.OnDatatransferProgressListener;
import org.apache.http.HttpStatus;
import org.apache.http.params.CoreProtocolPNames;
+import com.owncloud.android.Log_OC;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.network.BearerAuthScheme;
+import com.owncloud.android.network.BearerCredentials;
+
-import de.mobilcom.debitel.cloud.android.Log_OC;
-import de.mobilcom.debitel.cloud.android.MainApp;
-import de.mobilcom.debitel.cloud.android.network.BearerAuthScheme;
-import de.mobilcom.debitel.cloud.android.network.BearerCredentials;
import android.net.Uri;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
-import de.mobilcom.debitel.cloud.android.Log_OC;
+import com.owncloud.android.Log_OC;
+
import android.net.Uri;
<?xml version="1.0" encoding="utf-8"?>
<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="de.mobilcom.debitel.cloud.androidcloud.android.test"
+ package="com.owncloud.androidcloud.android.test"
android:versionCode="1"
android:versionName="1.0">
<!-- We add an application tag here just so that we can indicate that
"ade.mobilcom.debitel.cloud.androidnt -w de.mobilcom.debitel.cloud.android.tests/android.test.InstrumentationTestRunner"
-->
<instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="de.mobilcom.debitel.cloud.android"
- android:label="Tests for de.mobilcom.debitel.cloud.android"/>
+ android:targetPackage="com.owncloud.android"
+ android:label="Tests for com.owncloud.android"/>
</manifest>
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012 Bartek Przybylski
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * 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 com.owncloud.android.test;
+
+import com.owncloud.android.authentication.Accocom.owncloud.androiddroid.utils.OwnCloudVersion;
+
+importcom.owncloud.android
+
+public class AccountUtilsTest extends AndroidTestCase {
+
+ public void testGetWebdavPathAndOCVersion() {
+ OwnCloudVersion ocv12 = new OwnCloudVersion(0x010200);
+ OwnCloudVersion ocv12s = new OwnCloudVersion("1.2");
+ OwnCloudVersion ocv22 = new OwnCloudVersion(0x020200);
+ OwnCloudVersion ocv30 = new OwnCloudVersion(0x030000);
+ OwnCloudVersion ocv33s = new OwnCloudVersion("3.3.3");
+ OwnCloudVersion ocv45 = new OwnCloudVersion(0x040500);
+ OwnCloudVersion ocv70 = new OwnCloudVersion(0x070000);
+
+ assertTrue(AccountUtils.getWebdavPath(ocv12, false, false).equals("/webdav/owncloud.php"));
+ assertTrue(AccountUtils.getWebdavPath(ocv12s, false, false).equals("/webdav/owncloud.php"));
+ assertTrue(AccountUtils.getWebdavPath(ocv22, false, false).equals("/files/webdav.php"));
+ assertTrue(AccountUtils.getWebdavPath(ocv30,false, false).equals("/files/webdav.php"));
+ assertTrue(AccountUtils.getWebdavPath(ocv33s, false, false).equals("/files/webdav.php"));
+ assertTrue(AccountUtils.getWebdavPath(ocv45, false, false).equals("/remote.php/webdav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv70, false, false).equals("/remote.php/webdav"));
+ assertNull(AccountUtils.getWebdavPath(null, false, false));
+ assertTrue(AccountUtils.getWebdavPath(ocv12, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv12s, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv22, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv30, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv33s, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv45, true, false).equals("/remote.php/odav"));
+ assertTrue(AccountUtils.getWebdavPath(ocv70, true, false).equals("/remote.php/odav"));
+
+ OwnCloudVersion invalidVer = new OwnCloudVersion("a.b.c");
+ assertFalse(invalidVer.isVersionValid());
+
+ assertTrue(ocv45.toString().equals("4.5.0"));
+ }
+
+}
--- /dev/null
+package com.owncloud.android.test;
+
+import com.owncloud.androideta.ProviderTableMeta;
+import com.owncloud.com.owncloud.androidider;
+
+import android.annotation.TargetApi;
+import android.net.Uri;
+import android.os.Build;
+import android.test.ProviderTestCase2;
+import android.test.mock.MockContentResolver;
+import android.util.Log;
+
+@TargetApi(Build.VERSION_CODES.CUPCAKE)
+public class FileContentProviderTest extends ProviderTestCase2<FileContentProvider> {
+
+ private static final String TAG = FileContentProvider.class.getName();
+
+ private static MockContentResolver resolve;
+
+ public FileContentProviderTest(Class<FileContentProvider> providerClass,
+ String providerAuthority) {
+ super(providerClass, providerAuthority);
+ // TODO Auto-generated constructor stub
+ }
+
+ public FileContentProviderTest() {
+ super(FileContentProvider.class, "com.owncloud.android.provicom.owncloud.android
+ @Override
+ public void setUp() {
+ Log.i(TAG, "Entered setup");
+ try {
+ super.setUp();
+ resolve = this.getMockContentResolver();
+ } catch (Exception e) {
+
+ }
+ }
+
+ public void testGetTypeFile() {
+ Uri testuri = Uri.parse("content://org.owncloud/file/");
+ assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
+
+ testuri = Uri.parse("content://org.owncloud/file/123");
+ assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
+ }
+
+ public void testGetTypeRoot() {
+ Uri testuri = Uri.parse("content://org.owncloud/");
+ assertEquals(ProviderTableMeta.CONTENT_TYPE, resolve.getType(testuri));
+ }
+
+}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * 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 de.mobilcom.debitel.cloud.android.test;
-
-import android.test.AndroidTestCase;
-
-import de.mobilcom.debitel.cloud.android.authentication.AccountUtils;
-import de.mobilcom.debitel.cloud.android.utils.OwnCloudVersion;
-
-public class AccountUtilsTest extends AndroidTestCase {
-
- public void testGetWebdavPathAndOCVersion() {
- OwnCloudVersion ocv12 = new OwnCloudVersion(0x010200);
- OwnCloudVersion ocv12s = new OwnCloudVersion("1.2");
- OwnCloudVersion ocv22 = new OwnCloudVersion(0x020200);
- OwnCloudVersion ocv30 = new OwnCloudVersion(0x030000);
- OwnCloudVersion ocv33s = new OwnCloudVersion("3.3.3");
- OwnCloudVersion ocv45 = new OwnCloudVersion(0x040500);
- OwnCloudVersion ocv70 = new OwnCloudVersion(0x070000);
-
- assertTrue(AccountUtils.getWebdavPath(ocv12, false, false).equals("/webdav/owncloud.php"));
- assertTrue(AccountUtils.getWebdavPath(ocv12s, false, false).equals("/webdav/owncloud.php"));
- assertTrue(AccountUtils.getWebdavPath(ocv22, false, false).equals("/files/webdav.php"));
- assertTrue(AccountUtils.getWebdavPath(ocv30,false, false).equals("/files/webdav.php"));
- assertTrue(AccountUtils.getWebdavPath(ocv33s, false, false).equals("/files/webdav.php"));
- assertTrue(AccountUtils.getWebdavPath(ocv45, false, false).equals("/remote.php/webdav"));
- assertTrue(AccountUtils.getWebdavPath(ocv70, false, false).equals("/remote.php/webdav"));
- assertNull(AccountUtils.getWebdavPath(null, false, false));
- assertTrue(AccountUtils.getWebdavPath(ocv12, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv12s, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv22, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv30, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv33s, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv45, true, false).equals("/remote.php/odav"));
- assertTrue(AccountUtils.getWebdavPath(ocv70, true, false).equals("/remote.php/odav"));
-
- OwnCloudVersion invalidVer = new OwnCloudVersion("a.b.c");
- assertFalse(invalidVer.isVersionValid());
-
- assertTrue(ocv45.toString().equals("4.5.0"));
- }
-
-}
+++ /dev/null
-package de.mobilcom.debitel.cloud.android.test;
-
-import de.mobilcom.debitel.cloud.android.db.ProviderMeta.ProviderTableMeta;
-import de.mobilcom.debitel.cloud.android.providers.FileContentProvider;
-
-import android.annotation.TargetApi;
-import android.net.Uri;
-import android.os.Build;
-import android.test.ProviderTestCase2;
-import android.test.mock.MockContentResolver;
-import android.util.Log;
-
-@TargetApi(Build.VERSION_CODES.CUPCAKE)
-public class FileContentProviderTest extends ProviderTestCase2<FileContentProvider> {
-
- private static final String TAG = FileContentProvider.class.getName();
-
- private static MockContentResolver resolve;
-
- public FileContentProviderTest(Class<FileContentProvider> providerClass,
- String providerAuthority) {
- super(providerClass, providerAuthority);
- // TODO Auto-generated constructor stub
- }
-
- public FileContentProviderTest() {
- super(FileContentProvider.class, "de.mobilcom.debitel.cloud.android.providers.FileContentProvider");
- }
-
- @Override
- public void setUp() {
- Log.i(TAG, "Entered setup");
- try {
- super.setUp();
- resolve = this.getMockContentResolver();
- } catch (Exception e) {
-
- }
- }
-
- public void testGetTypeFile() {
- Uri testuri = Uri.parse("content://org.owncloud/file/");
- assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
-
- testuri = Uri.parse("content://org.owncloud/file/123");
- assertEquals(ProviderTableMeta.CONTENT_TYPE_ITEM, resolve.getType(testuri));
- }
-
- public void testGetTypeRoot() {
- Uri testuri = Uri.parse("content://org.owncloud/");
- assertEquals(ProviderTableMeta.CONTENT_TYPE, resolve.getType(testuri));
- }
-
-}