ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2014 ownCloud Inc.
+ Copyright (C) 2012-2015 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,
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest package="com.owncloud.android"
- android:versionCode="10600200"
- android:versionName="1.6.2" xmlns:android="http://schemas.android.com/apk/res/android">
+ android:versionCode="10700000"
+ android:versionName="1.7.0" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
android:name=".ui.activity.Preferences"
android:theme="@style/Theme.ownCloud" >
</activity>
- <activity android:name=".ui.activity.PreferencesNewSessionewSession" >
- </activity>
-
<activity
android:name=".ui.preview.PreviewImageActivity"
/>
--- /dev/null
+## 1.7.0 (19 February 2015)
+
+- Download full folders
+- Grid view for images
+- Remote thumbnails (OC Server 8.0+)
+- Added number of files and folders at the end of the list
+- "Open with" in contextual menu
+- Downloads added to Media Provider
+- Uploads:
+ + Local thumbnails in section "Files"
+ + Multiple selection in "Content from other apps" (Android 4.3+)
+- Gallery:
+ + proper handling of EXIF
+ + obey sorting in the list of files
+- Settings view updated
+- Improved subjects in e-mails
+- Bugs fixed
+...
+
+
-#Mon Jan 19 09:42:11 CET 2015
+#Sun Jan 18 17:01:43 CET 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.owncloud.android.workaround.accounts"
- android:versionCode="0100020"
- android:versionName="1.0.20" >
+ android:versionCode="0100021"
+ android:versionName="1.0.21" >
<uses-sdk
android:minSdkVersion="16"
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
/* ownCloud Jelly Bean Workaround for lost credentials
- * Copyright (C) 2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-Subproject commit e87f5f25ad91950d47ec9b6fa01401360cd7ec8d
+Subproject commit 0dd68c1f65c31bd716b2de26e644c87c98e9b9c2
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
android:orientation="horizontal" >\r
\r
<ImageView\r
- android:id="@+id/imageView1"\r
+ android:id="@+id/thumbnail"\r
android:layout_width="0dp"\r
android:layout_height="wrap_content"\r
android:layout_weight="1"\r
<!--
ownCloud Android client application
- Copyright (C) 2012-2014 ownCloud Inc.
+ Copyright (C) 2015 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,
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
- Copyright (C) 2014 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
android:padding="8dp" >\r
\r
<ImageView\r
- android:id="@+id/imageView1"\r
+ android:id="@+id/thumbnail"\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
android:layout_marginBottom="10dp"\r
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
<!--
ownCloud Android client application
- Copyright (C) 2012-2014 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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.
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+ ownCloud Android client application\r
+ Copyright (C) 2015 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
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:id="@+id/ListItemLayout"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="match_parent"\r
+ android:layout_gravity="center_horizontal"\r
+ android:background="@drawable/list_selector"\r
+ android:gravity="center_horizontal"\r
+ android:orientation="vertical" >\r
+\r
+ <FrameLayout\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content" >\r
+\r
+ <com.owncloud.android.ui.SquareImageView\r
+ android:id="@+id/thumbnail"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="match_parent"\r
+ android:paddingLeft="10dp"\r
+ android:paddingRight="10dp"\r
+ android:scaleType="centerCrop"\r
+ android:src="@drawable/ic_menu_archive"/>\r
+\r
+ <LinearLayout\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="top|right"\r
+ android:orientation="vertical"\r
+ android:layout_margin="4dp">\r
+\r
+ <ImageView\r
+ android:id="@+id/sharedIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginBottom="4dp"\r
+ android:src="@drawable/sharedlink" />\r
+\r
+ <ImageView\r
+ android:id="@+id/sharedWithMeIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginTop="4dp"\r
+ android:src="@drawable/shared_with_me"\r
+ android:visibility="invisible" />\r
+ </LinearLayout>\r
+\r
+ <ImageView\r
+ android:id="@+id/localFileIndicator"\r
+ android:layout_width="@dimen/file_icon_size"\r
+ android:layout_height="@dimen/file_icon_size"\r
+ android:layout_gravity="bottom|right"\r
+ android:layout_marginTop="4dp"\r
+ android:layout_marginBottom="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:src="@drawable/local_file_indicator" />\r
+\r
+ <ImageView\r
+ android:id="@+id/favoriteIcon"\r
+ android:layout_width="15dp"\r
+ android:layout_height="15dp"\r
+ android:layout_gravity="bottom|right"\r
+ android:layout_marginBottom="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:src="@drawable/ic_favorite" />\r
+ </FrameLayout>\r
+\r
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<!--\r
+ ownCloud Android client application\r
+ Copyright (C) 2015 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
+<com.owncloud.android.ui.SquareLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
+ android:id="@+id/ListItemLayout"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="match_parent"\r
+ android:layout_gravity="center_horizontal"\r
+ android:background="@drawable/list_selector"\r
+ android:gravity="center"\r
+ android:orientation="vertical" >\r
+\r
+ <FrameLayout\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center_horizontal" >\r
+\r
+ <ImageView\r
+ android:id="@+id/thumbnail"\r
+ android:layout_width="72dp"\r
+ android:layout_height="72dp"\r
+ android:layout_gravity="center_horizontal"\r
+ android:layout_marginLeft="10dp"\r
+ android:layout_marginRight="10dp"\r
+ android:src="@drawable/ic_menu_archive" />\r
+\r
+ <LinearLayout\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="top|right"\r
+ android:orientation="vertical"\r
+ android:layout_margin="2dp">\r
+\r
+ <ImageView\r
+ android:id="@+id/sharedIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginBottom="2dp"\r
+ android:src="@drawable/sharedlink" />\r
+\r
+ <ImageView\r
+ android:id="@+id/sharedWithMeIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginTop="2dp"\r
+ android:src="@drawable/shared_with_me"\r
+ android:visibility="invisible" />\r
+ </LinearLayout>\r
+\r
+ <ImageView\r
+ android:id="@+id/localFileIndicator"\r
+ android:layout_width="@dimen/file_icon_size"\r
+ android:layout_height="@dimen/file_icon_size"\r
+ android:layout_gravity="bottom|right"\r
+ android:layout_marginTop="2dp"\r
+ android:layout_marginRight="2dp"\r
+ android:src="@drawable/local_file_indicator" />\r
+\r
+ <ImageView\r
+ android:id="@+id/favoriteIcon"\r
+ android:layout_width="15dp"\r
+ android:layout_height="15dp"\r
+ android:layout_gravity="bottom|right"\r
+ android:layout_marginBottom="2dp"\r
+ android:layout_marginRight="2dp"\r
+ android:src="@drawable/ic_favorite" />\r
+\r
+\r
+\r
+ </FrameLayout>\r
+\r
+ <TextView\r
+ android:id="@+id/Filename"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content"\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:ellipsize="middle"\r
+ android:gravity="center_horizontal"\r
+ android:singleLine="true"\r
+ android:text="TextView"\r
+ android:textColor="@color/textColor"\r
+ android:textSize="16dip" />\r
+\r
+</com.owncloud.android.ui.SquareLinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
- -->
+-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1" >
<android.support.v4.widget.SwipeRefreshLayout
- android:id="@+id/swipe_refresh_files"
+ android:id="@+id/swipe_containing_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
- android:footerDividersEnabled="false" >
+ android:footerDividersEnabled="false"
+ android:visibility="visible" >
<com.owncloud.android.ui.ExtendedListView
android:id="@+id/list_root"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
+ android:layout_height="match_parent"
+ android:visibility="visible" />
+
</android.support.v4.widget.SwipeRefreshLayout>
-
- <android.support.v4.widget.SwipeRefreshLayout
- android:id="@+id/swipe_refresh_files_emptyView"
+
+ <android.support.v4.widget.SwipeRefreshLayout
+ android:id="@+id/swipe_containing_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:footerDividersEnabled="false"
android:visibility="gone" >
- <ScrollView
+ <third_parties.in.srain.cube.GridViewWithHeaderAndFooter
+ android:id="@+id/grid_root"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <TextView
- android:id="@+id/empty_list_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical|center_horizontal"
- android:text="@string/empty"
- android:layout_gravity="center"
- android:visibility="visible" />
-
- </ScrollView>
+ android:layout_height="match_parent"
+ android:columnWidth="100dp"
+ android:gravity="center"
+ android:horizontalSpacing="2dp"
+ android:stretchMode="columnWidth"
+ android:verticalSpacing="2dp"
+ android:visibility="visible" />
+
+ </android.support.v4.widget.SwipeRefreshLayout>
+
+ <android.support.v4.widget.SwipeRefreshLayout
+ android:id="@+id/swipe_containing_empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone" >
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <TextView
+ android:id="@+id/empty_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="@string/empty"
+ android:visibility="visible" />
+ </ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
-</FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
-->\r
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"\r
android:id="@+id/ListItemLayout"\r
- android:layout_width="fill_parent"\r
+ android:layout_width="match_parent"\r
android:background="@drawable/list_selector"\r
- android:orientation="horizontal"\r
+ android:orientation="vertical"\r
android:layout_height="56dp">\r
\r
- <FrameLayout\r
- android:layout_width="56dp"\r
- android:layout_height="56dp"\r
- android:focusable="false"\r
- android:focusableInTouchMode="false">\r
-\r
- <ImageView\r
- android:id="@+id/imageView2"\r
- android:layout_width="@dimen/file_icon_size"\r
- android:layout_height="@dimen/file_icon_size"\r
- android:layout_gravity="center_vertical"\r
- android:layout_marginLeft="22dp"\r
- android:src="@drawable/local_file_indicator" />\r
-\r
- <ImageView\r
- android:id="@+id/imageView1"\r
- android:layout_width="@dimen/file_icon_size"\r
- android:layout_height="@dimen/file_icon_size"\r
- android:layout_gravity="center_vertical"\r
- android:layout_marginLeft="9dp"\r
- android:src="@drawable/ic_menu_archive" />\r
-\r
- <ImageView\r
- android:id="@+id/imageView3"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_gravity="bottom|right"\r
- android:layout_marginBottom="10dp"\r
- android:layout_marginRight="2dp"\r
- android:src="@drawable/ic_favorite" />\r
- </FrameLayout>\r
-\r
<LinearLayout\r
- android:layout_width="0dp"\r
+ android:layout_width="match_parent"\r
android:layout_height="match_parent"\r
- android:layout_weight="1"\r
- android:gravity="center_vertical"\r
- android:orientation="vertical" >\r
-\r
- <TextView\r
- android:id="@+id/Filename"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_gravity="center_vertical"\r
- android:layout_marginLeft="4dp"\r
- android:layout_marginRight="4dp"\r
- android:ellipsize="middle"\r
- android:singleLine="true"\r
- android:text="TextView"\r
- android:textColor="#303030"\r
- android:textSize="16dip" />\r
+ android:orientation="horizontal">\r
+\r
+ <FrameLayout\r
+ android:layout_width="56dp"\r
+ android:layout_height="56dp"\r
+ android:focusable="false"\r
+ android:focusableInTouchMode="false">\r
+\r
+ <ImageView\r
+ android:id="@+id/localFileIndicator"\r
+ android:layout_width="@dimen/file_icon_size"\r
+ android:layout_height="@dimen/file_icon_size"\r
+ android:layout_gravity="center_vertical"\r
+ android:layout_marginLeft="22dp"\r
+ android:src="@drawable/local_file_indicator" />\r
+\r
+ <ImageView\r
+ android:id="@+id/thumbnail"\r
+ android:layout_width="@dimen/file_icon_size"\r
+ android:layout_height="@dimen/file_icon_size"\r
+ android:layout_gravity="center_vertical"\r
+ android:layout_marginLeft="9dp"\r
+ android:src="@drawable/ic_menu_archive" />\r
+\r
+ <ImageView\r
+ android:id="@+id/favoriteIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="bottom|right"\r
+ android:layout_marginBottom="10dp"\r
+ android:layout_marginRight="2dp"\r
+ android:src="@drawable/ic_favorite" />\r
+ </FrameLayout>\r
\r
<LinearLayout\r
- android:layout_width="match_parent"\r
- android:layout_height="wrap_content"\r
- android:layout_marginLeft="4dp"\r
- android:layout_marginRight="4dp"\r
- android:weightSum="1">\r
+ android:layout_width="0dp"\r
+ android:layout_height="match_parent"\r
+ android:layout_weight="1"\r
+ android:gravity="center_vertical"\r
+ android:orientation="vertical" >\r
\r
<TextView\r
- android:id="@+id/last_mod"\r
+ android:id="@+id/Filename"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
+ android:layout_gravity="center_vertical"\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:ellipsize="middle"\r
+ android:singleLine="true"\r
android:text="TextView"\r
- android:layout_weight=".5"\r
- android:textColor="@color/list_item_lastmod_and_filesize_text"\r
- android:textSize="12dip"/>\r
+ android:textColor="#303030"\r
+ android:textSize="16dip" />\r
\r
- <TextView\r
- android:id="@+id/file_size"\r
- android:layout_width="wrap_content"\r
+ <LinearLayout\r
+ android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
- android:gravity="right"\r
- android:text="TextView"\r
- android:textColor="@color/list_item_lastmod_and_filesize_text"\r
- android:layout_weight=".5"\r
- android:textSize="12dip"/>\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:weightSum="1">\r
+\r
+ <TextView\r
+ android:id="@+id/last_mod"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:text="TextView"\r
+ android:layout_weight=".5"\r
+ android:textColor="@color/list_item_lastmod_and_filesize_text"\r
+ android:textSize="12dip"/>\r
+\r
+ <TextView\r
+ android:id="@+id/file_size"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:gravity="right"\r
+ android:text="TextView"\r
+ android:textColor="@color/list_item_lastmod_and_filesize_text"\r
+ android:layout_weight=".5"\r
+ android:textSize="12dip"/>\r
+\r
+ </LinearLayout>\r
\r
</LinearLayout>\r
\r
- </LinearLayout>\r
+ <LinearLayout\r
+ android:layout_width="25dp"\r
+ android:layout_height="match_parent"\r
+ android:gravity="center_vertical"\r
+ android:orientation="vertical">\r
\r
- <LinearLayout\r
- android:layout_width="25dp"\r
- android:layout_height="match_parent"\r
- android:gravity="center_vertical"\r
- android:orientation="vertical">\r
-\r
- <ImageView\r
- android:id="@+id/sharedIcon"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_gravity="center"\r
- android:layout_marginLeft="4dp"\r
- android:layout_marginBottom="4dp"\r
- android:layout_marginRight="4dp"\r
- android:src="@drawable/sharedlink" />\r
-\r
- <ImageView\r
- android:id="@+id/sharedWithMeIcon"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_gravity="center"\r
- android:layout_marginLeft="4dp"\r
- android:layout_marginRight="4dp"\r
- android:layout_marginTop="4dp"\r
- android:src="@drawable/shared_with_me" />\r
+ <ImageView\r
+ android:id="@+id/sharedIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginBottom="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:src="@drawable/sharedlink" />\r
+\r
+ <ImageView\r
+ android:id="@+id/sharedWithMeIcon"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center"\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:layout_marginTop="4dp"\r
+ android:src="@drawable/shared_with_me"\r
+ android:visibility="invisible" />\r
\r
+ </LinearLayout>\r
+\r
+ <ImageView\r
+ android:id="@+id/custom_checkbox"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center_vertical"\r
+ android:layout_marginLeft="4dp"\r
+ android:layout_marginRight="4dp"\r
+ android:gravity=""\r
+ android:src="@android:drawable/checkbox_off_background" />\r
</LinearLayout>\r
\r
- <ImageView\r
- android:id="@+id/custom_checkbox"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_gravity="center_vertical"\r
- android:layout_marginLeft="4dp"\r
- android:layout_marginRight="4dp"\r
- android:gravity=""\r
- android:src="@android:drawable/checkbox_off_background" />\r
+ <View\r
+ android:layout_width="match_parent"\r
+ android:layout_height="1dp"\r
+ android:background="@color/list_divider_background"></View>\r
\r
</LinearLayout>\r
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loadingLayout"
android:layout_width="match_parent"
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<?xml version="1.0" encoding="utf-8"?>
<!--
ownCloud Android client application
- Copyright (C) 2014 ownCloud Inc.
+ Copyright (C) 2015 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,
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <EditText
+ android:id="@+id/share_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:inputType="textPassword">
+ </EditText>
+
+</LinearLayout>
\ No newline at end of file
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
android:textColor="@android:color/black"\r
android:gravity="center_horizontal"\r
/>\r
-\r <TextView\r
+\r
+ <TextView\r
android:id="@+id/pinHdrExpl"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
ownCloud Android client application\r
\r
Copyright (C) 2012 Bartek Przybylski\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
android:layout_centerInParent="true"
/>
- <com.owncloud.android.utils.TouchImageViewCustom
+ <third_parties.michaelOrtiz.TouchImageViewCustom
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!-- \r
ownCloud Android client application\r
\r
- Copyright (C) 2012-2013 ownCloud Inc.\r
+ Copyright (C) 2015 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
android:layout_height="fill_parent"\r
android:background="@color/background_color"\r
android:orientation="vertical" >\r
-
+\r
<fragment\r
android:id="@+id/local_files_list"\r
android:layout_width="match_parent"\r
android:layout_height="0dip"\r
android:layout_weight="1"\r
class="com.owncloud.android.ui.fragment.LocalFileListFragment" />\r
-
+\r
<LinearLayout\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
android:gravity="center"\r
android:orientation="horizontal" >\r
-\r <Button\r
+\r
+ <Button\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 <Button\r
+\r
+ <Button\r
android:id="@+id/upload_files_btn_upload"\r
android:layout_width="wrap_content"\r
android:layout_height="wrap_content"\r
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
android:layout_gravity="center_vertical|center"
android:layout_margin="4dp"
android:src="@drawable/ic_menu_archive"
- android:id="@+id/imageView1" />
+ android:id="@+id/thumbnail" />
<TextView
android:text="TextView"
<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2015 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/>.
+-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
-->
<body>
<p>
- Dieses Gerät läuft mit Android 4.1.x.
+ Dieses Ger�t l�uft mit Android 4.1.x.
</p>
<p>
- 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:
+ 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=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
Su dispositivo ejecuta Android 4.1.x.
</p>
<p>
- 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:
+ 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=com.owncloud.android.workaround.accounts">ownCloud Jelly Bean Workaround</a>
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<string name="saml_authentication_wrong_pass">كلمة مرور خاطئة</string>
<string name="folder_picker_choose_button_text">اختيار</string>
<string name="prefs_category_security">الأمان</string>
- <string name="shared_subject_header">مُشارك</string>
</resources>
<string name="actionbar_settings">Quraşdırmalar</string>
<string name="actionbar_see_details">Detallar</string>
<string name="actionbar_send_file">Göndər</string>
+ <string name="actionbar_sort">Çeşidləmək</string>
+ <string name="actionbar_sort_title">Təyinata görə çeşidləmək </string>
+ <string-array name="actionbar_sortby">
+ <item>A-Z</item>
+ <item>Yenisi - Köhnəsi</item>
+ </string-array>
<!--TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item>-->
<string name="prefs_category_general">Ümumi</string>
<string name="prefs_recommend">Dostuna məsləhət gör</string>
<string name="prefs_feedback">Geriyə cavab</string>
<string name="prefs_imprint">İşarələmək</string>
+ <string name="prefs_remember_last_share_location">Paylaşma ünvanını yadda saxla</string>
+ <string name="prefs_remember_last_upload_location_summary">Son paylaşılmış yüklənmə ünvanını yadda saxla</string>
<string name="recommend_subject">%1$s-i ağıllı telefonunuzda yoxlayın!</string>
<string name="recommend_text">Mən sizi öz smartfonunuzda %1$s istifadə etmək üçün dəvət etmək istəyirəm! Burdan endirin: %2$s</string>
<string name="auth_check_server">Serveri yoxla</string>
<string name="uploader_wrn_no_content_text">Heç bir kontent gəlmədi. Yukləmək üçün heçnə yoxdur.</string>
<string name="uploader_error_forbidden_content">%1$s yayımlanmış kontent üçün yetkili deyil</string>
<string name="uploader_info_uploading">Yüklənmə gedir</string>
+ <string name="file_list_seconds_ago">saniyələr öncə</string>
<string name="file_list_empty">Burda heçnə yoxdur. Nese yükləyin!</string>
<string name="file_list_loading">Yüklənir...</string>
<string name="local_file_list_empty">Bu qovluqda heç bir fayl movcud deyil.</string>
+ <string name="file_list_folder">qovluq</string>
+ <string name="file_list_folders">qovluqlar</string>
+ <string name="file_list_file">fayl</string>
+ <string name="file_list_files">fayllar</string>
<string name="filedetails_select_file">Faylın üstünə sıxın ki, əlavə məlumat ekrana çıxsın.</string>
<string name="filedetails_size">Həcm:</string>
<string name="filedetails_type">Tip:</string>
<string name="auth_unsupported_multiaccount">%1$s çoxlu hesab dəstəkləmir</string>
<string name="auth_fail_get_user_name">Sizin server düzgün istifadəçi id-si qaytarmır, xahiş olunur inzibatçı ilə əlaqə saxlayasınız</string>
<string name="auth_can_not_auth_against_server">Bu serverdə yenidən qeydiyyatdan keçmək olmur</string>
+ <string name="auth_account_does_not_exist">Hesab göstərilən avadanlıqda mövcud deyil</string>
<string name="fd_keep_in_sync">Faylı gündəmdə saxla</string>
<string name="common_rename">Adı dəyiş</string>
<string name="common_remove">Sil</string>
<string name="ssl_validator_reason_cert_not_trusted">Server sertifikati inamlı deyil</string>
<string name="ssl_validator_reason_cert_expired">- Server sertifikatının vaxtı bitmişdir</string>
<string name="ssl_validator_reason_cert_not_yet_valid">- Server sertifikatının düzgün tarixi gələcəkdədir</string>
+ <string name="ssl_validator_reason_hostname_not_verified">URL sertifikatda olan host adına uyğun deyil</string>
+ <string name="ssl_validator_question">İstənilən halda bu sertifikata inanmaq istəyirsinizmi?</string>
+ <string name="ssl_validator_not_saved">Sertifikat saxlanıla bilməz</string>
<string name="ssl_validator_btn_details_see">Detallar</string>
+ <string name="ssl_validator_btn_details_hide">Gizlə</string>
+ <string name="ssl_validator_label_subject">Verilir:</string>
+ <string name="ssl_validator_label_issuer">Tərəfindən verilib:</string>
+ <string name="ssl_validator_label_CN">Ümumi ad:</string>
+ <string name="ssl_validator_label_O">Təşkilat:</string>
+ <string name="ssl_validator_label_OU">Alt təşkilatOrganizational unit:</string>
+ <string name="ssl_validator_label_C">Ölkə:</string>
+ <string name="ssl_validator_label_ST">Dövlət:</string>
+ <string name="ssl_validator_label_L">Ərazi:</string>
+ <string name="ssl_validator_label_validity">Etibarlılıq:</string>
+ <string name="ssl_validator_label_validity_from">Kimdən:</string>
+ <string name="ssl_validator_label_validity_to">Kimə:</string>
+ <string name="ssl_validator_label_signature">İmza:</string>
+ <string name="ssl_validator_label_signature_algorithm">Alqıritm:</string>
+ <string name="ssl_validator_null_cert">Sertifikat görünə bilməz.</string>
+ <string name="ssl_validator_no_info_about_error">- Səhv haqqında məlumat yoxdur</string>
+ <string name="placeholder_sentence">Bu bir yer doldurucusudur</string>
+ <string name="placeholder_filename">yerdoldurucusu.txt</string>
+ <string name="placeholder_filetype">PNG Şəkil</string>
+ <string name="placeholder_filesize">389 KB</string>
+ <string name="placeholder_timestamp">2012/05/18 12:23</string>
+ <string name="placeholder_media_time">12:23:45</string>
+ <string name="instant_upload_on_wifi">Şəkilləri yalnız WiFi üzərindən yüklə</string>
+ <string name="instant_video_upload_on_wifi">Videoları yalnız WiFi üzərindən yüklə</string>
+ <string name="instant_upload_path">/CəldYükləmə</string>
+ <string name="conflict_title">Yüklənmə konflikti</string>
+ <string name="conflict_message">Uzaq fayl %s local faylla sinxronizasiya edilmədi. Faylın kontentinin serverdə dəyişdirilməsinə davam edirik.</string>
+ <string name="conflict_keep_both">Birlikdə saxla</string>
+ <string name="conflict_overwrite">Sil yenidən yaz</string>
+ <string name="conflict_dont_upload">Yükləmə</string>
+ <string name="preview_image_description">Şəkili göstər</string>
+ <string name="preview_image_error_unknown_format">Bu şəkil göstərilə bilməz</string>
+ <string name="error__upload__local_file_not_copied">%1$s nüsxələnə bilməz %2$s local qovluğa</string>
+ <string name="prefs_instant_upload_path_title">Yüklənmə ünvanı</string>
+ <string name="share_link_no_support_share_api">Üzr istəyirik, sizin yerverdə paylaşıma izin verilmir. Xahiş olunur
+inzibatçınızla əlaqə saxlayasınız.</string>
+ <string name="share_link_file_no_exist">Paylaşa bilinmir.</string>
+ <string name="share_link_file_error">Bu faylın yada qovluğun paylaşımı zamanı səhv baş verdi </string>
+ <string name="unshare_link_file_no_exist">Paylaşımı dayandırmaq olmur. Xahiş olunur fayl mövcudluğunu yoxlayasınız</string>
<string name="unshare_link_file_error">Bu fayl və ya qovluğun yayımlanmasının dayandırılmasında səhv baş verdi</string>
<string name="activity_chooser_send_file_title">Göndər</string>
<string name="copy_link">linki nüsxələ</string>
<string name="clipboard_text_copied">Mübadilə buferinə nüsxələndi</string>
+ <string name="error_cant_bind_to_operations_service">Kritik səhv: əməliyyat yerinə yetirilə bilinmir</string>
+ <string name="network_error_socket_exception">Serverlə əlaqəyə girdikdə səhv baş verdi.</string>
+ <string name="network_error_socket_timeout_exception">Serveri gözlədiyimiz müddətdə səhv baş verdi, əməliyyat bitə bilməz</string>
+ <string name="network_error_connect_timeout_exception">Serveri gözlədiyimiz müddətdə səhv baş verdi, əməliyyat bitə bilməz</string>
+ <string name="network_host_not_available">Əməliyyat bitə bilməz, serverə çatmaq mümkün deyil </string>
<string name="empty"></string>
<string name="forbidden_permissions">Sizin yetkiniz yoxdur %s</string>
+ <string name="forbidden_permissions_rename">faylın adını dəyişmək</string>
<string name="forbidden_permissions_delete">bu faylı silmək üçün</string>
<string name="share_link_forbidden_permissions">bu faylı yayımlamaq üçün</string>
+ <string name="unshare_link_forbidden_permissions">fayl paylaşımını dayandırmaq</string>
<string name="forbidden_permissions_create">fayl yaratmaq üçün</string>
<string name="uploader_upload_forbidden_permissions">bu qovluğa yükləmək üçün</string>
+ <string name="downloader_download_file_not_found">Bu fayla serverdə artıq uzun müddətdir ki, çatmaq mümkün deyil</string>
<string name="prefs_category_accounts">Hesablar</string>
<string name="prefs_add_account">Hesab əlavə et</string>
+ <string name="auth_redirect_non_secure_connection_title">Təhlükəsiz qoşulma, təhlükəsiz olmayan istiqamətə yönlədirilmişdir</string>
+ <string name="actionbar_logger">Jurnallar</string>
+ <string name="log_send_history_button">Tarixçəni göndər</string>
+ <string name="log_send_no_mail_app">Jurnalların ötürülməsi üçün proqram təminatı tapılmadı!</string>
+ <string name="log_send_mail_subject">%1$s Android proqram jurnalları</string>
+ <string name="log_progress_dialog_text">Data yüklənir...</string>
+ <string name="saml_authentication_required_text">Qeydiyyat tələb edilir</string>
<string name="saml_authentication_wrong_pass">Yalnış şifrə</string>
- <string name="shared_subject_header">yayımlanmış</string>
+ <string name="actionbar_move">Köçürmək</string>
+ <string name="file_list_empty_moving">Burda heçnə yoxdur. Siz qovluq əlavə edə bilərsiniz!</string>
+ <string name="folder_picker_choose_button_text">Seç</string>
+ <string name="move_file_not_found">Köçürmə mümkün olmur. Xahiş olunur faylın mövcudluğunu yoxlayasınız.</string>
+ <string name="move_file_invalid_into_descendent">Qovluğu bu nəsilə köçürmək mümkün deyil</string>
+ <string name="move_file_invalid_overwrite">Fayl artıq mənsəb qovluğunda mövcuddur</string>
+ <string name="move_file_error">Fayl və ya qovluğun köçürülməsi müddətində səhv baş verdi</string>
+ <string name="forbidden_permissions_move">bu faylı köçürtmək</string>
+ <string name="prefs_category_instant_uploading">Anında yükləmələr</string>
+ <string name="prefs_category_security">Təhlükəsizlik</string>
+ <string name="prefs_instant_video_upload_path_title">Video ünvanını yüklə</string>
+ <string name="download_folder_failed_content">Qovluğun endirilməsinin %1$s hissəsi tamamlana bilməz </string>
+ <string name="subject_token">%1$s paylaşdı \"%2$s\" sizinlə</string>
</resources>
<string name="auth_redirect_non_secure_connection_title">Сигурна връзка е пренасочена по несигурен път.</string>
<string name="actionbar_logger">Доклади</string>
<string name="log_send_history_button">Изпрати История</string>
+ <string name="log_send_no_mail_app">Не са намерени журнали за изпращане от приложението. Инсталирайте приложението за електронна поща!</string>
+ <string name="log_send_mail_subject">%1$s Android журнали на приложенията</string>
+ <string name="log_progress_dialog_text">Зареждане на данни...</string>
<string name="saml_authentication_required_text">Нужна е идентификация</string>
<string name="saml_authentication_wrong_pass">Грешна парола</string>
<string name="actionbar_move">Премести</string>
<string name="forbidden_permissions_move">за да преместиш този файл</string>
<string name="prefs_category_instant_uploading">Незабавно качване</string>
<string name="prefs_category_security">Сигурност</string>
- <string name="shared_subject_header">споделен</string>
+ <string name="prefs_instant_video_upload_path_title">Качване на видео път</string>
+ <string name="download_folder_failed_content">Свалянето на директорията %1$s не може да бъде завършено</string>
</resources>
<string name="folder_picker_choose_button_text">বেছে নিন</string>
<string name="move_file_not_found">সরাতে ব্যার্থ হলো। ফাইলটি রয়েছে কিনা দেখুন।</string>
<string name="prefs_category_security">নিরাপত্তা</string>
- <string name="shared_subject_header">ভাগাভাগিকৃত</string>
</resources>
<string name="saml_authentication_wrong_pass">Contrasenya incorrecta</string>
<string name="folder_picker_choose_button_text">Escull</string>
<string name="prefs_category_security">Seguretat</string>
- <string name="shared_subject_header">compartit</string>
</resources>
<string name="prefs_imprint">Imprint</string>
<string name="prefs_remember_last_share_location">Zapamatovat umístění sdílení</string>
<string name="prefs_remember_last_upload_location_summary">Zapamatovat poslední umístění pro nahrání sdílených souborů</string>
- <string name="recommend_subject">Zkuste %1$s na vašem smartphonu!</string>
+ <string name="recommend_subject">Zkuste %1$s na svém chytrém telefonu!</string>
<string name="recommend_text">Chtěl bych vás pozvat k používání %1$s na vašem chytrém telefonu!\nKe stažení zde: %2$s</string>
<string name="auth_check_server">Zkontrolovat server</string>
<string name="auth_host_url">Adresa serveru https://...</string>
<string name="auth_oauth_error">Neúspěšné přihlášení</string>
<string name="auth_oauth_error_access_denied">Přístup zamítnut autorizačním serverem</string>
<string name="auth_wtf_reenter_URL">Neočekávaný stav; prosím vložte znovu URL adresu serveru</string>
- <string name="auth_expired_oauth_token_toast">Vaše přihlášení vypršelo. Přihlašte se, prosím, znovu</string>
+ <string name="auth_expired_oauth_token_toast">Vaše přihlášení vypršelo. Přihlaste se prosím znovu</string>
<string name="auth_expired_basic_auth_toast">Zadejte prosím aktuální heslo</string>
- <string name="auth_expired_saml_sso_token_toast">Vaše přihlášení vypršelo. Přihlašte se, prosím, znovu</string>
+ <string name="auth_expired_saml_sso_token_toast">Vaše přihlášení vypršelo. Přihlaste se prosím znovu</string>
<string name="auth_connecting_auth_server">Připojuji se k přihlašovacímu serveru...</string>
<string name="auth_unsupported_auth_method">Server nepodporuje tuto přihlašovací metodu</string>
<string name="auth_unsupported_multiaccount">%1$s nepodporuje více účtů</string>
<string name="auth_fail_get_user_name">Váš server nevrací správné přihlašovací ID, kontaktujte prosím svého správce systému</string>
<string name="auth_can_not_auth_against_server">Není možné provést ověření </string>
+ <string name="auth_account_does_not_exist">V zařízení není zatím nastaven účet</string>
<string name="fd_keep_in_sync">Udržovat soubor aktuální</string>
<string name="common_rename">Přejmenovat</string>
<string name="common_remove">Odstranit</string>
<string name="share_link_file_error">Při pokusu o sdílení tohoto souboru či složky nastala chyba</string>
<string name="unshare_link_file_no_exist">Nelze ukončit sdílení. Zkontrolujte prosím že soubor existuje</string>
<string name="unshare_link_file_error">Při pokusu o zrušení sdílení tohoto souboru či složky nastala chyba</string>
+ <string name="share_link_password_title">Zadejte heslo</string>
+ <string name="share_link_empty_password">Musíte zadat heslo</string>
<string name="activity_chooser_send_file_title">Odeslat</string>
<string name="copy_link">Zkopírovat odkaz</string>
<string name="clipboard_text_copied">Zkopírováno do schránky</string>
<string name="prefs_category_security">Zabezpečení</string>
<string name="prefs_instant_video_upload_path_title">Cesta pro nahrávání videí</string>
<string name="download_folder_failed_content">Stažení adresáře %1$s nemohlo být dokončeno</string>
- <string name="shared_subject_header">sdílené</string>
- <string name="with_you_subject_header">s vámi</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s sdílí \"%2$s\" s vámi</string>
</resources>
<string name="auth_unsupported_multiaccount">%1$s understøtter ikke multiple konti</string>
<string name="auth_fail_get_user_name">Din server retunere ikke et korrekt bruger-id. Kontakt venligst din administrator</string>
<string name="auth_can_not_auth_against_server">Kan ikke autentificere mod denne server</string>
+ <string name="auth_account_does_not_exist">Kontoen findes endnu ikke på enheden</string>
<string name="fd_keep_in_sync">Hold filen opdateret</string>
<string name="common_rename">Omdøb</string>
<string name="common_remove">Fjern</string>
<string name="share_link_file_error">Der opstod en fejl ved deling af denne fil eller mappe</string>
<string name="unshare_link_file_no_exist">Kan ikke fjerne deling. Tjek venligst om filen findes.</string>
<string name="unshare_link_file_error">Der opstod en fejl ved stopning af deling af denne mappe.</string>
+ <string name="share_link_password_title">Angiv et kodeord</string>
+ <string name="share_link_empty_password">Du skal angive et kodeord</string>
<string name="activity_chooser_send_file_title">Send</string>
<string name="copy_link">Kopiér link</string>
<string name="clipboard_text_copied">Kopieret til udklipsholder</string>
<string name="prefs_category_security">Sikkerhed</string>
<string name="prefs_instant_video_upload_path_title">Sti til videoupload</string>
<string name="download_folder_failed_content">Download af %1$s mappe kunne ikke fuldføres</string>
- <string name="shared_subject_header">delt</string>
- <string name="with_you_subject_header">med dig</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s delt \"%2$s\" med dig</string>
</resources>
<string name="auth_no_net_conn_title">Keine Netzwerkverbindung</string>
<string name="auth_nossl_plain_ok_title">Sichere Verbindung nicht verfügbar.</string>
<string name="auth_connection_established">Verbindung hergestellt</string>
- <string name="auth_testing_connection">Verbindungstest …</string>
+ <string name="auth_testing_connection">Verbindungstest…</string>
<string name="auth_not_configured_title">Fehlerhafte Server Konfiguration</string>
<string name="auth_account_not_new">Ein Benutzerkonto für den gleichen Benutzer und Server existiert auf diesem Gerät bereits</string>
<string name="auth_account_not_the_same">Der eingegebene Benutzer passt nicht zu dem Benutzer dieses Benutzerkontos</string>
<string name="auth_fail_get_user_name">Ihr Server gibt keine richtige Benutzerkennung zurück, bitte kontaktieren Sie einen Administrator
⇥</string>
<string name="auth_can_not_auth_against_server">Die Legitimierung gegenüber dem Server konnte nicht durchgeführt werden</string>
+ <string name="auth_account_does_not_exist">Das Benutzerkonto ist bis jetzt noch nicht auf dem Gerät vorhanden</string>
<string name="fd_keep_in_sync">Datei aktuell halten</string>
<string name="common_rename">Umbenennen</string>
<string name="common_remove">Löschen</string>
<string name="share_link_file_error">Es ist ein Fehler beim Freigeben der Datei oder des Ordners aufgetreten.</string>
<string name="unshare_link_file_no_exist">Entfernen der Freigabe nicht möglich. Prüfen Sie, ob die Datei existiert</string>
<string name="unshare_link_file_error">Es ist ein Fehler beim Entfernen der Freigabe für diese Datei oder den Ordner aufgetreten.</string>
+ <string name="share_link_password_title">Passwort eingeben</string>
+ <string name="share_link_empty_password">Sie müssen ein Passwort eingeben</string>
<string name="activity_chooser_send_file_title">Senden</string>
<string name="copy_link">Link kopieren</string>
<string name="clipboard_text_copied">In die Zwischenablage kopiert</string>
<string name="prefs_category_security">Sicherheit</string>
<string name="prefs_instant_video_upload_path_title">Verzeichnis zum Hochladen der Videos</string>
<string name="download_folder_failed_content">Herunterladen des %1$s - Ordners konnte nicht abgeschlossen werden</string>
- <string name="shared_subject_header">geteilt</string>
- <string name="with_you_subject_header">Mit Ihnen</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s hat „%2$s“ mit Ihnen geteilt</string>
</resources>
<string name="recommend_subject">Probiere %1$s auf Deinem Smartphone!</string>
<string name="recommend_text">Ich möchte Dich zum Benutzen von %1$s auf Deinem Smartphone einladen!\nLade es hier herunter: %2$s</string>
<string name="auth_check_server">Überprüfe den Server</string>
- <string name="auth_host_url">Server-Adresse https://…</string>
+ <string name="auth_host_url">Serveradresse https://…</string>
<string name="auth_username">Benutzername</string>
<string name="auth_password">Passwort</string>
<string name="auth_register">Ist %1$s neu für dich?</string>
<string name="uploader_info_uploading">Lade hoch</string>
<string name="file_list_seconds_ago">Gerade eben</string>
<string name="file_list_empty">Alles leer. Lade etwas hoch!</string>
- <string name="file_list_loading">Ladevorgang …</string>
+ <string name="file_list_loading">Laden…</string>
<string name="local_file_list_empty">Es befinden sich keine Dateien in diesem Ordner.</string>
<string name="file_list_folder">Ordner</string>
<string name="file_list_folders">Ordner</string>
<string name="filedetails_download">Herunterladen</string>
<string name="filedetails_sync_file">Datei aktualisieren</string>
<string name="filedetails_renamed_in_upload_msg">Datei wurde wärend des Uploads zu %1$s umbenannt</string>
- <string name="action_share_file">Link Teilen</string>
+ <string name="action_share_file">Link teilen</string>
<string name="action_unshare_file">Link nicht mehr freigeben</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nein</string>
<string name="common_cancel">Abbrechen</string>
<string name="common_save_exit">Speichern & schließen</string>
<string name="common_error">Fehler</string>
- <string name="common_loading">Lädt ...</string>
+ <string name="common_loading">Lade…</string>
<string name="common_error_unknown">Unbekannter Fehler</string>
<string name="about_title">Über</string>
<string name="change_password">Passwort ändern</string>
<string name="delete_account">Account löschen</string>
<string name="create_account">Account erstellen</string>
- <string name="upload_chooser_title">Dateien hochladen von...</string>
+ <string name="upload_chooser_title">Dateien hochladen von…</string>
<string name="uploader_info_dirname">Ordnername</string>
- <string name="uploader_upload_in_progress_ticker">Hochladen...</string>
+ <string name="uploader_upload_in_progress_ticker">Hochladen…</string>
<string name="uploader_upload_in_progress_content">%1$d%% Hochladen %2$s</string>
<string name="uploader_upload_succeeded_ticker">Hochladen erfolgreich</string>
<string name="uploader_upload_succeeded_content_single">%1$s wurde(n) erfolgreich hochgeladen</string>
<string name="uploader_upload_failed_ticker">Hochladen fehlgeschlagen</string>
<string name="uploader_upload_failed_content_single">Hochladen von %1$s konnte nicht abgeschlossen werden</string>
<string name="uploader_upload_failed_credentials_error">Hochladen fehlgeschlagen, Du musst dich nochmals anmelden</string>
- <string name="downloader_download_in_progress_ticker">Herunterladen...</string>
+ <string name="downloader_download_in_progress_ticker">Herunterladen…</string>
<string name="downloader_download_in_progress_content">%1$d%% Herunterladen %2$s</string>
<string name="downloader_download_succeeded_ticker">Herunterladen erfolgreich</string>
<string name="downloader_download_succeeded_content">%1$s wurde erfolgreich heruntergeladen</string>
<string name="media_rewind_description">Zurückspielen Knopf</string>
<string name="media_play_pause_description">Play-/Pause Knopf</string>
<string name="media_forward_description">Vorspulen Knopf</string>
- <string name="auth_getting_authorization">Autorisierung empfangen...</string>
- <string name="auth_trying_to_login">Anmeldungsversuch...</string>
+ <string name="auth_getting_authorization">Autorisierung empfangen…</string>
+ <string name="auth_trying_to_login">Anmeldeversuch…</string>
<string name="auth_no_net_conn_title">Keine Netzwerkverbindung</string>
<string name="auth_nossl_plain_ok_title">Sichere Verbindung nicht verfügbar.</string>
<string name="auth_connection_established">Verbindung hergestellt</string>
- <string name="auth_testing_connection">Verbindung testen...</string>
+ <string name="auth_testing_connection">Verbindung testen…</string>
<string name="auth_not_configured_title">Fehlerhafte Server Konfiguration</string>
<string name="auth_account_not_new">Ein Benutzerkonto für den gleichen Benutzer und Server existiert auf diesem Gerät bereits</string>
<string name="auth_account_not_the_same">Der eingegebene Benutzer passt nicht zu dem Benutzer dieses Benutzerkontos</string>
<string name="auth_fail_get_user_name">Dein Server gibt keine korrekte Benutzer-ID zurück, bitte kontaktiere einen Administrator
</string>
<string name="auth_can_not_auth_against_server">Die Authentifizierung gegenüber dem Server konnte nicht durchgeführt werden</string>
+ <string name="auth_account_does_not_exist">Das Benutzerkonto ist bis jetzt noch nicht auf dem Gerät vorhanden</string>
<string name="fd_keep_in_sync">Datei aktuell halten</string>
<string name="common_rename">Umbenennen</string>
<string name="common_remove">Löschen</string>
<string name="wait_a_moment">Bitte warte einen Moment.</string>
<string name="filedisplay_unexpected_bad_get_content">Ein unerwartetes Problem ist aufgetreten. Bitte versuche, die Datei in einer anderen App zu öffnen</string>
<string name="filedisplay_no_file_selected">Es wurde keine Datei ausgewählt.</string>
- <string name="activity_chooser_title">Link senden an ...</string>
+ <string name="activity_chooser_title">Link senden an…</string>
<string name="oauth_check_onoff">Anmelden mit oAuth2</string>
<string name="oauth_login_connection">Verbinde mit dem oAuth2-Server.</string>
<string name="ssl_validator_header">Die Identität der Website konnte nicht überprüft werden</string>
<string name="share_link_file_error">Es ist ein Fehler beim Freigeben der Datei oder des Ordners aufgetreten.</string>
<string name="unshare_link_file_no_exist">Entfernen der Freigabe nicht möglich. Prüfe, dass die Datei existiert</string>
<string name="unshare_link_file_error">Es ist ein Fehler beim Entfernen der Freigabe für diese Datei oder den Ordner aufgetreten.</string>
+ <string name="share_link_password_title">Passwort eingeben</string>
+ <string name="share_link_empty_password">Du musst ein Passwort eingeben</string>
<string name="activity_chooser_send_file_title">Senden</string>
<string name="copy_link">Link kopieren</string>
<string name="clipboard_text_copied">In die Zwischenablage kopiert</string>
<string name="prefs_category_security">Sicherheit</string>
<string name="prefs_instant_video_upload_path_title">Verzeichnis zum Hochladen der Videos</string>
<string name="download_folder_failed_content">Herunterladen des %1$s - Ordners konnte nicht abgeschlossen werden</string>
- <string name="shared_subject_header">geteilt</string>
- <string name="with_you_subject_header">Mit Dir</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s hat „%2$s“ mit Dir geteilt</string>
</resources>
<string name="auth_fail_get_user_name">Ο διακομιστής σας δεν επιστρέφει το σωστό αναγνωριστικό χρήστη, παρακαλώ επικοινωνήστε με ένα διαχειριστή
⇥</string>
<string name="auth_can_not_auth_against_server">Δεν είναι δυνατή η πιστοποίηση με αυτόν το διακομιστή</string>
+ <string name="auth_account_does_not_exist">Ο λογαριασμός δεν υπάρχει στη συσκευή ακόμα.</string>
<string name="fd_keep_in_sync">Διατήρηση αρχείου σε ενημέρωση</string>
<string name="common_rename">Μετονομασία</string>
<string name="common_remove">Αφαίρεση</string>
<string name="share_link_file_error">Ένα σφάλμα προέκυψε κατά την προσπάθεια διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
<string name="unshare_link_file_no_exist">Αδύνατη η διακοπή κοινής χρήσης. Παρακαλώ ελέγξτε αν το αρχείο υπάρχει</string>
<string name="unshare_link_file_error">Ένα σφάλμα προέκυψε κατά τη διάρκεια ακύρωσης διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
+ <string name="share_link_password_title">Εισάγετε ένα κωδικό πρόσβασης.</string>
+ <string name="share_link_empty_password">Πρέπει να εισάγετε ένα κωδικό πρόσβασης.</string>
<string name="activity_chooser_send_file_title">Αποστολή</string>
<string name="copy_link">Αντιγραφή συνδέσμου</string>
<string name="clipboard_text_copied">Αντιγραφθηκε στο πρόχειρο</string>
<string name="prefs_category_security">Ασφάλεια</string>
<string name="prefs_instant_video_upload_path_title">Διαδρομή Μεταφόρτωσης Βίντεο</string>
<string name="download_folder_failed_content">Η λήψη του φακέλου %1$s δεν ολοκληρώθηκε με επιτυχία.</string>
- <string name="shared_subject_header">μοιρασμένο </string>
- <string name="with_you_subject_header">με εσένα</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s μοιράστηκε \"%2$s\" μαζί σας</string>
</resources>
<string name="auth_fail_get_user_name">Your server is not returning a correct user id, please contact an administrator
</string>
<string name="auth_can_not_auth_against_server">Cannot authenticate against this server</string>
+ <string name="auth_account_does_not_exist">Account does not exist on the device yet</string>
<string name="fd_keep_in_sync">Keep file up to date</string>
<string name="common_rename">Rename</string>
<string name="common_remove">Remove</string>
<string name="share_link_file_error">An error occurred while trying to share this file or folder</string>
<string name="unshare_link_file_no_exist">Unable to unshare. Please check whether the file exists</string>
<string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
+ <string name="share_link_password_title">Enter a password</string>
+ <string name="share_link_empty_password">You must enter a password</string>
<string name="activity_chooser_send_file_title">Send</string>
<string name="copy_link">Copy link</string>
<string name="clipboard_text_copied">Copied to clipboard</string>
<string name="prefs_category_security">Security</string>
<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
<string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
- <string name="shared_subject_header">shared</string>
- <string name="with_you_subject_header">with you</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s shared \"%2$s\" with you</string>
</resources>
<string name="saml_authentication_wrong_pass">Malĝusta pasvorto</string>
<string name="folder_picker_choose_button_text">Elekti</string>
<string name="prefs_category_security">Sekuro</string>
- <string name="shared_subject_header">kunhavigita</string>
</resources>
<string name="actionbar_settings">Configuración</string>
<string name="actionbar_see_details">Detalles</string>
<string name="actionbar_send_file">Mandar</string>
+ <string name="actionbar_sort">Orden</string>
+ <string name="actionbar_sort_title">Ordenar por</string>
+ <string-array name="actionbar_sortby">
+ <item>A-Z</item>
+ <item>Nuevos - Viejos</item>
+ </string-array>
<!--TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item>-->
<string name="prefs_category_general">General</string>
<string name="prefs_recommend">Recomendar a un amigo</string>
<string name="prefs_feedback">Sugerencias</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="prefs_remember_last_share_location">Recordar compartir ubicación </string>
+ <string name="prefs_remember_last_upload_location_summary">Recordar la ultima ubicación compartida de subida</string>
<string name="recommend_subject">¡Intento %1$s en tu teléfono inteligente!</string>
+ <string name="recommend_text">Quiero invitarte a usar %1$s en tu teléfono inteligente!\nDescárgalo aquí: %2$s</string>
<string name="auth_check_server">Verificar Servidor</string>
<string name="auth_host_url">Dirección del servidor https://...</string>
<string name="auth_username">Nombre de usuario</string>
<string name="conflict_dont_upload">No subir</string>
<string name="preview_image_description">Previsualización de imagen</string>
<string name="preview_image_error_unknown_format">Esta imagen no puede ser mostrada</string>
+ <string name="error__upload__local_file_not_copied">%1$s no pudo ser copiado a la carpeta local %2$s </string>
+ <string name="prefs_instant_upload_path_title">Dirección de subida</string>
+ <string name="share_link_no_support_share_api">Lo sentimos, compartir no esta activado en su servidor. Por favor contacte a su
+⇥⇥administrator.</string>
+ <string name="share_link_file_no_exist">Imposible compartir. Por favor revise si el archivo existe</string>
+ <string name="share_link_file_error">Un error ocurrió cuando se intentaba compartir el archivo o carpeta</string>
+ <string name="unshare_link_file_no_exist">Imposible dejar de compartir. Por favor revise si los archivos existen</string>
+ <string name="unshare_link_file_error">Un error ocurrió cuando se intentaba dejar de compartir el archivo o carpeta</string>
<string name="activity_chooser_send_file_title">Mandar</string>
+ <string name="copy_link">Copiar dirección url</string>
<string name="clipboard_text_copied">Copiado al portapapeles</string>
+ <string name="error_cant_bind_to_operations_service">Error critico: no se puede realizar operaciones</string>
+ <string name="network_error_socket_exception">Un error ocurrió mientras se conectaba con el Servidor.</string>
+ <string name="network_error_socket_timeout_exception">Un error ocurrió mientras se conectaba con el Servidor. La operación no se realizó </string>
+ <string name="network_error_connect_timeout_exception">Un error ocurrió esperando al Servidor, la operación no se realizó</string>
+ <string name="network_host_not_available">Operación no completada, Servidor no disponible.</string>
<string name="empty"></string>
+ <string name="forbidden_permissions">Tu no tienes permiso %s</string>
+ <string name="forbidden_permissions_rename">para renombrar este archivo</string>
+ <string name="forbidden_permissions_delete">para borrar este archivo</string>
+ <string name="share_link_forbidden_permissions">para compartir este archivo</string>
+ <string name="unshare_link_forbidden_permissions">para dejar de compartir este archivo</string>
+ <string name="forbidden_permissions_create">para crear el archivo</string>
+ <string name="uploader_upload_forbidden_permissions">para subir en esta carpeta</string>
+ <string name="downloader_download_file_not_found">El archivo no esta mas disponible en este Servidor</string>
<string name="prefs_category_accounts">Cuentas</string>
+ <string name="prefs_add_account">Añadir cuenta</string>
+ <string name="auth_redirect_non_secure_connection_title">Conexión segura redireccionada a una ruta insegura.</string>
+ <string name="actionbar_logger">Registro</string>
+ <string name="log_send_history_button">Enviar Historial</string>
+ <string name="log_send_no_mail_app">Aplicación para enviar registros no encontrada. Instale una aplicación de correo!</string>
+ <string name="log_send_mail_subject">%1$s Registros de la aplicación Android</string>
+ <string name="log_progress_dialog_text">Cargando datos...</string>
<string name="saml_authentication_required_text">Autentificación requerida</string>
<string name="saml_authentication_wrong_pass">Clave incorrecta</string>
+ <string name="actionbar_move">Mover</string>
+ <string name="file_list_empty_moving">Nada aquí. Puedes agregar una carpeta!</string>
<string name="folder_picker_choose_button_text">Elegir</string>
+ <string name="move_file_not_found">Imposible mover. Por favor revisa si el archivo existe</string>
+ <string name="move_file_invalid_overwrite">El archivo ya existe en la carpeta destino</string>
+ <string name="move_file_error">Un error ocurrió intentando mover el archivo o carpeta</string>
+ <string name="forbidden_permissions_move">para mover este archivo</string>
+ <string name="prefs_category_instant_uploading">Subida Instantánea </string>
<string name="prefs_category_security">Seguridad</string>
- <string name="shared_subject_header">compartido</string>
+ <string name="prefs_instant_video_upload_path_title">Dirección de subida del video</string>
+ <string name="download_folder_failed_content">La descarga de la carpeta %1$s no pudo ser completada</string>
</resources>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
+ <string name="actionbar_upload_files">Archivos</string>
<!--TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item>-->
+ <string name="sync_string_files">Archivos</string>
<string name="empty"></string>
</resources>
<string name="saml_authentication_wrong_pass">Contraseña incorrecta</string>
<string name="folder_picker_choose_button_text">Seleccionar</string>
<string name="prefs_category_security">Seguridad</string>
- <string name="shared_subject_header">compartido</string>
</resources>
<string name="actionbar_upload_from_apps">Contenido de otras aplicaciones</string>
<string name="actionbar_upload_files">Archivos</string>
<string name="actionbar_open_with">Abrir con</string>
- <string name="actionbar_mkdir">Nueva Carpeta</string>
+ <string name="actionbar_mkdir">Nueva carpeta</string>
<string name="actionbar_settings">Configuración</string>
<string name="actionbar_see_details">Detalles</string>
<string name="actionbar_send_file">Enviar</string>
<string name="prefs_imprint">pie de imprenta</string>
<string name="prefs_remember_last_share_location">Recordar la ubicación de los archivos compartidos</string>
<string name="prefs_remember_last_upload_location_summary">Recordar la ubicación de los últimos archivos compartidos subidos</string>
- <string name="recommend_subject">Prueba %1$s en tu smarthphone!</string>
+ <string name="recommend_subject">¡Prueba %1$s en su smarthphone!</string>
<string name="recommend_text">¡Quiero invitarle a usar %1$s en su smartphone!\nDescárguelo aquí: %2$s</string>
<string name="auth_check_server">Compruebe el servidor.</string>
<string name="auth_host_url">Dirección del servidor https://…</string>
<string name="uploader_btn_upload_text">Subir</string>
<string name="uploader_top_message">Escoger carpeta de carga:</string>
<string name="uploader_wrn_no_account_title">No se encontró la cuenta</string>
- <string name="uploader_wrn_no_account_text">No hay cuentas de %1$s en tu dispositivo. Por favor configura una cuenta primero.</string>
+ <string name="uploader_wrn_no_account_text">No hay cuentas de %1$s en su dispositivo. Por favor, configure una cuenta primero.</string>
<string name="uploader_wrn_no_account_setup_btn_text">Configuración</string>
<string name="uploader_wrn_no_account_quit_btn_text">Salir</string>
<string name="uploader_wrn_no_content_title">No hay contenido para subir</string>
<string name="common_cancel">Cancelar</string>
<string name="common_save_exit">Guardar & Salir</string>
<string name="common_error">Error</string>
- <string name="common_loading">Cargando ...</string>
+ <string name="common_loading">Cargando...</string>
<string name="common_error_unknown">Error desconocido</string>
<string name="about_title">Acerca de</string>
<string name="change_password">Cambiar contraseña</string>
<string name="uploader_upload_failed_ticker">Error en la subida</string>
<string name="uploader_upload_failed_content_single">La subida de %1$s no se pudo completar</string>
<string name="uploader_upload_failed_credentials_error">La carga falló, necesita volver a iniciar sesión</string>
- <string name="downloader_download_in_progress_ticker">Descargando ...</string>
+ <string name="downloader_download_in_progress_ticker">Descargando...</string>
<string name="downloader_download_in_progress_content">%1$d%% Descargado de %2$s</string>
<string name="downloader_download_succeeded_ticker">Descarga completa</string>
<string name="downloader_download_succeeded_content">%1$s se ha descargado con éxito</string>
<string name="downloader_download_failed_content">La descarga de %1$s no se pudo completar</string>
<string name="downloader_not_downloaded_yet">No descargado</string>
<string name="downloader_download_failed_credentials_error">Descarga fallida, necesita reinicar la sesión</string>
- <string name="common_choose_account">Elige una cuenta</string>
+ <string name="common_choose_account">Elija una cuenta</string>
<string name="sync_fail_ticker">Falló la sincronización</string>
<string name="sync_fail_ticker_unauthorized">La sincronización falló, debe reiniciar la sesión</string>
<string name="sync_fail_content">La sincronización de %1$s s no se pudo completar</string>
<string name="foreign_files_local_text">Local: %1$s</string>
<string name="foreign_files_remote_text">Remoto: %1$s</string>
<string name="upload_query_move_foreign_files">No hay suficiente espacio para copiar los archivos seleccionados a la carpeta %1$s. ¿Desea moverlos en vez de copiarlos?</string>
- <string name="pincode_enter_pin_code">Por favor, inserta tu PIN de aplicación</string>
+ <string name="pincode_enter_pin_code">Por favor, inserte su PIN de aplicación</string>
<string name="pincode_configure_your_pin">Introduzca un PIN para la aplicación</string>
<string name="pincode_configure_your_pin_explanation">Se solicitará el PIN cada vez que se inicie la aplicación</string>
<string name="pincode_reenter_your_pincode">Repita el PIN para la aplicación, por favor</string>
<string name="media_event_done">%1$s reproducción finalizada</string>
<string name="media_err_nothing_to_play">No se encontró el archivo multimedia</string>
<string name="media_err_no_account">No se ha proporcionado cuenta</string>
- <string name="media_err_not_in_owncloud">El archivo no esta en una cuenta valida </string>
- <string name="media_err_unsupported">Codec No Soportado</string>
+ <string name="media_err_not_in_owncloud">El archivo no está en una cuenta válida </string>
+ <string name="media_err_unsupported">Codec no soportado</string>
<string name="media_err_io">El archivo de medios no pudo ser leído </string>
<string name="media_err_malformed">Archivo no codificado correctamente</string>
<string name="media_err_timeout">Tiempo de espera agotado en el intento de reproducción</string>
<string name="media_err_unknown">El archivo de medios no se puede reproducir con el reproductor de medios por defecto </string>
<string name="media_err_security_ex">Error de seguridad al intentar reproducir %1$s</string>
<string name="media_err_io_ex">Error de entrada al intentar reproducir %1$s</string>
- <string name="media_err_unexpected">Error inesperado intentando reproducir %1$s</string>
- <string name="media_rewind_description">Botón Rebobinado</string>
+ <string name="media_err_unexpected">Error inesperado al intentar reproducir %1$s</string>
+ <string name="media_rewind_description">Botón de rebobinado</string>
<string name="media_play_pause_description">Botón de reproducción o pausa </string>
<string name="media_forward_description">Botón avance rápido</string>
<string name="auth_getting_authorization">Consiguiendo autorización...</string>
<string name="auth_bad_oc_version_title">No se reconoce la versión del servidor </string>
<string name="auth_wrong_connection_title">No se ha podido establecer la conexión</string>
<string name="auth_secure_connection">Conexión segura establecida</string>
- <string name="auth_unauthorized">Nombre de usuario o contraseña incorrecta</string>
+ <string name="auth_unauthorized">Nombre de usuario o contraseña incorrectos</string>
<string name="auth_oauth_error">Autorización no satisfactoria</string>
<string name="auth_oauth_error_access_denied">Acceso denegado por servidor de autorización</string>
<string name="auth_wtf_reenter_URL">Estado inesperado; por favor, introduzca la URL del servidor de nuevo</string>
<string name="auth_fail_get_user_name">Su servidor no está retornando una identificación de usuario correcta; contacte a un administrador
</string>
<string name="auth_can_not_auth_against_server">No puede autenticarse en este servidor.</string>
+ <string name="auth_account_does_not_exist">Aún no existe la cuenta en el dispositivo</string>
<string name="fd_keep_in_sync">Mantener el archivo actualizado</string>
<string name="common_rename">Renombrar</string>
<string name="common_remove">Borrar</string>
<string name="filename_forbidden_characters">Carácteres ilegales: / \\ < > : \" | ? *</string>
<string name="filename_empty">El nombre de archivo no puede estar vacío</string>
<string name="wait_a_moment">Espere un momento</string>
- <string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, prueba otra app para seleccionar el archivo</string>
+ <string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, pruebe otra app para seleccionar el archivo</string>
<string name="filedisplay_no_file_selected">No hay ficheros seleccionados.</string>
<string name="activity_chooser_title">Enviar enlace a...</string>
<string name="oauth_check_onoff">Ingresar con oAuth2</string>
<string name="ssl_validator_reason_cert_expired">- El certificado del servidor expiró</string>
<string name="ssl_validator_reason_cert_not_yet_valid">- El certificado del servidor es de una fecha que aún no ha llegado</string>
<string name="ssl_validator_reason_hostname_not_verified">- La URL no coincide con el nombre de dominio del certificado</string>
- <string name="ssl_validator_question">¿Confías de todas formas en este certificado?</string>
+ <string name="ssl_validator_question">¿Confía de todas formas en este certificado?</string>
<string name="ssl_validator_not_saved">El certificado no pudo ser guardado</string>
<string name="ssl_validator_btn_details_see">Detalles</string>
<string name="ssl_validator_btn_details_hide">Ocultar</string>
<string name="share_link_file_error">Ocurrió un error al tratar de compartir este archivo o carpeta</string>
<string name="unshare_link_file_no_exist">No se puede dejar de compartir. Revise si el archivo existe</string>
<string name="unshare_link_file_error">Ocurrió un error al tratar de ya no compartir este archivo o carpeta</string>
+ <string name="share_link_password_title">Introduzca una contraseña</string>
+ <string name="share_link_empty_password">Debe introducir una contraseña</string>
<string name="activity_chooser_send_file_title">Enviar</string>
<string name="copy_link">Copiar enlace</string>
<string name="clipboard_text_copied">Copiado al portapapeles</string>
<string name="saml_authentication_wrong_pass">Contraseña incorrecta</string>
<string name="actionbar_move">Mover</string>
<string name="file_list_empty_moving">Aquí no hay nada. ¡Puede agregar una carpeta!</string>
- <string name="folder_picker_choose_button_text">Seleccionar</string>
+ <string name="folder_picker_choose_button_text">Elegir</string>
<string name="move_file_not_found">No se puede mover. Revise si el archivo existe</string>
<string name="move_file_invalid_into_descendent">No se puede mover una carpeta dentro de una de SUS subcarpetas.</string>
<string name="move_file_invalid_overwrite">El archivo ya existe en la carpeta de destino</string>
<string name="prefs_category_instant_uploading">Subidas instantáneas</string>
<string name="prefs_category_security">Seguridad</string>
<string name="prefs_instant_video_upload_path_title">Guardar videos subidos en la carpeta:</string>
- <string name="download_folder_failed_content">Descarga de la carpeta %1$s no ha podido ser completada</string>
- <string name="shared_subject_header">compartido</string>
- <string name="with_you_subject_header">contigo</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">La descarga de la carpeta %1$s no ha podido ser completada</string>
+ <string name="subject_token">%1$s compartió \"%2$s\" contigo</string>
</resources>
<string name="forbidden_permissions_move">selle faili liigutamiseks</string>
<string name="prefs_category_instant_uploading">Kohesed üleslaadimised</string>
<string name="prefs_category_security">Turvalisus</string>
- <string name="shared_subject_header">jagatud</string>
</resources>
<string name="actionbar_see_details">Xehetasunak</string>
<string name="actionbar_send_file">Bidali</string>
<string name="actionbar_sort">Ordenatu</string>
+ <string name="actionbar_sort_title">Ordenatu honen arabera</string>
<string-array name="actionbar_sortby">
<item>A-Z</item>
<item>Berrienak - Zaharrenak</item>
<string name="prefs_feedback">Oharrak</string>
<string name="prefs_imprint">Imprint</string>
<string name="recommend_subject">Probatu %1$s zure telefono adimentsuan!</string>
+ <string name="recommend_text">Nik %1$s zure telefono adimentsuan erabitzera gonbidatu nahi zaitut!\nDeskargatu hemen: %2$s</string>
<string name="auth_check_server">Egiaztatu zerbitzaria</string>
<string name="auth_host_url">Zerbitzariaren helbidea https://</string>
<string name="auth_username">Erabiltzaile izena</string>
<string name="sync_fail_in_favourites_content">%1$d fitxategien edukiak ezin dira sinkronizatu (%2$d gatazka)</string>
<string name="sync_foreign_files_forgotten_ticker">Bertako fitxategi batzuk ahaztu dira</string>
<string name="sync_foreign_files_forgotten_content">%2$s karpetako %1$d fitxategi ezin dira dira kopiatu</string>
+ <string name="sync_foreign_files_forgotten_explanation">1.3.16 bertsioan, gailu honetatik igotzen diren fitxategiak bertako %1$s karpetara mugitzen dira datu galera ekiditeko fitxategi bat kontu ezberdinekin sinkronizatzen denean.\n\n Aldaketa hau dela eta, programa honen aurreko bertsioetan igotako fitxategi guztiak %2$s karpetara kopiatu dira. Hala ere, errore batek hau burutzea ekidin du kontuaren sinkronizazioa egiten ari zen bitartean. Orain fitxategiak dauden bezala utz ditzakezu eta %3$s rako lotura ezabatu, edo fitxategiak %1$s karpetara mugi ditzakezu eta %4$srako lotura mantendu.\n\nBehean bertako fitxategien zerrenda eta %5$s era lotuta zeuden urruneko fitxategiena.</string>
<string name="sync_current_folder_was_removed">%1$s karpeta dagoeneko ez da existitzen</string>
<string name="foreign_files_move">Mugitu denak</string>
<string name="foreign_files_success">Fitxategi guztiak mugitu dira</string>
<string name="error__upload__local_file_not_copied">%1$s ezin da %2$s karpeta lokalera kopiatu</string>
<string name="prefs_instant_upload_path_title">Igotzetarako Bidea</string>
<string name="share_link_no_support_share_api">Sentitzen dut, partekatzea ez dago zure zerbitzarian gaituta. Mesedez jarri harremanetan zure administratzailearekin.</string>
+ <string name="share_link_file_no_exist">Ezin izan da partekatu. Mesedez egiaztatu fitxategia existitzen dela</string>
<string name="share_link_file_error">Errore bat egon da fitxategaia edo karpeta partekatzerakoan</string>
+ <string name="unshare_link_file_no_exist">Ezin izan da partekatzea desegin. Mesedez egiaztatu fitxategia existitzen dela</string>
<string name="unshare_link_file_error">Errore bat egon da fitxategaia edo karpeta partekatzeari uzterakoan</string>
<string name="activity_chooser_send_file_title">Bidali</string>
<string name="copy_link">Lotura kopiatu</string>
<string name="downloader_download_file_not_found">Fitxategia jadanik ez dago eskuragarri zerbitzarian</string>
<string name="prefs_category_accounts">Kontuak</string>
<string name="prefs_add_account">Gehitu kontua</string>
+ <string name="auth_redirect_non_secure_connection_title">Konexio segurua birbideratu da segurua ez den bide batera.</string>
+ <string name="actionbar_logger">Egunkariak</string>
+ <string name="log_send_history_button">Bidali Historia</string>
+ <string name="log_send_no_mail_app">Egunkariak bidaltzeko aplikaziorik ez da aurkitu. Instalatu posta aplikazioa!</string>
<string name="log_send_mail_subject">%1$s Android aplikazioaren egunerokoak</string>
<string name="log_progress_dialog_text">Datuak kargatzen...</string>
<string name="saml_authentication_required_text">Autentikazioa beharrezkoa</string>
<string name="saml_authentication_wrong_pass">Pasahitz okerra</string>
<string name="actionbar_move">Mugitu</string>
+ <string name="file_list_empty_moving">Hemen ez dago ezer. Karpeta bat gehi dezakezu!</string>
<string name="folder_picker_choose_button_text">Aukeratu</string>
+ <string name="move_file_not_found">Ezin izan da mugitu. Mesedez egiaztatu fitxategia existitzen dela</string>
+ <string name="move_file_invalid_overwrite">Fitxategia dagoeneko existitzen da helburuko karpetan</string>
+ <string name="move_file_error">Errore bat gertatu da fitxategi edo karpeta hau mugitzen saiatzerakoan</string>
+ <string name="forbidden_permissions_move">fitxategi hau mugitzeko</string>
<string name="prefs_category_instant_uploading">Berehalako Igoerak</string>
<string name="prefs_category_security">Segurtasuna</string>
- <string name="shared_subject_header">konpartitua</string>
- <string name="with_you_subject_header">zurekin</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="prefs_instant_video_upload_path_title">Bideo Igoera Bidea</string>
+ <string name="download_folder_failed_content">%1$s karpetaren deskarga ezin izan da burutu</string>
</resources>
<string name="saml_authentication_wrong_pass">رمز عبور اشتباه است</string>
<string name="folder_picker_choose_button_text">انتخاب کردن</string>
<string name="prefs_category_security">امنیت</string>
- <string name="shared_subject_header">اشتراک گذاشته شده</string>
</resources>
<string name="auth_unsupported_auth_method">Palvelin ei tue tätä tunnistautumistapaa</string>
<string name="auth_unsupported_multiaccount">%1$s ei tue useita tilejä</string>
<string name="auth_can_not_auth_against_server">Tunnistautuminen palvelinta vastaan ei onnistu</string>
+ <string name="auth_account_does_not_exist">Tiliä ei ole olemassa vielä laitteella</string>
<string name="fd_keep_in_sync">Pidä tiedosto ajan tasalla</string>
<string name="common_rename">Nimeä uudelleen</string>
<string name="common_remove">Poista</string>
<string name="share_link_no_support_share_api">Jakaminen ei ole käytössä palvelimellasi. Ota yhteys
ylläpitäjään.</string>
<string name="share_link_file_error">Virhe tiedoston tai kansion jakamista yrittäessä</string>
+ <string name="share_link_password_title">Anna salasana</string>
+ <string name="share_link_empty_password">Salasana on pakko antaa</string>
<string name="activity_chooser_send_file_title">Lähetä</string>
<string name="copy_link">Kopioi linkki</string>
<string name="clipboard_text_copied">Kopioitu leikepöydälle</string>
<string name="move_file_error">Tämän tiedoston tai kansion siirtoa yrittäessä tapahtui virhe</string>
<string name="prefs_category_instant_uploading">Välittömät lähetykset</string>
<string name="prefs_category_security">Tietoturva</string>
- <string name="shared_subject_header">jaettu</string>
- <string name="with_you_subject_header">kanssasi</string>
+ <string name="subject_token">%1$s jakoi kohteen \"%2$s\" kanssasi</string>
</resources>
<string name="prefs_manage_accounts">Gestion des comptes</string>
<string name="prefs_pincode">Code de sécurité</string>
<string name="prefs_pincode_summary">Protéger l\'accès à l\'application</string>
- <string name="prefs_instant_upload">Envoi instantané des photos</string>
+ <string name="prefs_instant_upload">Téléversement immédiat des photos</string>
<string name="prefs_instant_upload_summary">Téléverser immédiatement les photos prises par la caméra</string>
- <string name="prefs_instant_video_upload">Envoi instantané des vidéos</string>
+ <string name="prefs_instant_video_upload">Téléversement immédiat des vidéos</string>
<string name="prefs_instant_video_upload_summary">Téléverser immédiatement les vidéos prises par la caméra</string>
<string name="prefs_log_title">Activer les logs</string>
<string name="prefs_log_summary">Utilisé pour enregistrer les problèmes dans les logs</string>
<string name="prefs_feedback">Commentaires</string>
<string name="prefs_imprint">Empreinte</string>
<string name="prefs_remember_last_share_location">Mémoriser l\'emplacement de partage</string>
- <string name="prefs_remember_last_upload_location_summary">Mémoriser le dernier emplacement d\'upload</string>
+ <string name="prefs_remember_last_upload_location_summary">Mémoriser le dernier emplacement de téléversement</string>
<string name="recommend_subject">Essayez %1$s sur votre smartphone !</string>
<string name="recommend_text">J\'aimerais vous inviter à utiliser %1$s sur votre smartphone !
Téléchargez-le ici : %2$s</string>
<string name="uploader_top_message">Sélectionner le dossier d\'envoi :</string>
<string name="uploader_wrn_no_account_title">Aucun compte n\'a été trouvé</string>
<string name="uploader_wrn_no_account_text">Aucun compte %1$s n\'a été trouvé. Veuillez commencer par en configurer un.</string>
- <string name="uploader_wrn_no_account_setup_btn_text">Paramètres</string>
+ <string name="uploader_wrn_no_account_setup_btn_text">Configuration</string>
<string name="uploader_wrn_no_account_quit_btn_text">Quitter</string>
<string name="uploader_wrn_no_content_title">Rien à envoyer</string>
<string name="uploader_wrn_no_content_text">Aucun contenu reçu. Rien à envoyer.</string>
<string name="uploader_error_forbidden_content">%1$s n\'est pas autorisé à accéder au contenu partagé</string>
- <string name="uploader_info_uploading">Téléversement</string>
+ <string name="uploader_info_uploading">Téléversement...</string>
<string name="file_list_seconds_ago">il y a quelques secondes</string>
<string name="file_list_empty">Il n\'y a rien ici ! Envoyez donc quelque chose :)</string>
<string name="file_list_loading">Chargement…</string>
<string name="file_list_folders">dossiers</string>
<string name="file_list_file">fichier</string>
<string name="file_list_files">fichiers</string>
- <string name="filedetails_select_file">Effleurez un fichier pour afficher les informations complémentaires</string>
+ <string name="filedetails_select_file">Effleurez un fichier pour afficher les informations complémentaires.</string>
<string name="filedetails_size">Taille :</string>
<string name="filedetails_type">Type :</string>
<string name="filedetails_created">Créé le :</string>
<string name="common_no">Non</string>
<string name="common_ok">OK</string>
<string name="common_cancel_download">Annuler le téléchargement</string>
- <string name="common_cancel_upload">Annuler l\'envoi</string>
+ <string name="common_cancel_upload">Annuler le téléversement</string>
<string name="common_cancel">Annuler</string>
<string name="common_save_exit">Sauvegarder & Quitter</string>
<string name="common_error">Erreur</string>
<string name="common_error_unknown">Erreur inconnue </string>
<string name="about_title">À propos de</string>
<string name="change_password">Changer de mot de passe</string>
- <string name="delete_account">Effacer ce compte</string>
+ <string name="delete_account">Supprimer ce compte</string>
<string name="create_account">Créer un compte</string>
<string name="upload_chooser_title">Téléverser un fichier depuis…</string>
<string name="uploader_info_dirname">Nom du dossier</string>
<string name="uploader_upload_in_progress_ticker">Téléversement…</string>
<string name="uploader_upload_in_progress_content">Envoi du fichier %2$s : %1$d%% effectués</string>
<string name="uploader_upload_succeeded_ticker">Téléversement réussi</string>
- <string name="uploader_upload_succeeded_content_single">Le fichier %1$s a été envoyé avec succès</string>
+ <string name="uploader_upload_succeeded_content_single">Le fichier %1$s a été téléversé avec succès</string>
<string name="uploader_upload_failed_ticker">Échec de l\'envoi</string>
<string name="uploader_upload_failed_content_single">L\'envoi de %1$s a échoué</string>
<string name="uploader_upload_failed_credentials_error">Le téléversement a échoué, vous devez vous connecter à nouveau</string>
<string name="downloader_download_failed_credentials_error">Le téléchargement a échoué, vous devez vous connecter à nouveau</string>
<string name="common_choose_account">Choisissez un compte</string>
<string name="sync_fail_ticker">La synchronisation a échoué</string>
- <string name="sync_fail_ticker_unauthorized">Échec de la synchronisation, vous devez vous reconnecter à nouveau</string>
+ <string name="sync_fail_ticker_unauthorized">Échec de la synchronisation, vous devez vous reconnecter</string>
<string name="sync_fail_content">La synchronisation de %1$s n\'a pu être terminée</string>
<string name="sync_fail_content_unauthorized">Mot de passe non valide pour %1$s</string>
<string name="sync_conflicts_in_favourites_ticker">Des conflits ont été trouvés</string>
<string name="sync_fail_in_favourites_content">Le contenu de %1$d fichiers n\'a pu être synchronisé (%2$d conflits)</string>
<string name="sync_foreign_files_forgotten_ticker">Certains fichiers locaux ont été oubliés</string>
<string name="sync_foreign_files_forgotten_content">%1$d fichiers du dossier %2$s n\'ont pas pu être copiés dans</string>
- <string name="sync_foreign_files_forgotten_explanation">Depuis la version 1.3.16, les fichiers envoyé depuis ce périphérique sont copiés dans le dossier local %1$s pour éviter une perte de données lorsqu\'un même fichier est synchronisé avec plusieurs comptes.
+ <string name="sync_foreign_files_forgotten_explanation">Depuis la version 1.3.16, les fichiers envoyés depuis ce périphérique sont copiés dans le dossier local %1$s pour éviter une perte de données lorsqu\'un même fichier est synchronisé avec plusieurs comptes.
-En raison de cette modification, tous les fichiers envoyés avec des versions antérieures de cette application ont été copiés dans le dossier %2$s. Cependant une erreur a empêché l\'achèvement de cette opération pendant la synchronisation du compte. Vous pouvez soit laisser les fichiers tels quels et supprimer le lien vers %3$s, soit déplacer les fichiers dans le dossier %1$s et garder le lien vers %4$s.
+En raison de cette modification, tous les fichiers envoyés avec des versions antérieures de cette application ont été copiés dans le dossier %2$s. Cependant, une erreur a empêché l\'achèvement de cette opération pendant la synchronisation du compte. Vous pouvez soit laisser les fichiers tels quels et supprimer le lien vers %3$s, soit déplacer les fichiers dans le dossier %1$s et garder le lien vers %4$s.
Ci-dessous la liste des fichiers locaux, et les fichiers distants dans %5$s auxquels ils étaient liés.</string>
<string name="sync_current_folder_was_removed">Le dossier %1$s n\'existe plus</string>
<string name="foreign_files_fail">Certains fichiers n\'ont pu être déplacés</string>
<string name="foreign_files_local_text">Local : %1$s</string>
<string name="foreign_files_remote_text">Distant : %1$s</string>
- <string name="upload_query_move_foreign_files">Il n\'y a pas assez de place disponible pour copier les fichiers sélectionnés dans le dossier %1$s. Voulez-vous quand même les déplacer ?</string>
+ <string name="upload_query_move_foreign_files">Il n\'y a pas assez de place disponible pour copier les fichiers sélectionnés dans le dossier %1$s. Voulez-vous les déplacer à la place ?</string>
<string name="pincode_enter_pin_code">Veuillez saisir votre code de sécurité</string>
<string name="pincode_configure_your_pin">Veuillez saisir votre code de sécurité </string>
- <string name="pincode_configure_your_pin_explanation">Le code PIN vous sera demandé à chaque lancement de l\'application</string>
- <string name="pincode_reenter_your_pincode">Veuillez saisir à nouveau votre code de sécurité</string>
+ <string name="pincode_configure_your_pin_explanation">Le code de sécurité vous sera demandé à chaque lancement de l\'application</string>
+ <string name="pincode_reenter_your_pincode">Veuillez saisir de nouveau votre code de sécurité</string>
<string name="pincode_remove_your_pincode">Retirer le code de sécurité</string>
<string name="pincode_mismatch">Les deux codes saisis ne concordent pas</string>
<string name="pincode_wrong">Code de sécurité incorrect</string>
<string name="auth_connection_established">Connexion établie</string>
<string name="auth_testing_connection">Test de la connexion…</string>
<string name="auth_not_configured_title">Configuration du serveur erronée</string>
- <string name="auth_account_not_new">Un compte pour le même utilisateur et serveur existe déjà sur ce périphérique</string>
+ <string name="auth_account_not_new">Un compte pour le même utilisateur et serveur existe déjà sur cet appareil</string>
<string name="auth_account_not_the_same">L\'utilisateur entré ne correspond pas à l\'utilisateur de ce compte</string>
- <string name="auth_unknown_error_title">Une erreur inconnue s\'est produite</string>
+ <string name="auth_unknown_error_title">Une erreur inconnue s\'est produite.</string>
<string name="auth_unknown_host_title">Impossible de trouver l\'hôte</string>
<string name="auth_incorrect_path_title">Aucune instance du serveur n\'a été trouvée</string>
- <string name="auth_timeout_title">Le serveur met trop longtemps à répondre</string>
+ <string name="auth_timeout_title">Le serveur a pris trop de temps à répondre</string>
<string name="auth_incorrect_address_title">Adresse non valide</string>
<string name="auth_ssl_general_error_title">Échec de l\'initialisation SSL</string>
<string name="auth_ssl_unverified_server_title">Impossible de vérifier l\'identité du serveur SSL</string>
<string name="auth_fail_get_user_name">Votre serveur a retourné un identifiant d\'utilisateur incorrect. Veuillez prendre contact avec votre administrateur
</string>
<string name="auth_can_not_auth_against_server">Impossible de s\'authentifier sur ce serveur</string>
+ <string name="auth_account_does_not_exist">Le compte n\'existe pas encore sur ce périphérique</string>
<string name="fd_keep_in_sync">Maintenir le fichier à jour</string>
<string name="common_rename">Renommer</string>
<string name="common_remove">Supprimer</string>
<string name="placeholder_media_time">12:23:45</string>
<string name="instant_upload_on_wifi">Téléverser les images via une connexion WiFi uniquement</string>
<string name="instant_video_upload_on_wifi">Téléverser les vidéos via une connexion WiFi uniquement</string>
- <string name="instant_upload_path">/Instantané</string>
+ <string name="instant_upload_path">/InstantUpload</string>
<string name="conflict_title">Conflit de mise à jour</string>
- <string name="conflict_message">Le fichier distant %s n\'est pas synchronisé avec le fichier local. En choisissant de continuer, vous remplacerez le contenu de fichier sur le serveur.</string>
+ <string name="conflict_message">Le fichier distant %s n\'est pas synchronisé avec le fichier local. En choisissant de continuer, vous remplacerez le contenu du fichier sur le serveur.</string>
<string name="conflict_keep_both">Garder les deux versions</string>
<string name="conflict_overwrite">Écraser</string>
<string name="conflict_dont_upload">Ne pas téléverser</string>
<string name="preview_image_description">Prévisualisation de l\'image</string>
<string name="preview_image_error_unknown_format">Cette image ne peut pas être affichée</string>
<string name="error__upload__local_file_not_copied">%1$s n\'a pas pu être copié dans le dossier local %2$s</string>
- <string name="prefs_instant_upload_path_title">Répertoire d\'envoi</string>
+ <string name="prefs_instant_upload_path_title">Répertoire de téléversement</string>
<string name="share_link_no_support_share_api">Désolé, le partage n\'est pas disponible sur votre serveur. Veuillez contacter votre administrateur.</string>
<string name="share_link_file_no_exist">Impossible de partager. Vérifiez que le fichier est bien présent</string>
<string name="share_link_file_error">Une erreur est survenue lors de la tentative de partage de ce fichier ou répertoire</string>
<string name="unshare_link_file_no_exist">Impossible de supprimer le partage. Vérifiez que le fichier est bien présent</string>
<string name="unshare_link_file_error">Une erreur est survenue lors de la tentative d’annulation du partage de ce fichier ou répertoire</string>
+ <string name="share_link_password_title">Saisir un mot de passe</string>
+ <string name="share_link_empty_password">Vous devez saisir un mot de passe</string>
<string name="activity_chooser_send_file_title">Envoyer</string>
<string name="copy_link">Copier le lien</string>
<string name="clipboard_text_copied">Copié dans le presse-papiers</string>
<string name="error_cant_bind_to_operations_service">Erreur critique : impossible de réaliser des opérations</string>
- <string name="network_error_socket_exception">Une erreur s\'est produite pendant la connection au serveur</string>
- <string name="network_error_socket_timeout_exception">Une erreur est survenue pendant l\'attente du serveur. L\'opération n\'a pas pu être effectuée.</string>
- <string name="network_error_connect_timeout_exception">Une erreur est survenue pendant l\'attente du serveur. L\'opération n\'a pas pu être effectuée.</string>
- <string name="network_host_not_available">L\'opération n\'a pas pu être terminée, le serveur n\'est pas disponible.</string>
+ <string name="network_error_socket_exception">Une erreur est survenue pendant la connexion au serveur.</string>
+ <string name="network_error_socket_timeout_exception">Une erreur est survenue pendant l\'attente du serveur. L\'opération n\'a pas pu être effectuée</string>
+ <string name="network_error_connect_timeout_exception">Une erreur est survenue pendant l\'attente du serveur. L\'opération n\'a pas pu être effectuée</string>
+ <string name="network_host_not_available">L\'opération n\'a pas pu être terminée, le serveur n\'est pas disponible</string>
<string name="empty"></string>
<string name="forbidden_permissions">Vous ne possédez pas les droits suffisants %s</string>
<string name="forbidden_permissions_rename">afin de renommer ce fichier</string>
<string name="downloader_download_file_not_found">Ce fichier n’est plus disponible sur le serveur</string>
<string name="prefs_category_accounts">Comptes</string>
<string name="prefs_add_account">Ajouter un compte</string>
- <string name="auth_redirect_non_secure_connection_title">La connexion sécurisée est redirigée via une route non-sécurisée.</string>
+ <string name="auth_redirect_non_secure_connection_title">Le connexion sécurisée est redirigée vers une route non-sécurisée.</string>
<string name="actionbar_logger">Journaux</string>
<string name="log_send_history_button">Envoyer l\'historique</string>
- <string name="log_send_no_mail_app">Aucune application trouvée pour envoyer les logs. Installez une application de courriel !</string>
+ <string name="log_send_no_mail_app">Aucune application trouvée pour l\'envoi de journaux. Installer une application de courriel !</string>
<string name="log_send_mail_subject">Journaux de l\'application Android %1$s</string>
- <string name="log_progress_dialog_text">Chargement des données...</string>
+ <string name="log_progress_dialog_text">Chargement des données…</string>
<string name="saml_authentication_required_text">Authentification requise</string>
<string name="saml_authentication_wrong_pass">Mot de passe incorrect</string>
<string name="actionbar_move">Déplacer</string>
<string name="move_file_invalid_overwrite">Le fichier existe déjà dans le dossier de destination</string>
<string name="move_file_error">Une erreur est survenue lors de la tentative de déplacement de ce fichier ou dossier</string>
<string name="forbidden_permissions_move">de déplacer ce fichier</string>
- <string name="prefs_category_instant_uploading">Envoi instantané</string>
+ <string name="prefs_category_instant_uploading">Envois immédiats</string>
<string name="prefs_category_security">Sécurité</string>
- <string name="prefs_instant_video_upload_path_title">Répertoire d\'envoi des vidéos</string>
- <string name="download_folder_failed_content">Le téléchargement du dossier %1$s n\'a pas pu être achevé complètement</string>
- <string name="shared_subject_header">partagé(e)</string>
- <string name="with_you_subject_header">avec vous</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="prefs_instant_video_upload_path_title">Répertoire de téléversement des vidéos</string>
+ <string name="download_folder_failed_content">Le téléchargement du dossier %1$s n\'a pas pu être achevé</string>
+ <string name="subject_token">%1$s a partagé \"%2$s\" avec vous</string>
</resources>
<string name="actionbar_upload_files">Ficheiros</string>
<string name="actionbar_open_with">Abrir con</string>
<string name="actionbar_mkdir">Novo cartafol</string>
- <string name="actionbar_settings">Preferencias</string>
+ <string name="actionbar_settings">Axustes</string>
<string name="actionbar_see_details">Detalles</string>
<string name="actionbar_send_file">Enviar</string>
<string name="actionbar_sort">Ordenar</string>
<string name="auth_fail_get_user_name">O seu servidor non devolveu un ID de usuario correcto, contacte cun administrador
</string>
<string name="auth_can_not_auth_against_server">Non pode autenticarse neste servidor</string>
+ <string name="auth_account_does_not_exist">Aínda non existe a conta no dispositivo</string>
<string name="fd_keep_in_sync">Manter actualizado o ficheiro</string>
<string name="common_rename">Renomear</string>
<string name="common_remove">Retirar</string>
<string name="share_link_file_error">Produciuse un erro ao tentar compartir este ficheiro ou cartafol.</string>
<string name="unshare_link_file_no_exist">Non foi posíbel deixar de compartir. Comprobe que existe o ficheiro</string>
<string name="unshare_link_file_error">Produciuse un erro ao tentar deixar de compartir este ficheiro ou cartafol</string>
+ <string name="share_link_password_title">Escriba un contrasinal</string>
+ <string name="share_link_empty_password">Ten que escribir un contrasinal</string>
<string name="activity_chooser_send_file_title">Enviar</string>
<string name="copy_link">Copiar a ligazón</string>
<string name="clipboard_text_copied">Copiado no portapapeis.</string>
<string name="prefs_category_instant_uploading">Envío instantáneo</string>
<string name="prefs_category_security">Seguranza</string>
<string name="prefs_instant_video_upload_path_title">Enviar a ruta do vídeo</string>
- <string name="download_folder_failed_content">A descarga do cartafol %1$s non se puido completar</string>
- <string name="shared_subject_header">compartido</string>
- <string name="with_you_subject_header">con vostede</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Non foi posíbel completar a descarga do cartafol %1$s</string>
+ <string name="subject_token">%1$s compartiu «%2$s» con vostede</string>
</resources>
<string name="actionbar_move">Mozgatás</string>
<string name="folder_picker_choose_button_text">Válasszon</string>
<string name="prefs_category_security">Biztonság</string>
- <string name="shared_subject_header">Megosztott</string>
</resources>
<string name="forbidden_permissions_move">untuk memindahkan berkas ini</string>
<string name="prefs_category_instant_uploading">Unggah Cepat</string>
<string name="prefs_category_security">Keamanan</string>
- <string name="shared_subject_header">dibagikan</string>
</resources>
<string name="auth_fail_get_user_name">Il tuo server non ha restituito un id utente corretto, contatta un amministratore
</string>
<string name="auth_can_not_auth_against_server">Impossibile eseguire l\'autenticazione su questo server</string>
+ <string name="auth_account_does_not_exist">L\'account non esiste ancora sul dispositivo</string>
<string name="fd_keep_in_sync">Tieni aggiornato il file</string>
<string name="common_rename">Rinomina</string>
<string name="common_remove">Rimuovi</string>
<string name="share_link_file_error">Si è verificato un errore durante il tentativo di condivisione del file o della cartella</string>
<string name="unshare_link_file_no_exist">Impossibile rimuovere dalla condivisione. Assicurati che il file esista</string>
<string name="unshare_link_file_error">Si è verificato un errore durante il tentativo di rimuovere la condivisione del file o della cartella</string>
+ <string name="share_link_password_title">Digita una password</string>
+ <string name="share_link_empty_password">Devi digitare una password</string>
<string name="activity_chooser_send_file_title">Invia</string>
<string name="copy_link">Copia collegamento</string>
<string name="clipboard_text_copied">Copiato negli appunti</string>
<string name="prefs_category_security">Protezione</string>
<string name="prefs_instant_video_upload_path_title">Percorso di caricamento video</string>
<string name="download_folder_failed_content">Lo scaricamento della cartella %1$s non può essere completato</string>
- <string name="shared_subject_header">condiviso</string>
- <string name="with_you_subject_header">con te</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s ha condiviso \"%2$s\" con te</string>
</resources>
<string name="auth_connecting_auth_server">認証サーバーに接続中 ...</string>
<string name="auth_unsupported_auth_method">サーバーはこの認証方式をサポートしていません</string>
<string name="auth_unsupported_multiaccount">%1$s は複数アカウントをサポートしていません</string>
- <string name="auth_fail_get_user_name">サーバーが正しいユーザーIDを返しませんでした。管理者にご連絡ください。
+ <string name="auth_fail_get_user_name">サーバーが正しいユーザーIDを返しませんでした。管理者に連絡してください。
</string>
<string name="auth_can_not_auth_against_server">このサーバーに対して認証できません</string>
+ <string name="auth_account_does_not_exist">デバイス上にまだアカウントが存在しません</string>
<string name="fd_keep_in_sync">ファイルを最新に保つ</string>
<string name="common_rename">名前を変更</string>
<string name="common_remove">削除</string>
<string name="placeholder_filesize">389 KB</string>
<string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
<string name="placeholder_media_time">12:23:45</string>
- <string name="instant_upload_on_wifi">WiFi経由でのみ写真をアップロード</string>
+ <string name="instant_upload_on_wifi">WiFi経由でのみ画像をアップロード</string>
<string name="instant_video_upload_on_wifi">WiFi経由でのみ動画をアップロード</string>
<string name="instant_upload_path">/InstantUpload</string>
<string name="conflict_title">更新が競合</string>
<string name="preview_image_error_unknown_format">この画像は表示できません</string>
<string name="error__upload__local_file_not_copied">%1$s は、ローカルフォルダー %2$s にコピーできませんでした。</string>
<string name="prefs_instant_upload_path_title">アップロードパス</string>
- <string name="share_link_no_support_share_api">申し訳ございません。共有がサーバー上で有効になっていません。 管理者に
- ご連絡ください。</string>
+ <string name="share_link_no_support_share_api">すみませんが、サーバーで共有が有効になっていません。
+ 管理者に連絡してください。</string>
<string name="share_link_file_no_exist">共有できません。ファイルがあるか確認してください。</string>
<string name="share_link_file_error">このファイルまたはフォルダーを共有する際にエラーが発生しました</string>
<string name="unshare_link_file_no_exist">共有を解除できません。ファイルがあるか確認してください。</string>
<string name="unshare_link_file_error">このファイルまたはフォルダーの共有を解除する際にエラーが発生しました</string>
+ <string name="share_link_password_title">パスワードを入力</string>
+ <string name="share_link_empty_password">パスワードを入力しなければなりません</string>
<string name="activity_chooser_send_file_title">送信</string>
<string name="copy_link">リンクをコピー</string>
<string name="clipboard_text_copied">クリップボードにコピー</string>
<string name="prefs_category_instant_uploading">自動アップロード</string>
<string name="prefs_category_security">セキュリティ</string>
<string name="prefs_instant_video_upload_path_title">動画のアップロードパス</string>
- <string name="shared_subject_header">共有中</string>
- <string name="with_you_subject_header">あなたと</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">%1$s のフォルダのダウンロードが完了しませんでした。</string>
+ <string name="subject_token">%1$sがあなたと\"%2$s\"を共有しました</string>
</resources>
<resources>
<string name="about_android">%1$s កម្មវិធីអានដ្រយ</string>
<string name="about_version">ជំនាន់ %1$s</string>
+ <string name="actionbar_sync">គណនីឱ្យថ្មីឡើងវិញ</string>
<string name="actionbar_upload">ផ្ទុកឡើង</string>
+ <string name="actionbar_upload_from_apps">មាតិការពីកម្មវិធីផ្សេងទៀត</string>
<string name="actionbar_upload_files">ឯកសារ</string>
<string name="actionbar_open_with">បើកជាមួយ</string>
<string name="actionbar_mkdir">ថតថ្មី</string>
<string name="prefs_accounts">គណនី</string>
<string name="prefs_manage_accounts">គ្រប់គ្រងគណនី</string>
<string name="prefs_pincode">ភីនកូដ កម្មវិធី</string>
+ <string name="prefs_log_title">ដំណើរការការចូលទៅកាន់</string>
+ <string name="prefs_log_summary">នេះជាបញ្ហាសម្រាប់អ្នកដែលបានចូលទៅកាន់</string>
+ <string name="prefs_log_title_history">ប្រវត្តិនៃការចូលទៅកាន់</string>
+ <string name="prefs_log_summary_history">នៅទីនេះគឺបង្ហាញការដែលបានចូលទៅកាន់</string>
+ <string name="prefs_log_delete_history_button">លុបប្រវត្តិ</string>
<string name="prefs_help">ជំនួយ</string>
+ <string name="prefs_recommend">ផ្ដល់អនុសាសន៍ទៅកាន់មិត្តភក្ដិ</string>
+ <string name="prefs_feedback">មតិត្រឡប់</string>
<string name="auth_username">ឈ្មោះអ្នកប្រើ</string>
<string name="auth_password">ពាក្យសម្ងាត់</string>
<string name="sync_string_files">ឯកសារ</string>
<string name="auth_fail_get_user_name">서버에서 올바른 사용자 ID를 반환하지 않았습니다. 관리자에게 연락하십시오
</string>
<string name="auth_can_not_auth_against_server">이 서버에 인증할 수 없음</string>
+ <string name="auth_account_does_not_exist">장치에 아직 계정이 존재하지 않습니다.</string>
<string name="fd_keep_in_sync">파일을 최신 정보로 유지</string>
<string name="common_rename">이름 바꾸기</string>
<string name="common_remove">삭제</string>
<string name="share_link_file_error">이 파일이나 폴더를 공유하는 중 오류 발생</string>
<string name="unshare_link_file_no_exist">공유를 해제할 수 없습니다. 파일이 있는지 확인하십시오</string>
<string name="unshare_link_file_error">이 파일이나 폴더의 공유를 해제하는 중 오류 발생</string>
+ <string name="share_link_password_title">비밀번호를 입력하십시요.</string>
+ <string name="share_link_empty_password">비밀번호를 입력해야만 합니다.</string>
<string name="activity_chooser_send_file_title">보내기</string>
<string name="copy_link">링크 주소 복사</string>
<string name="clipboard_text_copied">클립보드로 복사됨</string>
<string name="prefs_category_instant_uploading">즉시 업로드</string>
<string name="prefs_category_security">보안</string>
<string name="prefs_instant_video_upload_path_title">동영상 업로드 경로</string>
- <string name="shared_subject_header">공유됨</string>
- <string name="with_you_subject_header">나와</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">%1$s 폴더를 다운로드할 수 없습니다</string>
+ <string name="subject_token">%1$s에서 \"%2$s\"를 당신과 공유하였습니다.</string>
</resources>
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<string name="saml_authentication_wrong_pass">Neteisingas slaptažodis</string>
<string name="folder_picker_choose_button_text">Pasirinkite</string>
<string name="prefs_category_security">Saugumas</string>
- <string name="shared_subject_header">bendrinamas</string>
</resources>
<string name="actionbar_upload">Augšupielādēt</string>
<string name="actionbar_upload_from_apps">Saturs no citām lietotnēm</string>
<string name="actionbar_upload_files">Datnes</string>
+ <string name="actionbar_open_with">Atvērt ar</string>
<string name="actionbar_mkdir">Jauna mape</string>
<string name="actionbar_settings">Iestatījumi</string>
<string name="actionbar_send_file">Sūtīt</string>
+ <string name="actionbar_sort">Kārtot</string>
+ <string name="actionbar_sort_title">Kārtot pēc</string>
<!--TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item>-->
<string name="prefs_category_general">Vispārīgi</string>
<string name="saml_authentication_wrong_pass">Погрешна лозинка</string>
<string name="folder_picker_choose_button_text">Избери</string>
<string name="prefs_category_security">Безбедност</string>
- <string name="shared_subject_header">споделен</string>
</resources>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--TODO re-enable when server-side folder size calculation is available
+ <item>Biggest - Smallest</item>-->
+ <string name="empty"></string>
+</resources>
<string name="actionbar_see_details">Detaljer</string>
<string name="actionbar_send_file">Send</string>
<string name="actionbar_sort">Sorter</string>
- <string name="actionbar_sort_title">Sorter på</string>
+ <string name="actionbar_sort_title">Sorter etter</string>
<string-array name="actionbar_sortby">
<item>A-Z</item>
<item>Nyeste - Eldste</item>
<string name="prefs_category_instant_uploading">Umiddelbare opplastinger</string>
<string name="prefs_category_security">Sikkerhet</string>
<string name="prefs_instant_video_upload_path_title">Sti til video-opplasting</string>
- <string name="shared_subject_header">delt</string>
- <string name="with_you_subject_header">med deg</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Nedlasting av %1$s mappen kunne ikke fullføres</string>
</resources>
<string name="auth_fail_get_user_name">Uw server geeft geen goede userid terug, neem contact op met uw beheerder
</string>
<string name="auth_can_not_auth_against_server">Kan niet authenticeren tegen deze server</string>
+ <string name="auth_account_does_not_exist">Het account bestaat nog niet in dit apparaat</string>
<string name="fd_keep_in_sync">Houd bestand actueel</string>
<string name="common_rename">Hernoemen</string>
<string name="common_remove">Verwijderen</string>
<string name="share_link_file_error">Er trad een fout op bij uw poging dit bestand of deze map te delen</string>
<string name="unshare_link_file_no_exist">Kan delen niet beëindigen. Ga na of het bestand bestaat</string>
<string name="unshare_link_file_error">Er trad een fout op bij uw poging het delen van dit bestand of deze map te beëindigen</string>
+ <string name="share_link_password_title">Vul het wachtwoord in</string>
+ <string name="share_link_empty_password">U moet een wachtwoord opgeven</string>
<string name="activity_chooser_send_file_title">Versturen</string>
<string name="copy_link">Link kopiëren</string>
<string name="clipboard_text_copied">Gekopieerd naar het klembord</string>
<string name="prefs_category_security">Beveiliging</string>
<string name="prefs_instant_video_upload_path_title">Upload Video Pad</string>
<string name="download_folder_failed_content">Download van %1$s map kon niet worden voltooid</string>
- <string name="shared_subject_header">gedeeld</string>
- <string name="with_you_subject_header">met u</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s deelde \"%2$s\" met u</string>
</resources>
<string name="prefs_feedback">Wsparcie</string>
<string name="prefs_imprint">Stopka</string>
<string name="prefs_remember_last_share_location">Zapamiętaj położenie udostępnienia</string>
+ <string name="prefs_remember_last_upload_location_summary">Zapamiętaj ostatnią lokalizację wgrywania</string>
<string name="recommend_subject">Wypróbuj %1$s na swoim smartphonie!</string>
<string name="recommend_text">Chciałbym zaprosić Cię do używania %1$s na swoim smartfonie!\nŚciągnij tutaj: %2$s</string>
<string name="auth_check_server">Sprawdź serwer</string>
<string name="auth_redirect_non_secure_connection_title">Bezpieczne połączenie jest przekierowywane przez niezabezpieczone trasy.</string>
<string name="actionbar_logger">Logi</string>
<string name="log_send_history_button">Wyślij historię</string>
+ <string name="log_send_no_mail_app">Brak aplikacji do wysyłania logów. Zainstaluj klienta poczty!</string>
<string name="log_send_mail_subject">%1$s Logi aplikacji Android</string>
<string name="log_progress_dialog_text">Ładuję dane...</string>
<string name="saml_authentication_required_text">Wymagana autoryzacja</string>
<string name="prefs_category_instant_uploading">Automatyczne wysyłanie</string>
<string name="prefs_category_security">Bezpieczeństwo</string>
<string name="prefs_instant_video_upload_path_title">Katalog wysyłania dla wideo</string>
- <string name="shared_subject_header">współdzielone</string>
+ <string name="download_folder_failed_content">Pobieranie %1$s katalogu nie może zostać ukończone</string>
</resources>
<string name="auth_fail_get_user_name">Seu servidor não está retornando um ID de usuário correto, por favor, entre em contato com um administrador
⇥</string>
<string name="auth_can_not_auth_against_server">Não foi possível autenticar neste servidor</string>
+ <string name="auth_account_does_not_exist">Conta ainda não existe no dispositivo</string>
<string name="fd_keep_in_sync">Manter arquivo atualizado</string>
<string name="common_rename">Renomear</string>
<string name="common_remove">Remover</string>
<string name="share_link_file_error">Ocorreu um erro durante a tentativa de compartilhar esse arquivo ou pasta</string>
<string name="unshare_link_file_no_exist">Não é possível cancelar o compartilhamento. Por favor verifique se o arquivo existe</string>
<string name="unshare_link_file_error">Ocorreu um erro ao tentar descompartilhar este arquivo ou pasta</string>
+ <string name="share_link_password_title">Digite uma senha</string>
+ <string name="share_link_empty_password">Você deve digitar uma senha</string>
<string name="activity_chooser_send_file_title">Enviar</string>
<string name="copy_link">Copiar o link</string>
<string name="clipboard_text_copied">Copiado para área de transferência</string>
<string name="prefs_category_instant_uploading">Envios Instantâneos</string>
<string name="prefs_category_security">Segurança</string>
<string name="prefs_instant_video_upload_path_title">Enviar o Caminho do Vídeo</string>
- <string name="shared_subject_header">compartilhado</string>
- <string name="with_you_subject_header">com você</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Baixar %1$s da pasta não pode ser completado</string>
+ <string name="subject_token">%1$s compartilhou \"%2$s\" com você</string>
</resources>
<string name="auth_unsupported_multiaccount">%1$s não suporta contas múltiplas</string>
<string name="auth_fail_get_user_name">O seu servidor não transmite o ID correcto. Por favor contacte o administrador.</string>
<string name="auth_can_not_auth_against_server">Não foi possível autenticar no servidor</string>
+ <string name="auth_account_does_not_exist">Conta ainda não existe no dispositivo</string>
<string name="fd_keep_in_sync">manter ficheiro actualizado</string>
<string name="common_rename">Renomear</string>
<string name="common_remove">Remover</string>
<string name="share_link_file_error">Ocorreu um erro enquanto tentava partilhar este ficheiro ou pasta</string>
<string name="unshare_link_file_no_exist">Não é possível retirar a partilha. Verifique se o ficheiro existe</string>
<string name="unshare_link_file_error">Ocorreu um erro enquanto retirava a partilha deste ficheiro ou pasta</string>
+ <string name="share_link_password_title">Introduza uma palavra-passe</string>
+ <string name="share_link_empty_password">Você deve introduzir uma palavra-passe</string>
<string name="activity_chooser_send_file_title">Enviar</string>
<string name="copy_link">Copiar hiperligação</string>
<string name="clipboard_text_copied">Copiado para a área de transferência</string>
<string name="prefs_category_instant_uploading">Envios Instantâneos</string>
<string name="prefs_category_security">Segurança</string>
<string name="prefs_instant_video_upload_path_title">Envio do Caminho do Vídeo</string>
- <string name="shared_subject_header">partilhado</string>
- <string name="with_you_subject_header">consigo</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Não foi possível completar o download da pasta %1$s</string>
+ <string name="subject_token">%1$s partilhou \"%2$s\" consigo</string>
</resources>
<string name="prefs_recommend">Recomandati unui prieten</string>
<string name="prefs_feedback">Feedback</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="prefs_remember_last_share_location">Reține contribuie locația</string>
+ <string name="prefs_remember_last_upload_location_summary">Reține locația fișierului încărcat precedent</string>
<string name="recommend_subject">Încearcă %1$s pe smartphone-ul tău!</string>
+ <string name="recommend_text">Te invit sa folosești %1$s pe smartfonul tău!\nDescarcă aici: %2$s</string>
<string name="auth_check_server">Verificaţi Serverul</string>
<string name="auth_host_url">Adresa serverului https://...</string>
<string name="auth_username">Nume utilizator</string>
<string name="sync_fail_in_favourites_content">Conținutul a%1$d fișiere nu a putut fi sincronizat (conflicte %2$d)</string>
<string name="sync_foreign_files_forgotten_ticker">Unele fisiere locale au fost uitate</string>
<string name="sync_foreign_files_forgotten_content">%1$d fisiere din dosarul %2$s nu a putut fi copiat in</string>
+ <string name="sync_foreign_files_forgotten_explanation">Conform ediției 1.3.16, fișierele încărcate de pe această platformă sunt copiate în dosarul local %1$s pentru a preveni pierderi de date atunci cînd un singur fișier este sincronizat cu mai multe conturi.\n\nDin cauza acestei schimbări, toate fișierele încărcate în edițiile precedente ale acestui app au fost încărcate in dosarul %2$s. Însă acest proces nu fost completat in timpul sincronizării contului din cauza unei erori. Ai opțiunea de a lăsa fișierul intact (fișierele intacte) și de a transfera sursa în dosarul %3$s sau de a schimba locația fișierului(-elor) în dosarul %1$s și de a păstra sursa în %4$s.\n\nMai jos găsești enumerate fișierul local(fișierele locale) și fișierul separat(fișierele separate) în %5$s cu sursa respectivă.</string>
<string name="sync_current_folder_was_removed">Folderul %1$s nu mai există</string>
<string name="foreign_files_move">Muta tot/toate</string>
<string name="foreign_files_success">Toate fişierele au fost mutate</string>
<string name="auth_unsupported_multiaccount">%1$s nu suporta conturi multiple</string>
<string name="auth_fail_get_user_name">Server-ul dvs. nu întoarce un ID de utilizator corect, vă rugăm să contactați un administrator </string>
<string name="auth_can_not_auth_against_server">Nu se poate autentifica cu acest server</string>
+ <string name="auth_account_does_not_exist">Contul nu există încă în dispozitiv</string>
<string name="fd_keep_in_sync">Păstrează fișierul actualizat</string>
<string name="common_rename">Redenumește</string>
<string name="common_remove">Elimină</string>
<string name="preview_image_description">Previzualizare imagine</string>
<string name="preview_image_error_unknown_format">Aceasta imagine nu poate fi arătată</string>
<string name="error__upload__local_file_not_copied">%1$s nu a putut fi copiat in dosarul local %2$s </string>
+ <string name="prefs_instant_upload_path_title">Calea de încărcare</string>
<string name="share_link_no_support_share_api">Ne pare rău, partajarea nu este activată pe server. Vă rugăm să contactați administratorul dvs.</string>
<string name="share_link_file_error">A apărut o eroare în timp ce încerca să partajeze acest fișier sau folder</string>
<string name="unshare_link_file_error">A apărut o eroare în timp ce încerca să departajeze sau unshare acest fișier sau folder</string>
<string name="downloader_download_file_not_found">Fișierul nu mai este disponibil pe server</string>
<string name="prefs_category_accounts">Conturi</string>
<string name="prefs_add_account">Adaugă cont</string>
+ <string name="auth_redirect_non_secure_connection_title">Conexiunea securizată este redirecționată către un traseu neasigurat.</string>
+ <string name="actionbar_logger">Înregistrări</string>
+ <string name="log_send_history_button">Trimite Istoria</string>
+ <string name="log_send_no_mail_app">App-ul de trimitere a inregistrărilor nu a fost găsit. Instalează mail app-ul!</string>
+ <string name="log_send_mail_subject">%1$s înregistrările app-ului Android</string>
+ <string name="log_progress_dialog_text">Datele se încarcă...</string>
<string name="saml_authentication_required_text">Autentificare necesară</string>
<string name="saml_authentication_wrong_pass">Parolă greșită</string>
<string name="actionbar_move">Mutare</string>
<string name="file_list_empty_moving">Nu este nimic aici. Poți adăuga un director!</string>
<string name="folder_picker_choose_button_text">Alege</string>
+ <string name="move_file_not_found">Incapabil de trasfer. Verifică existența fișierului</string>
+ <string name="move_file_invalid_overwrite">Fișierul există deja în dosarul de destinație</string>
+ <string name="move_file_error">O eroare apare la transferarea acestui fișier sau dosar</string>
<string name="forbidden_permissions_move">pentru a muta acest fișier</string>
<string name="prefs_category_instant_uploading">Încărcări instante</string>
<string name="prefs_category_security">Securitate</string>
+ <string name="prefs_instant_video_upload_path_title">Calea de încărcare Video</string>
+ <string name="download_folder_failed_content">Descărcarea fișierului %1$s nu s-a finisat</string>
</resources>
<string name="prefs_instant_upload">Мгновенная загрузка фотографий</string>
<string name="prefs_instant_upload_summary">Немедленно загружать фотографии сделанные камерой</string>
<string name="prefs_instant_video_upload">Мгновенная загрузка видео</string>
- <string name="prefs_instant_video_upload_summary">Немедленно загружать видео сделанные камерой</string>
+ <string name="prefs_instant_video_upload_summary">Немедленно загружать видео, сделанные камерой</string>
<string name="prefs_log_title">Включить журналирование</string>
<string name="prefs_log_summary">Используется для регистрации ошибок</string>
<string name="prefs_log_title_history">Журнал</string>
<string name="uploader_btn_upload_text">Загрузить</string>
<string name="uploader_top_message">Выберите каталог для загрузки</string>
<string name="uploader_wrn_no_account_title">Учётная запись не найдена</string>
- <string name="uploader_wrn_no_account_text">На вашем устройстве нет учётных записей %1$s. Пожалуйста настройте учётную запись.</string>
+ <string name="uploader_wrn_no_account_text">На вашем устройстве нет учётных записей %1$s. Пожалуйста, настройте учётную запись.</string>
<string name="uploader_wrn_no_account_setup_btn_text">Настройка</string>
<string name="uploader_wrn_no_account_quit_btn_text">Выход</string>
<string name="uploader_wrn_no_content_title">Нет содержимого для загрузки</string>
<string name="auth_fail_get_user_name">Сервер вернул некорректный пользовательский идентификатор. Пожалуйста, свяжитесь с вашим администратором
⇥</string>
<string name="auth_can_not_auth_against_server">Невозможно авторизоваться на этом сервере</string>
+ <string name="auth_account_does_not_exist">Аккаунт не существует на устройстве ещё</string>
<string name="fd_keep_in_sync">Обновлять файл</string>
<string name="common_rename">Переименовать</string>
<string name="common_remove">Удалить</string>
<string name="share_link_file_error">При попытке поделиться этим файлом или каталогом произошла ошибка</string>
<string name="unshare_link_file_no_exist">Невозможно закрыть доступ. Убедитесь что файл существует</string>
<string name="unshare_link_file_error">При попытке закрыть доступ к этому файлу или каталогу произошла ошибка</string>
+ <string name="share_link_password_title">Введите пароль</string>
+ <string name="share_link_empty_password">Вы должны ввести пароль</string>
<string name="activity_chooser_send_file_title">Отправить</string>
<string name="copy_link">Копировать ссылку</string>
<string name="clipboard_text_copied">Скопировано в буфер обмена</string>
<string name="prefs_category_security">Безопасность</string>
<string name="prefs_instant_video_upload_path_title">Путь для загрузки Видео</string>
<string name="download_folder_failed_content">Загрузка папки %1$s не может быть завершена</string>
- <string name="shared_subject_header">общие</string>
- <string name="with_you_subject_header">с вами</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s предоставил вам доступ к \"%2$s\"</string>
</resources>
<string name="auth_fail_get_user_name">Váš server nevracia správne používateľské id, kontaktujte prosím správcu systému
</string>
<string name="auth_can_not_auth_against_server">Nie je možné vykonať autentifikáciu na server</string>
+ <string name="auth_account_does_not_exist">Účet zatiaľ v zariadení neexistuje</string>
<string name="fd_keep_in_sync">Udržiavať súbor aktuálny.</string>
<string name="common_rename">Premenuj</string>
<string name="common_remove">Odober</string>
<string name="prefs_category_security">Zabezpečenie</string>
<string name="prefs_instant_video_upload_path_title">Cesta pre nahrávanie videí</string>
<string name="download_folder_failed_content">Sťahovanie %1$s priečinka nebolo dokončené</string>
- <string name="shared_subject_header">zdieľané</string>
- <string name="with_you_subject_header">s vami</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s vám zdieľa „%2$s“</string>
</resources>
<string name="auth_fail_get_user_name">Strežnik ne vrača ustreznega ID uporabnika. Stopite v stik s skrbnikom sistema.
</string>
<string name="auth_can_not_auth_against_server">Ni mogoče preveriti pristnosti strežnika</string>
+ <string name="auth_account_does_not_exist">Račun na napravi še ne obstaja.</string>
<string name="fd_keep_in_sync">Datoteka naj bo posodobljena</string>
<string name="common_rename">Preimenuj</string>
<string name="common_remove">Odstrani</string>
<string name="share_link_file_error">Prišlo je do napake med poskusom omogočanja souporabe te datoteke ali mape</string>
<string name="unshare_link_file_no_exist">Ni mogoče prekiniti souporabe. Preverite, ali datoteka obstaja.</string>
<string name="unshare_link_file_error">Prišlo je do napake med poskusom odstranjevanja souporabe te datoteke ali mape</string>
+ <string name="share_link_password_title">Vpis gesla</string>
+ <string name="share_link_empty_password">Vpisati je treba geslo.</string>
<string name="activity_chooser_send_file_title">Pošlji</string>
<string name="copy_link">Kopiraj povezavo</string>
<string name="clipboard_text_copied">Kopirano v odložišče</string>
<string name="prefs_category_instant_uploading">Takojšnje pošiljanje v oblak</string>
<string name="prefs_category_security">Varnost</string>
<string name="prefs_instant_video_upload_path_title">Pot videa za pošiljanje</string>
- <string name="shared_subject_header">v souporabi</string>
- <string name="with_you_subject_header">z vami</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Imenika %1$s ni mogoče prejeti v celoti</string>
+ <string name="subject_token">Uporabnik %1$s je omogočil souporabo \"%2$s\".</string>
</resources>
<string name="saml_authentication_wrong_pass">Fjalëkalim i gabuar</string>
<string name="folder_picker_choose_button_text">Zgjidh</string>
<string name="prefs_category_security">Siguria</string>
- <string name="shared_subject_header">Ndarë</string>
</resources>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
+ <string name="about_android">%1$s Андроид апликација</string>
+ <string name="about_version">верзија %1$s</string>
+ <string name="actionbar_sync">Освежи налог</string>
<string name="actionbar_upload">Отпреми</string>
- <string name="actionbar_upload_from_apps">Садржај са других апликација</string>
- <string name="actionbar_upload_files">Датотеке</string>
+ <string name="actionbar_upload_from_apps">Садржај из других апликација</string>
+ <string name="actionbar_upload_files">Фајлови</string>
+ <string name="actionbar_open_with">Отвори помоћу</string>
+ <string name="actionbar_mkdir">Нова фасцикла</string>
<string name="actionbar_settings">Поставке</string>
+ <string name="actionbar_see_details">Детаљи</string>
<string name="actionbar_send_file">Пошаљи</string>
+ <string name="actionbar_sort">Разврстај</string>
+ <string name="actionbar_sort_title">Разврставање</string>
+ <string-array name="actionbar_sortby">
+ <item>A-Z</item>
+ <item>новији - старији</item>
+ </string-array>
<!--TODO re-enable when server-side folder size calculation is available
<item>Biggest - Smallest</item>-->
<string name="prefs_category_general">Опште</string>
<string name="prefs_category_more">Више</string>
<string name="prefs_accounts">Налози</string>
+ <string name="prefs_manage_accounts">Управљање налозима</string>
+ <string name="prefs_pincode">ПИБ апликације</string>
+ <string name="prefs_pincode_summary">Заштитите програм</string>
+ <string name="prefs_instant_upload">Тренутно отпремање фотографија</string>
+ <string name="prefs_instant_upload_summary">Тренутно отпремај фотографије сликане камером</string>
+ <string name="prefs_instant_video_upload">Тренутно отпремање видеа</string>
+ <string name="prefs_instant_video_upload_summary">Тренутно отпремај видео снимљен камером</string>
+ <string name="prefs_log_title">Укључи бележење</string>
+ <string name="prefs_log_summary">Ово се користи за бележење проблема</string>
+ <string name="prefs_log_title_history">Историјат бележења</string>
+ <string name="prefs_log_summary_history">Ово приказује сачуване записнике</string>
+ <string name="prefs_log_delete_history_button">Обриши историјат</string>
<string name="prefs_help">Помоћ</string>
+ <string name="prefs_recommend">Препоручи пријатељу</string>
+ <string name="prefs_feedback">Ваше мишљење</string>
+ <string name="prefs_imprint">Жиг</string>
+ <string name="prefs_remember_last_share_location">Упамти локацију дељења</string>
+ <string name="prefs_remember_last_upload_location_summary">Памти последњу локацију отпремања дељења</string>
+ <string name="recommend_subject">Пробајте %1$s на вашем телефону!</string>
+ <string name="recommend_text">Предлажем вам да пробате %1$s на вашем телефону!\nПреузмите овде: %2$s</string>
+ <string name="auth_check_server">Провери сервер</string>
+ <string name="auth_host_url">Адреса сервера https://…</string>
<string name="auth_username">Корисничко име</string>
<string name="auth_password">Лозинка</string>
+ <string name="auth_register">Нов вам је %1$s?</string>
<string name="sync_string_files">Фајлови</string>
- <string name="setup_btn_connect">Повежи ме</string>
+ <string name="setup_btn_connect">Повежи се</string>
<string name="uploader_btn_upload_text">Отпреми</string>
+ <string name="uploader_top_message">Изаберите фасциклу отпремања:</string>
<string name="uploader_wrn_no_account_title">Нема налога</string>
+ <string name="uploader_wrn_no_account_text">Нема %1$s налога на вашем уређају. Прво подесите налог.</string>
<string name="uploader_wrn_no_account_setup_btn_text">Подеси</string>
- <string name="uploader_wrn_no_account_quit_btn_text">Ð\98заÑ\92и</string>
+ <string name="uploader_wrn_no_account_quit_btn_text">Ð\9dапÑ\83Ñ\81Ñ\82и</string>
<string name="uploader_wrn_no_content_title">Нема садржаја за отпремање</string>
- <string name="uploader_wrn_no_content_text">Садржај није примљен. Нема ништа да се отпреми.</string>
+ <string name="uploader_wrn_no_content_text">Садржај није примљен. Нема шта да се отпреми.</string>
<string name="uploader_info_uploading">Отпремање</string>
- <string name="file_list_seconds_ago">пÑ\80е неколико секунди</string>
+ <string name="file_list_seconds_ago">пÑ\80е паÑ\80 секунди</string>
<string name="file_list_empty">Овде нема ничег. Отпремите нешто!</string>
- <string name="filedetails_select_file">Додирните датотеку ради приказа додатних информација.</string>
+ <string name="file_list_loading">Учитавам</string>
+ <string name="local_file_list_empty">Нема фајлова у овој фасцикли.</string>
+ <string name="filedetails_select_file">Тапните на фајл ради приказа додатних информација.</string>
<string name="filedetails_size">Величина:</string>
<string name="filedetails_type">Врста:</string>
- <string name="filedetails_created">Направљено:</string>
- <string name="filedetails_modified">Измењено:</string>
+ <string name="filedetails_created">Направљен:</string>
+ <string name="filedetails_modified">Измењен:</string>
<string name="filedetails_download">Преузми</string>
- <string name="filedetails_sync_file">Освежи датотеку</string>
+ <string name="filedetails_sync_file">Освежи фајл</string>
+ <string name="filedetails_renamed_in_upload_msg">Фајл је преименован у %1$s током отпремања</string>
+ <string name="action_share_file">Веза дељења</string>
+ <string name="action_unshare_file">Не дели везом</string>
<string name="common_yes">Да</string>
<string name="common_no">Не</string>
<string name="common_ok">У реду</string>
- <string name="common_cancel_download">Обустави преузимање</string>
- <string name="common_cancel_upload">Ð\9fÑ\80екини Ñ\81лање</string>
+ <string name="common_cancel_download">Откажи преузимање</string>
+ <string name="common_cancel_upload">Ð\9eÑ\82кажи оÑ\82пÑ\80емање</string>
<string name="common_cancel">Откажи</string>
<string name="common_save_exit">Сачувај и изађи</string>
<string name="common_error">Грешка</string>
+ <string name="common_loading">Учитавам...</string>
+ <string name="common_error_unknown">Непозната грешка</string>
<string name="about_title">О програму</string>
<string name="change_password">Измени лозинку</string>
<string name="delete_account">Обриши налог</string>
<string name="create_account">Отвори налог</string>
<string name="upload_chooser_title">Отпреми из…</string>
+ <string name="uploader_info_dirname">Назив фасцикле</string>
<string name="uploader_upload_in_progress_ticker">Отпремам…</string>
<string name="uploader_upload_in_progress_content">%1$d%% Отпремам %2$s</string>
<string name="uploader_upload_succeeded_ticker">Отпремање је успело</string>
+ <string name="uploader_upload_succeeded_content_single">%1$s је успешно отпремљен</string>
<string name="uploader_upload_failed_ticker">Отпремање није успело</string>
- <string name="uploader_upload_failed_content_single">Не могу да довршим отпремање датотеке %1$s</string>
+ <string name="uploader_upload_failed_content_single">Не могу да довршим отпремање %1$s</string>
+ <string name="uploader_upload_failed_credentials_error">Отпремање неуспешно. Поново се пријавите.</string>
<string name="downloader_download_in_progress_ticker">Преузимам…</string>
<string name="downloader_download_in_progress_content">%1$d%% Преузимам %2$s</string>
<string name="downloader_download_succeeded_ticker">Преузимање успешно</string>
<string name="downloader_download_succeeded_content">%1$s је успешно преузет</string>
<string name="downloader_download_failed_ticker">Преузимање није успело</string>
- <string name="downloader_download_failed_content">Не могу да довршим преузимање датотеке %1$s</string>
+ <string name="downloader_download_failed_content">Не могу да довршим преузимање %1$s</string>
<string name="downloader_not_downloaded_yet">Још увек није преузето</string>
- <string name="common_choose_account">Изабери налог</string>
+ <string name="downloader_download_failed_credentials_error">Преузимање неуспешно. Пријавите се поново</string>
+ <string name="common_choose_account">Изаберите налог</string>
<string name="sync_fail_ticker">Синхронизовање није успело</string>
- <string name="sync_fail_content">Не могу да довршим синхронизацију датотеке %1$s</string>
- <string name="foreign_files_success">Све датотеке су померене</string>
- <string name="foreign_files_fail">Неке датотеке нису могле бити померене</string>
- <string name="pincode_enter_pin_code">Унесите PIN апликације</string>
- <string name="pincode_configure_your_pin_explanation">Са сваким покретањем апликације мораћете да унесете PIN</string>
+ <string name="sync_fail_ticker_unauthorized">Синхронизовање неуспешно. Пријавите се поново</string>
+ <string name="sync_fail_content">Не могу да довршим синхронизацију %1$s</string>
+ <string name="sync_fail_content_unauthorized">Неисправна лозинка за %1$s</string>
+ <string name="sync_conflicts_in_favourites_ticker">Постоје сукоби</string>
+ <string name="sync_foreign_files_forgotten_explanation">Од верзије 1.3.16, фајлови отпремљени са уређаја се копирају у локалну фасциклу %1$s да би се спречио губитак података када се исти фајл синхронизује са више налога.\n\nЗбог ове измене, сви фајлови отпремљени са претходним верзијама ове апликације се копирају у фасциклу %2$s. Међутим, грешка је онемогућила довршавање ове радње током синхронизације налога. Можете или оставити фајлове како јесу и уклонити линк до %3$s или преместити фајлове у фасциклу %1$s и задржати везу до %4$s.\n\nИспод су наведени локални фајлови и удаљени фајлови у %5$s са којима су повезани.</string>
+ <string name="sync_current_folder_was_removed">Фасцикла %1$s више не постоји</string>
+ <string name="foreign_files_move">Премести све</string>
+ <string name="foreign_files_success">Сви фајлови су премештени</string>
+ <string name="foreign_files_fail">Неки фајлови нису могли бити премештени</string>
+ <string name="foreign_files_local_text">Локално: %1$s</string>
+ <string name="foreign_files_remote_text">Удаљено: %1$s</string>
+ <string name="upload_query_move_foreign_files">Нема довољно простора да би се изабрани фајлови копирали у фасциклу %1$s. Желите ли да их преместите? </string>
+ <string name="pincode_enter_pin_code">Унесите ПИБ апликације</string>
+ <string name="pincode_configure_your_pin">Унесите ПИБ за апликацију</string>
+ <string name="pincode_configure_your_pin_explanation">Са сваким покретањем апликације мораћете да унесете ПИБ</string>
+ <string name="pincode_reenter_your_pincode">Унесите ПИБ поново</string>
+ <string name="pincode_remove_your_pincode">Уклоните ПИБ апликације</string>
+ <string name="pincode_mismatch">Бројеви се не поклапају</string>
+ <string name="pincode_wrong">Неисправан ПИБ</string>
+ <string name="pincode_removed">ПИБ је уклоњен</string>
+ <string name="pincode_stored">ПИБ је упамћен</string>
+ <string name="media_notif_ticker">%1$s музички плејер</string>
+ <string name="media_state_playing">%1$s (пуштам)</string>
+ <string name="media_state_loading">%1$s (учитавам)</string>
+ <string name="media_event_done">%1$s пуштање завршено</string>
+ <string name="media_err_nothing_to_play">Нема медијских фајлова</string>
+ <string name="media_err_no_account">Није наведен налог</string>
+ <string name="media_err_not_in_owncloud">Фајл није у исправном налогу</string>
+ <string name="media_err_unsupported">Неподржан кодек</string>
+ <string name="media_err_io">Медијски фајл се не може читати</string>
+ <string name="media_err_malformed">Медијски фајл није исправно кодиран</string>
+ <string name="media_err_timeout">Време истекло у покушавању пуштања</string>
+ <string name="media_err_invalid_progressive_playback">Медијски фајл се не може пустити</string>
+ <string name="media_err_unknown">Медијски фајл се не може пустити са фабричким плејером</string>
+ <string name="media_err_security_ex">Безбедносна грешка при покушају пуштања %1$s</string>
+ <string name="media_err_io_ex">Улазна грешка при покушају пуштања %1$s</string>
+ <string name="media_err_unexpected">Неочекивана грешка при покушају пуштања %1$s</string>
+ <string name="media_rewind_description">Уназад</string>
+ <string name="media_play_pause_description">Пуштање-пауза</string>
+ <string name="media_forward_description">Унапред</string>
+ <string name="auth_getting_authorization">Тражим ауторизацију...</string>
+ <string name="auth_trying_to_login">Покушавам пријављивање...</string>
<string name="auth_no_net_conn_title">Нема мрежне везе</string>
<string name="auth_nossl_plain_ok_title">Безбедна веза није доступна.</string>
<string name="auth_connection_established">Веза је успостављена</string>
- <string name="auth_unknown_error_title">Дошло је до непознате грешке.</string>
+ <string name="auth_testing_connection">Тестирам везу...</string>
+ <string name="auth_not_configured_title">Лоше подешавање сервера</string>
+ <string name="auth_account_not_new">Налог са истим корисником и сервером већ постоји на уређају</string>
+ <string name="auth_account_not_the_same">Унесени корисник се не поклапа са корисником овог налога</string>
+ <string name="auth_unknown_error_title">Дошло је до непознате грешке!</string>
<string name="auth_unknown_host_title">Не могу да пронађем домаћина</string>
<string name="auth_incorrect_path_title">Не могу да пронађем примерак сервера</string>
<string name="auth_timeout_title">Серверу је требало предуго да се одазове</string>
<string name="auth_incorrect_address_title">Погрешно уобличена адреса</string>
- <string name="auth_ssl_general_error_title">Покретање SSL-а није успело</string>
+ <string name="auth_ssl_general_error_title">ССЛ иницијализација није успела</string>
+ <string name="auth_ssl_unverified_server_title">Не могу да проверим ССЛ идентитет сервера</string>
+ <string name="auth_bad_oc_version_title">Непозната верзија сервера</string>
<string name="auth_wrong_connection_title">Не могу да успоставим везу</string>
<string name="auth_secure_connection">Безбедна веза је успостављена</string>
- <string name="fd_keep_in_sync">Редовно ажурирај датотеку</string>
+ <string name="auth_unauthorized">Погрешно име или лозинка</string>
+ <string name="auth_oauth_error">Неуспешна ауторизација</string>
+ <string name="auth_oauth_error_access_denied">Сервер ауторизације је одбио приступ</string>
+ <string name="auth_wtf_reenter_URL">Неочекивано стање. Унесите поново адресу сервера</string>
+ <string name="auth_expired_oauth_token_toast">Ауторизација је истекла. Урадите је поново</string>
+ <string name="auth_expired_basic_auth_toast">Унесите тренутну лозинку</string>
+ <string name="auth_expired_saml_sso_token_toast">Сесија је истекла. Повежите се поново</string>
+ <string name="auth_connecting_auth_server">Повезујем се на сервер аутентификације...</string>
+ <string name="auth_unsupported_auth_method">Сервер не подржава овај начин аутентификације</string>
+ <string name="auth_unsupported_multiaccount">%1$s не подржава вишеструке налоге</string>
+ <string name="auth_fail_get_user_name">Сервер не враћа исправан ИД корисника. Контактирајте администратора
+ </string>
+ <string name="auth_can_not_auth_against_server">Не могу да аутентификујем са овим сервером</string>
+ <string name="auth_account_does_not_exist">Не постоји налог на уређају</string>
+ <string name="fd_keep_in_sync">Редовно ажурирај фајл</string>
<string name="common_rename">Преименуј</string>
<string name="common_remove">Уклони</string>
+ <string name="confirmation_remove_alert">Желите да уклоните %1$s?</string>
+ <string name="confirmation_remove_folder_alert">Желите да уклоните %1$s и њен садржај?</string>
<string name="confirmation_remove_local">Само локално</string>
+ <string name="confirmation_remove_folder_local">Само локални садржај</string>
<string name="confirmation_remove_remote">Уклони са сервера</string>
<string name="confirmation_remove_remote_and_local">Удаљено и локално</string>
- <string name="rename_dialog_title">Унесите ново име</string>
+ <string name="remove_success_msg">Уклањање успешно</string>
+ <string name="remove_fail_msg">Уклањање неуспешно</string>
+ <string name="rename_dialog_title">Унесите нов назив</string>
+ <string name="rename_local_fail_msg">Локална копија се не може преименовати. Покушајте други назив</string>
<string name="rename_server_fail_msg">Не могу да довршим преименовање</string>
- <string name="sync_file_fail_msg">Удаљена датотека се не може проверити</string>
+ <string name="sync_file_fail_msg">Удаљени фајл се не може проверити</string>
+ <string name="sync_file_nothing_to_do_msg">Садржај је већ синхронизован</string>
+ <string name="create_dir_fail_msg">Фасцикла се не може направити</string>
+ <string name="filename_forbidden_characters">Забрањени знакови: / \\ < > : \" | ? *</string>
+ <string name="filename_empty">Назив фајла не може бити празан</string>
<string name="wait_a_moment">Сачекајте тренутак</string>
- <string name="filedisplay_no_file_selected">Нисте изабрали датотеку</string>
+ <string name="filedisplay_unexpected_bad_get_content">Неочекивани проблем. Изаберите фајл другом апликацијом</string>
+ <string name="filedisplay_no_file_selected">Нисте изабрали фајл</string>
+ <string name="activity_chooser_title">Пошаљи везу ...</string>
+ <string name="oauth_check_onoff">Пријави се помоћу „oAuth2“</string>
+ <string name="oauth_login_connection">Повезујем се на „oAuth2“ сервер...</string>
<string name="ssl_validator_header">Не могу да проверим идентитет сајта</string>
- <string name="ssl_validator_reason_cert_not_trusted">â\80\93 СеÑ\80Ñ\82иÑ\84икаÑ\82 Ñ\81еÑ\80веÑ\80а ниÑ\98е повеÑ\80Ñ\99ив</string>
+ <string name="ssl_validator_reason_cert_not_trusted">â\80\93 СеÑ\80Ñ\82иÑ\84икаÑ\82 Ñ\81еÑ\80веÑ\80а ниÑ\98е од повеÑ\80еÑ\9aа</string>
<string name="ssl_validator_reason_cert_expired">– Сертификат сервера је истекао</string>
+ <string name="ssl_validator_reason_cert_not_yet_valid">- Датуми важења сертификата су у будућности</string>
<string name="ssl_validator_reason_hostname_not_verified">– Адреса се не поклапа са именом домаћина у сертификату</string>
- <string name="ssl_validator_question">Ð\96елиÑ\82е ли ипак да ознаÑ\87иÑ\82е Ñ\81еÑ\80Ñ\82иÑ\84икаÑ\82 као повеÑ\80Ñ\99ив?</string>
+ <string name="ssl_validator_question">Ð\96елиÑ\82е ли ипак да веÑ\80Ñ\83Ñ\98еÑ\82е Ñ\81еÑ\80Ñ\82иÑ\84икаÑ\82Ñ\83?</string>
<string name="ssl_validator_not_saved">Не могу да сачувам сертификат</string>
<string name="ssl_validator_btn_details_see">Подаци</string>
<string name="ssl_validator_btn_details_hide">Сакриј</string>
<string name="ssl_validator_label_subject">Издато за:</string>
- <string name="ssl_validator_label_issuer">Ð\98здао/ла:</string>
+ <string name="ssl_validator_label_issuer">Ð\98здаваÑ\87:</string>
<string name="ssl_validator_label_CN">Уобичајено име:</string>
<string name="ssl_validator_label_O">Организација:</string>
<string name="ssl_validator_label_OU">Организациона јединица:</string>
- <string name="ssl_validator_label_C">Ð\97емÑ\99а:</string>
- <string name="ssl_validator_label_ST">Ð\94Ñ\80жава:</string>
+ <string name="ssl_validator_label_C">Ð\94Ñ\80жава:</string>
+ <string name="ssl_validator_label_ST">Ð\9fокÑ\80аÑ\98ина:</string>
<string name="ssl_validator_label_L">Локација:</string>
<string name="ssl_validator_label_validity">Ваљаност:</string>
<string name="ssl_validator_label_validity_from">Од:</string>
<string name="ssl_validator_label_validity_to">За:</string>
<string name="ssl_validator_label_signature">Потпис:</string>
<string name="ssl_validator_label_signature_algorithm">Алгоритам:</string>
+ <string name="ssl_validator_null_cert">Сертификат се не може приказати.</string>
+ <string name="ssl_validator_no_info_about_error">- Нема података о грешци</string>
+ <string name="placeholder_sentence">Ово је местодржач</string>
+ <string name="placeholder_filename">чувамместо.txt</string>
+ <string name="placeholder_filetype">ПНГ слика</string>
+ <string name="placeholder_filesize">389 KB</string>
+ <string name="placeholder_timestamp">2012/05/18 12:23 ПоП</string>
+ <string name="placeholder_media_time">12:23:45</string>
<string name="instant_upload_on_wifi">Отпремај слике само путем бежичне мреже</string>
+ <string name="instant_video_upload_on_wifi">Отпремај видео само путем бежичне мреже</string>
<string name="conflict_title">Ажурирај сукоб</string>
+ <string name="conflict_message">Удаљени фајл %s није синхронизован са локалним. Ако наставите, заменићете фајл на серверу.</string>
+ <string name="conflict_keep_both">Задржи оба</string>
+ <string name="conflict_overwrite">Пребриши</string>
+ <string name="conflict_dont_upload">Не отпремај</string>
+ <string name="preview_image_description">Преглед слике</string>
+ <string name="preview_image_error_unknown_format">Слика се не може приказати</string>
+ <string name="error__upload__local_file_not_copied">%1$s се не може копирати у локалну фасциклу %2$s</string>
+ <string name="prefs_instant_upload_path_title">Путања отпремања</string>
+ <string name="share_link_no_support_share_api">Дељење није укључено на вашем серверу. Контактирајте
+ администратора.</string>
+ <string name="share_link_file_no_exist">Не могу да делим. Проверите да ли фајл постоји</string>
+ <string name="share_link_file_error">Дошло је до грешке приликом покушаја дељења овог фајла или фасцикле</string>
+ <string name="unshare_link_file_no_exist">Не могу да прекинем дељење. Проверите да ли фајл постоји</string>
+ <string name="unshare_link_file_error">Дошло је до грешке приликом покушаја укидања дељења овог фајла или фасцикле</string>
+ <string name="share_link_password_title">Унесите лозинку</string>
+ <string name="share_link_empty_password">Морате унети лозинку</string>
<string name="activity_chooser_send_file_title">Пошаљи</string>
+ <string name="copy_link">Копирај везу</string>
+ <string name="clipboard_text_copied">Копирано у клипборд</string>
+ <string name="error_cant_bind_to_operations_service">Критична грешка: не могу да радим</string>
+ <string name="network_error_socket_exception">Дошло је до грешке при повезивању са сервером.</string>
+ <string name="network_error_socket_timeout_exception">Дошло је до грешке при чекању на сервер. Радња није могла бити урађена</string>
+ <string name="network_error_connect_timeout_exception">Дошло је до грешке при чекању на сервер. Радња није могла бити урађена</string>
+ <string name="network_host_not_available">Радња није могла бити довршена. Сервер је недоступан</string>
<string name="empty"></string>
+ <string name="forbidden_permissions">Немате дозволу %s</string>
+ <string name="forbidden_permissions_rename">да преименујете овај фајл</string>
+ <string name="forbidden_permissions_delete">да обришете овај фајл</string>
+ <string name="share_link_forbidden_permissions">да делите овај фајл</string>
+ <string name="unshare_link_forbidden_permissions">да укинете дељење овог фајла</string>
+ <string name="forbidden_permissions_create">да направите фајл</string>
+ <string name="uploader_upload_forbidden_permissions">да отпремате у ову фасциклу</string>
+ <string name="downloader_download_file_not_found">Фајл није више доступан на серверу</string>
<string name="prefs_category_accounts">Налози</string>
+ <string name="prefs_add_account">Додај налог</string>
+ <string name="auth_redirect_non_secure_connection_title">Безбедна веза је преусмерена на небезбедну руту</string>
+ <string name="actionbar_logger">Записници</string>
+ <string name="log_send_history_button">Историјат слања</string>
+ <string name="log_send_no_mail_app">Нема начина за слање записника. Инсталирајте апликацију е-поште!</string>
+ <string name="log_send_mail_subject">Записници %1$s Андроид апликације</string>
+ <string name="log_progress_dialog_text">Учитавам податке...</string>
+ <string name="saml_authentication_required_text">Неопходна провера идентитета</string>
+ <string name="saml_authentication_wrong_pass">Погрешна лозинка</string>
+ <string name="actionbar_move">Премести</string>
+ <string name="file_list_empty_moving">Овде нема ничега. Можете додати фасциклу!</string>
<string name="folder_picker_choose_button_text">Одабери</string>
+ <string name="move_file_not_found">Не могу да преместим. Проверите да ли фајл постоји</string>
+ <string name="move_file_invalid_into_descendent">Није могуће преместити фасциклу у њену потфасциклу</string>
+ <string name="move_file_invalid_overwrite">Фајл већ постоји у одредишној фасцикли</string>
+ <string name="move_file_error">Дошло је до грешке при премештању фајла или фасцикле</string>
+ <string name="forbidden_permissions_move">да преместите овај фајл</string>
+ <string name="prefs_category_instant_uploading">Тренутна отпремања</string>
<string name="prefs_category_security">Безбедност</string>
+ <string name="prefs_instant_video_upload_path_title">Путања отпремања видеа</string>
+ <string name="download_folder_failed_content">Преузимање фасцикле %1$s не може бити довршено</string>
+ <string name="subject_token">%1$s подели „%2$s“ са вама</string>
</resources>
<string name="prefs_category_instant_uploading">Direktuppladning</string>
<string name="prefs_category_security">Säkerhet</string>
<string name="prefs_instant_video_upload_path_title">Uppladdnings-sökväg för video</string>
- <string name="shared_subject_header">delad</string>
- <string name="with_you_subject_header">med dig</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
</resources>
<string name="file_list_loading">Yükleniyor...</string>
<string name="local_file_list_empty">Bu klasörde dosya yok.</string>
<string name="file_list_folder">klasör</string>
- <string name="file_list_folders">klasörler</string>
+ <string name="file_list_folders">klasör</string>
<string name="file_list_file">dosya</string>
- <string name="file_list_files">dosyalar</string>
+ <string name="file_list_files">dosya</string>
<string name="filedetails_select_file">Ek bilgileri görmek için dosyaya dokunun.</string>
<string name="filedetails_size">Boyut:</string>
<string name="filedetails_type">Tür:</string>
<string name="auth_fail_get_user_name">Sunucunuz geçerli bir kullanıcı kimliği döndürmüyor, lütfen yöneticinizle iletişime geçin
</string>
<string name="auth_can_not_auth_against_server">Bu sunucuya karşı kimlik doğrulama yapılamaz</string>
+ <string name="auth_account_does_not_exist">Hesap henüz cihazda mevcut değil</string>
<string name="fd_keep_in_sync">Dosyayı güncel tut</string>
<string name="common_rename">Yeniden adlandır</string>
<string name="common_remove">Kaldır</string>
<string name="share_link_file_error">Bu dosya veya klasörü paylaşmaya çalışılırken bir hata oluştu</string>
<string name="unshare_link_file_no_exist">Paylaşımı kaldırma başarısız. Lütfen dosyanın mevcut olup olmadığını denetleyin</string>
<string name="unshare_link_file_error">Bu dosya veya klasör paylaşımı kaldırılmaya çalışılırken bir hata oluştu</string>
+ <string name="share_link_password_title">Bir parola girin</string>
+ <string name="share_link_empty_password">Bir parola girmelisiniz</string>
<string name="activity_chooser_send_file_title">Gönder</string>
<string name="copy_link">Bağlantıyı kopyala</string>
<string name="clipboard_text_copied">Panoya kopyalandı</string>
<string name="prefs_category_security">Güvenlik</string>
<string name="prefs_instant_video_upload_path_title">Video Yükleme Yolu</string>
<string name="download_folder_failed_content">%1$s klasörün indirilmesi tamamlanamadı</string>
- <string name="shared_subject_header">paylaşılan</string>
- <string name="with_you_subject_header">sizinle</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s sizinle \"%2$s\" paylaşımını yaptı</string>
</resources>
<string name="empty"></string>
<string name="prefs_category_accounts">ھېساباتلار</string>
<string name="prefs_category_security">بىخەتەرلىك</string>
- <string name="shared_subject_header">ھەمبەھىرلەنگەن</string>
</resources>
<string name="uploader_upload_failed_ticker">Помилка завантаження</string>
<string name="uploader_upload_failed_content_single">Завантаження %1$s не може завершитись</string>
<string name="uploader_upload_failed_credentials_error">Завантажити не вдалося, необхідно повторити вхід</string>
- <string name="downloader_download_in_progress_ticker">Ð\97качування …</string>
+ <string name="downloader_download_in_progress_ticker">Скачування …</string>
<string name="downloader_download_in_progress_content">%1$d%% Зкачування %2$s</string>
<string name="downloader_download_succeeded_ticker">Успішно зкачано</string>
<string name="downloader_download_succeeded_content">%1$s успішно завантажено</string>
<string name="prefs_category_instant_uploading">Миттєво завантаження</string>
<string name="prefs_category_security">Безпека</string>
<string name="prefs_instant_video_upload_path_title">Шлях завантаження відео</string>
- <string name="shared_subject_header">спільне</string>
- <string name="with_you_subject_header">з тобою</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">Скачування теки %1$s не може бути завершено</string>
</resources>
<string name="prefs_category_instant_uploading">即时上传</string>
<string name="prefs_category_security">安全</string>
<string name="prefs_instant_video_upload_path_title">视频上传路径</string>
- <string name="shared_subject_header">分享</string>
+ <string name="download_folder_failed_content">%1$s 文件夹的下载无法完成</string>
</resources>
<string name="prefs_category_instant_uploading">即時上傳</string>
<string name="prefs_category_security">安全性</string>
<string name="prefs_instant_video_upload_path_title">影片上傳路徑</string>
- <string name="shared_subject_header">以分享的</string>
- <string name="with_you_subject_header">與你</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="download_folder_failed_content">%1$s 目錄的下載未完成</string>
</resources>
<!--
ownCloud Android client application
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
<color name="filelist_icon_backgorund">#DDDDDD</color>
<color name="owncloud_blue_bright">#00ddff</color>
<color name="list_item_lastmod_and_filesize_text">#989898</color>
+ <color name="textColor">#303030</color>
+ <color name="list_divider_background">#fff0f0f0</color>
</resources>
\ No newline at end of file
-->
<resources>
<dimen name="file_icon_size">32dp</dimen>
+ <dimen name="file_icon_size_grid">128dp</dimen>
</resources>
<string name="auth_fail_get_user_name">Your server is not returning a correct user id, please contact an administrator
</string>
<string name="auth_can_not_auth_against_server">Cannot authenticate against this server</string>
+ <string name="auth_account_does_not_exist">Account does not exist in the device yet</string>
<string name="fd_keep_in_sync">Keep file up to date</string>
<string name="common_rename">Rename</string>
<string name="share_link_file_error">An error occurred while trying to share this file or folder</string>
<string name="unshare_link_file_no_exist">Unable to unshare. Please check whether the file exists</string>
<string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
+ <string name="share_link_password_title">Enter a password</string>
+ <string name="share_link_empty_password">You must enter a password</string>
<string name="activity_chooser_send_file_title">Send</string>
<string name="prefs_instant_video_upload_path_title">Upload Video Path</string>
<string name="download_folder_failed_content">Download of %1$s folder could not be completed</string>
- <string name="shared_subject_header">shared</string>
- <string name="with_you_subject_header">with you</string>
- <string name="subject_token">%1$s %2$s >>%3$s<< %4$s</string>
+ <string name="subject_token">%1$s shared \"%2$s\" with you</string>
</resources>
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
ownCloud Android client application
Copyright (C) 2012 Bartek Przybylski
- Copyright (C) 2012-2013 ownCloud Inc.
+ Copyright (C) 2015 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,
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* Contains methods to build the "static" strings. These strings were before constants in different
* classes
- *
- * @author masensio
- * @author David A. Velasco
*/
public class MainApp extends Application {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
* 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
+ * TODO - review completeness
*/
public class AccountAuthenticator extends AbstractAccountAuthenticator {
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 confirmCredentials(AccountAuthenticatorResponse response,
+ Account account, Bundle options) throws NetworkErrorException {
+ try {
+ validateAccountType(account.type);
+ } catch (AuthenticatorException e) {
+ Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
+ + e.getMessage());
+ e.printStackTrace();
+ return e.getFailureBundle();
+ }
+ Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+ intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
+ response);
+ intent.putExtra(KEY_ACCOUNT, account);
+ intent.putExtra(KEY_LOGIN_OPTIONS, options);
+
+ setIntentFlags(intent);
+
+ Bundle resultBundle = new Bundle();
+ resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);
+ return resultBundle;
+ }
+
+ @Override
+ public Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType) {
+ return null;
+ }
+
/**
* {@inheritDoc}
*/
}
}
- private void validateAuthTokenType(String authTokenType)\r
- throws UnsupportedAuthTokenTypeException {\r
- if (!authTokenType.equals(MainApp.getAuthTokenType()) &&\r
- !authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType())) &&\r
- !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType())) &&\r
+ private void validateAuthTokenType(String authTokenType)
+ throws UnsupportedAuthTokenTypeException {
+ if (!authTokenType.equals(MainApp.getAuthTokenType()) &&
+ !authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType())) &&
+ !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType())) &&
!authTokenType.equals(AccountTypeUtils.getAuthTokenTypeRefreshToken(MainApp.getAccountType())) &&
- !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()))) {\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
+ !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()))) {
+ throw new UnsupportedAuthTokenTypeException();
+ }
+ }
+
+ public static class AuthenticatorException extends Exception {
+ private static final long serialVersionUID = 1L;
+ private Bundle mFailureBundle;
+
+ public AuthenticatorException(int code, String errorMsg) {
+ mFailureBundle = new Bundle();
+ mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);
+ mFailureBundle
+ .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
+ }
+
+ public Bundle getFailureBundle() {
+ return mFailureBundle;
+ }
+ }
+
+ public static class UnsupportedAccountTypeException extends
+ AuthenticatorException {
+ private static final long serialVersionUID = 1L;
+
+ public UnsupportedAccountTypeException() {
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "Unsupported account type");
+ }
+ }
+
+ public static class UnsupportedAuthTokenTypeException extends
+ AuthenticatorException {
+ private static final long serialVersionUID = 1L;
+
+ public UnsupportedAuthTokenTypeException() {
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "Unsupported auth token type");
+ }
+ }
+
+ public static class UnsupportedFeaturesException extends
+ AuthenticatorException {
+ public static final long serialVersionUID = 1L;
+
+ public UnsupportedFeaturesException() {
+ super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "Unsupported features");
+ }
+ }
+
+ public static class AccessDeniedException extends AuthenticatorException {
+ public AccessDeniedException(int code, String errorMsg) {
+ super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ }
+}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
* Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
+ * @author masensio\r
* Copyright (C) 2012 Bartek Przybylski\r
- * Copyright (C) 2012-2014 ownCloud Inc.\r
+ * Copyright (C) 2015 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
import com.owncloud.android.MainApp;\r
import com.owncloud.android.R;\r
import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;\r
+import com.owncloud.android.lib.common.OwnCloudCredentials;\r
+import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;\r
import com.owncloud.android.lib.common.accounts.AccountTypeUtils;\r
+import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;\r
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;\r
import com.owncloud.android.lib.common.network.CertificateCombinedException;\r
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;\r
import com.owncloud.android.lib.common.operations.RemoteOperationResult;\r
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;\r
import com.owncloud.android.lib.common.utils.Log_OC;\r
-import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;\r
import com.owncloud.android.lib.resources.status.OwnCloudVersion;\r
import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;\r
import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod;\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
- * @author masensio\r
*/\r
public class AuthenticatorActivity extends AccountAuthenticatorActivity\r
-implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener, \r
-SsoWebViewClientListener, OnSslUntrustedCertListener {\r
+ implements OnRemoteOperationListener, OnFocusChangeListener, OnEditorActionListener,\r
+ SsoWebViewClientListener, OnSslUntrustedCertListener,\r
+ AuthenticatorAsyncTask.OnAuthenticatorTaskListener {\r
\r
private static final String TAG = AuthenticatorActivity.class.getSimpleName();\r
\r
private static final String CREDENTIALS_DIALOG_TAG = "CREDENTIALS_DIALOG";\r
private static final String KEY_AUTH_IS_FIRST_ATTEMPT_TAG = "KEY_AUTH_IS_FIRST_ATTEMPT";\r
\r
+ private static final String KEY_USERNAME = "USERNAME";\r
+ private static final String KEY_PASSWORD = "PASSWORD";\r
+ private static final String KEY_ASYNC_TASK_IN_PROGRESS = "AUTH_IN_PROGRESS";\r
\r
/// parameters from EXTRAs in starter Intent\r
private byte mAction;\r
private int mAuthStatusText = 0, mAuthStatusIcon = 0;\r
\r
private String mAuthToken = "";\r
+ private AuthenticatorAsyncTask mAsyncTask;\r
\r
private boolean mIsFirstAuthAttempt;\r
-\r
\r
/// Identifier of operation in progress which result shouldn't be lost \r
private long mWaitingForOpId = Long.MAX_VALUE;\r
\r
- \r
+ private final String BASIC_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());\r
+ private final String OAUTH_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());\r
+ private final String SAML_TOKEN_TYPE =\r
+ AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());\r
+\r
+\r
/**\r
* {@inheritDoc}\r
* \r
setContentView(R.layout.account_setup);\r
\r
/// initialize general UI elements\r
- initOverallUi(savedInstanceState);\r
+ initOverallUi();\r
\r
mOkButton = findViewById(R.id.buttonOK);\r
\r
\r
private String chooseAuthTokenType(boolean oauth, boolean saml) {\r
if (saml) {\r
- return AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());\r
+ return SAML_TOKEN_TYPE;\r
} else if (oauth) {\r
- return AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());\r
+ return OAUTH_TOKEN_TYPE;\r
} else {\r
- return AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());\r
+ return BASIC_TOKEN_TYPE;\r
}\r
}\r
\r
\r
/**\r
* Configures elements in the user interface under direct control of the Activity.\r
- * \r
- * @param savedInstanceState Saved activity state, as in {{@link #onCreate(Bundle)}\r
*/\r
- private void initOverallUi(Bundle savedInstanceState) {\r
+ private void initOverallUi() {\r
\r
/// step 1 - load and process relevant inputs (resources, intent, savedInstanceState)\r
boolean isWelcomeLinkVisible = getResources().getBoolean(R.bool.show_welcome_link);\r
if (\r
AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(\r
MainApp.getAccountType()\r
- ).equals(mAuthTokenType) &&\r
- mHostUrlInput.hasFocus()\r
- ) {\r
+ ).equals(mAuthTokenType) &&\r
+ mHostUrlInput.hasFocus()\r
+ ) {\r
checkOcServer();\r
}\r
}\r
* intended to defer the processing of the redirection caught in \r
* {@link #onNewIntent(Intent)} until {@link #onResume()} \r
* \r
- * See {@link #loadSavedInstanceState(Bundle)}\r
+ * See {@link #onSaveInstanceState(Bundle)}\r
*/\r
@Override\r
protected void onSaveInstanceState(Bundle outState) {\r
/// authentication\r
outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt);\r
\r
+ /// AsyncTask (User and password)\r
+ outState.putString(KEY_USERNAME, mUsernameInput.getText().toString());\r
+ outState.putString(KEY_PASSWORD, mPasswordInput.getText().toString());\r
+\r
+ if (mAsyncTask != null) {\r
+ mAsyncTask.cancel(true);\r
+ outState.putBoolean(KEY_ASYNC_TASK_IN_PROGRESS, true);\r
+ } else {\r
+ outState.putBoolean(KEY_ASYNC_TASK_IN_PROGRESS, false);\r
+ }\r
+ mAsyncTask = null;\r
+\r
//Log_OC.wtf(TAG, "onSaveInstanceState end" );\r
}\r
\r
+ @Override\r
+ public void onRestoreInstanceState(Bundle savedInstanceState) {\r
+ super.onRestoreInstanceState(savedInstanceState);\r
+\r
+ // AsyncTask\r
+ boolean inProgress = savedInstanceState.getBoolean(KEY_ASYNC_TASK_IN_PROGRESS);\r
+ if (inProgress){\r
+ String username = savedInstanceState.getString(KEY_USERNAME);\r
+ String password = savedInstanceState.getString(KEY_PASSWORD);\r
+\r
+ OwnCloudCredentials credentials = null;\r
+ if (BASIC_TOKEN_TYPE.equals(mAuthTokenType)) {\r
+ credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);\r
+\r
+ } else if (OAUTH_TOKEN_TYPE.equals(mAuthTokenType)) {\r
+ credentials = OwnCloudCredentialsFactory.newBearerCredentials(mAuthToken);\r
+\r
+ }\r
+ accessRootFolder(credentials);\r
+ }\r
+ }\r
\r
/**\r
* The redirection triggered by the OAuth authentication server as response to the \r
*/\r
@Override\r
protected void onResume() {\r
- //Log_OC.wtf(TAG, "onResume init" );\r
super.onResume();\r
\r
// bound here to avoid spurious changes triggered by Android on device rotations\r
doOnResumeAndBound();\r
}\r
\r
- //Log_OC.wtf(TAG, "onResume end" );\r
}\r
\r
\r
@Override\r
protected void onPause() {\r
- //Log_OC.wtf(TAG, "onPause init" );\r
if (mOperationsServiceBinder != null) {\r
- //Log_OC.wtf(TAG, "unregistering to listen for operation callbacks" );\r
mOperationsServiceBinder.removeOperationListener(this);\r
}\r
\r
mHostUrlInput.setOnFocusChangeListener(null);\r
\r
super.onPause();\r
- //Log_OC.wtf(TAG, "onPause end" );\r
}\r
\r
@Override\r
public void onFocusChange(View view, boolean hasFocus) {\r
if (view.getId() == R.id.hostUrlInput) { \r
if (!hasFocus) {\r
- onUrlInputFocusLost((TextView) view);\r
+ onUrlInputFocusLost();\r
}\r
else {\r
showRefreshButton(false);\r
}\r
\r
} else if (view.getId() == R.id.account_password) {\r
- onPasswordFocusChanged((TextView) view, hasFocus);\r
+ onPasswordFocusChanged(hasFocus);\r
}\r
}\r
\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
+ private void onUrlInputFocusLost() {\r
if (!mServerInfo.mBaseUrl.equals(\r
normalizeUrl(mHostUrlInput.getText().toString(), mServerInfo.mIsSslConn))) {\r
// check server again only if the user changed something in the field\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
+ private void onPasswordFocusChanged(boolean hasFocus) {\r
if (hasFocus) {\r
showViewPasswordButton();\r
} else {\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
dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);\r
\r
/// validate credentials accessing the root folder\r
- accessRootFolderRemoteOperation(username, password);\r
- \r
+ OwnCloudCredentials credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password);\r
+ accessRootFolder(credentials);\r
}\r
\r
- private void accessRootFolderRemoteOperation(String username, String password) {\r
- Intent existenceCheckIntent = new Intent();\r
- existenceCheckIntent.setAction(OperationsService.ACTION_EXISTENCE_CHECK);\r
- existenceCheckIntent.putExtra(OperationsService.EXTRA_SERVER_URL, mServerInfo.mBaseUrl);\r
- existenceCheckIntent.putExtra(OperationsService.EXTRA_REMOTE_PATH, "/");\r
- existenceCheckIntent.putExtra(OperationsService.EXTRA_USERNAME, username);\r
- existenceCheckIntent.putExtra(OperationsService.EXTRA_PASSWORD, password);\r
- existenceCheckIntent.putExtra(OperationsService.EXTRA_AUTH_TOKEN, mAuthToken);\r
- \r
- if (mOperationsServiceBinder != null) {\r
- //Log_OC.wtf(TAG, "starting existenceCheckRemoteOperation..." );\r
- mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(existenceCheckIntent);\r
- }\r
+ private void accessRootFolder(OwnCloudCredentials credentials) {\r
+ mAsyncTask = new AuthenticatorAsyncTask(this);\r
+ Object[] params = { mServerInfo.mBaseUrl, credentials };\r
+ mAsyncTask.execute(params);\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
* in the server.\r
*/\r
private void startSamlBasedFederatedSingleSignOnAuthorization() {\r
- // be gentle with the user\r
+ /// be gentle with the user\r
mAuthStatusIcon = R.drawable.progress_small;\r
mAuthStatusText = R.string.auth_connecting_auth_server;\r
showAuthStatus();\r
- IndeterminateProgressDialog dialog = \r
- IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, true);\r
- dialog.show(getSupportFragmentManager(), WAIT_DIALOG_TAG);\r
-\r
- /// validate credentials accessing the root folder\r
- accessRootFolderRemoteOperation("", "");\r
\r
+ /// Show SAML-based SSO web dialog\r
+ String targetUrl = mServerInfo.mBaseUrl\r
+ + AccountUtils.getWebdavPath(mServerInfo.mVersion, mAuthTokenType);\r
+ SamlWebViewDialog dialog = SamlWebViewDialog.newInstance(targetUrl, targetUrl);\r
+ dialog.show(getSupportFragmentManager(), SAML_DIALOG_TAG);\r
}\r
\r
/**\r
} else if (operation instanceof OAuth2GetAccessToken) {\r
onGetOAuthAccessTokenFinish(result);\r
\r
- } else if (operation instanceof ExistenceCheckRemoteOperation) {\r
- //Log_OC.wtf(TAG, "received detection response through callback" );\r
- if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).\r
- equals(mAuthTokenType)) {\r
- onSamlBasedFederatedSingleSignOnAuthorizationStart(result);\r
-\r
- } else {\r
- onAuthorizationCheckFinish(result);\r
- }\r
} else if (operation instanceof GetRemoteUserNameOperation) {\r
onGetUserNameFinish(result);\r
}\r
if (!mUsernameInput.getText().toString().equals(username)) {\r
// fail - not a new account, but an existing one; disallow\r
result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);\r
- /*\r
- OwnCloudClientManagerFactory.getDefaultSingleton().removeClientFor(\r
- new OwnCloudAccount(\r
- Uri.parse(mServerInfo.mBaseUrl),\r
- OwnCloudCredentialsFactory.newSamlSsoCredentials(mAuthToken))\r
- );\r
- */\r
mAuthToken = "";\r
updateAuthStatusIconAndText(result);\r
showAuthStatus();\r
Log_OC.d(TAG, result.getLogMessage());\r
} else {\r
- updateToken();\r
- success = true;\r
+ try {\r
+ updateAccountAuthentication();\r
+ success = true;\r
+\r
+ } catch (AccountNotFoundException e) {\r
+ Log_OC.e(TAG, "Account " + mAccount + " was removed!", e);\r
+ Toast.makeText(this, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();\r
+ finish();\r
+ }\r
}\r
}\r
\r
\r
}\r
\r
- private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperationResult result) {\r
- mWaitingForOpId = Long.MAX_VALUE;\r
- dismissDialog(WAIT_DIALOG_TAG);\r
-
- if (result.isIdPRedirection()) {
- String url = result.getRedirectedLocation();\r
- String targetUrl = mServerInfo.mBaseUrl \r
- + AccountUtils.getWebdavPath(mServerInfo.mVersion, mAuthTokenType);\r
-\r
- // Show dialog\r
- SamlWebViewDialog dialog = SamlWebViewDialog.newInstance(url, targetUrl); \r
- dialog.show(getSupportFragmentManager(), SAML_DIALOG_TAG);\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
+ *\r
* @param result Result of the check.\r
*/\r
private void onGetServerInfoFinish(RemoteOperationResult result) {\r
\r
\r
private boolean authSupported(AuthenticationMethod authMethod) {\r
- String basic = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());\r
- String oAuth = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());\r
- String saml = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());\r
- \r
- return (( mAuthTokenType.equals(basic) && \r
- authMethod.equals(AuthenticationMethod.BASIC_HTTP_AUTH) ) ||\r
- ( mAuthTokenType.equals(oAuth) && \r
- authMethod.equals(AuthenticationMethod.BEARER_TOKEN)) ||\r
- ( mAuthTokenType.equals(saml) && \r
- authMethod.equals(AuthenticationMethod.SAML_WEB_SSO))\r
+ return (( BASIC_TOKEN_TYPE.equals(mAuthTokenType) &&\r
+ AuthenticationMethod.BASIC_HTTP_AUTH.equals(authMethod) ) ||\r
+ ( OAUTH_TOKEN_TYPE.equals(mAuthTokenType) &&\r
+ AuthenticationMethod.BEARER_TOKEN.equals(authMethod)) ||\r
+ ( SAML_TOKEN_TYPE.equals(mAuthTokenType) &&\r
+ AuthenticationMethod.SAML_WEB_SSO.equals(authMethod))\r
);\r
}\r
\r
Map<String, String> tokens = (Map<String, String>)(result.getData().get(0));\r
mAuthToken = tokens.get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
- \r
- accessRootFolderRemoteOperation("", "");\r
+\r
+ /// validate token accessing to root folder / getting session\r
+ OwnCloudCredentials credentials = OwnCloudCredentialsFactory.newBearerCredentials(mAuthToken);\r
+ accessRootFolder(credentials);\r
\r
} else {\r
updateAuthStatusIconAndText(result);\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
+ *\r
* @param result Result of the operation.\r
*/\r
- private void onAuthorizationCheckFinish(RemoteOperationResult result) {\r
+ @Override\r
+ public void onAuthenticatorTaskCallback(RemoteOperationResult result) {\r
mWaitingForOpId = Long.MAX_VALUE;\r
dismissDialog(WAIT_DIALOG_TAG);\r
\r
success = createAccount();\r
\r
} else {\r
- updateToken();\r
- success = true;\r
+ try {\r
+ updateAccountAuthentication();\r
+ success = true;\r
+\r
+ } catch (AccountNotFoundException e) {\r
+ Log_OC.e(TAG, "Account " + mAccount + " was removed!", e);\r
+ Toast.makeText(this, R.string.auth_account_does_not_exist, Toast.LENGTH_SHORT).show();\r
+ finish();\r
+ }\r
}\r
\r
if (success) {\r
finish();\r
}\r
\r
- } else if (result.isServerFail() || result.isException()) {
+ } else if (result.isServerFail() || result.isException()) {\r
/// server errors or exceptions in authorization take to requiring a new check of \r
/// the server\r
mServerIsChecked = true;\r
\r
\r
/**\r
- * Sets the proper response to get that the Account Authenticator that started this activity \r
+ * Updates the authentication token.\r
+ *\r
+ * Sets the proper response so that the AccountAuthenticator that started this activity\r
* saves a new authorization token for mAccount.\r
+ *\r
+ * Kills the session kept by OwnCloudClientManager so that a new one will created with\r
+ * the new credentials when needed.\r
*/\r
- private void updateToken() {\r
+ private void updateAccountAuthentication() throws AccountNotFoundException {\r
+ \r
Bundle response = new Bundle();\r
response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\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 \r
- // addAccountExplicitly, or in KEY_USERDATA
+ // addAccountExplicitly, or in KEY_USERDATA\r
mAccountMgr.setUserData(\r
mAccount, Constants.KEY_OC_VERSION, mServerInfo.mVersion.getVersion()\r
);\r
mAccountMgr.setUserData(\r
mAccount, Constants.KEY_OC_BASE_URL, mServerInfo.mBaseUrl\r
);\r
-
+\r
if (isSaml) {\r
mAccountMgr.setUserData(mAccount, Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
} else if (isOAuth) {\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
- * @param serverStatusText Resource identifier of the text to show.\r
- * @param serverStatusIcon Resource identifier of the icon to show.\r
+ *\r
*/\r
private void showServerStatus() {\r
if (mServerStatusIcon == 0 && mServerStatusText == 0) {\r
public void onCheckClick(View view) {\r
CheckBox oAuth2Check = (CheckBox)view;\r
if (oAuth2Check.isChecked()) {\r
- mAuthTokenType = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());\r
+ mAuthTokenType = OAUTH_TOKEN_TYPE;\r
} else {\r
- mAuthTokenType = AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType());\r
+ mAuthTokenType = BASIC_TOKEN_TYPE;\r
}\r
updateAuthenticationPreFragmentVisibility();\r
}\r
getUserNameIntent.putExtra(OperationsService.EXTRA_COOKIE, sessionCookie);\r
\r
if (mOperationsServiceBinder != null) {\r
- //Log_OC.wtf(TAG, "starting getRemoteUserNameOperation..." );\r
mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getUserNameIntent);\r
}\r
}\r
if (component.equals(\r
new ComponentName(AuthenticatorActivity.this, OperationsService.class)\r
)) {\r
- //Log_OC.wtf(TAG, "Operations service connected");\r
mOperationsServiceBinder = (OperationsServiceBinder) service;\r
\r
doOnResumeAndBound();\r
public void doNegativeAuthenticatioDialogClick(){\r
mIsFirstAuthAttempt = true;\r
}\r
+\r
+\r
}\r
--- /dev/null
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio on 09/02/2015.
+ * Copyright (C) 2015 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.Activity;
+import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
+
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientFactory;
+import com.owncloud.android.lib.common.OwnCloudCredentials;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
+
+import java.lang.ref.WeakReference;
+
+
+/**
+ * Async Task to verify the credentials of a user
+ */
+public class AuthenticatorAsyncTask extends AsyncTask<Object, Void, RemoteOperationResult> {
+
+ private static String REMOTE_PATH = "/";
+ private static boolean SUCCESS_IF_ABSENT = false;
+
+ private Context mContext;
+ private final WeakReference<OnAuthenticatorTaskListener> mListener;
+ protected Activity mActivity;
+
+ public AuthenticatorAsyncTask(Activity activity) {
+ mContext = activity.getApplicationContext();
+ mListener = new WeakReference<OnAuthenticatorTaskListener>((OnAuthenticatorTaskListener)activity);
+ }
+
+ @Override
+ protected RemoteOperationResult doInBackground(Object... params) {
+
+ RemoteOperationResult result;
+ if (params!= null && params.length==2) {
+ String url = (String)params[0];
+ OwnCloudCredentials credentials = (OwnCloudCredentials)params[1];
+
+ // Client
+ Uri uri = Uri.parse(url);
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(uri, mContext, true);
+
+ client.setCredentials(credentials);
+
+ // Operation
+ ExistenceCheckRemoteOperation operation = new ExistenceCheckRemoteOperation(
+ REMOTE_PATH,
+ mContext,
+ SUCCESS_IF_ABSENT
+ );
+ result = operation.execute(client);
+
+ } else {
+ result = new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR);
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(RemoteOperationResult result) {
+
+ if (result!= null)
+ {
+ OnAuthenticatorTaskListener listener = mListener.get();
+ if (listener!= null)
+ {
+ listener.onAuthenticatorTaskCallback(result);
+ }
+ }
+ }
+ /*
+ * Interface to retrieve data from recognition task
+ */
+ public interface OnAuthenticatorTaskListener{
+
+ void onAuthenticatorTaskCallback(RemoteOperationResult result);
+ }
+}
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* 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 {
view.setVisibility(View.GONE);
CookieManager cookieManager = CookieManager.getInstance();
final String cookies = cookieManager.getCookie(url);
- Log_OC.d(TAG, "Cookies: " + cookies);
+ //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 doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
- Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
- }
-
@Override
public void onReceivedSslError (final WebView view, final SslErrorHandler handler, SslError error) {
- Log_OC.d(TAG, "onReceivedSslError : " + error);
+ Log_OC.e(TAG, "onReceivedSslError : " + error);
// Test 1
X509Certificate x509Certificate = getX509CertificateFromError(error);
boolean isKnownServer = false;
if (x509Certificate != null) {
- Log_OC.d(TAG, "------>>>>> x509Certificate " + x509Certificate.toString());
-
try {
isKnownServer = NetworkUtils.isCertInKnownServersStore((Certificate) x509Certificate, mContext);
} catch (Exception e) {
((AuthenticatorActivity)mContext).createAuthenticationDialog(view, handler);
}
- @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;
- }
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
+import android.provider.MediaStore;
public class FileDataStorageManager {
* HERE ONLY DATA CONSISTENCY SHOULD BE GRANTED
*
* @param folder
- * @param files
- * @param removeNotUpdated
+ * @param updatedFiles
+ * @param filesToRemove
*/
public void saveFolder(
OCFile folder, Collection<OCFile> updatedFiles, Collection<OCFile> filesToRemove
if (removeLocalCopy && file.isDown() && localPath != null && success) {
success = new File(localPath).delete();
if (success) {
- triggerMediaScan(localPath);
+ deleteFileInMediaScan(localPath);
}
if (!removeDBData && success) {
// maybe unnecessary, but should be checked TODO remove if unnecessary
private boolean removeLocalFolder(OCFile folder) {
boolean success = true;
- File localFolder = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder));
+ String localFolderPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, folder);
+ File localFolder = new File(localFolderPath);
if (localFolder.exists()) {
// stage 1: remove the local files already registered in the files database
Vector<OCFile> files = getFolderContent(folder.getFileId());
success &= removeLocalFolder(file);
} else {
if (file.isDown()) {
- String path = file.getStoragePath();
File localFile = new File(file.getStoragePath());
success &= localFile.delete();
if (success) {
+ // notify MediaScanner about removed file
+ deleteFileInMediaScan(file.getStoragePath());
file.setStoragePath(null);
saveFile(file);
- triggerMediaScan(path); // notify MediaScanner about removed file
}
}
}
} else {
String path = localFile.getAbsolutePath();
success &= localFile.delete();
- triggerMediaScan(path); // notify MediaScanner about removed file
}
}
}
Iterator<String> it = originalPathsToTriggerMediaScan.iterator();
while (it.hasNext()) {
// Notify MediaScanner about removed file
- triggerMediaScan(it.next());
+ deleteFileInMediaScan(it.next());
}
it = newPathsToTriggerMediaScan.iterator();
while (it.hasNext()) {
MainApp.getAppContext().sendBroadcast(intent);
}
+ public void deleteFileInMediaScan(String path) {
+
+ String mimetypeString = FileStorageUtils.getMimeTypeFromName(path);
+ ContentResolver contentResolver = getContentResolver();
+
+ if (contentResolver != null) {
+ if (mimetypeString.startsWith("image/")) {
+ // Images
+ contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Images.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("audio/")) {
+ // Audio
+ contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Audio.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("video/")) {
+ // Video
+ contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Video.Media.DATA + "=?", new String[]{path});
+ }
+ } else {
+ ContentProviderClient contentProviderClient = getContentProviderClient();
+ try {
+ if (mimetypeString.startsWith("image/")) {
+ // Images
+ contentProviderClient.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Images.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("audio/")) {
+ // Audio
+ contentProviderClient.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Audio.Media.DATA + "=?", new String[]{path});
+ } else if (mimetypeString.startsWith("video/")) {
+ // Video
+ contentProviderClient.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+ MediaStore.Video.Media.DATA + "=?", new String[]{path});
+ }
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception deleting media file in MediaStore " + e.getMessage());
+ }
+ }
+
+ }
+
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
import android.os.Parcel;
import android.os.Parcelable;
-import android.webkit.MimeTypeMap;
import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.utils.FileStorageUtils;
import java.io.File;
*/
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 : "";
+ FileStorageUtils.getMimeTypeFromName(mRemotePath).startsWith("image/"));
}
public String getPermissions() {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import com.owncloud.android.utils.DisplayUtils;
/**
- * Manager for concurrent access to thumbnails cache.
- *
- * @author Tobias Kaminsky
- * @author David A. Velasco
+ * Manager for concurrent access to thumbnails cache.
*/
public class ThumbnailsCacheManager {
}
mFile = params[0];
-
+
if (mFile instanceof OCFile) {
thumbnail = doOCFileInBackground();
} else if (mFile instanceof File) {
private int getThumbnailDimension(){
// Converts dp to pixel
Resources r = MainApp.getAppContext().getResources();
- return (int) Math.round(r.getDimension(R.dimen.file_icon_size));
+ return (int) Math.round(r.getDimension(R.dimen.file_icon_size_grid));
}
private Bitmap doOCFileInBackground() {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
* Copyright (C) 2011-2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* Custom database helper for ownCloud
- *
- * @author Bartek Przybylski
- *
*/
public class DbHandler {
private SQLiteDatabase mDB;
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
\r
/**\r
* Meta-Class that holds various static field information\r
- * \r
- * @author Bartek Przybylski\r
- * \r
*/\r
public class ProviderMeta {\r
\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* App-registered receiver catching the broadcast intent reporting that the system was
* just boot up.
- *
- * @author David A. Velasco
*/
public class BootupBroadcastReceiver extends BroadcastReceiver {
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Filters out the file actions available in a given {@link Menu} for a given {@link OCFile}
* according to the current state of the latest.
- *
- * @author David A. Velasco
*/
public class FileMenuFilter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2015 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import com.owncloud.android.ui.dialog.ShareLinkToDialog;
/**
- *
- * @author masensio
- * @author David A. Velasco
+ *
*/
public class FileOperationsHelper {
}
- public void shareFileWithLinkToApp(OCFile file, Intent sendIntent) {
+ public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
if (file != null) {
mFileActivity.showLoadingDialog();
service.setAction(OperationsService.ACTION_CREATE_SHARE);
service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+ service.putExtra(OperationsService.EXTRA_PASSWORD_SHARE, password);
service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
* Copyright (C) 2012-2015 ownCloud Inc.
*
import java.util.Vector;
import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.utils.ErrorMessageAdapter;
import android.accounts.Account;
+import android.accounts.AccountManager;
import android.accounts.AccountsException;
+import android.accounts.OnAccountsUpdateListener;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.support.v4.app.NotificationCompat;
import android.util.Pair;
-public class FileDownloader extends Service implements OnDatatransferProgressListener {
-
+public class FileDownloader extends Service
+ implements OnDatatransferProgressListener, OnAccountsUpdateListener {
+
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_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 EXTRA_LINKED_TO_PATH = "LINKED_TO";
public static final String ACCOUNT_NAME = "ACCOUNT_NAME";
-
+
private static final String TAG = "FileDownloader";
private Looper mServiceLooper;
private OwnCloudClient mDownloadClient = null;
private Account mCurrentAccount = null;
private FileDataStorageManager mStorageManager;
-
+
private IndexedForest<DownloadFileOperation> mPendingDownloads = new IndexedForest<DownloadFileOperation>();
private DownloadFileOperation mCurrentDownload = null;
-
+
private NotificationManager mNotificationManager;
private NotificationCompat.Builder mNotificationBuilder;
private int mLastPercent;
-
+
public static String getDownloadAddedMessage() {
return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE;
}
-
+
public static String getDownloadFinishMessage() {
return FileDownloader.class.getName() + DOWNLOAD_FINISH_MESSAGE;
}
-
+
/**
* Service initialization
*/
@Override
public void onCreate() {
super.onCreate();
+ Log_OC.d(TAG, "Creating service");
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- HandlerThread thread = new HandlerThread("FileDownloaderThread",
- Process.THREAD_PRIORITY_BACKGROUND);
+ HandlerThread thread = new HandlerThread("FileDownloaderThread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper, this);
mBinder = new FileDownloaderBinder();
+
+ // add AccountsUpdatedListener
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.addOnAccountsUpdatedListener(this, null, false);
+ }
+
+
+ /**
+ * Service clean up
+ */
+ @Override
+ public void onDestroy() {
+ Log_OC.v(TAG, "Destroying service");
+ mBinder = null;
+ mServiceHandler = null;
+ mServiceLooper.quit();
+ mServiceLooper = null;
+ mNotificationManager = null;
+
+ // remove AccountsUpdatedListener
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.removeOnAccountsUpdatedListener(this);
+
+ super.onDestroy();
}
+
/**
* Entry point to add one or several files to the queue of downloads.
- *
+ * <p/>
* 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) ||
+ Log_OC.d(TAG, "Starting command with id " + startId);
+
+ if (!intent.hasExtra(EXTRA_ACCOUNT) ||
!intent.hasExtra(EXTRA_FILE)
- ) {
+ ) {
Log_OC.e(TAG, "Not enough information provided in intent");
return START_NOT_STICKY;
} else {
"Received request to download file"
);*/
- AbstractList<String> requestedDownloads = new Vector<String>();
- try {
- DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
- newDownload.addDatatransferProgressListener(this);
- newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
- Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
+ AbstractList<String> requestedDownloads = new Vector<String>();
+ try {
+ DownloadFileOperation newDownload = new DownloadFileOperation(account, file);
+ newDownload.addDatatransferProgressListener(this);
+ newDownload.addDatatransferProgressListener((FileDownloaderBinder) mBinder);
+ Pair<String, String> putResult = mPendingDownloads.putIfAbsent(
account, file.getRemotePath(), newDownload
- );
- String downloadKey = putResult.first;
- requestedDownloads.add(downloadKey);
+ );
+ String downloadKey = putResult.first;
+ requestedDownloads.add(downloadKey);
/*Log_OC.v(
"NOW " + TAG + ", thread " + Thread.currentThread().getName(),
"Download on " + file.getRemotePath() + " added to queue"
);*/
- // Store file on db with state 'downloading'
+ // Store file on db with state 'downloading'
/*
TODO - check if helps with UI responsiveness, letting only folders use FileDownloaderBinder to check
FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver());
storageManager.saveFile(file);
*/
- sendBroadcastNewDownload(newDownload, putResult.second);
+ sendBroadcastNewDownload(newDownload, putResult.second);
- } catch (IllegalArgumentException e) {
- Log_OC.e(TAG, "Not enough information provided in intent: " + e.getMessage());
- return START_NOT_STICKY;
- }
+ } 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);
- }
+ if (requestedDownloads.size() > 0) {
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ msg.obj = requestedDownloads;
+ mServiceHandler.sendMessage(msg);
+ }
//}
}
/**
* Provides a binder object that clients can use to perform operations on the queue of downloads,
* excepting the addition of new files.
- *
+ * <p/>
* Implemented to perform cancellation, pause and resume of existing downloads.
*/
@Override
*/
@Override
public boolean onUnbind(Intent intent) {
- ((FileDownloaderBinder)mBinder).clearListeners();
+ ((FileDownloaderBinder) mBinder).clearListeners();
return false; // not accepting rebinding (default behaviour)
}
+ @Override
+ public void onAccountsUpdated(Account[] accounts) {
+ //review the current download and cancel it if its account doesn't exist
+ if (mCurrentDownload != null &&
+ !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+ mCurrentDownload.cancel();
+ }
+ // The rest of downloads are cancelled when they try to start
+ }
+
/**
- * Binder to let client components to perform operations on the queue of downloads.
- *
- * It provides by itself the available operations.
+ * Binder to let client components to perform operations on the queue of downloads.
+ * <p/>
+ * 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.
*/
/**
* 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
+ * @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) {
/*Log_OC.v(
);
Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
"Removing download of " + file.getRemotePath());*/
- Pair<DownloadFileOperation, String> removeResult = mPendingDownloads.remove(account, file.getRemotePath());
+ Pair<DownloadFileOperation, String> removeResult =
+ mPendingDownloads.remove(account, file.getRemotePath());
DownloadFileOperation download = removeResult.first;
if (download != null) {
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
}
}
}
-
-
+
+ /**
+ * Cancels a pending or current upload for an account
+ *
+ * @param account Owncloud accountName where the remote file will be stored.
+ */
+ public void cancel(Account account) {
+ Log_OC.d(TAG, "Account= " + account.name);
+
+ if (mCurrentDownload != null) {
+ Log_OC.d(TAG, "Current Download Account= " + mCurrentDownload.getAccount().name);
+ if (mCurrentDownload.getAccount().name.equals(account.name)) {
+ mCurrentDownload.cancel();
+ }
+ }
+ // Cancel pending downloads
+ cancelDownloadsForAccount(account);
+ }
+
public void clearListeners() {
mBoundListeners.clear();
}
/**
* Returns True when the file described by 'file' in the ownCloud account 'account' is downloading or
* waiting to download.
- *
+ * <p/>
* If 'file' is a directory, returns 'true' if any 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.
+ *
+ * @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;
return (mPendingDownloads.contains(account, file.getRemotePath()));
}
-
+
/**
* 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.
+ *
+ * @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 (
+ public void addDatatransferProgressListener(
OnDatatransferProgressListener listener, Account account, OCFile file
) {
if (account == null || file == null || listener == null) return;
//String targetKey = buildKey(account, file.getRemotePath());
mBoundListeners.put(file.getFileId(), 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.
+ *
+ * @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 (
+ public void removeDatatransferProgressListener(
OnDatatransferProgressListener listener, Account account, OCFile file
) {
if (account == null || file == null || listener == null) return;
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
- String fileName) {
+ String fileName) {
//String key = buildKey(mCurrentDownload.getAccount(), mCurrentDownload.getFile().getRemotePath());
OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId());
if (boundListener != null) {
boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
}
}
-
+
+ /**
+ * Review downloads and cancel it if its account doesn't exist
+ */
+ public void checkAccountOfCurrentDownload() {
+ if (mCurrentDownload != null &&
+ !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+ mCurrentDownload.cancel();
+ }
+ // The rest of downloads are cancelled when they try to start
+ }
+
}
-
-
- /**
- * 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()}.
+
+
+ /**
+ * Download worker. Performs the pending downloads in the order they were requested.
+ * <p/>
+ * 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)
Iterator<String> it = requestedDownloads.iterator();
while (it.hasNext()) {
String next = it.next();
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Handling download file " + next);*/
mService.downloadFile(next);
}
}
+ Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
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
+ *
+ * @param downloadKey Key to access the download to perform, contained in mPendingDownloads
*/
private void downloadFile(String downloadKey) {
mCurrentDownload = mPendingDownloads.get(downloadKey);
if (mCurrentDownload != null) {
-
- notifyDownloadStart(mCurrentDownload);
+ // Detect if the account exists
+ if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) {
+ Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().name + " exists");
+ notifyDownloadStart(mCurrentDownload);
- RemoteOperationResult downloadResult = null;
- try {
- /// prepare client object to send the request to the ownCloud server
- if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
- mCurrentAccount = mCurrentDownload.getAccount();
- mStorageManager = new FileDataStorageManager(
- mCurrentAccount,
- getContentResolver()
- );
- } // else, reuse storage manager from previous operation
-
- // always get client from client manager, to get fresh credentials in case of update
- OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
- mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
- getClientFor(ocAccount, this);
-
-
- /// perform the download
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
+ RemoteOperationResult downloadResult = null;
+ try {
+ /// prepare client object to send the request to the ownCloud server
+ if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentDownload.getAccount())) {
+ mCurrentAccount = mCurrentDownload.getAccount();
+ mStorageManager = new FileDataStorageManager(
+ mCurrentAccount,
+ getContentResolver()
+ );
+ } // else, reuse storage manager from previous operation
+
+ // always get client from client manager, to get fresh credentials in case of update
+ OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this);
+ mDownloadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+ getClientFor(ocAccount, this);
+
+
+ /// perform the download
+ /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
"Executing download of " + mCurrentDownload.getRemotePath());*/
- downloadResult = mCurrentDownload.execute(mDownloadClient);
- if (downloadResult.isSuccess()) {
- saveDownloadedFile();
- }
-
- } catch (AccountsException e) {
- Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
- downloadResult = new RemoteOperationResult(e);
- } catch (IOException e) {
- Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
- downloadResult = new RemoteOperationResult(e);
-
- } finally {
+ downloadResult = mCurrentDownload.execute(mDownloadClient);
+ if (downloadResult.isSuccess()) {
+ saveDownloadedFile();
+ }
+
+ } catch (AccountsException e) {
+ Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
+ downloadResult = new RemoteOperationResult(e);
+ } catch (IOException e) {
+ Log_OC.e(TAG, "Error while trying to get authorization for " + mCurrentAccount.name, e);
+ downloadResult = new RemoteOperationResult(e);
+
+ } finally {
/*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
"Removing payload " + mCurrentDownload.getRemotePath());*/
- Pair<DownloadFileOperation, String> removeResult =
- mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
+ Pair<DownloadFileOperation, String> removeResult =
+ mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath());
- /// notify result
- notifyDownloadResult(mCurrentDownload, downloadResult);
+ /// notify result
+ notifyDownloadResult(mCurrentDownload, downloadResult);
- sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
- }
+ sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second);
+ }
+ } else {
+ // Cancel the transfer
+ Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + " doesn't exist");
+ cancelDownloadsForAccount(mCurrentDownload.getAccount());
+ }
}
}
/**
* Creates a status notification to show the download progress
- *
- * @param download Download operation starting.
+ *
+ * @param download Download operation starting.
*/
private void notifyDownloadStart(DownloadFileOperation download) {
/// create status notification with a progress bar
mLastPercent = 0;
- mNotificationBuilder =
+ mNotificationBuilder =
NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this);
mNotificationBuilder
.setSmallIcon(R.drawable.notification_icon)
String.format(getString(R.string.downloader_download_in_progress_content), 0,
new File(download.getSavePath()).getName())
);
-
+
/// includes a pending intent in the notification showing the details view of the file
Intent showDetailsIntent = null;
if (PreviewImageFragment.canBePreviewed(download.getFile())) {
showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, download.getFile());
showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, download.getAccount());
showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
+
mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
- this, (int) System.currentTimeMillis(), showDetailsIntent, 0
+ this, (int) System.currentTimeMillis(), showDetailsIntent, 0
));
mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotificationBuilder.build());
}
-
+
/**
* Callback method to update the progress bar in the status notification.
*/
@Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath)
- {
- int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) {
+ int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
if (percent != mLastPercent) {
mNotificationBuilder.setProgress(100, percent, totalToTransfer < 0);
String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
}
mLastPercent = percent;
}
-
-
+
+
/**
* Updates the status notification with the result of a download operation.
- *
- * @param downloadResult Result of the download operation.
- * @param download Finished 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 tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker :
+ R.string.downloader_download_failed_ticker;
+
boolean needsToUpdateCredentials = (
downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
- downloadResult.isIdPRedirection()
+ downloadResult.isIdPRedirection()
);
- tickerId = (needsToUpdateCredentials) ?
+ tickerId = (needsToUpdateCredentials) ?
R.string.downloader_download_failed_credentials_error : tickerId;
-
+
mNotificationBuilder
- .setTicker(getString(tickerId))
- .setContentTitle(getString(tickerId))
- .setAutoCancel(true)
- .setOngoing(false)
- .setProgress(0, 0, false);
-
+ .setTicker(getString(tickerId))
+ .setContentTitle(getString(tickerId))
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setProgress(0, 0, false);
+
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.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
mNotificationBuilder
- .setContentIntent(PendingIntent.getActivity(
- this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
-
+ .setContentIntent(PendingIntent.getActivity(
+ this, (int) System.currentTimeMillis(), updateAccountCredentials, PendingIntent.FLAG_ONE_SHOT));
+
} else {
// TODO put something smart in showDetailsIntent
- Intent showDetailsIntent = new Intent();
+ Intent showDetailsIntent = new Intent();
mNotificationBuilder
- .setContentIntent(PendingIntent.getActivity(
- this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
+ .setContentIntent(PendingIntent.getActivity(
+ this, (int) System.currentTimeMillis(), showDetailsIntent, 0));
}
-
+
mNotificationBuilder.setContentText(
ErrorMessageAdapter.getErrorCauseMessage(downloadResult, download, getResources())
);
mNotificationManager.notify(tickerId, mNotificationBuilder.build());
-
+
// Remove success notification
- if (downloadResult.isSuccess()) {
+ if (downloadResult.isSuccess()) {
// Sleep 2 seconds, so show the notification before remove it
NotificationDelayer.cancelWithDelay(
- mNotificationManager,
- R.string.downloader_download_succeeded_ticker,
+ mNotificationManager,
+ R.string.downloader_download_succeeded_ticker,
2000);
}
-
+
}
}
-
-
+
+
/**
* 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
- * @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
+ *
+ * @param download Finished download operation
+ * @param downloadResult Result of the download operation
+ * @param unlinkedFromRemotePath Path in the downloads tree where the download was unlinked from
*/
private void sendBroadcastDownloadFinished(
DownloadFileOperation download,
}
sendStickyBroadcast(end);
}
-
-
+
+
/**
* Sends a broadcast when a new download is added to the queue.
- *
- * @param download Added download operation
- * @param linkedToRemotePath Path in the downloads tree where the download was linked to
+ *
+ * @param download Added download operation
+ * @param linkedToRemotePath Path in the downloads tree where the download was linked to
*/
private void sendBroadcastNewDownload(DownloadFileOperation download, String linkedToRemotePath) {
Intent added = new Intent(getDownloadAddedMessage());
sendStickyBroadcast(added);
}
+ /**
+ * Remove downloads of an account
+ *
+ * @param account Downloads account to remove
+ */
+ private void cancelDownloadsForAccount(Account account) {
+ // Cancel pending downloads
+ mPendingDownloads.remove(account);
+ }
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2012-2015 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,
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountsException;
+import android.accounts.OnAccountsUpdateListener;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import com.owncloud.android.utils.UriUtils;
-
-public class FileUploader extends Service implements OnDatatransferProgressListener {
+public class FileUploader extends Service
+ implements OnDatatransferProgressListener, OnAccountsUpdateListener {
private static final String UPLOAD_FINISH_MESSAGE = "UPLOAD_FINISH";
public static final String EXTRA_UPLOAD_RESULT = "RESULT";
private static final String MIME_TYPE_PDF = "application/pdf";
private static final String FILE_EXTENSION_PDF = ".pdf";
-
+
public static String getUploadFinishMessage() {
return FileUploader.class.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
*/
/**
* 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
@Override
public void onCreate() {
super.onCreate();
- Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size());
+ Log_OC.d(TAG, "Creating service");
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();
+
+ // add AccountsUpdatedListener
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.addOnAccountsUpdatedListener(this, null, false);
}
/**
+ * Service clean up
+ */
+ @Override
+ public void onDestroy() {
+ Log_OC.v(TAG, "Destroying service" );
+ mBinder = null;
+ mServiceHandler = null;
+ mServiceLooper.quit();
+ mServiceLooper = null;
+ mNotificationManager = null;
+
+ // remove AccountsUpdatedListener
+ AccountManager am = AccountManager.get(getApplicationContext());
+ am.removeOnAccountsUpdatedListener(this);
+
+ super.onDestroy();
+ }
+
+
+ /**
* 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) {
+ Log_OC.d(TAG, "Starting command with id " + 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");
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;
AccountManager aMgr = AccountManager.get(this);
String version = aMgr.getUserData(account, Constants.KEY_OC_VERSION);
OwnCloudVersion ocv = new OwnCloudVersion(version);
-
+
boolean chunked = FileUploader.chunkedUploadIsSupported(ocv);
AbstractList<String> requestedUploads = new Vector<String>();
String uploadKey = null;
try {
for (int i = 0; i < files.length; i++) {
uploadKey = buildRemoteName(account, files[i].getRemotePath());
- newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction,
+ newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction,
getApplicationContext());
if (isInstant) {
newUpload.setRemoteFolderToBeCreated();
/**
* 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.
*/
public IBinder onBind(Intent arg0) {
return mBinder;
}
-
+
/**
* Called when ALL the bound clients were onbound.
*/
((FileUploaderBinder)mBinder).clearListeners();
return false; // not accepting rebinding (default behaviour)
}
-
+
+ @Override
+ public void onAccountsUpdated(Account[] accounts) {
+ // Review current upload, and cancel it if its account doen't exist
+ if (mCurrentUpload != null &&
+ !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+ mCurrentUpload.cancel();
+ }
+ // The rest of uploads are cancelled when they try to start
+ }
/**
* 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
*/
upload.cancel();
}
}
-
-
-
+
+ /**
+ * Cancels a pending or current upload for an account
+ *
+ * @param account Owncloud accountName where the remote file will be stored.
+ */
+ public void cancel(Account account) {
+ Log_OC.d(TAG, "Account= " + account.name);
+
+ if (mCurrentUpload != null) {
+ Log_OC.d(TAG, "Current Upload Account= " + mCurrentUpload.getAccount().name);
+ if (mCurrentUpload.getAccount().name.equals(account.name)) {
+ mCurrentUpload.cancel();
+ }
+ }
+ // Cancel pending uploads
+ cancelUploadForAccount(account.name);
+ }
+
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
*/
/**
* 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.
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.
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
- String fileName) {
+ String fileName) {
String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
OnDatatransferProgressListener boundListener = mBoundListeners.get(key);
if (boundListener != null) {
boundListener.onTransferProgress(progressRate, totalTransferredSoFar, totalToTransfer, fileName);
}
}
-
+
+ /**
+ * Review uploads and cancel it if its account doesn't exist
+ */
+ public void checkAccountOfCurrentUpload() {
+ if (mCurrentUpload != null &&
+ !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+ mCurrentUpload.cancel();
+ }
+ // The rest of uploads are cancelled when they try to start
+ }
}
/**
* 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()}.
*/
mService.uploadFile(it.next());
}
}
+ Log_OC.d(TAG, "Stopping command after id " + msg.arg1);
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
*/
if (mCurrentUpload != null) {
- notifyUploadStart(mCurrentUpload);
+ // Detect if the account exists
+ if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) {
+ Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists");
- 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());
- OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
- mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
- getClientFor(ocAccount, this);
- }
-
- /// 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();
+ 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());
+ OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this);
+ mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+ getClientFor(ocAccount, this);
+ }
+
+ /// 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;
}
- } 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);
+ /// notify result
+ notifyUploadResult(uploadResult, mCurrentUpload);
+ sendFinalBroadcast(mCurrentUpload, uploadResult);
+
+ } else {
+ // Cancel the transfer
+ Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " doesn't exist");
+ cancelUploadForAccount(mCurrentUpload.getAccount().name);
+
+ }
}
}
/**
* 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 ExistenceCheckRemoteOperation(pathToGrant, this, false);
RemoteOperationResult result = operation.execute(mUploadClient);
- if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
+ if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND &&
+ mCurrentUpload.isRemoteFolderToBeCreated()) {
SyncOperation syncOp = new CreateFolderOperation( pathToGrant, true);
result = syncOp.execute(mUploadClient, mStorageManager);
}
return result;
}
-
+
private OCFile createLocalFolder(String remotePath) {
String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
+ parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ?
+ parentPath : parentPath + OCFile.PATH_SEPARATOR;
OCFile parent = mStorageManager.getFileByPath(parentPath);
if (parent == null) {
parent = createLocalFolder(parentPath);
}
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() {
updateOCFile(file, (RemoteFile) result.getData().get(0));
file.setLastSyncDateForProperties(syncDate);
}
-
+
// / maybe this would be better as part of UploadFileOperation... or
// maybe all this method
if (mCurrentUpload.wasRenamed()) {
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()
+ // coincidence; nothing else is needed, the storagePath is right
+ // in the instance returned by mCurrentUpload.getFile()
}
file.setNeedsUpdateThumbnail(true);
mStorageManager.saveFile(file);
}
private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
- FileDataStorageManager storageManager) {
+ FileDataStorageManager storageManager) {
// MIME type
if (mimeType == null || mimeType.length() <= 0) {
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
+ // are checked when the UploadFileOperation instance is created
newFile.setMimetype(mimeType);
/**
* Creates a status notification to show the upload progress
- *
+ *
* @param upload Upload operation starting.
*/
private void notifyUploadStart(UploadFileOperation upload) {
// / create status notification with a progress bar
mLastPercent = 0;
- mNotificationBuilder =
+ mNotificationBuilder =
NotificationBuilderWithProgressBar.newNotificationBuilderWithProgressBar(this);
mNotificationBuilder
.setOngoing(true)
showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
- this, (int) System.currentTimeMillis(), showDetailsIntent, 0
+ this, (int) System.currentTimeMillis(), showDetailsIntent, 0
));
mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotificationBuilder.build());
/**
* Updates the status notification with the result of an upload operation.
- *
+ *
* @param uploadResult Result of the upload operation.
* @param upload Finished upload operation
*/
Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode());
// / cancelled operation or success -> silent removal of progress notification
mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
-
+
// Show the result: success or fail notification
if (!uploadResult.isCancelled()) {
- int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
- R.string.uploader_upload_failed_ticker;
-
+ int tickerId = (uploadResult.isSuccess()) ? R.string.uploader_upload_succeeded_ticker :
+ R.string.uploader_upload_failed_ticker;
+
String content = null;
// check credentials error
boolean needsToUpdateCredentials = (
- uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
- uploadResult.isIdPRedirection()
+ uploadResult.getCode() == ResultCode.UNAUTHORIZED ||
+ uploadResult.isIdPRedirection()
);
- tickerId = (needsToUpdateCredentials) ?
+ tickerId = (needsToUpdateCredentials) ?
R.string.uploader_upload_failed_credentials_error : tickerId;
mNotificationBuilder
- .setTicker(getString(tickerId))
- .setContentTitle(getString(tickerId))
- .setAutoCancel(true)
- .setOngoing(false)
- .setProgress(0, 0, false);
-
+ .setTicker(getString(tickerId))
+ .setContentTitle(getString(tickerId))
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setProgress(0, 0, false);
+
content = ErrorMessageAdapter.getErrorCauseMessage(
uploadResult, upload, getResources()
);
-
+
if (needsToUpdateCredentials) {
// let the user update credentials with one click
Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
AuthenticatorActivity.EXTRA_ACCOUNT, upload.getAccount()
);
updateAccountCredentials.putExtra(
- AuthenticatorActivity.EXTRA_ACTION,
+ AuthenticatorActivity.EXTRA_ACTION,
AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN
);
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
updateAccountCredentials.addFlags(Intent.FLAG_FROM_BACKGROUND);
mNotificationBuilder.setContentIntent(PendingIntent.getActivity(
- this,
- (int) System.currentTimeMillis(),
- updateAccountCredentials,
- PendingIntent.FLAG_ONE_SHOT
+ this,
+ (int) System.currentTimeMillis(),
+ updateAccountCredentials,
+ PendingIntent.FLAG_ONE_SHOT
));
-
- mUploadClient = null;
- // grant that future retries on the same account will get the fresh credentials
+
+ mUploadClient = null;
+ // grant that future retries on the same account will get the fresh credentials
} else {
mNotificationBuilder.setContentText(content);
-
+
if (upload.isInstant()) {
DbHandler db = null;
try {
if (uploadResult.getCode() == ResultCode.QUOTA_EXCEEDED) {
//message = getString(R.string.failed_upload_quota_exceeded_text);
if (db.updateFileState(
- upload.getOriginalStoragePath(),
+ upload.getOriginalStoragePath(),
DbHandler.UPLOAD_STATUS_UPLOAD_FAILED,
message) == 0) {
db.putFileForLater(
- upload.getOriginalStoragePath(),
- upload.getAccount().name,
+ upload.getOriginalStoragePath(),
+ upload.getAccount().name,
message
);
}
}
}
}
-
+
mNotificationBuilder.setContentText(content);
mNotificationManager.notify(tickerId, mNotificationBuilder.build());
-
+
if (uploadResult.isSuccess()) {
-
+
DbHandler db = new DbHandler(this.getBaseContext());
db.removeIUPendingFile(mCurrentUpload.getOriginalStoragePath());
db.close();
// remove success notification, with a delay of 2 seconds
NotificationDelayer.cancelWithDelay(
- mNotificationManager,
- R.string.uploader_upload_succeeded_ticker,
+ mNotificationManager,
+ R.string.uploader_upload_succeeded_ticker,
2000);
-
+
}
}
}
/**
* 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
+ // path, after
+ // possible
+ // automatic
+ // renaming
if (upload.wasRenamed()) {
end.putExtra(EXTRA_OLD_REMOTE_PATH, upload.getOldFile().getRemotePath());
}
* @return true if is needed to add the pdf file extension to the file
*/
private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, String mimeType) {
- return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) &&
- mimeType.equals(MIME_TYPE_PDF) &&
+ return localPath.startsWith(UriUtils.URI_CONTENT_SCHEME) &&
+ mimeType.equals(MIME_TYPE_PDF) &&
!localPath.endsWith(FILE_EXTENSION_PDF);
}
+ /**
+ * Remove uploads of an account
+ * @param accountName
+ */
+ private void cancelUploadForAccount(String accountName){
+ // this can be slow if there are many uploads :(
+ Iterator<String> it = mPendingUploads.keySet().iterator();
+ Log_OC.d(TAG, "Number of pending updloads= " + mPendingUploads.size());
+ while (it.hasNext()) {
+ String key = it.next();
+ Log_OC.d(TAG, "mPendingUploads CANCELLED " + key);
+ if (key.startsWith(accountName)) {
+ synchronized (mPendingUploads) {
+ mPendingUploads.remove(key);
+ }
+ }
+ }
+ }
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
import android.util.Pair;
import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.utils.Log_OC;
import java.io.File;
import java.util.HashSet;
* A map provides the indexation based in hashing.
*
* A tree is created per account.
- *
- * @author David A. Velasco
*/
public class IndexedForest<V> {
/**
+ * Remove the elements that contains account as a part of its key
+ * @param account
+ */
+ public void remove(Account account){
+ Iterator<String> it = mMap.keySet().iterator();
+ while (it.hasNext()) {
+ String key = it.next();
+ Log_OC.d("IndexedForest", "Number of pending downloads= " + mMap.size());
+ if (key.startsWith(account.name)) {
+ mMap.remove(key);
+ }
+ }
+ }
+
+ /**
* Builds a key to index files
*
* @param account Account where the file to download is stored
return account.name + remotePath;
}
-
-
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
- *
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* a progress bar is available in every Android version, because
* {@link NotificationCompat.Builder#setProgress(int, int, boolean)} has no
* real effect for Android < 4.0
- *
- * @author David A. Velasco
*/
public class NotificationBuilderWithProgressBar extends NotificationCompat.Builder {
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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.notifications;
import java.util.Random;
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Access to remote operation performing the creation of a new folder in the ownCloud server.
* Save the new folder in Database
- *
- * @author David A. Velasco
- * @author masensio
*/
public class CreateFolderOperation extends SyncOperation implements OnRemoteOperationListener{
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Creates a new share from a given file
- *
- * @author masensio
- *
*/
import android.content.Context;
public class CreateShareOperation extends SyncOperation {
private static final String TAG = CreateShareOperation.class.getSimpleName();
-
protected FileDataStorageManager mStorageManager;
* To obtain combinations, add the desired values together.
* For instance, for Re-Share, delete, read, update, add 16+8+2+1 = 27.
*/
- public CreateShareOperation(Context context, String path, ShareType shareType, String shareWith, boolean publicUpload,
- String password, int permissions, Intent sendIntent) {
+ public CreateShareOperation(Context context, String path, ShareType shareType, String shareWith,
+ boolean publicUpload, String password, int permissions,
+ Intent sendIntent) {
mContext = context;
mPath = path;
RemoteOperationResult result = ((GetRemoteSharesForFileOperation)operation).execute(client);
if (!result.isSuccess() || result.getData().size() <= 0) {
- operation = new CreateRemoteShareOperation(mPath, mShareType, mShareWith, mPublicUpload, mPassword, mPermissions);
+ operation = new CreateRemoteShareOperation(mPath, mShareType, mShareWith, mPublicUpload,
+ mPassword, mPermissions);
result = ((CreateRemoteShareOperation)operation).execute(client);
}
return result;
}
-
+ public String getPath() {
+ return mPath;
+ }
+
+ public ShareType getShareType() {
+ return mShareType;
+ }
+
+ public String getShareWith() {
+ return mShareWith;
+ }
+
+ public boolean getPublicUpload() {
+ return mPublicUpload;
+ }
+
+ public String getPassword() {
+ return mPassword;
+ }
+
+ public int getPermissions() {
+ return mPermissions;
+ }
+
public Intent getSendIntent() {
return mSendIntent;
}
if (file!=null) {
mSendIntent.putExtra(Intent.EXTRA_TEXT, share.getShareLink());
mSendIntent.putExtra(Intent.EXTRA_SUBJECT, String.format(mContext.getString(R.string.subject_token),
- getClient().getCredentials().getUsername(), mContext.getString(R.string.shared_subject_header),
- file.getFileName(), mContext.getString(R.string.with_you_subject_header)));
+ getClient().getCredentials().getUsername(), file.getFileName()));
file.setPublicLink(share.getShareLink());
file.setShareByLink(true);
getStorageManager().saveFile(file);
-/* ownCloud Android Library is available under MIT license
- * Copyright (C) 2014 ownCloud Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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/>.
*
*/
* When successful, the instance of {@link RemoteOperationResult} passed
* through {@link OnRemoteOperationListener#onRemoteOperationFinish(RemoteOperation,
* RemoteOperationResult)} returns in {@link RemoteOperationResult#getData()}
- * a value of {@link AuthenticationMethod}.
- *
- * @author David A. Velasco
+ * a value of {@link AuthenticationMethod}.
*/
public class DetectAuthenticationMethodOperation extends RemoteOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Remote mDownloadOperation performing the download of a file to an ownCloud server
- *
- * @author David A. Velasco
- * @author masensio
*/
public class DownloadFileOperation extends RemoteOperation {
mDataTransferListeners.remove(listener);
}
}
-
}
-/* ownCloud Android Library is available under MIT license
- * Copyright (C) 2014 ownCloud Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2015 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/>.
*
*/
*
* Checks the existence of a configured ownCloud server in the URL, gets its version
* and finds out what authentication method is needed to access files in it.
- *
- * @author David A. Velasco
- * @author masensio
*/
public class GetServerInfoOperation extends RemoteOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * Copyright (C) 2015 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,
import com.owncloud.android.operations.common.SyncOperation;
/**
- * Provide a list shares for a specific file.
- *
- * @author masensio
- *
+ * Provide a list shares for a specific file.
*/
public class GetSharesForFileOperation extends SyncOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Access to remote operation to get the share files/folders
* Save the data in Database
- *
- * @author masensio
- * @author David A. Velasco
*/
public class GetSharesOperation extends SyncOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Operation mmoving an {@link OCFile} to a different folder.
- *
- * @author David A. Velasco
*/
public class MoveFileOperation extends SyncOperation {
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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.util.ArrayList;
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* properties, and updates the local database with them.
*
* Does NOT enter in the child folders to synchronize their contents also.
- *
- * @author David A. Velasco
*/
public class RefreshFolderOperation extends RemoteOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Remote operation performing the removal of a remote file or folder in the ownCloud server.
- *
- * @author David A. Velasco
- * @author masensio
*/
public class RemoveFileOperation extends SyncOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Remote operation performing the rename of a remote file (or folder?) in the ownCloud server.
- *
- * @author David A. Velasco
- * @author masensio
*/
public class RenameFileOperation extends SyncOperation {
* Constructor
*
* @param remotePath RemotePath of the 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.
*/
public RenameFileOperation(String remotePath, String newName) {
private void saveLocalFile() {
mFile.setFileName(mNewName);
-
+
// try to rename the local copy of the file
if (mFile.isDown()) {
String oldPath = mFile.getStoragePath();
String newPath = parentStoragePath + mNewName;
mFile.setStoragePath(newPath);
- // notify MediaScanner about removed file - TODO really works?
- getStorageManager().triggerMediaScan(oldPath);
+ // notify MediaScanner about removed file
+ getStorageManager().deleteFileInMediaScan(oldPath);
// notify to scan about new file
getStorageManager().triggerMediaScan(newPath);
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * @author masensio
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* Remote operation performing the read of remote file in the ownCloud server.
- *
- * @author David A. Velasco
- * @author masensio
*/
public class SynchronizeFileOperation extends SyncOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* properties, and updates the local database with them.
*
* Does NOT enter in the child folders to synchronize their contents also.
- *
- * @author David A. Velasco
*/
public class SynchronizeFolderOperation extends SyncOperation {
*/
private void startContentSynchronizations(List<SyncOperation> filesToSyncContents, OwnCloudClient client)
throws OperationCancelledException {
-
+
+ Log_OC.v(TAG, "Starting content synchronization... ");
RemoteOperationResult contentsResult = null;
for (SyncOperation op: filesToSyncContents) {
if (mCancellationRequested.get()) {
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * Copyright (C) 2015 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,
/**
* Unshare file/folder
* Save the data in Database
- *
- * @author masensio
*/
public class UnshareLinkOperation extends SyncOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Remote operation that checks the version of an ownCloud server and stores it locally
- *
- * @author David A. Velasco
*/
public class UpdateOCVersionOperation extends RemoteOperation {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.httpclient.methods.PutMethod;
/**
* Remote operation performing the upload of a file to an ownCloud server
- *
- * @author David A. Velasco
*/
public class UploadFileOperation extends RemoteOperation {
private String mOriginalStoragePath = null;
PutMethod mPutMethod = null;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
- private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+ private AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
private Context mContext;
private UploadRemoteFileOperation mUploadOperation;
// 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 (!mOriginalStoragePath.equals(expectedPath) &&
+ mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY) {
if (FileStorageUtils.getUsableSpace(mAccount.name) < originalFile.length()) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_FULL);
} else {
- String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
+ String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) +
+ mFile.getRemotePath();
mFile.setStoragePath(temporalPath);
temporalFile = new File(temporalPath);
int nRead;
byte[] data = new byte[16384];
- while ((nRead = in.read(data, 0, data.length)) != -1) {
+ while (!mCancellationRequested.get() &&
+ (nRead = in.read(data, 0, data.length)) != -1) {
out.write(data, 0, nRead);
}
-
out.flush();
} else {
out = new FileOutputStream(temporalFile);
byte[] buf = new byte[1024];
int len;
- while ((len = in.read(buf)) > 0) {
+ while (!mCancellationRequested.get() && (len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
+ if (mCancellationRequested.get()) {
+ result = new RemoteOperationResult(new OperationCancelledException());
+ }
+
+
} catch (Exception e) {
result = new RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_COPIED);
return result;
if (in != null)
in.close();
} catch (Exception e) {
- Log_OC.d(TAG, "Weird exception while closing input stream for " + mOriginalStoragePath + " (ignoring)", 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);
+ Log_OC.d(TAG, "Weird exception while closing output stream for " +
+ expectedPath + " (ignoring)", e);
}
}
}
}
- localCopyPassed = true;
+ localCopyPassed = (result == null);
/// perform the upload
- if ( mChunked && (new File(mFile.getStoragePath())).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
- mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(),
- mFile.getMimetype());
+ if ( mChunked &&
+ (new File(mFile.getStoragePath())).length() >
+ ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) {
+ mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(),
+ mFile.getRemotePath(), mFile.getMimetype());
} else {
- mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(),
- mFile.getMimetype());
+ mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(),
+ mFile.getRemotePath(), mFile.getMimetype());
}
Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
while (listener.hasNext()) {
mUploadOperation.addDatatransferProgressListener(listener.next());
}
- result = mUploadOperation.execute(client);
-
- /// move local temporal file or original file to its corresponding
- // location in the ownCloud local folder
- if (result.isSuccess()) {
- 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;
+ if (!mCancellationRequested.get()) {
+ result = mUploadOperation.execute(client);
+
+ /// move local temporal file or original file to its corresponding
+ // location in the ownCloud local folder
+ if (result.isSuccess()) {
+ 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;
+ }
}
}
}
temporalFile.delete();
}
if (result.isSuccess()) {
- Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage());
+ 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)
+ complement = " (while copying local file to " +
+ FileStorageUtils.getSavePath(mAccount.name)
+ ")";
}
- Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage() + complement, result.getException());
+ 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());
+ Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath +
+ ": " + result.getLogMessage());
}
}
}
newFile.setFileLength(mFile.getFileLength());
newFile.setMimetype(mFile.getMimetype());
newFile.setModificationTimestamp(mFile.getModificationTimestamp());
- newFile.setModificationTimestampAtLastSyncForData(mFile.getModificationTimestampAtLastSyncForData());
+ newFile.setModificationTimestampAtLastSyncForData(
+ mFile.getModificationTimestampAtLastSyncForData());
// newFile.setEtag(mFile.getEtag())
newFile.setKeepInSync(mFile.keepInSync());
newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties());
* 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
+ * @param wc
+ * @param remotePath
* @return
*/
private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) throws Exception {
}
private boolean existsFile(OwnCloudClient client, String remotePath){
- ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, false);
+ ExistenceCheckRemoteOperation existsOperation =
+ new ExistenceCheckRemoteOperation(remotePath, mContext, false);
RemoteOperationResult result = existsOperation.execute(client);
return result.isSuccess();
}
public void cancel() {
- mUploadOperation.cancel();
+ mCancellationRequested = new AtomicBoolean(true);
+ if (mUploadOperation != null) {
+ mUploadOperation.cancel();
+ }
}
}
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* with local data in the device.
*
* Provides methods to execute the operation both synchronously or asynchronously.
- *
- * @author David A. Velasco
*/
public abstract class SyncOperation extends RemoteOperation {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* The ContentProvider for the ownCloud App.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
- *
*/
public class FileContentProvider extends ContentProvider {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.users.GetRemoteUserNameOperation;
import com.owncloud.android.operations.common.SyncOperation;
public static final String EXTRA_RESULT = "RESULT";
public static final String EXTRA_NEW_PARENT_PATH = "NEW_PARENT_PATH";
public static final String EXTRA_FILE = "FILE";
+ public static final String EXTRA_PASSWORD_SHARE = "PASSWORD_SHARE";
- // TODO review if ALL OF THEM are necessary
- public static final String EXTRA_SUCCESS_IF_ABSENT = "SUCCESS_IF_ABSENT";
- public static final String EXTRA_USERNAME = "USERNAME";
- public static final String EXTRA_PASSWORD = "PASSWORD";
- public static final String EXTRA_AUTH_TOKEN = "AUTH_TOKEN";
public static final String EXTRA_COOKIE = "COOKIE";
public static final String ACTION_CREATE_SHARE = "CREATE_SHARE";
public static final String ACTION_UNSHARE = "UNSHARE";
public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
public static final String ACTION_OAUTH2_GET_ACCESS_TOKEN = "OAUTH2_GET_ACCESS_TOKEN";
- public static final String ACTION_EXISTENCE_CHECK = "EXISTENCE_CHECK";
public static final String ACTION_GET_USER_NAME = "GET_USER_NAME";
public static final String ACTION_RENAME = "RENAME";
public static final String ACTION_REMOVE = "REMOVE";
public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";
public static final String ACTION_SYNC_FILE = "SYNC_FILE";
public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER"; // for the moment, just to download
- //public static final String ACTION_CANCEL_SYNC_FOLDER = "CANCEL_SYNC_FOLDER"; // for the moment, just to download
public static final String ACTION_MOVE_FILE = "MOVE_FILE";
public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED";
private static class Target {
public Uri mServerUrl = null;
public Account mAccount = null;
- public String mUsername = null;
- public String mPassword = null;
- public String mAuthToken = null;
public String mCookie = null;
- public Target(Account account, Uri serverUrl, String username, String password, String authToken,
- String cookie) {
+ public Target(Account account, Uri serverUrl, String cookie) {
mAccount = account;
mServerUrl = serverUrl;
- mUsername = username;
- mPassword = password;
- mAuthToken = authToken;
mCookie = cookie;
}
}
@Override
public void onCreate() {
super.onCreate();
+ Log_OC.d(TAG, "Creating service");
+
/// First worker thread for most of operations
HandlerThread thread = new HandlerThread("Operations thread", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- // WIP: for the moment, only SYNC_FOLDER and CANCEL_SYNC_FOLDER is expected here;
+ Log_OC.d(TAG, "Starting command with id " + startId);
+
+ // WIP: for the moment, only SYNC_FOLDER is expected here;
// the rest of the operations are requested through the Binder
if (ACTION_SYNC_FOLDER.equals(intent.getAction())) {
- /*Log_OC.v("NOW " + TAG + ", thread " + Thread.currentThread().getName(), "Received request to sync folder");*/
-
if (!intent.hasExtra(EXTRA_ACCOUNT) || !intent.hasExtra(EXTRA_REMOTE_PATH)) {
Log_OC.e(TAG, "Not enough information provided in intent");
return START_NOT_STICKY;
Message msg = mSyncFolderHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = itemSyncKey;
- /*Log_OC.v(
- "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Sync folder " + remotePath + " added to queue"
- );*/
mSyncFolderHandler.sendMessage(msg);
}
@Override
public void onDestroy() {
- //Log_OC.wtf(TAG, "onDestroy init" );
+ Log_OC.v(TAG, "Destroying service" );
// Saving cookies
try {
OwnCloudClientManagerFactory.getDefaultSingleton().
e.printStackTrace();
}
- //Log_OC.wtf(TAG, "Clear mUndispatchedFinishedOperations" );
mUndispatchedFinishedOperations.clear();
-
- //Log_OC.wtf(TAG, "onDestroy end" );
+
+ mOperationsBinder = null;
+
+ mOperationsHandler.getLooper().quit();
+ mOperationsHandler = null;
+
+ mSyncFolderHandler.getLooper().quit();
+ mSyncFolderHandler = null;
+
super.onDestroy();
}
*/
@Override
public boolean onUnbind(Intent intent) {
- ((OperationsServiceBinder)mOperationsBinder).clearListeners();
+ mOperationsBinder.clearListeners();
return false; // not accepting rebinding (default behaviour)
}
* @param file A folder in the queue of pending synchronizations
*/
public void cancel(Account account, OCFile file) {
- /*Log_OC.v(
- "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Received request to cancel folder " + file.getRemotePath()
- );*/
mSyncFolderHandler.cancel(account, file);
}
@Override
public void handleMessage(Message msg) {
nextOperation();
+ Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
mService.stopSelf(msg.arg1);
}
);
} else {
OwnCloudCredentials credentials = null;
- if (mLastTarget.mUsername != null &&
- mLastTarget.mUsername.length() > 0) {
- credentials = OwnCloudCredentialsFactory.newBasicCredentials(
- mLastTarget.mUsername,
- mLastTarget.mPassword); // basic
-
- } else if (mLastTarget.mAuthToken != null &&
- mLastTarget.mAuthToken.length() > 0) {
- credentials = OwnCloudCredentialsFactory.newBearerCredentials(
- mLastTarget.mAuthToken); // bearer token
-
- } else if (mLastTarget.mCookie != null &&
+ if (mLastTarget.mCookie != null &&
mLastTarget.mCookie.length() > 0) {
+ // just used for GetUserName
+ // TODO refactor to run GetUserName as AsyncTask in the context of AuthenticatorActivity
credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(
mLastTarget.mCookie); // SAML SSO
}
}
//sendBroadcastOperationFinished(mLastTarget, mCurrentOperation, result);
- mService.dispatchResultToOperationListeners(mLastTarget, mCurrentOperation, result);
+ mService.dispatchResultToOperationListeners(mCurrentOperation, result);
}
}
} else {
Account account = operationIntent.getParcelableExtra(EXTRA_ACCOUNT);
String serverUrl = operationIntent.getStringExtra(EXTRA_SERVER_URL);
- String username = operationIntent.getStringExtra(EXTRA_USERNAME);
- String password = operationIntent.getStringExtra(EXTRA_PASSWORD);
- String authToken = operationIntent.getStringExtra(EXTRA_AUTH_TOKEN);
String cookie = operationIntent.getStringExtra(EXTRA_COOKIE);
target = new Target(
account,
(serverUrl == null) ? null : Uri.parse(serverUrl),
- username,
- password,
- authToken,
cookie
);
String action = operationIntent.getAction();
if (action.equals(ACTION_CREATE_SHARE)) { // Create Share
String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
+ String password = operationIntent.getStringExtra(EXTRA_PASSWORD_SHARE);
Intent sendIntent = operationIntent.getParcelableExtra(EXTRA_SEND_INTENT);
if (remotePath.length() > 0) {
- operation = new CreateShareOperation(OperationsService.this, remotePath, ShareType.PUBLIC_LINK,
- "", false, "", 1, sendIntent);
+ operation = new CreateShareOperation(OperationsService.this, remotePath,
+ ShareType.PUBLIC_LINK,
+ "", false, password, 1, sendIntent);
}
} else if (action.equals(ACTION_UNSHARE)) { // Unshare file
getString(R.string.oauth2_grant_type),
oauth2QueryParameters);
- } else if (action.equals(ACTION_EXISTENCE_CHECK)) {
- // Existence Check
- String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH);
- boolean successIfAbsent = operationIntent.getBooleanExtra(EXTRA_SUCCESS_IF_ABSENT, false);
- operation = new ExistenceCheckRemoteOperation(remotePath, OperationsService.this, successIfAbsent);
-
} else if (action.equals(ACTION_GET_USER_NAME)) {
// Get User Name
operation = new GetRemoteUserNameOperation();
/**
* Notifies the currently subscribed listeners about the end of an operation.
*
- * @param target Account or URL pointing to an OC server.
* @param operation Finished operation.
* @param result Result of the operation.
*/
protected void dispatchResultToOperationListeners(
- Target target, final RemoteOperation operation, final RemoteOperationResult result) {
+ final RemoteOperation operation, final RemoteOperationResult result
+ ) {
int count = 0;
Iterator<OnRemoteOperationListener> listeners = mOperationsBinder.mBoundListeners.keySet().iterator();
while (listeners.hasNext()) {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2015 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
@Override
public void handleMessage(Message msg) {
Pair<Account, String> itemSyncKey = (Pair<Account, String>) msg.obj;
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Handling sync folder " + itemSyncKey.second);*/
doOperation(itemSyncKey.first, itemSyncKey.second);
+ Log_OC.d(TAG, "Stopping after command with id " + msg.arg1);
mService.stopSelf(msg.arg1);
}
*/
private void doOperation(Account account, String remotePath) {
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Getting sync folder " + remotePath);*/
mCurrentSyncOperation = mPendingOperations.get(account, remotePath);
if (mCurrentSyncOperation != null) {
mOwnCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, mService);
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Executing sync folder " + remotePath);*/
result = mCurrentSyncOperation.execute(mOwnCloudClient, mStorageManager);
} catch (AccountsException e) {
} catch (IOException e) {
Log_OC.e(TAG, "Error while trying to get authorization", e);
} finally {
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Removing payload " + remotePath);*/
-
mPendingOperations.removePayload(account, remotePath);
- mService.dispatchResultToOperationListeners(null, mCurrentSyncOperation, result);
+ mService.dispatchResultToOperationListeners(mCurrentSyncOperation, result);
sendBroadcastFinishedSyncFolder(account, remotePath, result.isSuccess());
}
Log_OC.e(TAG, "Cannot cancel with NULL parameters");
return;
}
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Removing sync folder " + file.getRemotePath());*/
Pair<SynchronizeFolderOperation, String> removeResult =
mPendingOperations.remove(account, file.getRemotePath());
SynchronizeFolderOperation synchronization = removeResult.first;
if (synchronization != null) {
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Canceling returned sync of " + file.getRemotePath());*/
synchronization.cancel();
} else {
// TODO synchronize?
if (mCurrentSyncOperation != null && mCurrentAccount != null &&
mCurrentSyncOperation.getRemotePath().startsWith(file.getRemotePath()) &&
account.name.equals(mCurrentAccount.name)) {
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Canceling current sync as descendant: " + mCurrentSyncOperation.getRemotePath());*/
mCurrentSyncOperation.cancel();
- } else {
- /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(),
- "Nothing else in cancelation of " + file.getRemotePath());*/
}
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
* memory. To minimize the impact of this, the service always returns
* Service.START_STICKY, and the later restart of the service is explicitly
* considered in {@link FileObserverService#onStartCommand(Intent, int, int)}.
- *
- * @author David A. Velasco
*/
public class FileObserverService extends Service {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* The second case requires to monitor the folder parent of the files, since a direct
* {@link FileObserver} on it will not receive more events after the file is deleted to
* be replaced.
- *
- * @author David A. Velasco
*/
public class FolderObserver extends FileObserver {
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author sassman\r
+ * @author David A. Velasco\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
* resource types, like FileSync, ConcatsSync, CalendarSync, etc..\r
* \r
* Implements the standard {@link AbstractThreadedSyncAdapter}.\r
- * \r
- * @author sassman\r
- * @author David A. Velasco\r
*/\r
public abstract class AbstractOwnCloudSyncAdapter extends\r
AbstractThreadedSyncAdapter {\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
* ownCloud files.
*
* Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, String, ContentProviderClient, SyncResult)}.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
*/
public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter {
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
/**\r
* Background service for synchronizing remote files with their local state.\r
* \r
- * Serves as a connector to an instance of {@link FileSyncAdapter}, as required by standard Android APIs. \r
- * \r
- * @author Bartek Przybylski\r
- * @author David A. Velasco\r
+ * Serves as a connector to an instance of {@link FileSyncAdapter}, as required by standard Android APIs.\r
*/\r
public class FileSyncService extends Service {\r
\r
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
\r
/**\r
* Represents an Item on the ActionBar.\r
- * \r
- * @author Bartek Przybylski\r
- * \r
*/\r
public class ActionItem {\r
private Drawable mIcon;\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2014 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Lorensius. W. T\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
\r
/**\r
* Represents a custom PopupWindows\r
- * \r
- * @author Lorensius. W. T\r
- * \r
*/\r
public class CustomPopup {\r
protected final View mAnchor;\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2012-2015 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,
import android.util.AttributeSet;
import android.widget.ListView;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
/**
* 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
+ *
+ * The cleanest way I found to overcome the problem due to getHeight() returns 0 until the view is really drawn.
*/
public class ExtendedListView extends ListView {
- private int mPositionToSetAndCenter;
+ private static final String TAG = ExtendedListView.class.getSimpleName();
+
+ private int mPositionToSetAndCenter = 0;
public ExtendedListView(Context context) {
super(context);
/**
* {@inheritDoc}
- *
- *
+ *
+ *
*/
@Override
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
if (mPositionToSetAndCenter > 0) {
+ Log_OC.v(TAG, "Centering around position " + mPositionToSetAndCenter);
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;
}
-}
+
+}
\ No newline at end of file
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2014 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Lorensius. W. T\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
/**\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
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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;
--- /dev/null
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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.util.AttributeSet;
+import android.widget.ImageView;
+
+public class SquareImageView extends ImageView {
+
+ public SquareImageView(Context context) {
+ super(context);
+ }
+
+ public SquareImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
--- /dev/null
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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.util.AttributeSet;
+import android.widget.LinearLayout;
+
+public class SquareLinearLayout extends LinearLayout {
+
+ public SquareLinearLayout(Context context) {
+ super(context);
+ }
+
+ public SquareLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SquareLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* Wrapper activity which will be launched if keep-in-sync file will be modified by external
- * application.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
+ * application.
*/
public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Activity copying the text of the received Intent into the system clibpoard.
- *
- * @author David A. Velasco
*/
@SuppressWarnings("deprecation")
public class CopyToClipboardActivity extends Activity {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* 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 {
/**
* 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
+ * as the secondary text.
*/
public class ErrorsWhileCopyingListAdapter extends ArrayAdapter<String> {
/**
* 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> {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
import com.owncloud.android.operations.CreateShareOperation;
import com.owncloud.android.operations.SynchronizeFolderOperation;
import com.owncloud.android.operations.UnshareLinkOperation;
-
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
+import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.LoadingDialog;
+import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
import com.owncloud.android.utils.ErrorMessageAdapter;
/**
* Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
- *
- * @author David A. Velasco
*/
-public class FileActivity extends SherlockFragmentActivity
-implements OnRemoteOperationListener, ComponentsGetter {
+public class FileActivity extends SherlockFragmentActivity
+ implements OnRemoteOperationListener, ComponentsGetter {
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 EXTRA_FROM_NOTIFICATION= "com.owncloud.android.ui.activity.FROM_NOTIFICATION";
+ public static final String EXTRA_FROM_NOTIFICATION = "com.owncloud.android.ui.activity.FROM_NOTIFICATION";
public static final String TAG = FileActivity.class.getSimpleName();
private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
- private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";;
+ private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID";
+ private static final String DIALOG_SHARE_PASSWORD = "DIALOG_SHARE_PASSWORD";
+ private static final String KEY_TRY_SHARE_AGAIN = "TRY_SHARE_AGAIN";
protected static final long DELAY_TO_REQUEST_OPERATION_ON_ACTIVITY_RESULTS = 200;
protected FileDownloaderBinder mDownloaderBinder = null;
protected FileUploaderBinder mUploaderBinder = null;
private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
+
+ private boolean mTryShareAgain = false;
/**
mFileOperationsHelper.setOpIdWaitingFor(
savedInstanceState.getLong(KEY_WAITING_FOR_OP_ID, Long.MAX_VALUE)
);
+ mTryShareAgain = savedInstanceState.getBoolean(KEY_TRY_SHARE_AGAIN);
} else {
account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
if (mUploadServiceConnection != null) {
bindService(new Intent(this, FileUploader.class), mUploadServiceConnection, Context.BIND_AUTO_CREATE);
}
-
+
}
unbindService(mUploadServiceConnection);
mUploadServiceConnection = null;
}
+
super.onDestroy();
}
* 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
outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification);
outState.putLong(KEY_WAITING_FOR_OP_ID, mFileOperationsHelper.getOpIdWaitingFor());
+ outState.putBoolean(KEY_TRY_SHARE_AGAIN, mTryShareAgain);
}
protected boolean isRedirectingToSetupAccount() {
return mRedirectingToSetupAccount;
}
-
+
+ public boolean isTryShareAgain(){
+ return mTryShareAgain;
+ }
+
+ public void setTryShareAgain(boolean tryShareAgain) {
+ mTryShareAgain = tryShareAgain;
+ }
public OperationsServiceBinder getOperationsServiceBinder() {
return mOperationsServiceBinder;
protected ServiceConnection newTransferenceServiceConnection() {
return null;
}
-
/**
* 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> {
if (result.getCode() == ResultCode.UNAUTHORIZED) {
dismissLoadingDialog();
- Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+ Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result,
+ operation, getResources()),
Toast.LENGTH_LONG);
t.show();
}
+ mTryShareAgain = false;
} else if (operation instanceof CreateShareOperation) {
onCreateShareOperationFinish((CreateShareOperation) operation, result);
}
- private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) {
+ private void onCreateShareOperationFinish(CreateShareOperation operation,
+ RemoteOperationResult result) {
dismissLoadingDialog();
if (result.isSuccess()) {
+ mTryShareAgain = false;
updateFileFromDB();
Intent sendIntent = operation.getSendIntent();
startActivity(sendIntent);
-
- } else {
- Toast t = Toast.makeText(this, ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
- Toast.LENGTH_LONG);
- t.show();
+ } else {
+ // Detect Failure (403) --> needs Password
+ if (result.getCode() == ResultCode.SHARE_FORBIDDEN) {
+ if (!isTryShareAgain()) {
+ SharePasswordDialogFragment dialog =
+ SharePasswordDialogFragment.newInstance(new OCFile(operation.getPath()),
+ operation.getSendIntent());
+ dialog.show(getSupportFragmentManager(), DIALOG_SHARE_PASSWORD);
+ } else {
+ Toast t = Toast.makeText(this,
+ ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+ Toast.LENGTH_LONG);
+ t.show();
+ mTryShareAgain = false;
+ }
+ } else {
+ Toast t = Toast.makeText(this,
+ ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
+ Toast.LENGTH_LONG);
+ t.show();
+ }
}
}
@Override
public FileUploaderBinder getFileUploaderBinder() {
return mUploaderBinder;
- };
+ }
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
package com.owncloud.android.ui.activity;
import java.io.File;
-import java.io.IOException;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import com.owncloud.android.operations.UnshareLinkOperation;
import com.owncloud.android.services.observer.FileObserverService;
import com.owncloud.android.syncadapter.FileSyncAdapter;
-import com.owncloud.android.ui.adapter.FileListListAdapter;
import com.owncloud.android.ui.dialog.CreateFolderDialogFragment;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
/**
* Displays, what files the user has available in his ownCloud.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
*/
public class FileDisplayActivity extends HookActivity implements
setNavigationListWithFolder(file);
if (!stateWasRecovered) {
- Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
+ Log_OC.d(TAG, "Initializing Fragments in onAccountChanged..");
initFragmentsWithFile();
if (file.isFolder()) {
startSyncFolderOperation(file, false);
}
private void startSynchronization() {
- Log_OC.e(TAG, "Got to start sync");
+ Log_OC.d(TAG, "Got to start sync");
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
- Log_OC.e(TAG, "Canceling all syncs for " + MainApp.getAuthority());
+ Log_OC.d(TAG, "Canceling all syncs for " + MainApp.getAuthority());
ContentResolver.cancelSync(null, MainApp.getAuthority()); // cancel the current synchronizations of any ownCloud account
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
- Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
+ Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority());
ContentResolver.requestSync(
getAccount(),
MainApp.getAuthority(), bundle);
} else {
- Log_OC.e(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
+ Log_OC.d(TAG, "Requesting sync for " + getAccount().name + " at " + MainApp.getAuthority() + " with new API");
SyncRequest.Builder builder = new SyncRequest.Builder();
builder.setSyncAdapter(getAccount(), MainApp.getAuthority());
builder.setExpedited(true);
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);
}
} finally {
if (filepath == null) {
- Log_OC.e(TAG, "Couldnt resolve path to file");
+ Log_OC.e(TAG, "Couldn't resolve path to file");
Toast t = Toast.makeText(this, getString(R.string.filedisplay_unexpected_bad_get_content), Toast.LENGTH_LONG);
t.show();
return;
@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");
+ Log_OC.d(TAG, "onSaveInstanceState() start");
super.onSaveInstanceState(outState);
outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
@Override
protected void onResume() {
super.onResume();
- Log_OC.e(TAG, "onResume() start");
+ Log_OC.d(TAG, "onResume() start");
// refresh list of files
refreshListOfFilesFragment();
@Override
protected void onPause() {
- Log_OC.e(TAG, "onPause() start");
+ Log_OC.d(TAG, "onPause() start");
if (mSyncBroadcastReceiver != null) {
unregisterReceiver(mSyncBroadcastReceiver);
//LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
- OwnCloudClient client = null;
+
try {
- OwnCloudAccount ocAccount =
+ OwnCloudClient client;
+ OwnCloudAccount ocAccount =
new OwnCloudAccount(getAccount(), context);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
- // TODO get rid of these exceptions
- } catch (AccountNotFoundException e) {
- e.printStackTrace();
- } catch (AuthenticatorException e) {
- e.printStackTrace();
- } catch (OperationCanceledException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- if (client != null) {
- OwnCloudCredentials cred = client.getCredentials();
- if (cred != null) {
- AccountManager am = AccountManager.get(context);
- if (cred.authTokenExpires()) {
- am.invalidateAuthToken(
- getAccount().type,
- cred.getAuthToken()
- );
- } else {
- am.clearPassword(getAccount());
+
+ if (client != null) {
+ OwnCloudCredentials cred = client.getCredentials();
+ if (cred != null) {
+ AccountManager am = AccountManager.get(context);
+ if (cred.authTokenExpires()) {
+ am.invalidateAuthToken(
+ getAccount().type,
+ cred.getAuthToken()
+ );
+ } else {
+ am.clearPassword(getAccount());
+ }
}
}
+ requestCredentialsUpdate();
+
+ } catch (AccountNotFoundException e) {
+ Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
}
-
- requestCredentialsUpdate();
-
+
}
}
removeStickyBroadcast(intent);
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
(synchResult.isException() && synchResult.getException()
instanceof AuthenticatorException))) {
- OwnCloudClient client = null;
try {
- OwnCloudAccount ocAccount =
+ OwnCloudClient client;
+ OwnCloudAccount ocAccount =
new OwnCloudAccount(getAccount(), context);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
- // TODO get rid of these exceptions
- } catch (AccountNotFoundException e) {
- e.printStackTrace();
- } catch (AuthenticatorException e) {
- e.printStackTrace();
- } catch (OperationCanceledException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- if (client != null) {
- OwnCloudCredentials cred = client.getCredentials();
- if (cred != null) {
- AccountManager am = AccountManager.get(context);
- if (cred.authTokenExpires()) {
- am.invalidateAuthToken(
- getAccount().type,
- cred.getAuthToken()
- );
- } else {
- am.clearPassword(getAccount());
+
+ if (client != null) {
+ OwnCloudCredentials cred = client.getCredentials();
+ if (cred != null) {
+ AccountManager am = AccountManager.get(context);
+ if (cred.authTokenExpires()) {
+ am.invalidateAuthToken(
+ getAccount().type,
+ cred.getAuthToken()
+ );
+ } else {
+ am.clearPassword(getAccount());
+ }
}
}
+ requestCredentialsUpdate();
+
+ } catch (AccountNotFoundException e) {
+ Log_OC.e(TAG, "Account " + getAccount() + " was removed!", e);
}
-
- requestCredentialsUpdate();
-
+
}
}
removeStickyBroadcast(intent);
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
*
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
+
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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.support.v4.widget.SwipeRefreshLayout;
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2011 Bartek Przybylski
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
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.os.Handler;
+import android.os.IBinder;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
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.files.FileOperationsHelper;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.OperationsService;
import com.owncloud.android.ui.RadioButtonPreference;
import com.owncloud.android.utils.DisplayUtils;
+import java.io.File;
+
/**
* An Activity that allows the user to change the application's settings.
- *
- * @author Bartek Przybylski
- * @author David A. Velasco
*/
-public class Preferences extends SherlockPreferenceActivity implements AccountManagerCallback<Boolean> {
+public class Preferences extends SherlockPreferenceActivity
+ implements AccountManagerCallback<Boolean>, ComponentsGetter {
private static final String TAG = "OwnCloudPreferences";
private Preference mPrefInstantVideoUploadPathWiFi;
private String mUploadVideoPath;
+ protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
+ protected FileUploader.FileUploaderBinder mUploaderBinder = null;
+ private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
@SuppressWarnings("deprecation")
@Override
String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
- String recommendText = String.format(getString(R.string.recommend_text), appName, downloadUrl, username);
+ String recommendText = String.format(getString(R.string.recommend_text),
+ appName, downloadUrl, username);
intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
intent.putExtra(Intent.EXTRA_TEXT, recommendText);
startActivity(intent);
-
return(true);
}
loadInstantUploadPath();
loadInstantUploadVideoPath();
+ /* ComponentsGetter */
+ mDownloadServiceConnection = newTransferenceServiceConnection();
+ if (mDownloadServiceConnection != null) {
+ bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+ mUploadServiceConnection = newTransferenceServiceConnection();
+ if (mUploadServiceConnection != null) {
+ bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
}
private void toggleInstantPictureOptions(Boolean value){
// Remove account
am.removeAccount(a, this, mHandler);
+ Log_OC.d(TAG, "Remove an account " + a.name);
}
}
}
@Override
public void run(AccountManagerFuture<Boolean> future) {
if (future.isDone()) {
+ // after remove account
+ Account account = new Account(mAccountName, MainApp.getAccountType());
+ if (!AccountUtils.exists(account, MainApp.getAppContext())) {
+ // Cancel tranfers
+ if (mUploaderBinder != null) {
+ mUploaderBinder.cancel(account);
+ }
+ if (mDownloaderBinder != null) {
+ mDownloaderBinder.cancel(account);
+ }
+ }
+
Account a = AccountUtils.getCurrentOwnCloudAccount(this);
String accountName = "";
if (a == null) {
@Override
protected void onDestroy() {
mDbHandler.close();
+
+ if (mDownloadServiceConnection != null) {
+ unbindService(mDownloadServiceConnection);
+ mDownloadServiceConnection = null;
+ }
+ if (mUploadServiceConnection != null) {
+ unbindService(mUploadServiceConnection);
+ mUploadServiceConnection = null;
+ }
+
super.onDestroy();
}
editor.putString("instant_video_upload_path", mUploadVideoPath);
editor.commit();
}
+
+ // Methods for ComponetsGetter
+ @Override
+ public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
+ return mDownloaderBinder;
+ }
+
+
+ @Override
+ public FileUploader.FileUploaderBinder getFileUploaderBinder() {
+ return mUploaderBinder;
+ }
+
+ @Override
+ public OperationsService.OperationsServiceBinder getOperationsServiceBinder() {
+ return null;
+ }
+
+ @Override
+ public FileDataStorageManager getStorageManager() {
+ return null;
+ }
+
+ @Override
+ public FileOperationsHelper getFileOperationsHelper() {
+ return null;
+ }
+
+ protected ServiceConnection newTransferenceServiceConnection() {
+ return new PreferencesServiceConnection();
+ }
+
+ /** Defines callbacks for service binding, passed to bindService() */
+ private class PreferencesServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+
+ if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
+ mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
+
+ } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service connected");
+ mUploaderBinder = (FileUploader.FileUploaderBinder) service;
+ } else {
+ return;
+ }
+
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
+ Log_OC.d(TAG, "Download service suddenly disconnected");
+ mDownloaderBinder = null;
+ } else if (component.equals(new ComponentName(Preferences.this, FileUploader.class))) {
+ Log_OC.d(TAG, "Upload service suddenly disconnected");
+ mUploaderBinder = null;
+ }
+ }
+ };
}
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* 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
* 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> {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* This can be used to upload things to an ownCloud instance.
- *
- * @author Bartek Przybylski
- *
*/
public class Uploader extends FileActivity implements OnItemClickListener, android.view.View.OnClickListener {
private static final String TAG = "ownCloudUploader";
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* TODO
- *
- * @author masensio
- * @author David A. Velasco
*
*/
public class CertificateCombinedExceptionViewAdapter implements SslUntrustedCertDialog.ErrorViewAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
+ * @author Tobias Kaminsky\r
+ * @author David A. Velasco\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2014 ownCloud Inc.\r
+ * Copyright (C) 2015 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
import android.view.LayoutInflater;\r
import android.view.View;\r
import android.view.ViewGroup;\r
+import android.widget.AbsListView;\r
import android.widget.BaseAdapter;\r
+import android.widget.GridView;\r
import android.widget.ImageView;\r
import android.widget.ListAdapter;\r
-import android.widget.ListView;\r
import android.widget.TextView;\r
\r
import com.owncloud.android.R;\r
/**\r
* This Adapter populates a ListView with all files and folders in an ownCloud\r
* instance.\r
- * \r
- * @author Bartek Przybylski\r
- * @author Tobias Kaminsky\r
- * @author David A. Velasco\r
*/\r
public class FileListListAdapter extends BaseAdapter implements ListAdapter {\r
private final static String PERMISSION_SHARED_WITH_ME = "S";\r
- \r
+\r
private Context mContext;\r
private OCFile mFile = null;\r
private Vector<OCFile> mFiles = null;\r
+ private Vector<OCFile> mFilesOrig = new Vector<OCFile>();\r
private boolean mJustFolders;\r
\r
private FileDataStorageManager mStorageManager;\r
private Account mAccount;\r
private ComponentsGetter mTransferServiceGetter;\r
+ private boolean mGridMode;\r
+\r
+ private enum ViewType {LIST_ITEM, GRID_IMAGE, GRID_ITEM };\r
\r
private SharedPreferences mAppPreferences;\r
\r
Context context,\r
ComponentsGetter transferServiceGetter\r
) {\r
-\r
+ \r
mJustFolders = justFolders;\r
mContext = context;\r
mAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);\r
-\r
mTransferServiceGetter = transferServiceGetter;\r
\r
mAppPreferences = PreferenceManager\r
// initialise thumbnails cache on background thread\r
new ThumbnailsCacheManager.InitDiskCacheTask().execute();\r
\r
+ mGridMode = false;\r
}\r
\r
@Override\r
\r
@Override\r
public View getView(int position, View convertView, ViewGroup parent) {\r
+\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
+ OCFile file = null;\r
+ LayoutInflater inflator = (LayoutInflater) mContext\r
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\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
+ file = mFiles.get(position);\r
+ }\r
+\r
+ // Find out which layout should be displayed\r
+ ViewType viewType;\r
+ if (!mGridMode){\r
+ viewType = ViewType.LIST_ITEM;\r
+ } else if (file.isImage()){\r
+ viewType = ViewType.GRID_IMAGE;\r
+ } else {\r
+ viewType = ViewType.GRID_ITEM;\r
+ }\r
+\r
+ // Create View\r
+ switch (viewType){\r
+ case GRID_IMAGE:\r
+ view = inflator.inflate(R.layout.grid_image, null);\r
+ break;\r
+ case GRID_ITEM:\r
+ view = inflator.inflate(R.layout.grid_item, null);\r
+ break;\r
+ case LIST_ITEM:\r
+ view = inflator.inflate(R.layout.list_item, null);\r
+ break;\r
+ }\r
+\r
+ view.invalidate();\r
\r
- fileName.setText(name);\r
- ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);\r
+ if (file != null){\r
+\r
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);\r
fileIcon.setTag(file.getFileId());\r
- ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);\r
- ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);\r
- sharedWithMeIconV.setVisibility(View.GONE);\r
-\r
- ImageView localStateView = (ImageView) view.findViewById(R.id.imageView2);\r
- localStateView.bringToFront();\r
- FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
- FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
- boolean downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file));\r
- OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder();\r
- downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, file.getRemotePath()));\r
- if (downloading) {\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
+ TextView fileName;\r
+ String name;\r
+\r
+ switch (viewType){\r
+ case LIST_ITEM:\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
+ lastModV.setVisibility(View.VISIBLE);\r
+ lastModV.setText(showRelativeTimestamp(file));\r
+\r
+ checkBoxV.setVisibility(View.GONE);\r
+\r
+ fileSizeV.setVisibility(View.VISIBLE);\r
+ fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
+\r
+ if (!file.isFolder()) {\r
+ AbsListView parentList = (AbsListView)parent;\r
+ if (parentList.getChoiceMode() == AbsListView.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
+ } else { //Folder\r
+ fileSizeV.setVisibility(View.INVISIBLE);\r
+ }\r
+\r
+ case GRID_ITEM:\r
+ // filename\r
+ fileName = (TextView) view.findViewById(R.id.Filename);\r
+ name = file.getFileName();\r
+ fileName.setText(name);\r
+\r
+ case GRID_IMAGE:\r
+ // sharedIcon\r
+ ImageView sharedIconV = (ImageView) view.findViewById(R.id.sharedIcon);\r
+ if (file.isShareByLink()) {\r
+ sharedIconV.setVisibility(View.VISIBLE);\r
+ sharedIconV.bringToFront();\r
+ } else {\r
+ sharedIconV.setVisibility(View.GONE);\r
+ }\r
+\r
+ // local state\r
+ ImageView localStateView = (ImageView) view.findViewById(R.id.localFileIndicator);\r
+ localStateView.bringToFront();\r
+ FileDownloaderBinder downloaderBinder = mTransferServiceGetter.getFileDownloaderBinder();\r
+ FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder();\r
+ boolean downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, file));\r
+ OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder();\r
+ downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, file.getRemotePath()));\r
+ if (downloading) {\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
+ // share with me icon\r
+ if (!file.isFolder()) {\r
+ ImageView sharedWithMeIconV = (ImageView) view.findViewById(R.id.sharedWithMeIcon);\r
+ sharedWithMeIconV.bringToFront();\r
+ if (checkIfFileIsSharedWithMe(file)) {\r
+ sharedWithMeIconV.setVisibility(View.VISIBLE);\r
+ } else {\r
+ sharedWithMeIconV.setVisibility(View.GONE);\r
+ }\r
+ }\r
+\r
+ break;\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
+ // For all Views\r
\r
+ // this if-else is needed even though favorite icon is visible by default\r
+ // because android reuses views in listview\r
+ if (!file.keepInSync()) {\r
+ view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);\r
+ } else {\r
+ view.findViewById(R.id.favoriteIcon).setVisibility(View.VISIBLE);\r
+ }\r
+ \r
+ // No Folder\r
if (!file.isFolder()) {\r
- fileSizeV.setVisibility(View.VISIBLE);\r
- fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(showRelativeTimestamp(file));\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
- // get Thumbnail if file is image\r
if (file.isImage() && file.getRemoteId() != null){\r
- // Thumbnail in Cache?\r
+ // Thumbnail in Cache?\r
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(\r
String.valueOf(file.getRemoteId())\r
- );\r
+ );\r
if (thumbnail != null && !file.needsUpdateThumbnail()){\r
fileIcon.setImageBitmap(thumbnail);\r
} else {\r
-\r
// generate new Thumbnail\r
if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {\r
final ThumbnailsCacheManager.ThumbnailGenerationTask task =\r
new ThumbnailsCacheManager.ThumbnailGenerationTask(\r
fileIcon, mStorageManager, mAccount\r
- );\r
+ );\r
if (thumbnail == null) {\r
thumbnail = ThumbnailsCacheManager.mDefaultImg;\r
}\r
mContext.getResources(), \r
thumbnail, \r
task\r
- );\r
+ );\r
fileIcon.setImageDrawable(asyncDrawable);\r
task.execute(file);\r
}\r
} else {\r
fileIcon.setImageResource(DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName()));\r
}\r
-\r
- if (checkIfFileIsSharedWithMe(file)) {\r
- sharedWithMeIconV.setVisibility(View.VISIBLE);\r
- }\r
- } \r
- else {\r
- // TODO Re-enable when server supports folder-size calculation\r
-// if (FileStorageUtils.getDefaultSavePathFor(mAccount.name, file) != null){\r
-// fileSizeV.setVisibility(View.VISIBLE);\r
-// fileSizeV.setText(getFolderSizeHuman(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)));\r
-// } else {\r
- fileSizeV.setVisibility(View.INVISIBLE);\r
-// }\r
-\r
- lastModV.setVisibility(View.VISIBLE);\r
- lastModV.setText(showRelativeTimestamp(file));\r
- checkBoxV.setVisibility(View.GONE);\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
-\r
+ } else {\r
+ // Folder\r
if (checkIfFileIsSharedWithMe(file)) {\r
fileIcon.setImageResource(R.drawable.shared_with_me_folder);\r
- sharedWithMeIconV.setVisibility(View.VISIBLE);\r
+ } else if (file.isShareByLink()) {\r
+ // If folder is sharedByLink, icon folder must be changed to\r
+ // folder-public one\r
+ fileIcon.setImageResource(R.drawable.folder_public);\r
} else {\r
fileIcon.setImageResource(\r
DisplayUtils.getFileTypeIconId(file.getMimetype(), file.getFileName())\r
);\r
}\r
-\r
- // If folder is sharedByLink, icon folder must be changed to\r
- // folder-public one\r
- if (file.isShareByLink()) {\r
- fileIcon.setImageResource(R.drawable.folder_public);\r
- }\r
- }\r
-\r
- if (file.isShareByLink()) {\r
- sharedIconV.setVisibility(View.VISIBLE);\r
- } else {\r
- sharedIconV.setVisibility(View.GONE);\r
}\r
}\r
\r
}\r
if (mStorageManager != null) {\r
mFiles = mStorageManager.getFolderContent(mFile);\r
+ mFilesOrig.clear();\r
+ mFilesOrig.addAll(mFiles);\r
+ \r
if (mJustFolders) {\r
mFiles = getFolders(mFiles);\r
}\r
mFiles = FileStorageUtils.sortFolder(mFiles);\r
notifyDataSetChanged();\r
\r
- } \r
+ }\r
\r
private CharSequence showRelativeTimestamp(OCFile file){\r
return DisplayUtils.getRelativeDateTimeString(mContext, file.getModificationTimestamp(),\r
DateUtils.SECOND_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, 0);\r
}\r
+\r
+ public void setGridMode(boolean gridMode) {\r
+ mGridMode = gridMode;\r
+ }\r
}\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
import com.owncloud.android.utils.BitmapUtils;
import com.owncloud.android.utils.DisplayUtils;
+import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
+
/**
* 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 {
String name = file.getName();
fileName.setText(name);
- ImageView fileIcon = (ImageView) view.findViewById(R.id.imageView1);
+ ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
if (!file.isDirectory()) {
fileIcon.setImageResource(R.drawable.file);
} else {
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;
+ ListView parentList = (ListView) parent;
if (parentList.getChoiceMode() == ListView.CHOICE_MODE_NONE) {
checkBoxV.setVisibility(View.GONE);
} else {
lastModV.setVisibility(View.GONE);
checkBoxV.setVisibility(View.GONE);
}
-
- view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE); // not GONE; the alignment would change
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);
+
+ // not GONE; the alignment changes; ugly way to keep it
+ view.findViewById(R.id.localFileIndicator).setVisibility(View.INVISIBLE);
+ view.findViewById(R.id.favoriteIcon).setVisibility(View.GONE);
view.findViewById(R.id.sharedIcon).setVisibility(View.GONE);
view.findViewById(R.id.sharedWithMeIcon).setVisibility(View.GONE);
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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;
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* TODO
- *
- * @author masensio
- * @author David A. Velasco
*/
public class SslCertificateViewAdapter implements SslUntrustedCertDialog.CertificateViewAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog to show an Untrusted Certificate
- *
- * @author masensio
- * @author David A. Velasco
- *
*/
public class SslErrorViewAdapter implements SslUntrustedCertDialog.ErrorViewAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import android.widget.TextView;
/**
- *
- * @author masensio
- * @author David A. Velasco
*
*/
public class X509CertificateViewAdapter implements SslUntrustedCertDialog.CertificateViewAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* Dialog which will be displayed to user upon keep-in-sync file conflict.
- *
- * @author Bartek Przybylski
- *
*/
public class ConflictsResolveDialog extends SherlockDialogFragment {
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog to input the name for a new folder to create.
*
- * Triggers the folder creation when name is confirmed.
- *
- * @author David A. Velasco
+ * Triggers the folder creation when name is confirmed.
*/
public class CreateFolderDialogFragment
extends SherlockDialogFragment implements DialogInterface.OnClickListener {
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
- * Copyright (C) 2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog requiring confirmation before removing a given OCFile.
*
- * Triggers the removal according to the user response.
- *
- * @author David A. Velasco
+ * Triggers the removal according to the user response.
*/
import java.util.Vector;
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2014 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
/**
* Dialog to input a new name for a file or folder to rename.
*
- * Triggers the rename operation when name is confirmed.
- *
- * @author David A. Velasco
+ * Triggers the rename operation when name is confirmed.
*/
public class RenameFileDialogFragment
extends SherlockDialogFragment implements DialogInterface.OnClickListener {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author Maria Asensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog to show the WebView for SAML Authentication
- *
- * @author Maria Asensio
- * @author David A. Velasco
*/
public class SamlWebViewDialog extends SherlockDialogFragment {
* @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);
public SamlWebViewDialog() {
super();
- Log_OC.d(TAG, "constructor");
}
@Override
public void onAttach(Activity activity) {
- Log_OC.d(TAG, "onAttach");
+ Log_OC.v(TAG, "onAttach");
super.onAttach(activity);
try {
mSsoWebViewClientListener = (SsoWebViewClientListener) activity;
@SuppressLint("SetJavaScriptEnabled")
@Override
public void onCreate(Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreate, savedInstanceState is " + savedInstanceState);
+ Log_OC.v(TAG, "onCreate, savedInstanceState is " + savedInstanceState);
super.onCreate(savedInstanceState);
setRetainInstance(true);
@SuppressLint("SetJavaScriptEnabled")
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log_OC.d(TAG, "onCreateView, savedInsanceState is " + savedInstanceState);
+ Log_OC.v(TAG, "onCreateView, savedInsanceState is " + savedInstanceState);
// Inflate layout of the dialog
RelativeLayout ssoRootView = (RelativeLayout) inflater.inflate(R.layout.sso_dialog, container, false); // null parent view because it will go in the dialog layout
mSsoWebView.setFocusableInTouchMode(true);
mSsoWebView.setClickable(true);
- CookieManager cookieManager = CookieManager.getInstance();
- cookieManager.setAcceptCookie(true);
- cookieManager.removeAllCookie();
- mSsoWebView.loadUrl(mInitialUrl);
-
WebSettings webSettings = mSsoWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(false);
webSettings.setSavePassword(false);
webSettings.setUserAgentString(OwnCloudClient.USER_AGENT);
webSettings.setSaveFormData(false);
+
+ CookieManager cookieManager = CookieManager.getInstance();
+ cookieManager.setAcceptCookie(true);
+ cookieManager.removeAllCookie();
+
+ mSsoWebView.loadUrl(mInitialUrl);
}
mWebViewClient.setTargetUrl(mTargetUrl);
@Override
public void onSaveInstanceState(Bundle outState) {
- Log_OC.d(TAG, "onSaveInstanceState being CALLED");
+ Log_OC.v(TAG, "onSaveInstanceState being CALLED");
super.onSaveInstanceState(outState);
// save URLs
@Override
public void onDestroyView() {
- Log_OC.d(TAG, "onDestroyView");
+ Log_OC.v(TAG, "onDestroyView");
if ((ViewGroup)mSsoWebView.getParent() != null) {
((ViewGroup)mSsoWebView.getParent()).removeView(mSsoWebView);
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");
+ Log_OC.v(TAG, "onDestroy");
super.onDestroy();
}
@Override
public void onDetach() {
- Log_OC.d(TAG, "onDetach");
+ Log_OC.v(TAG, "onDetach");
mSsoWebViewClientListener = null;
mWebViewClient = null;
super.onDetach();
@Override
public void onStart() {
- Log_OC.d(TAG, "onStart");
+ Log_OC.v(TAG, "onStart");
super.onStart();
}
@Override
public void onStop() {
- Log_OC.d(TAG, "onStop");
+ Log_OC.v(TAG, "onStop");
super.onStop();
}
@Override
public void onResume() {
- Log_OC.d(TAG, "onResume");
+ Log_OC.v(TAG, "onResume");
super.onResume();
mSsoWebView.onResume();
}
@Override
public void onPause() {
- Log_OC.d(TAG, "onPause");
+ Log_OC.v(TAG, "onPause");
mSsoWebView.onPause();
super.onPause();
}
@Override
public int show (FragmentTransaction transaction, String tag) {
- Log_OC.d(TAG, "show (transaction)");
+ Log_OC.v(TAG, "show (transaction)");
return super.show(transaction, tag);
}
@Override
public void show (FragmentManager manager, String tag) {
- Log_OC.d(TAG, "show (manager)");
+ Log_OC.v(TAG, "show (manager)");
super.show(manager, tag);
}
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Dialog showing a list activities able to resolve a given Intent,
* filtering out the activities matching give package names.
- *
- * @author David A. Velasco
*/
public class ShareLinkToDialog extends SherlockDialogFragment {
} else {
// Create a new share resource
((ComponentsGetter)getSherlockActivity()).getFileOperationsHelper()
- .shareFileWithLinkToApp(mFile, mIntent);
+ .shareFileWithLinkToApp(mFile, "", mIntent);
}
}
})
--- /dev/null
+/**
+ * ownCloud Android client application
+ * @author masensio
+ * Copyright (C) 2015 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.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.ui.activity.FileActivity;
+
+/**
+ * Dialog to input the password for sharing a file/folder.
+ *
+ * Triggers the share when the password is introduced.
+ */
+
+public class SharePasswordDialogFragment extends SherlockDialogFragment
+ implements DialogInterface.OnClickListener {
+
+ private static final String ARG_FILE = "FILE";
+ private static final String ARG_SEND_INTENT = "SEND_INTENT";
+
+ public static final String PASSWORD_FRAGMENT = "PASSWORD_FRAGMENT";
+
+ private OCFile mFile;
+ private Intent mSendIntent;
+
+ /**
+ * Public factory method to create new SharePasswordDialogFragment instances.
+ *
+ * @param file
+ * @param sendIntent
+ * @return Dialog ready to show.
+ */
+ public static SharePasswordDialogFragment newInstance(OCFile file, Intent sendIntent) {
+ SharePasswordDialogFragment frag = new SharePasswordDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_FILE, file);
+ args.putParcelable(ARG_SEND_INTENT, sendIntent);
+ frag.setArguments(args);
+ return frag;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mFile = getArguments().getParcelable(ARG_FILE);
+ mSendIntent = getArguments().getParcelable(ARG_SEND_INTENT);
+
+ // Inflate the layout for the dialog
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ View v = inflater.inflate(R.layout.password_dialog, null);
+
+ // Setup layout
+ EditText inputText = ((EditText)v.findViewById(R.id.share_password));
+ inputText.setText("");
+ inputText.requestFocus();
+
+ // Build the dialog
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setView(v)
+ .setPositiveButton(R.string.common_ok, this)
+ .setNegativeButton(R.string.common_cancel, this)
+ .setTitle(R.string.share_link_password_title);
+ Dialog d = builder.create();
+ d.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ return d;
+ }
+
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ // Enable the flag "Share again"
+ ((FileActivity) getSherlockActivity()).setTryShareAgain(true);
+
+ String password =
+ ((TextView)(getDialog().findViewById(R.id.share_password)))
+ .getText().toString();
+
+ if (password.length() <= 0) {
+ Toast.makeText(
+ getActivity(),
+ R.string.share_link_empty_password,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ // Share the file
+ ((FileActivity)getSherlockActivity()).getFileOperationsHelper()
+ .shareFileWithLinkToApp(mFile, password, mSendIntent);
+
+ } else {
+ // Disable the flag "Share again"
+ ((FileActivity) getSherlockActivity()).setTryShareAgain(false);
+ }
+ }
+}
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* to decide trust on it or not.
*
* Abstract implementation of common functionality for different dialogs that
- * get the information about the error and the certificate from different classes.
- *
- * @author masensio
- * @author David A. Velasco
+ * get the information about the error and the certificate from different classes.
*/
public class SslUntrustedCertDialog extends SherlockDialogFragment {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2012-2015 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,
import java.util.ArrayList;
+import android.content.Context;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.GridView;
import android.widget.ListAdapter;
-import android.widget.ListView;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.ExtendedListView;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
+import com.owncloud.android.ui.adapter.FileListListAdapter;
+
+import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
/**
* TODO extending SherlockListFragment instead of SherlockFragment
private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
- protected ExtendedListView mList;
-
- private SwipeRefreshLayout mRefreshLayout;
+ private SwipeRefreshLayout mRefreshListLayout;
+ private SwipeRefreshLayout mRefreshGridLayout;
private SwipeRefreshLayout mRefreshEmptyLayout;
private TextView mEmptyListMessage;
private OnEnforceableRefreshListener mOnRefreshListener = null;
-
- public void setListAdapter(ListAdapter listAdapter) {
- mList.setAdapter(listAdapter);
- mList.invalidate();
+ protected AbsListView mCurrentListView;
+ private ExtendedListView mListView;
+ private View mListFooterView;
+ private GridViewWithHeaderAndFooter mGridView;
+ private View mGridFooterView;
+
+ private ListAdapter mAdapter;
+
+
+ protected void setListAdapter(ListAdapter listAdapter) {
+ mAdapter = listAdapter;
+ mCurrentListView.setAdapter(listAdapter);
+ mCurrentListView.invalidate();
}
- public void setFooterView(View footer) {
- mList.addFooterView(footer, null, false);
- mList.invalidate();
+ protected AbsListView getListView() {
+ return mCurrentListView;
}
- public ListView getListView() {
- return mList;
+
+ protected void switchToGridView() {
+ if ((mCurrentListView == mListView)) {
+
+ mListView.setAdapter(null);
+ mRefreshListLayout.setVisibility(View.GONE);
+
+ if (mAdapter instanceof FileListListAdapter) {
+ ((FileListListAdapter) mAdapter).setGridMode(true);
+ }
+ mGridView.setAdapter(mAdapter);
+ mRefreshGridLayout.setVisibility(View.VISIBLE);
+
+ mCurrentListView = mGridView;
+ }
}
+
+ protected void switchToListView() {
+ if (mCurrentListView == mGridView) {
+ mGridView.setAdapter(null);
+ mRefreshGridLayout.setVisibility(View.GONE);
+
+ if (mAdapter instanceof FileListListAdapter) {
+ ((FileListListAdapter) mAdapter).setGridMode(false);
+ }
+ mListView.setAdapter(mAdapter);
+ mRefreshListLayout.setVisibility(View.VISIBLE);
+ mCurrentListView = mListView;
+ }
+ }
+
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- Log_OC.e(TAG, "onCreateView");
+ Log_OC.d(TAG, "onCreateView");
View v = inflater.inflate(R.layout.list_fragment, null);
- mEmptyListMessage = (TextView) v.findViewById(R.id.empty_list_view);
- mList = (ExtendedListView) (v.findViewById(R.id.list_root));
- mList.setOnItemClickListener(this);
- mList.setDivider(getResources().getDrawable(R.drawable.uploader_list_separator));
- mList.setDividerHeight(1);
+ mListView = (ExtendedListView)(v.findViewById(R.id.list_root));
+ mListView.setOnItemClickListener(this);
+ mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
+
+ mGridView = (GridViewWithHeaderAndFooter) (v.findViewById(R.id.grid_root));
+ mGridView.setNumColumns(GridView.AUTO_FIT);
+ mGridView.setOnItemClickListener(this);
+ mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
if (savedInstanceState != null) {
int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
- setReferencePosition(referencePosition);
+ if (mCurrentListView == mListView) {
+ Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
+ mListView.setAndCenterSelection(referencePosition);
+ } else {
+ Log_OC.v(TAG, "Setting grid position " + referencePosition);
+ mGridView.setSelection(referencePosition);
+ }
}
- // Pull down refresh
- mRefreshLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files);
- mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_refresh_files_emptyView);
+ // Pull-down to refresh layout
+ mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_list);
+ mRefreshGridLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_grid);
+ mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_empty);
+ mEmptyListMessage = (TextView) v.findViewById(R.id.empty_list_view);
- onCreateSwipeToRefresh(mRefreshLayout);
+ onCreateSwipeToRefresh(mRefreshListLayout);
+ onCreateSwipeToRefresh(mRefreshGridLayout);
onCreateSwipeToRefresh(mRefreshEmptyLayout);
-
- mList.setEmptyView(mRefreshEmptyLayout);
+
+ mListView.setEmptyView(mRefreshEmptyLayout);
+ mGridView.setEmptyView(mRefreshEmptyLayout);
+
+ mCurrentListView = mListView; // list as default
return v;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
- Log_OC.e(TAG, "onSaveInstanceState()");
+ Log_OC.d(TAG, "onSaveInstanceState()");
savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
* 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
+ * 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;
+ if (mCurrentListView != null) {
+ return (mCurrentListView.getFirstVisiblePosition() + mCurrentListView.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);
- }
- }
-
/*
* Restore index and position
// needs to be checked; not every browse-up had a browse-down before
int index = mIndexes.remove(mIndexes.size() - 1);
-
- int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
-
+ final int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
int top = mTops.remove(mTops.size() - 1);
-
- mList.setSelectionFromTop(firstPosition, top);
-
- // Move the scroll if the selection is not visible
- int indexPosition = mHeightCell*index;
- int height = mList.getHeight();
-
- if (indexPosition > height) {
- if (android.os.Build.VERSION.SDK_INT >= 11)
- {
- mList.smoothScrollToPosition(index);
+
+ Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: " + top + "; index: " + index);
+
+ if (mCurrentListView == mListView) {
+ if (mHeightCell*index <= mListView.getHeight()) {
+ mListView.setSelectionFromTop(firstPosition, top);
+ } else {
+ mListView.setSelectionFromTop(index, 0);
}
- else if (android.os.Build.VERSION.SDK_INT >= 8)
- {
- mList.setSelectionFromTop(index, 0);
+
+ } else {
+ if (mHeightCell*index <= mGridView.getHeight()) {
+ mGridView.setSelection(firstPosition);
+ //mGridView.smoothScrollToPosition(firstPosition);
+ } else {
+ mGridView.setSelection(index);
+ //mGridView.smoothScrollToPosition(index);
}
-
}
+
}
}
mIndexes.add(index);
- int firstPosition = mList.getFirstVisiblePosition();
+ int firstPosition = mCurrentListView.getFirstVisiblePosition();
mFirstPositions.add(firstPosition);
- View view = mList.getChildAt(0);
+ View view = mCurrentListView.getChildAt(0);
int top = (view == null) ? 0 : view.getTop() ;
mTops.add(top);
@Override
public void onRefresh() {
- // to be @overriden
- mRefreshLayout.setRefreshing(false);
+ mRefreshListLayout.setRefreshing(false);
+ mRefreshGridLayout.setRefreshing(false);
mRefreshEmptyLayout.setRefreshing(false);
-
+
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh();
}
/**
- * Enables swipe gesture
- */
- public void enableSwipe() {
- mRefreshLayout.setEnabled(true);
- }
-
- /**
- * Disables swipe gesture. It prevents manual gestures but keeps the option you show
- * refreshing programmatically.
- */
- public void disableSwipe() {
- mRefreshLayout.setEnabled(false);
- }
-
- /**
- * It shows the SwipeRefreshLayout progress
- */
- public void showSwipeProgress() {
- mRefreshLayout.setRefreshing(true);
- }
-
- /**
- * It shows the SwipeRefreshLayout progress
+ * Disables swipe gesture.
+ *
+ * Sets the 'enabled' state of the refresh layouts contained in the fragment.
+ *
+ * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
+ *
+ * @param enabled Desired state for capturing swipe gesture.
*/
- public void hideSwipeProgress() {
- mRefreshLayout.setRefreshing(false);
+ public void setSwipeEnabled(boolean enabled) {
+ mRefreshListLayout.setEnabled(enabled);
+ mRefreshGridLayout.setEnabled(enabled);
+ mRefreshEmptyLayout.setEnabled(enabled);
}
/**
@Override
public void onRefresh(boolean ignoreETag) {
- mRefreshLayout.setRefreshing(false);
+ mRefreshListLayout.setRefreshing(false);
+ mRefreshGridLayout.setRefreshing(false);
mRefreshEmptyLayout.setRefreshing(false);
if (mOnRefreshListener != null) {
mOnRefreshListener.onRefresh(ignoreETag);
}
}
+
+
+ protected void setChoiceMode(int choiceMode) {
+ mListView.setChoiceMode(choiceMode);
+ mGridView.setChoiceMode(choiceMode);
+ }
+
+ protected void registerForContextMenu() {
+ registerForContextMenu(mListView);
+ registerForContextMenu(mGridView);
+ mListView.setOnCreateContextMenuListener(this);
+ mGridView.setOnCreateContextMenuListener(this);
+ }
+
+ /**
+ * TODO doc
+ * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
+ *
+ * @param enabled
+ */
+ protected void setFooterEnabled(boolean enabled) {
+ if (enabled) {
+ if (mGridView.getFooterViewCount() == 0) {
+ if (mGridFooterView.getParent() != null ) {
+ ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
+ }
+ mGridView.addFooterView(mGridFooterView, null, false);
+ }
+ mGridFooterView.invalidate();
+
+ if (mListView.getFooterViewsCount() == 0) {
+ if (mListFooterView.getParent() != null ) {
+ ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
+ }
+ mListView.addFooterView(mListFooterView, null, false);
+ }
+ mListFooterView.invalidate();
+
+ } else {
+ mGridView.removeFooterView(mGridFooterView);
+ mListView.removeFooterView(mListFooterView);
+ }
+ }
+
+ /**
+ * TODO doc
+ * @param text
+ */
+ protected void setFooterText(String text) {
+ if (text != null && text.length() > 0) {
+ ((TextView)mListFooterView.findViewById(R.id.footerText)).setText(text);
+ ((TextView)mGridFooterView.findViewById(R.id.footerText)).setText(text);
+ setFooterEnabled(true);
+
+ } else {
+ setFooterEnabled(false);
+ }
+ }
+
}
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* 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 {
/**
- * Helper class responsible for updating the progress bar shown for file uploading or downloading
- *
- * @author David A. Velasco
+ * Helper class responsible for updating the progress bar shown for file uploading or downloading
*/
private class ProgressListener implements OnDatatransferProgressListener {
int mLastPercent = 0;
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Common methods for {@link Fragment}s containing {@link OCFile}s
- *
- * @author David A. Velasco
- *
*/
public class FileFragment extends SherlockFragment {
/**
* Interface to implement by any Activity that includes some instance of FileListFragment
* Interface to implement by any Activity that includes some instance of FileFragment
- *
- * @author David A. Velasco
*/
public interface ContainerActivity extends ComponentsGetter {
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
/**
* 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";
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);
- disableSwipe(); // Disable pull refresh
+ setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ setSwipeEnabled(false); // Disable pull-to-refresh
setMessageForEmptyList(getString(R.string.local_file_list_empty));
Log_OC.i(TAG, "onCreateView() end");
return v;
- }
+ }
/**
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.
*/
directory = directory.getParentFile();
}
- mList.clearChoices(); // by now, only files in the same directory will be kept as selected
+ mCurrentListView.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);
+ mCurrentListView.setSelection(0);
}
mDirectory = directory;
}
*/
public String[] getCheckedFilePaths() {
ArrayList<String> result = new ArrayList<String>();
- SparseBooleanArray positions = mList.getCheckedItemPositions();
+ SparseBooleanArray positions = mCurrentListView.getCheckedItemPositions();
if (positions.size() > 0) {
for (int i = 0; i < positions.size(); i++) {
if (positions.get(positions.keyAt(i)) == true) {
- result.add(((File) mList.getItemAtPosition(positions.keyAt(i))).getAbsolutePath());
+ result.add(((File) mCurrentListView.getItemAtPosition(positions.keyAt(i))).getAbsolutePath());
}
}
/**
* 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
+ * @param directory
*/
public void onDirectoryClick(File directory);
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author Bartek Przybylski
+ * @author masensio
+ * @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2014 ownCloud Inc.
+ * Copyright (C) 2015 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,
package com.owncloud.android.ui.fragment;
import java.io.File;
-import java.util.Vector;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.TextView;
-import android.view.LayoutInflater;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager;
* A Fragment that lists all files and folders in a given path.
*
* TODO refactorize to get rid of direct dependency on FileDisplayActivity
- *
- * @author Bartek Przybylski
- * @author masensio
- * @author David A. Velasco
*/
public class OCFileListFragment extends ExtendedListFragment {
private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
+ private final static Double THUMBNAIL_THRESHOLD = 0.5;
+
private FileFragment.ContainerActivity mContainerActivity;
private OCFile mFile = null;
private FileListListAdapter mAdapter;
- private View mFooterView;
+ private boolean mJustFolders;
private OCFile mTargetFile;
mFile = savedInstanceState.getParcelable(KEY_FILE);
}
- mFooterView = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(
- R.layout.list_footer, null, false);
- setFooterView(mFooterView);
+ if (mJustFolders) {
+ setFooterEnabled(false);
+ } else {
+ setFooterEnabled(true);
+ }
Bundle args = getArguments();
- boolean justFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
+ mJustFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false);
mAdapter = new FileListListAdapter(
- justFolders,
+ mJustFolders,
getSherlockActivity(),
mContainerActivity
);
setListAdapter(mAdapter);
- registerForContextMenu(getListView());
- getListView().setOnCreateContextMenuListener(this);
- }
+ registerForContextMenu();
+ }
/**
* Saves the current listed folder.
mAdapter.swapDirectory(directory, storageManager);
if (mFile == null || !mFile.equals(directory)) {
- mList.setSelectionFromTop(0, 0);
+ mCurrentListView.setSelection(0);
}
mFile = directory;
-
- // Update Footer
- TextView footerText = (TextView) mFooterView.findViewById(R.id.footerText);
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
- footerText.setText(generateFooterText(directory));
- Log_OC.d("footer", String.valueOf(System.currentTimeMillis()));
+
+ updateLayout();
+
}
}
-
- private String generateFooterText(OCFile directory) {
- Integer files = 0;
- Integer folders = 0;
- FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
- Vector<OCFile> mFiles = storageManager.getFolderContent(mFile);
+ private void updateLayout() {
+ if (!mJustFolders) {
+ int filesCount = 0, foldersCount = 0, imagesCount = 0;
+ int count = mAdapter.getCount();
+ OCFile file;
+ for (int i=0; i < count ; i++) {
+ file = (OCFile) mAdapter.getItem(i);
+ if (file.isFolder()) {
+ foldersCount++;
+ } else {
+ filesCount++;
+ if (file.isImage()){
+ imagesCount++;
+ }
+ }
+ }
+ // set footer text
+ setFooterText(generateFooterText(filesCount, foldersCount));
- for (OCFile ocFile : mFiles) {
- if (ocFile.isFolder()) {
- folders++;
+ // decide grid vs list view
+ if (((double)imagesCount / (double)filesCount) >= THUMBNAIL_THRESHOLD) {
+ switchToGridView();
} else {
- files++;
+ switchToListView();
}
}
+ }
+ private String generateFooterText(int filesCount, int foldersCount) {
String output = "";
-
- if (files > 0){
- if (files == 1) {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_file);
+ if (filesCount > 0){
+ if (filesCount == 1) {
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_file);
} else {
- output = output + files.toString() + " " + getResources().getString(R.string.file_list_files);
+ output = output + filesCount + " " + getResources().getString(R.string.file_list_files);
}
}
- if (folders > 0 && files > 0){
+ if (foldersCount > 0 && filesCount > 0){
output = output + ", ";
}
- if (folders == 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folder);
- } else if (folders > 1) {
- output = output + folders.toString() + " " + getResources().getString(R.string.file_list_folders);
+ if (foldersCount == 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folder);
+ } else if (foldersCount > 1) {
+ output = output + foldersCount + " " + getResources().getString(R.string.file_list_folders);
}
-
+
return output;
}
-
+
+
public void sortByName(boolean descending) {
mAdapter.setSortOrder(FileStorageUtils.SORT_NAME, descending);
}
-/* ownCloud Android client application
- *
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* This Fragment is used to monitor the progress of a file downloading.
- *
- * @author David A. Velasco
*/
public class FileDownloadFragment extends FileFragment implements OnClickListener {
/**
- * Helper class responsible for updating the progress bar shown for file uploading or downloading
- *
- * @author David A. Velasco
+ * Helper class responsible for updating the progress bar shown for file uploading or downloading
*/
private class ProgressListener implements OnDatatransferProgressListener {
int mLastPercent = 0;
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* 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,
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import com.owncloud.android.ui.dialog.RemoveFileDialogFragment;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.utils.BitmapUtils;
-import com.owncloud.android.utils.TouchImageViewCustom;
+import third_parties.michaelOrtiz.TouchImageViewCustom;
/**
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import com.owncloud.android.utils.FileStorageUtils;
/**
- * Adapter class that provides Fragment instances
- *
- * @author David A. Velasco
+ * Adapter class that provides Fragment instances
*/
//public class PreviewImagePagerAdapter extends PagerAdapter {
public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* 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 {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
* 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
+ * the activity is finished.
*/
public class PreviewVideoActivity extends FileActivity implements OnCompletionListener, OnPreparedListener, OnErrorListener {
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
/**
* Utility class with methods for decoding Bitmaps.
- *
- * @author David A. Velasco
*/
public class BitmapUtils {
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
+ * @author David A. Velasco\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
import java.util.HashMap;\r
import java.util.HashSet;\r
import java.util.Set;\r
+import java.util.Vector;\r
\r
import android.annotation.TargetApi;\r
import android.content.Context;\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
\r
/**\r
* Converts Unix time to human readable format\r
- * @param miliseconds that have passed since 01/01/1970\r
+ * @param milliseconds 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
}\r
return path;\r
}\r
+\r
}\r
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
+ * @author masensio
* Copyright (C) 2014 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
import com.owncloud.android.operations.UploadFileOperation;
/**
- * Class to choose proper error messages to show to the user depending on the results of operations, always following the same policy
- *
- * @author masensio
- *
+ * Class to choose proper error messages to show to the user depending on the results of operations,
+ * always following the same policy
*/
public class ErrorMessageAdapter {
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * @author David A. Velasco
+ * Copyright (C) 2015 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,
import android.net.Uri;
import android.os.Environment;
import android.os.StatFs;
+import android.webkit.MimeTypeMap;
/**
* Static methods to help in access to local file system.
- *
- * @author David A. Velasco
*/
public class FileStorageUtils {
public static Integer mSortOrder;
/**
* Creates and populates a new {@link RemoteFile} object with the data read from an {@link OCFile}.
*
- * @param oCFile OCFile
+ * @param ocFile OCFile
* @return New RemoteFile instance representing the resource described by ocFile.
*/
public static RemoteFile fillRemoteFile(OCFile ocFile){
/**
* Sorts list by Date
- * @param sortAscending true: ascending, false: descending
+ * @param files
*/
public static Vector<OCFile> sortByDate(Vector<OCFile> files){
final Integer val;
/**
* Sorts list by Name
- * @param sortAscending true: ascending, false: descending
+ * @param files files to sort
*/
public static Vector<OCFile> sortByName(Vector<OCFile> files){
final Integer val;
return result;
}
return 0;
- }
+ }
+
+ /**
+ * Mimetype String of a file
+ * @param path
+ * @return
+ */
+ public static String getMimeTypeFromName(String path) {
+ String extension = "";
+ int pos = path.lastIndexOf('.');
+ if (pos >= 0) {
+ extension = path.substring(pos + 1);
+ }
+ String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
+ return (result != null) ? result : "";
+ }
}
-/* ownCloud Android client application\r
+/**\r
+ * ownCloud Android client application\r
+ *\r
+ * @author Bartek Przybylski\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2015 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
\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
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
+++ /dev/null
-/*
- * TouchImageView.java
- * By: Michael Ortiz
- * Updated By: Patrick Lackemacher
- * Updated By: Babay88
- * Updated By: @ipsilondev
- * Updated By: hank-cp
- * Updated By: singpolyma
- * -------------------
- * Extends Android ImageView to include pinch zooming, panning, fling and double tap zoom.
- */
-
-package com.owncloud.android.utils;
-
-import com.owncloud.android.ui.preview.ImageViewCustom;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.View;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.widget.OverScroller;
-import android.widget.Scroller;
-
-public class TouchImageViewCustom extends ImageViewCustom {
- private static final String DEBUG = "DEBUG";
-
- //
- // SuperMin and SuperMax multipliers. Determine how much the image can be
- // zoomed below or above the zoom boundaries, before animating back to the
- // min/max zoom boundary.
- //
- private static final float SUPER_MIN_MULTIPLIER = .75f;
- private static final float SUPER_MAX_MULTIPLIER = 1.25f;
-
- //
- // Scale of image ranges from minScale to maxScale, where minScale == 1
- // when the image is stretched to fit view.
- //
- private float normalizedScale;
-
- //
- // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal.
- // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix
- // saved prior to the screen rotating.
- //
- private Matrix matrix, prevMatrix;
-
- private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM };
- private State state;
-
- private float minScale;
- private float maxScale;
- private float superMinScale;
- private float superMaxScale;
- private float[] m;
-
- private Context context;
- private Fling fling;
-
- private ScaleType mScaleType;
-
- private boolean imageRenderedAtLeastOnce;
- private boolean onDrawReady;
-
- private ZoomVariables delayedZoomVariables;
-
- //
- // Size of view and previous view size (ie before rotation)
- //
- private int viewWidth, viewHeight, prevViewWidth, prevViewHeight;
-
- //
- // Size of image when it is stretched to fit view. Before and After rotation.
- //
- private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight;
-
- private ScaleGestureDetector mScaleDetector;
- private GestureDetector mGestureDetector;
- private GestureDetector.OnDoubleTapListener doubleTapListener = null;
- private OnTouchListener userTouchListener = null;
- private OnTouchImageViewListener touchImageViewListener = null;
-
- public TouchImageViewCustom(Context context) {
- super(context);
- sharedConstructing(context);
- }
-
- public TouchImageViewCustom(Context context, AttributeSet attrs) {
- super(context, attrs);
- sharedConstructing(context);
- }
-
- public TouchImageViewCustom(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- sharedConstructing(context);
- }
-
- private void sharedConstructing(Context context) {
- super.setClickable(true);
- this.context = context;
- mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
- mGestureDetector = new GestureDetector(context, new GestureListener());
- matrix = new Matrix();
- prevMatrix = new Matrix();
- m = new float[9];
- normalizedScale = 1;
- if (mScaleType == null) {
- mScaleType = ScaleType.FIT_CENTER;
- }
- minScale = 1;
- maxScale = 3;
- superMinScale = SUPER_MIN_MULTIPLIER * minScale;
- superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
- setImageMatrix(matrix);
- setScaleType(ScaleType.MATRIX);
- setState(State.NONE);
- onDrawReady = false;
- super.setOnTouchListener(new PrivateOnTouchListener());
- }
-
- @Override
- public void setOnTouchListener(View.OnTouchListener l) {
- userTouchListener = l;
- }
-
- public void setOnTouchImageViewListener(OnTouchImageViewListener l) {
- touchImageViewListener = l;
- }
-
- public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener l) {
- doubleTapListener = l;
- }
-
- @Override
- public void setImageResource(int resId) {
- super.setImageResource(resId);
- savePreviousImageValues();
- fitImageToView();
- }
-
- @Override
- public void setImageBitmap(Bitmap bm) {
- super.setImageBitmap(bm);
- savePreviousImageValues();
- fitImageToView();
- }
-
- @Override
- public void setImageDrawable(Drawable drawable) {
- super.setImageDrawable(drawable);
- savePreviousImageValues();
- fitImageToView();
- }
-
- @Override
- public void setImageURI(Uri uri) {
- super.setImageURI(uri);
- savePreviousImageValues();
- fitImageToView();
- }
-
- @Override
- public void setScaleType(ScaleType type) {
- if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) {
- throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
- }
- if (type == ScaleType.MATRIX) {
- super.setScaleType(ScaleType.MATRIX);
-
- } else {
- mScaleType = type;
- if (onDrawReady) {
- //
- // If the image is already rendered, scaleType has been called programmatically
- // and the TouchImageView should be updated with the new scaleType.
- //
- setZoom(this);
- }
- }
- }
-
- @Override
- public ScaleType getScaleType() {
- return mScaleType;
- }
-
- /**
- * Returns false if image is in initial, unzoomed state. False, otherwise.
- * @return true if image is zoomed
- */
- public boolean isZoomed() {
- return normalizedScale != 1;
- }
-
- /**
- * Return a Rect representing the zoomed image.
- * @return rect representing zoomed image
- */
- public RectF getZoomedRect() {
- if (mScaleType == ScaleType.FIT_XY) {
- throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY");
- }
- PointF topLeft = transformCoordTouchToBitmap(0, 0, true);
- PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true);
-
- float w = getDrawable().getIntrinsicWidth();
- float h = getDrawable().getIntrinsicHeight();
- return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h);
- }
-
- /**
- * Save the current matrix and view dimensions
- * in the prevMatrix and prevView variables.
- */
- private void savePreviousImageValues() {
- if (matrix != null && viewHeight != 0 && viewWidth != 0) {
- matrix.getValues(m);
- prevMatrix.setValues(m);
- prevMatchViewHeight = matchViewHeight;
- prevMatchViewWidth = matchViewWidth;
- prevViewHeight = viewHeight;
- prevViewWidth = viewWidth;
- }
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Bundle bundle = new Bundle();
- bundle.putParcelable("instanceState", super.onSaveInstanceState());
- bundle.putFloat("saveScale", normalizedScale);
- bundle.putFloat("matchViewHeight", matchViewHeight);
- bundle.putFloat("matchViewWidth", matchViewWidth);
- bundle.putInt("viewWidth", viewWidth);
- bundle.putInt("viewHeight", viewHeight);
- matrix.getValues(m);
- bundle.putFloatArray("matrix", m);
- bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce);
- return bundle;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (state instanceof Bundle) {
- Bundle bundle = (Bundle) state;
- normalizedScale = bundle.getFloat("saveScale");
- m = bundle.getFloatArray("matrix");
- prevMatrix.setValues(m);
- prevMatchViewHeight = bundle.getFloat("matchViewHeight");
- prevMatchViewWidth = bundle.getFloat("matchViewWidth");
- prevViewHeight = bundle.getInt("viewHeight");
- prevViewWidth = bundle.getInt("viewWidth");
- imageRenderedAtLeastOnce = bundle.getBoolean("imageRendered");
- super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
- return;
- }
-
- super.onRestoreInstanceState(state);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- onDrawReady = true;
- imageRenderedAtLeastOnce = true;
- if (delayedZoomVariables != null) {
- setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType);
- delayedZoomVariables = null;
- }
- super.onDraw(canvas);
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- savePreviousImageValues();
- }
-
- /**
- * Get the max zoom multiplier.
- * @return max zoom multiplier.
- */
- public float getMaxZoom() {
- return maxScale;
- }
-
- /**
- * Set the max zoom multiplier. Default value: 3.
- * @param max max zoom multiplier.
- */
- public void setMaxZoom(float max) {
- maxScale = max;
- superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
- }
-
- /**
- * Get the min zoom multiplier.
- * @return min zoom multiplier.
- */
- public float getMinZoom() {
- return minScale;
- }
-
- /**
- * Get the current zoom. This is the zoom relative to the initial
- * scale, not the original resource.
- * @return current zoom multiplier.
- */
- public float getCurrentZoom() {
- return normalizedScale;
- }
-
- /**
- * Set the min zoom multiplier. Default value: 1.
- * @param min min zoom multiplier.
- */
- public void setMinZoom(float min) {
- minScale = min;
- superMinScale = SUPER_MIN_MULTIPLIER * minScale;
- }
-
- /**
- * Reset zoom and translation to initial state.
- */
- public void resetZoom() {
- normalizedScale = 1;
- fitImageToView();
- }
-
- /**
- * Set zoom to the specified scale. Image will be centered by default.
- * @param scale
- */
- public void setZoom(float scale) {
- setZoom(scale, 0.5f, 0.5f);
- }
-
- /**
- * Set zoom to the specified scale. Image will be centered around the point
- * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
- * as a fraction from the left and top of the view. For example, the top left
- * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
- * @param scale
- * @param focusX
- * @param focusY
- */
- public void setZoom(float scale, float focusX, float focusY) {
- setZoom(scale, focusX, focusY, mScaleType);
- }
-
- /**
- * Set zoom to the specified scale. Image will be centered around the point
- * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
- * as a fraction from the left and top of the view. For example, the top left
- * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
- * @param scale
- * @param focusX
- * @param focusY
- * @param scaleType
- */
- public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
- //
- // setZoom can be called before the image is on the screen, but at this point,
- // image and view sizes have not yet been calculated in onMeasure. Thus, we should
- // delay calling setZoom until the view has been measured.
- //
- if (!onDrawReady) {
- delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
- return;
- }
-
- if (scaleType != mScaleType) {
- setScaleType(scaleType);
- }
- resetZoom();
- scaleImage(scale, viewWidth / 2, viewHeight / 2, true);
- matrix.getValues(m);
- m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
- m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
- matrix.setValues(m);
- fixTrans();
- setImageMatrix(matrix);
- }
-
- /**
- * Set zoom parameters equal to another TouchImageView. Including scale, position,
- * and ScaleType.
- * @param TouchImageView
- */
- public void setZoom(TouchImageViewCustom img) {
- PointF center = img.getScrollPosition();
- setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType());
- }
-
- /**
- * Return the point at the center of the zoomed image. The PointF coordinates range
- * in value between 0 and 1 and the focus point is denoted as a fraction from the left
- * and top of the view. For example, the top left corner of the image would be (0, 0).
- * And the bottom right corner would be (1, 1).
- * @return PointF representing the scroll position of the zoomed image.
- */
- public PointF getScrollPosition() {
- Drawable drawable = getDrawable();
- if (drawable == null) {
- return null;
- }
- int drawableWidth = drawable.getIntrinsicWidth();
- int drawableHeight = drawable.getIntrinsicHeight();
-
- PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true);
- point.x /= drawableWidth;
- point.y /= drawableHeight;
- return point;
- }
-
- /**
- * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the
- * left and top of the view. The focus points can range in value between 0 and 1.
- * @param focusX
- * @param focusY
- */
- public void setScrollPosition(float focusX, float focusY) {
- setZoom(normalizedScale, focusX, focusY);
- }
-
- /**
- * Performs boundary checking and fixes the image matrix if it
- * is out of bounds.
- */
- private void fixTrans() {
- matrix.getValues(m);
- float transX = m[Matrix.MTRANS_X];
- float transY = m[Matrix.MTRANS_Y];
-
- float fixTransX = getFixTrans(transX, viewWidth, getImageWidth());
- float fixTransY = getFixTrans(transY, viewHeight, getImageHeight());
-
- if (fixTransX != 0 || fixTransY != 0) {
- matrix.postTranslate(fixTransX, fixTransY);
- }
- }
-
- /**
- * When transitioning from zooming from focus to zoom from center (or vice versa)
- * the image can become unaligned within the view. This is apparent when zooming
- * quickly. When the content size is less than the view size, the content will often
- * be centered incorrectly within the view. fixScaleTrans first calls fixTrans() and
- * then makes sure the image is centered correctly within the view.
- */
- private void fixScaleTrans() {
- fixTrans();
- matrix.getValues(m);
- if (getImageWidth() < viewWidth) {
- m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2;
- }
-
- if (getImageHeight() < viewHeight) {
- m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2;
- }
- matrix.setValues(m);
- }
-
- private float getFixTrans(float trans, float viewSize, float contentSize) {
- float minTrans, maxTrans;
-
- if (contentSize <= viewSize) {
- minTrans = 0;
- maxTrans = viewSize - contentSize;
-
- } else {
- minTrans = viewSize - contentSize;
- maxTrans = 0;
- }
-
- if (trans < minTrans)
- return -trans + minTrans;
- if (trans > maxTrans)
- return -trans + maxTrans;
- return 0;
- }
-
- private float getFixDragTrans(float delta, float viewSize, float contentSize) {
- if (contentSize <= viewSize) {
- return 0;
- }
- return delta;
- }
-
- private float getImageWidth() {
- return matchViewWidth * normalizedScale;
- }
-
- private float getImageHeight() {
- return matchViewHeight * normalizedScale;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- Drawable drawable = getDrawable();
- if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
- setMeasuredDimension(0, 0);
- return;
- }
-
- int drawableWidth = drawable.getIntrinsicWidth();
- int drawableHeight = drawable.getIntrinsicHeight();
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- viewWidth = setViewSize(widthMode, widthSize, drawableWidth);
- viewHeight = setViewSize(heightMode, heightSize, drawableHeight);
-
- //
- // Set view dimensions
- //
- setMeasuredDimension(viewWidth, viewHeight);
-
- //
- // Fit content within view
- //
- fitImageToView();
- }
-
- /**
- * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise,
- * it is made to fit the screen according to the dimensions of the previous image matrix. This
- * allows the image to maintain its zoom after rotation.
- */
- private void fitImageToView() {
- Drawable drawable = getDrawable();
- if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
- return;
- }
- if (matrix == null || prevMatrix == null) {
- return;
- }
-
- int drawableWidth = drawable.getIntrinsicWidth();
- int drawableHeight = drawable.getIntrinsicHeight();
-
- //
- // Scale image for view
- //
- float scaleX = (float) viewWidth / drawableWidth;
- float scaleY = (float) viewHeight / drawableHeight;
-
- switch (mScaleType) {
- case CENTER:
- scaleX = scaleY = 1;
- break;
-
- case CENTER_CROP:
- scaleX = scaleY = Math.max(scaleX, scaleY);
- break;
-
- case CENTER_INSIDE:
- scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY));
-
- case FIT_CENTER:
- scaleX = scaleY = Math.min(scaleX, scaleY);
- break;
-
- case FIT_XY:
- break;
-
- default:
- //
- // FIT_START and FIT_END not supported
- //
- throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
-
- }
-
- //
- // Center the image
- //
- float redundantXSpace = viewWidth - (scaleX * drawableWidth);
- float redundantYSpace = viewHeight - (scaleY * drawableHeight);
- matchViewWidth = viewWidth - redundantXSpace;
- matchViewHeight = viewHeight - redundantYSpace;
- if (!isZoomed() && !imageRenderedAtLeastOnce) {
- //
- // Stretch and center image to fit view
- //
- matrix.setScale(scaleX, scaleY);
- matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
- normalizedScale = 1;
-
- } else {
- //
- // These values should never be 0 or we will set viewWidth and viewHeight
- // to NaN in translateMatrixAfterRotate. To avoid this, call savePreviousImageValues
- // to set them equal to the current values.
- //
- if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) {
- savePreviousImageValues();
- }
-
- prevMatrix.getValues(m);
-
- //
- // Rescale Matrix after rotation
- //
- m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale;
- m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale;
-
- //
- // TransX and TransY from previous matrix
- //
- float transX = m[Matrix.MTRANS_X];
- float transY = m[Matrix.MTRANS_Y];
-
- //
- // Width
- //
- float prevActualWidth = prevMatchViewWidth * normalizedScale;
- float actualWidth = getImageWidth();
- translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth);
-
- //
- // Height
- //
- float prevActualHeight = prevMatchViewHeight * normalizedScale;
- float actualHeight = getImageHeight();
- translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight);
-
- //
- // Set the matrix to the adjusted scale and translate values.
- //
- matrix.setValues(m);
- }
- fixTrans();
- setImageMatrix(matrix);
- }
-
- /**
- * Set view dimensions based on layout params
- *
- * @param mode
- * @param size
- * @param drawableWidth
- * @return
- */
- private int setViewSize(int mode, int size, int drawableWidth) {
- int viewSize;
- switch (mode) {
- case MeasureSpec.EXACTLY:
- viewSize = size;
- break;
-
- case MeasureSpec.AT_MOST:
- viewSize = Math.min(drawableWidth, size);
- break;
-
- case MeasureSpec.UNSPECIFIED:
- viewSize = drawableWidth;
- break;
-
- default:
- viewSize = size;
- break;
- }
- return viewSize;
- }
-
- /**
- * After rotating, the matrix needs to be translated. This function finds the area of image
- * which was previously centered and adjusts translations so that is again the center, post-rotation.
- *
- * @param axis Matrix.MTRANS_X or Matrix.MTRANS_Y
- * @param trans the value of trans in that axis before the rotation
- * @param prevImageSize the width/height of the image before the rotation
- * @param imageSize width/height of the image after rotation
- * @param prevViewSize width/height of view before rotation
- * @param viewSize width/height of view after rotation
- * @param drawableSize width/height of drawable
- */
- private void translateMatrixAfterRotate(int axis, float trans, float prevImageSize, float imageSize, int prevViewSize, int viewSize, int drawableSize) {
- if (imageSize < viewSize) {
- //
- // The width/height of image is less than the view's width/height. Center it.
- //
- m[axis] = (viewSize - (drawableSize * m[Matrix.MSCALE_X])) * 0.5f;
-
- } else if (trans > 0) {
- //
- // The image is larger than the view, but was not before rotation. Center it.
- //
- m[axis] = -((imageSize - viewSize) * 0.5f);
-
- } else {
- //
- // Find the area of the image which was previously centered in the view. Determine its distance
- // from the left/top side of the view as a fraction of the entire image's width/height. Use that percentage
- // to calculate the trans in the new view width/height.
- //
- float percentage = (Math.abs(trans) + (0.5f * prevViewSize)) / prevImageSize;
- m[axis] = -((percentage * imageSize) - (viewSize * 0.5f));
- }
- }
-
- private void setState(State state) {
- this.state = state;
- }
-
- public boolean canScrollHorizontallyFroyo(int direction) {
- return canScrollHorizontally(direction);
- }
-
- @Override
- public boolean canScrollHorizontally(int direction) {
- matrix.getValues(m);
- float x = m[Matrix.MTRANS_X];
-
- if (getImageWidth() < viewWidth) {
- return false;
-
- } else if (x >= -1 && direction < 0) {
- return false;
-
- } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Gesture Listener detects a single click or long click and passes that on
- * to the view's listener.
- * @author Ortiz
- *
- */
- private class GestureListener extends GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e)
- {
- if(doubleTapListener != null) {
- return doubleTapListener.onSingleTapConfirmed(e);
- }
- return performClick();
- }
-
- @Override
- public void onLongPress(MotionEvent e)
- {
- performLongClick();
- }
-
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
- {
- if (fling != null) {
- //
- // If a previous fling is still active, it should be cancelled so that two flings
- // are not run simultaenously.
- //
- fling.cancelFling();
- }
- fling = new Fling((int) velocityX, (int) velocityY);
- compatPostOnAnimation(fling);
- return super.onFling(e1, e2, velocityX, velocityY);
- }
-
- @Override
- public boolean onDoubleTap(MotionEvent e) {
- boolean consumed = false;
- if(doubleTapListener != null) {
- consumed = doubleTapListener.onDoubleTap(e);
- }
- if (state == State.NONE) {
- float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;
- DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, e.getX(), e.getY(), false);
- compatPostOnAnimation(doubleTap);
- consumed = true;
- }
- return consumed;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- if(doubleTapListener != null) {
- return doubleTapListener.onDoubleTapEvent(e);
- }
- return false;
- }
- }
-
- public interface OnTouchImageViewListener {
- public void onMove();
- }
-
- /**
- * Responsible for all touch events. Handles the heavy lifting of drag and also sends
- * touch events to Scale Detector and Gesture Detector.
- * @author Ortiz
- *
- */
- private class PrivateOnTouchListener implements OnTouchListener {
-
- //
- // Remember last point position for dragging
- //
- private PointF last = new PointF();
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- mScaleDetector.onTouchEvent(event);
- mGestureDetector.onTouchEvent(event);
- PointF curr = new PointF(event.getX(), event.getY());
-
- if (state == State.NONE || state == State.DRAG || state == State.FLING) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- last.set(curr);
- if (fling != null)
- fling.cancelFling();
- setState(State.DRAG);
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (state == State.DRAG) {
- float deltaX = curr.x - last.x;
- float deltaY = curr.y - last.y;
- float fixTransX = getFixDragTrans(deltaX, viewWidth, getImageWidth());
- float fixTransY = getFixDragTrans(deltaY, viewHeight, getImageHeight());
- matrix.postTranslate(fixTransX, fixTransY);
- fixTrans();
- last.set(curr.x, curr.y);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_POINTER_UP:
- setState(State.NONE);
- break;
- }
- }
-
- setImageMatrix(matrix);
-
- //
- // User-defined OnTouchListener
- //
- if(userTouchListener != null) {
- userTouchListener.onTouch(v, event);
- }
-
- //
- // OnTouchImageViewListener is set: TouchImageView dragged by user.
- //
- if (touchImageViewListener != null) {
- touchImageViewListener.onMove();
- }
-
- //
- // indicate event was handled
- //
- return true;
- }
- }
-
- /**
- * ScaleListener detects user two finger scaling and scales image.
- * @author Ortiz
- *
- */
- private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- setState(State.ZOOM);
- return true;
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
-
- //
- // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user.
- //
- if (touchImageViewListener != null) {
- touchImageViewListener.onMove();
- }
- return true;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- super.onScaleEnd(detector);
- setState(State.NONE);
- boolean animateToZoomBoundary = false;
- float targetZoom = normalizedScale;
- if (normalizedScale > maxScale) {
- targetZoom = maxScale;
- animateToZoomBoundary = true;
-
- } else if (normalizedScale < minScale) {
- targetZoom = minScale;
- animateToZoomBoundary = true;
- }
-
- if (animateToZoomBoundary) {
- DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true);
- compatPostOnAnimation(doubleTap);
- }
- }
- }
-
- private void scaleImage(double deltaScale, float focusX, float focusY, boolean stretchImageToSuper) {
-
- float lowerScale, upperScale;
- if (stretchImageToSuper) {
- lowerScale = superMinScale;
- upperScale = superMaxScale;
-
- } else {
- lowerScale = minScale;
- upperScale = maxScale;
- }
-
- float origScale = normalizedScale;
- normalizedScale *= deltaScale;
- if (normalizedScale > upperScale) {
- normalizedScale = upperScale;
- deltaScale = upperScale / origScale;
- } else if (normalizedScale < lowerScale) {
- normalizedScale = lowerScale;
- deltaScale = lowerScale / origScale;
- }
-
- matrix.postScale((float) deltaScale, (float) deltaScale, focusX, focusY);
- fixScaleTrans();
- }
-
- /**
- * DoubleTapZoom calls a series of runnables which apply
- * an animated zoom in/out graphic to the image.
- * @author Ortiz
- *
- */
- private class DoubleTapZoom implements Runnable {
-
- private long startTime;
- private static final float ZOOM_TIME = 500;
- private float startZoom, targetZoom;
- private float bitmapX, bitmapY;
- private boolean stretchImageToSuper;
- private AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
- private PointF startTouch;
- private PointF endTouch;
-
- DoubleTapZoom(float targetZoom, float focusX, float focusY, boolean stretchImageToSuper) {
- setState(State.ANIMATE_ZOOM);
- startTime = System.currentTimeMillis();
- this.startZoom = normalizedScale;
- this.targetZoom = targetZoom;
- this.stretchImageToSuper = stretchImageToSuper;
- PointF bitmapPoint = transformCoordTouchToBitmap(focusX, focusY, false);
- this.bitmapX = bitmapPoint.x;
- this.bitmapY = bitmapPoint.y;
-
- //
- // Used for translating image during scaling
- //
- startTouch = transformCoordBitmapToTouch(bitmapX, bitmapY);
- endTouch = new PointF(viewWidth / 2, viewHeight / 2);
- }
-
- @Override
- public void run() {
- float t = interpolate();
- double deltaScale = calculateDeltaScale(t);
- scaleImage(deltaScale, bitmapX, bitmapY, stretchImageToSuper);
- translateImageToCenterTouchPosition(t);
- fixScaleTrans();
- setImageMatrix(matrix);
-
- //
- // OnTouchImageViewListener is set: double tap runnable updates listener
- // with every frame.
- //
- if (touchImageViewListener != null) {
- touchImageViewListener.onMove();
- }
-
- if (t < 1f) {
- //
- // We haven't finished zooming
- //
- compatPostOnAnimation(this);
-
- } else {
- //
- // Finished zooming
- //
- setState(State.NONE);
- }
- }
-
- /**
- * Interpolate between where the image should start and end in order to translate
- * the image so that the point that is touched is what ends up centered at the end
- * of the zoom.
- * @param t
- */
- private void translateImageToCenterTouchPosition(float t) {
- float targetX = startTouch.x + t * (endTouch.x - startTouch.x);
- float targetY = startTouch.y + t * (endTouch.y - startTouch.y);
- PointF curr = transformCoordBitmapToTouch(bitmapX, bitmapY);
- matrix.postTranslate(targetX - curr.x, targetY - curr.y);
- }
-
- /**
- * Use interpolator to get t
- * @return
- */
- private float interpolate() {
- long currTime = System.currentTimeMillis();
- float elapsed = (currTime - startTime) / ZOOM_TIME;
- elapsed = Math.min(1f, elapsed);
- return interpolator.getInterpolation(elapsed);
- }
-
- /**
- * Interpolate the current targeted zoom and get the delta
- * from the current zoom.
- * @param t
- * @return
- */
- private double calculateDeltaScale(float t) {
- double zoom = startZoom + t * (targetZoom - startZoom);
- return zoom / normalizedScale;
- }
- }
-
- /**
- * This function will transform the coordinates in the touch event to the coordinate
- * system of the drawable that the imageview contain
- * @param x x-coordinate of touch event
- * @param y y-coordinate of touch event
- * @param clipToBitmap Touch event may occur within view, but outside image content. True, to clip return value
- * to the bounds of the bitmap size.
- * @return Coordinates of the point touched, in the coordinate system of the original drawable.
- */
- private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitmap) {
- matrix.getValues(m);
- float origW = getDrawable().getIntrinsicWidth();
- float origH = getDrawable().getIntrinsicHeight();
- float transX = m[Matrix.MTRANS_X];
- float transY = m[Matrix.MTRANS_Y];
- float finalX = ((x - transX) * origW) / getImageWidth();
- float finalY = ((y - transY) * origH) / getImageHeight();
-
- if (clipToBitmap) {
- finalX = Math.min(Math.max(finalX, 0), origW);
- finalY = Math.min(Math.max(finalY, 0), origH);
- }
-
- return new PointF(finalX , finalY);
- }
-
- /**
- * Inverse of transformCoordTouchToBitmap. This function will transform the coordinates in the
- * drawable's coordinate system to the view's coordinate system.
- * @param bx x-coordinate in original bitmap coordinate system
- * @param by y-coordinate in original bitmap coordinate system
- * @return Coordinates of the point in the view's coordinate system.
- */
- private PointF transformCoordBitmapToTouch(float bx, float by) {
- matrix.getValues(m);
- float origW = getDrawable().getIntrinsicWidth();
- float origH = getDrawable().getIntrinsicHeight();
- float px = bx / origW;
- float py = by / origH;
- float finalX = m[Matrix.MTRANS_X] + getImageWidth() * px;
- float finalY = m[Matrix.MTRANS_Y] + getImageHeight() * py;
- return new PointF(finalX , finalY);
- }
-
- /**
- * Fling launches sequential runnables which apply
- * the fling graphic to the image. The values for the translation
- * are interpolated by the Scroller.
- * @author Ortiz
- *
- */
- private class Fling implements Runnable {
-
- CompatScroller scroller;
- int currX, currY;
-
- Fling(int velocityX, int velocityY) {
- setState(State.FLING);
- scroller = new CompatScroller(context);
- matrix.getValues(m);
-
- int startX = (int) m[Matrix.MTRANS_X];
- int startY = (int) m[Matrix.MTRANS_Y];
- int minX, maxX, minY, maxY;
-
- if (getImageWidth() > viewWidth) {
- minX = viewWidth - (int) getImageWidth();
- maxX = 0;
-
- } else {
- minX = maxX = startX;
- }
-
- if (getImageHeight() > viewHeight) {
- minY = viewHeight - (int) getImageHeight();
- maxY = 0;
-
- } else {
- minY = maxY = startY;
- }
-
- scroller.fling(startX, startY, (int) velocityX, (int) velocityY, minX,
- maxX, minY, maxY);
- currX = startX;
- currY = startY;
- }
-
- public void cancelFling() {
- if (scroller != null) {
- setState(State.NONE);
- scroller.forceFinished(true);
- }
- }
-
- @Override
- public void run() {
-
- //
- // OnTouchImageViewListener is set: TouchImageView listener has been flung by user.
- // Listener runnable updated with each frame of fling animation.
- //
- if (touchImageViewListener != null) {
- touchImageViewListener.onMove();
- }
-
- if (scroller.isFinished()) {
- scroller = null;
- return;
- }
-
- if (scroller.computeScrollOffset()) {
- int newX = scroller.getCurrX();
- int newY = scroller.getCurrY();
- int transX = newX - currX;
- int transY = newY - currY;
- currX = newX;
- currY = newY;
- matrix.postTranslate(transX, transY);
- fixTrans();
- setImageMatrix(matrix);
- compatPostOnAnimation(this);
- }
- }
- }
-
- @TargetApi(Build.VERSION_CODES.GINGERBREAD)
- private class CompatScroller {
- Scroller scroller;
- OverScroller overScroller;
- boolean isPreGingerbread;
-
- public CompatScroller(Context context) {
- if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
- isPreGingerbread = true;
- scroller = new Scroller(context);
-
- } else {
- isPreGingerbread = false;
- overScroller = new OverScroller(context);
- }
- }
-
- public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
- if (isPreGingerbread) {
- scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
- } else {
- overScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
- }
- }
-
- public void forceFinished(boolean finished) {
- if (isPreGingerbread) {
- scroller.forceFinished(finished);
- } else {
- overScroller.forceFinished(finished);
- }
- }
-
- public boolean isFinished() {
- if (isPreGingerbread) {
- return scroller.isFinished();
- } else {
- return overScroller.isFinished();
- }
- }
-
- public boolean computeScrollOffset() {
- if (isPreGingerbread) {
- return scroller.computeScrollOffset();
- } else {
- overScroller.computeScrollOffset();
- return overScroller.computeScrollOffset();
- }
- }
-
- public int getCurrX() {
- if (isPreGingerbread) {
- return scroller.getCurrX();
- } else {
- return overScroller.getCurrX();
- }
- }
-
- public int getCurrY() {
- if (isPreGingerbread) {
- return scroller.getCurrY();
- } else {
- return overScroller.getCurrY();
- }
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private void compatPostOnAnimation(Runnable runnable) {
- if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
- postOnAnimation(runnable);
-
- } else {
- postDelayed(runnable, 1000/60);
- }
- }
-
- private class ZoomVariables {
- public float scale;
- public float focusX;
- public float focusY;
- public ScaleType scaleType;
-
- public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleType) {
- this.scale = scale;
- this.focusX = focusX;
- this.focusY = focusY;
- this.scaleType = scaleType;
- }
- }
-
- private void printMatrixInfo() {
- float[] n = new float[9];
- matrix.getValues(n);
- Log.d(DEBUG, "Scale: " + n[Matrix.MSCALE_X] + " TransX: " + n[Matrix.MTRANS_X] + " TransY: " + n[Matrix.MTRANS_Y]);
- }
-}
\ No newline at end of file
-/* ownCloud Android client application
- * Copyright (C) 2012-2014 ownCloud Inc.
+/**
+ * ownCloud Android client application
+ *
+ * Copyright (C) 2015 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,
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,
--- /dev/null
+
+/*
+ * Copyright (C) 2013 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 third_parties.in.srain.cube;
+
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.database.DataSetObservable;
+import android.database.DataSetObserver;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.FrameLayout;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+import android.widget.WrapperListAdapter;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+/**
+ * A {@link android.widget.GridView} that supports adding header rows in a
+ * very similar way to {@link android.widget.ListView}.
+ * See {@link GridViewWithHeaderAndFooter#addHeaderView(View, Object, boolean)}
+ * See {@link GridViewWithHeaderAndFooter#addFooterView(View, Object, boolean)}
+ */
+public class GridViewWithHeaderAndFooter extends GridView {
+
+ public static boolean DEBUG = false;
+
+ /**
+ * A class that represents a fixed view in a list, for example a header at the top
+ * or a footer at the bottom.
+ */
+ private static class FixedViewInfo {
+ /**
+ * The view to add to the grid
+ */
+ public View view;
+ public ViewGroup viewContainer;
+ /**
+ * The data backing the view. This is returned from {@link android.widget.ListAdapter#getItem(int)}.
+ */
+ public Object data;
+ /**
+ * <code>true</code> if the fixed view should be selectable in the grid
+ */
+ public boolean isSelectable;
+ }
+
+ private int mNumColumns = AUTO_FIT;
+ private View mViewForMeasureRowHeight = null;
+ private int mRowHeight = -1;
+ private static final String LOG_TAG = "grid-view-with-header-and-footer";
+
+ private ArrayList<FixedViewInfo> mHeaderViewInfos = new ArrayList<FixedViewInfo>();
+ private ArrayList<FixedViewInfo> mFooterViewInfos = new ArrayList<FixedViewInfo>();
+
+ private void initHeaderGridView() {
+ }
+
+ public GridViewWithHeaderAndFooter(Context context) {
+ super(context);
+ initHeaderGridView();
+ }
+
+ public GridViewWithHeaderAndFooter(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initHeaderGridView();
+ }
+
+ public GridViewWithHeaderAndFooter(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initHeaderGridView();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
+ ((HeaderViewGridAdapter) adapter).setNumColumns(getNumColumnsCompatible());
+ ((HeaderViewGridAdapter) adapter).setRowHeight(getRowHeight());
+ }
+ }
+
+ @Override
+ public void setClipChildren(boolean clipChildren) {
+ // Ignore, since the header rows depend on not being clipped
+ }
+
+ /**
+ * Do not call this method unless you know how it works.
+ *
+ * @param clipChildren
+ */
+ public void setClipChildrenSupper(boolean clipChildren) {
+ super.setClipChildren(false);
+ }
+
+ /**
+ * Add a fixed view to appear at the top of the grid. If addHeaderView is
+ * called more than once, the views will appear in the order they were
+ * added. Views added using this call can take focus if they want.
+ * <p/>
+ * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
+ * the supplied cursor with one that will also account for header views.
+ *
+ * @param v The view to add.
+ */
+ public void addHeaderView(View v) {
+ addHeaderView(v, null, true);
+ }
+
+ /**
+ * Add a fixed view to appear at the top of the grid. If addHeaderView is
+ * called more than once, the views will appear in the order they were
+ * added. Views added using this call can take focus if they want.
+ * <p/>
+ * NOTE: Call this before calling setAdapter. This is so HeaderGridView can wrap
+ * the supplied cursor with one that will also account for header views.
+ *
+ * @param v The view to add.
+ * @param data Data to associate with this view
+ * @param isSelectable whether the item is selectable
+ */
+ public void addHeaderView(View v, Object data, boolean isSelectable) {
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && !(adapter instanceof HeaderViewGridAdapter)) {
+ throw new IllegalStateException(
+ "Cannot add header view to grid -- setAdapter has already been called.");
+ }
+
+ ViewGroup.LayoutParams lyp = v.getLayoutParams();
+
+ FixedViewInfo info = new FixedViewInfo();
+ FrameLayout fl = new FullWidthFixedViewLayout(getContext());
+
+ if (lyp != null) {
+ v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
+ fl.setLayoutParams(new LayoutParams(lyp.width, lyp.height));
+ }
+ fl.addView(v);
+ info.view = v;
+ info.viewContainer = fl;
+ info.data = data;
+ info.isSelectable = isSelectable;
+ mHeaderViewInfos.add(info);
+ // in the case of re-adding a header view, or adding one later on,
+ // we need to notify the observer
+ if (adapter != null) {
+ ((HeaderViewGridAdapter) adapter).notifyDataSetChanged();
+ }
+ }
+
+ public void addFooterView(View v) {
+ addFooterView(v, null, true);
+ }
+
+ public void addFooterView(View v, Object data, boolean isSelectable) {
+ ListAdapter mAdapter = getAdapter();
+ if (mAdapter != null && !(mAdapter instanceof HeaderViewGridAdapter)) {
+ throw new IllegalStateException(
+ "Cannot add header view to grid -- setAdapter has already been called.");
+ }
+
+ ViewGroup.LayoutParams lyp = v.getLayoutParams();
+
+ FixedViewInfo info = new FixedViewInfo();
+ FrameLayout fl = new FullWidthFixedViewLayout(getContext());
+
+ if (lyp != null) {
+ v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
+ fl.setLayoutParams(new LayoutParams(lyp.width, lyp.height));
+ }
+ fl.addView(v);
+ info.view = v;
+ info.viewContainer = fl;
+ info.data = data;
+ info.isSelectable = isSelectable;
+ mFooterViewInfos.add(info);
+
+ if (mAdapter != null) {
+ ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged();
+ }
+ }
+
+ public int getHeaderViewCount() {
+ return mHeaderViewInfos.size();
+ }
+
+ public int getFooterViewCount() {
+ return mFooterViewInfos.size();
+ }
+
+ /**
+ * Removes a previously-added header view.
+ *
+ * @param v The view to remove
+ * @return true if the view was removed, false if the view was not a header
+ * view
+ */
+ public boolean removeHeaderView(View v) {
+ if (mHeaderViewInfos.size() > 0) {
+ boolean result = false;
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && ((HeaderViewGridAdapter) adapter).removeHeader(v)) {
+ result = true;
+ }
+ removeFixedViewInfo(v, mHeaderViewInfos);
+ return result;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a previously-added footer view.
+ *
+ * @param v The view to remove
+ * @return true if the view was removed, false if the view was not a header
+ * view
+ */
+ public boolean removeFooterView(View v) {
+ if (mFooterViewInfos.size() > 0) {
+ boolean result = false;
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && ((HeaderViewGridAdapter) adapter).removeFooter(v)) {
+ result = true;
+ }
+ removeFixedViewInfo(v, mFooterViewInfos);
+ return result;
+ }
+ return false;
+ }
+
+ private void removeFixedViewInfo(View v, ArrayList<FixedViewInfo> where) {
+ int len = where.size();
+ for (int i = 0; i < len; ++i) {
+ FixedViewInfo info = where.get(i);
+ if (info.view == v) {
+ where.remove(i);
+ break;
+ }
+ }
+ }
+
+ @TargetApi(11)
+ private int getNumColumnsCompatible() {
+ if (Build.VERSION.SDK_INT >= 11) {
+ return super.getNumColumns();
+ } else {
+ try {
+ Field numColumns = getClass().getSuperclass().getDeclaredField("mNumColumns");
+ numColumns.setAccessible(true);
+ return numColumns.getInt(this);
+ } catch (Exception e) {
+ if (mNumColumns != -1) {
+ return mNumColumns;
+ }
+ throw new RuntimeException("Can not determine the mNumColumns for this API platform, please call setNumColumns to set it.");
+ }
+ }
+ }
+
+ @TargetApi(16)
+ private int getColumnWidthCompatible() {
+ if (Build.VERSION.SDK_INT >= 16) {
+ return super.getColumnWidth();
+ } else {
+ try {
+ Field numColumns = getClass().getSuperclass().getDeclaredField("mColumnWidth");
+ numColumns.setAccessible(true);
+ return numColumns.getInt(this);
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mViewForMeasureRowHeight = null;
+ }
+
+ public void invalidateRowHeight() {
+ mRowHeight = -1;
+ }
+
+ public int getRowHeight() {
+ if (mRowHeight > 0) {
+ return mRowHeight;
+ }
+ ListAdapter adapter = getAdapter();
+ int numColumns = getNumColumnsCompatible();
+
+ // adapter has not been set or has no views in it;
+ if (adapter == null || adapter.getCount() <= numColumns * (mHeaderViewInfos.size() + mFooterViewInfos.size())) {
+ return -1;
+ }
+ int mColumnWidth = getColumnWidthCompatible();
+ View view = getAdapter().getView(numColumns * mHeaderViewInfos.size(), mViewForMeasureRowHeight, this);
+ LayoutParams p = (LayoutParams) view.getLayoutParams();
+ if (p == null) {
+ p = new LayoutParams(-1, -2, 0);
+ view.setLayoutParams(p);
+ }
+ int childHeightSpec = getChildMeasureSpec(
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);
+ int childWidthSpec = getChildMeasureSpec(
+ MeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);
+ view.measure(childWidthSpec, childHeightSpec);
+ mViewForMeasureRowHeight = view;
+ mRowHeight = view.getMeasuredHeight();
+ return mRowHeight;
+ }
+
+ @TargetApi(11)
+ public void tryToScrollToBottomSmoothly() {
+ int lastPos = getAdapter().getCount() - 1;
+ if (Build.VERSION.SDK_INT >= 11) {
+ smoothScrollToPositionFromTop(lastPos, 0);
+ } else {
+ setSelection(lastPos);
+ }
+ }
+
+ @TargetApi(11)
+ public void tryToScrollToBottomSmoothly(int duration) {
+ int lastPos = getAdapter().getCount() - 1;
+ if (Build.VERSION.SDK_INT >= 11) {
+ smoothScrollToPositionFromTop(lastPos, 0, duration);
+ } else {
+ setSelection(lastPos);
+ }
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ if (mHeaderViewInfos.size() > 0 || mFooterViewInfos.size() > 0) {
+ HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
+ int numColumns = getNumColumnsCompatible();
+ if (numColumns > 1) {
+ headerViewGridAdapter.setNumColumns(numColumns);
+ }
+ headerViewGridAdapter.setRowHeight(getRowHeight());
+ super.setAdapter(headerViewGridAdapter);
+ } else {
+ super.setAdapter(adapter);
+ }
+ }
+
+ /**
+ * full width
+ */
+ private class FullWidthFixedViewLayout extends FrameLayout {
+
+ public FullWidthFixedViewLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int realLeft = GridViewWithHeaderAndFooter.this.getPaddingLeft() + getPaddingLeft();
+ // Try to make where it should be, from left, full width
+ if (realLeft != left) {
+ offsetLeftAndRight(realLeft - left);
+ }
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int targetWidth = GridViewWithHeaderAndFooter.this.getMeasuredWidth()
+ - GridViewWithHeaderAndFooter.this.getPaddingLeft()
+ - GridViewWithHeaderAndFooter.this.getPaddingRight();
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
+ MeasureSpec.getMode(widthMeasureSpec));
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ public void setNumColumns(int numColumns) {
+ super.setNumColumns(numColumns);
+ mNumColumns = numColumns;
+ ListAdapter adapter = getAdapter();
+ if (adapter != null && adapter instanceof HeaderViewGridAdapter) {
+ ((HeaderViewGridAdapter) adapter).setNumColumns(numColumns);
+ }
+ }
+
+ /**
+ * ListAdapter used when a HeaderGridView has header views. This ListAdapter
+ * wraps another one and also keeps track of the header views and their
+ * associated data objects.
+ * <p>This is intended as a base class; you will probably not need to
+ * use this class directly in your own code.
+ */
+ private static class HeaderViewGridAdapter implements WrapperListAdapter, Filterable {
+ // This is used to notify the container of updates relating to number of columns
+ // or headers changing, which changes the number of placeholders needed
+ private final DataSetObservable mDataSetObservable = new DataSetObservable();
+ private final ListAdapter mAdapter;
+ static final ArrayList<FixedViewInfo> EMPTY_INFO_LIST =
+ new ArrayList<FixedViewInfo>();
+
+ // This ArrayList is assumed to NOT be null.
+ ArrayList<FixedViewInfo> mHeaderViewInfos;
+ ArrayList<FixedViewInfo> mFooterViewInfos;
+ private int mNumColumns = 1;
+ private int mRowHeight = -1;
+ boolean mAreAllFixedViewsSelectable;
+ private final boolean mIsFilterable;
+ private boolean mCachePlaceHoldView = true;
+ // From Recycle Bin or calling getView, this a question...
+ private boolean mCacheFirstHeaderView = false;
+
+ public HeaderViewGridAdapter(ArrayList<FixedViewInfo> headerViewInfos, ArrayList<FixedViewInfo> footViewInfos, ListAdapter adapter) {
+ mAdapter = adapter;
+ mIsFilterable = adapter instanceof Filterable;
+ if (headerViewInfos == null) {
+ mHeaderViewInfos = EMPTY_INFO_LIST;
+ } else {
+ mHeaderViewInfos = headerViewInfos;
+ }
+
+ if (footViewInfos == null) {
+ mFooterViewInfos = EMPTY_INFO_LIST;
+ } else {
+ mFooterViewInfos = footViewInfos;
+ }
+ mAreAllFixedViewsSelectable = areAllListInfosSelectable(mHeaderViewInfos)
+ && areAllListInfosSelectable(mFooterViewInfos);
+ }
+
+ public void setNumColumns(int numColumns) {
+ if (numColumns < 1) {
+ return;
+ }
+ if (mNumColumns != numColumns) {
+ mNumColumns = numColumns;
+ notifyDataSetChanged();
+ }
+ }
+
+ public void setRowHeight(int height) {
+ mRowHeight = height;
+ }
+
+ public int getHeadersCount() {
+ return mHeaderViewInfos.size();
+ }
+
+ public int getFootersCount() {
+ return mFooterViewInfos.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return (mAdapter == null || mAdapter.isEmpty()) && getHeadersCount() == 0 && getFootersCount() == 0;
+ }
+
+ private boolean areAllListInfosSelectable(ArrayList<FixedViewInfo> infos) {
+ if (infos != null) {
+ for (FixedViewInfo info : infos) {
+ if (!info.isSelectable) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean removeHeader(View v) {
+ for (int i = 0; i < mHeaderViewInfos.size(); i++) {
+ FixedViewInfo info = mHeaderViewInfos.get(i);
+ if (info.view == v) {
+ mHeaderViewInfos.remove(i);
+ mAreAllFixedViewsSelectable =
+ areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
+ mDataSetObservable.notifyChanged();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean removeFooter(View v) {
+ for (int i = 0; i < mFooterViewInfos.size(); i++) {
+ FixedViewInfo info = mFooterViewInfos.get(i);
+ if (info.view == v) {
+ mFooterViewInfos.remove(i);
+ mAreAllFixedViewsSelectable =
+ areAllListInfosSelectable(mHeaderViewInfos) && areAllListInfosSelectable(mFooterViewInfos);
+ mDataSetObservable.notifyChanged();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getCount() {
+ if (mAdapter != null) {
+ return (getFootersCount() + getHeadersCount()) * mNumColumns + getAdapterAndPlaceHolderCount();
+ } else {
+ return (getFootersCount() + getHeadersCount()) * mNumColumns;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ if (mAdapter != null) {
+ return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
+ } else {
+ return true;
+ }
+ }
+
+ private int getAdapterAndPlaceHolderCount() {
+ final int adapterCount = (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns);
+ return adapterCount;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ // Header (negative positions will throw an IndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders) {
+ return position % mNumColumns == 0
+ && mHeaderViewInfos.get(position / mNumColumns).isSelectable;
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = getAdapterAndPlaceHolderCount();
+ if (adjPosition < adapterCount) {
+ return adjPosition < mAdapter.getCount() && mAdapter.isEnabled(adjPosition);
+ }
+ }
+
+ // Footer (off-limits positions will throw an IndexOutOfBoundsException)
+ final int footerPosition = adjPosition - adapterCount;
+ return footerPosition % mNumColumns == 0
+ && mFooterViewInfos.get(footerPosition / mNumColumns).isSelectable;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders) {
+ if (position % mNumColumns == 0) {
+ return mHeaderViewInfos.get(position / mNumColumns).data;
+ }
+ return null;
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = getAdapterAndPlaceHolderCount();
+ if (adjPosition < adapterCount) {
+ if (adjPosition < mAdapter.getCount()) {
+ return mAdapter.getItem(adjPosition);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // Footer (off-limits positions will throw an IndexOutOfBoundsException)
+ final int footerPosition = adjPosition - adapterCount;
+ if (footerPosition % mNumColumns == 0) {
+ return mFooterViewInfos.get(footerPosition).data;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (mAdapter != null && position >= numHeadersAndPlaceholders) {
+ int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = mAdapter.getCount();
+ if (adjPosition < adapterCount) {
+ return mAdapter.getItemId(adjPosition);
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ if (mAdapter != null) {
+ return mAdapter.hasStableIds();
+ }
+ return false;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("getView: %s, reused: %s", position, convertView == null));
+ }
+ // Header (negative positions will throw an ArrayIndexOutOfBoundsException)
+ int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ if (position < numHeadersAndPlaceholders) {
+ View headerViewContainer = mHeaderViewInfos
+ .get(position / mNumColumns).viewContainer;
+ if (position % mNumColumns == 0) {
+ return headerViewContainer;
+ } else {
+ if (convertView == null) {
+ convertView = new View(parent.getContext());
+ }
+ // We need to do this because GridView uses the height of the last item
+ // in a row to determine the height for the entire row.
+ convertView.setVisibility(View.INVISIBLE);
+ convertView.setMinimumHeight(headerViewContainer.getHeight());
+ return convertView;
+ }
+ }
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = getAdapterAndPlaceHolderCount();
+ if (adjPosition < adapterCount) {
+ if (adjPosition < mAdapter.getCount()) {
+ View view = mAdapter.getView(adjPosition, convertView, parent);
+ return view;
+ } else {
+ if (convertView == null) {
+ convertView = new View(parent.getContext());
+ }
+ convertView.setVisibility(View.INVISIBLE);
+ convertView.setMinimumHeight(mRowHeight);
+ return convertView;
+ }
+ }
+ }
+ // Footer
+ final int footerPosition = adjPosition - adapterCount;
+ if (footerPosition < getCount()) {
+ View footViewContainer = mFooterViewInfos
+ .get(footerPosition / mNumColumns).viewContainer;
+ if (position % mNumColumns == 0) {
+ return footViewContainer;
+ } else {
+ if (convertView == null) {
+ convertView = new View(parent.getContext());
+ }
+ // We need to do this because GridView uses the height of the last item
+ // in a row to determine the height for the entire row.
+ convertView.setVisibility(View.INVISIBLE);
+ convertView.setMinimumHeight(footViewContainer.getHeight());
+ return convertView;
+ }
+ }
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+
+ final int numHeadersAndPlaceholders = getHeadersCount() * mNumColumns;
+ final int adapterViewTypeStart = mAdapter == null ? 0 : mAdapter.getViewTypeCount() - 1;
+ int type = AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
+ if (mCachePlaceHoldView) {
+ // Header
+ if (position < numHeadersAndPlaceholders) {
+ if (position == 0) {
+ if (mCacheFirstHeaderView) {
+ type = adapterViewTypeStart + mHeaderViewInfos.size() + mFooterViewInfos.size() + 1 + 1;
+ }
+ }
+ if (position % mNumColumns != 0) {
+ type = adapterViewTypeStart + (position / mNumColumns + 1);
+ }
+ }
+ }
+
+ // Adapter
+ final int adjPosition = position - numHeadersAndPlaceholders;
+ int adapterCount = 0;
+ if (mAdapter != null) {
+ adapterCount = getAdapterAndPlaceHolderCount();
+ if (adjPosition >= 0 && adjPosition < adapterCount) {
+ if (adjPosition < mAdapter.getCount()) {
+ type = mAdapter.getItemViewType(adjPosition);
+ } else {
+ if (mCachePlaceHoldView) {
+ type = adapterViewTypeStart + mHeaderViewInfos.size() + 1;
+ }
+ }
+ }
+ }
+
+ if (mCachePlaceHoldView) {
+ // Footer
+ final int footerPosition = adjPosition - adapterCount;
+ if (footerPosition >= 0 && footerPosition < getCount() && (footerPosition % mNumColumns) != 0) {
+ type = adapterViewTypeStart + mHeaderViewInfos.size() + 1 + (footerPosition / mNumColumns + 1);
+ }
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("getItemViewType: pos: %s, result: %s", position, type, mCachePlaceHoldView, mCacheFirstHeaderView));
+ }
+ return type;
+ }
+
+ /**
+ * content view, content view holder, header[0], header and footer placeholder(s)
+ *
+ * @return
+ */
+ @Override
+ public int getViewTypeCount() {
+ int count = mAdapter == null ? 1 : mAdapter.getViewTypeCount();
+ if (mCachePlaceHoldView) {
+ int offset = mHeaderViewInfos.size() + 1 + mFooterViewInfos.size();
+ if (mCacheFirstHeaderView) {
+ offset += 1;
+ }
+ count += offset;
+ }
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("getViewTypeCount: %s", count));
+ }
+ return count;
+ }
+
+ @Override
+ public void registerDataSetObserver(DataSetObserver observer) {
+ mDataSetObservable.registerObserver(observer);
+ if (mAdapter != null) {
+ mAdapter.registerDataSetObserver(observer);
+ }
+ }
+
+ @Override
+ public void unregisterDataSetObserver(DataSetObserver observer) {
+ mDataSetObservable.unregisterObserver(observer);
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(observer);
+ }
+ }
+
+ @Override
+ public Filter getFilter() {
+ if (mIsFilterable) {
+ return ((Filterable) mAdapter).getFilter();
+ }
+ return null;
+ }
+
+ @Override
+ public ListAdapter getWrappedAdapter() {
+ return mAdapter;
+ }
+
+ public void notifyDataSetChanged() {
+ mDataSetObservable.notifyChanged();
+ }
+ }
+
+
+ /**
+ * Sets the selected item and positions the selection y pixels from the top edge of the ListView.
+ * (If in touch mode, the item will not be selected but it will still be positioned appropriately.)
+ *
+ * @param position Index (starting at 0) of the data item to be selected.
+ * @param y The distance from the top edge of the ListView (plus padding)
+ * that the item will be positioned.
+ *
+ * @see <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/widget/ListView.java#ListView.setSelectionFromTop%28int%2Cint%29">Original code</a>
+ */
+ public void setSelectionFromTop(int position, int y) {
+ if (getAdapter() == null) {
+ return;
+ }
+
+ setSelection(position);
+ //setSelectionInt(position);
+
+ /*if (!isInTouchMode()) {
+ position = super.lookForSelectablePosition(position, true);
+ if (position >= 0) {
+ setNextSelectedPositionInt(position);
+ }
+ } else {
+ mResurrectToPosition = position;
+ }*/
+
+ /*
+ if (position >= 0) {
+ mLayoutMode = LAYOUT_SPECIFIC;
+ mSpecificTop = mListPadding.top + y;
+
+ if (mNeedSync) {
+ mSyncPosition = position;
+ mSyncRowId = getAdapter().getItemId(position);
+ }
+
+ if (mPositionScroller != null) {
+ mPositionScroller.stop();
+ }
+
+ requestLayout();
+ }
+ */
+ }
+
+}
--- /dev/null
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
\ No newline at end of file
--- /dev/null
+/**
+ * @author Michael Ortiz
+ * @updated Patrick Lackemacher
+ * @updated Babay88
+ * @updated @ipsilondev
+ * @updated hank-cp
+ * @updated singpolyma
+ * Copyright (c) 2012 Michael Ortiz
+ */
+
+package third_parties.michaelOrtiz;
+
+import com.owncloud.android.ui.preview.ImageViewCustom;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.OverScroller;
+import android.widget.Scroller;
+
+/**
+ * Extends Android ImageView to include pinch zooming, panning, fling and double tap zoom.
+ */
+public class TouchImageViewCustom extends ImageViewCustom {
+ private static final String DEBUG = "DEBUG";
+
+ //
+ // SuperMin and SuperMax multipliers. Determine how much the image can be
+ // zoomed below or above the zoom boundaries, before animating back to the
+ // min/max zoom boundary.
+ //
+ private static final float SUPER_MIN_MULTIPLIER = .75f;
+ private static final float SUPER_MAX_MULTIPLIER = 1.25f;
+
+ //
+ // Scale of image ranges from minScale to maxScale, where minScale == 1
+ // when the image is stretched to fit view.
+ //
+ private float normalizedScale;
+
+ //
+ // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal.
+ // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix
+ // saved prior to the screen rotating.
+ //
+ private Matrix matrix, prevMatrix;
+
+ private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM };
+ private State state;
+
+ private float minScale;
+ private float maxScale;
+ private float superMinScale;
+ private float superMaxScale;
+ private float[] m;
+
+ private Context context;
+ private Fling fling;
+
+ private ScaleType mScaleType;
+
+ private boolean imageRenderedAtLeastOnce;
+ private boolean onDrawReady;
+
+ private ZoomVariables delayedZoomVariables;
+
+ //
+ // Size of view and previous view size (ie before rotation)
+ //
+ private int viewWidth, viewHeight, prevViewWidth, prevViewHeight;
+
+ //
+ // Size of image when it is stretched to fit view. Before and After rotation.
+ //
+ private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight;
+
+ private ScaleGestureDetector mScaleDetector;
+ private GestureDetector mGestureDetector;
+ private GestureDetector.OnDoubleTapListener doubleTapListener = null;
+ private OnTouchListener userTouchListener = null;
+ private OnTouchImageViewListener touchImageViewListener = null;
+
+ public TouchImageViewCustom(Context context) {
+ super(context);
+ sharedConstructing(context);
+ }
+
+ public TouchImageViewCustom(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ sharedConstructing(context);
+ }
+
+ public TouchImageViewCustom(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ sharedConstructing(context);
+ }
+
+ private void sharedConstructing(Context context) {
+ super.setClickable(true);
+ this.context = context;
+ mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
+ mGestureDetector = new GestureDetector(context, new GestureListener());
+ matrix = new Matrix();
+ prevMatrix = new Matrix();
+ m = new float[9];
+ normalizedScale = 1;
+ if (mScaleType == null) {
+ mScaleType = ScaleType.FIT_CENTER;
+ }
+ minScale = 1;
+ maxScale = 3;
+ superMinScale = SUPER_MIN_MULTIPLIER * minScale;
+ superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
+ setImageMatrix(matrix);
+ setScaleType(ScaleType.MATRIX);
+ setState(State.NONE);
+ onDrawReady = false;
+ super.setOnTouchListener(new PrivateOnTouchListener());
+ }
+
+ @Override
+ public void setOnTouchListener(View.OnTouchListener l) {
+ userTouchListener = l;
+ }
+
+ public void setOnTouchImageViewListener(OnTouchImageViewListener l) {
+ touchImageViewListener = l;
+ }
+
+ public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener l) {
+ doubleTapListener = l;
+ }
+
+ @Override
+ public void setImageResource(int resId) {
+ super.setImageResource(resId);
+ savePreviousImageValues();
+ fitImageToView();
+ }
+
+ @Override
+ public void setImageBitmap(Bitmap bm) {
+ super.setImageBitmap(bm);
+ savePreviousImageValues();
+ fitImageToView();
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ super.setImageDrawable(drawable);
+ savePreviousImageValues();
+ fitImageToView();
+ }
+
+ @Override
+ public void setImageURI(Uri uri) {
+ super.setImageURI(uri);
+ savePreviousImageValues();
+ fitImageToView();
+ }
+
+ @Override
+ public void setScaleType(ScaleType type) {
+ if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) {
+ throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
+ }
+ if (type == ScaleType.MATRIX) {
+ super.setScaleType(ScaleType.MATRIX);
+
+ } else {
+ mScaleType = type;
+ if (onDrawReady) {
+ //
+ // If the image is already rendered, scaleType has been called programmatically
+ // and the TouchImageView should be updated with the new scaleType.
+ //
+ setZoom(this);
+ }
+ }
+ }
+
+ @Override
+ public ScaleType getScaleType() {
+ return mScaleType;
+ }
+
+ /**
+ * Returns false if image is in initial, unzoomed state. False, otherwise.
+ * @return true if image is zoomed
+ */
+ public boolean isZoomed() {
+ return normalizedScale != 1;
+ }
+
+ /**
+ * Return a Rect representing the zoomed image.
+ * @return rect representing zoomed image
+ */
+ public RectF getZoomedRect() {
+ if (mScaleType == ScaleType.FIT_XY) {
+ throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY");
+ }
+ PointF topLeft = transformCoordTouchToBitmap(0, 0, true);
+ PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true);
+
+ float w = getDrawable().getIntrinsicWidth();
+ float h = getDrawable().getIntrinsicHeight();
+ return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h);
+ }
+
+ /**
+ * Save the current matrix and view dimensions
+ * in the prevMatrix and prevView variables.
+ */
+ private void savePreviousImageValues() {
+ if (matrix != null && viewHeight != 0 && viewWidth != 0) {
+ matrix.getValues(m);
+ prevMatrix.setValues(m);
+ prevMatchViewHeight = matchViewHeight;
+ prevMatchViewWidth = matchViewWidth;
+ prevViewHeight = viewHeight;
+ prevViewWidth = viewWidth;
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("instanceState", super.onSaveInstanceState());
+ bundle.putFloat("saveScale", normalizedScale);
+ bundle.putFloat("matchViewHeight", matchViewHeight);
+ bundle.putFloat("matchViewWidth", matchViewWidth);
+ bundle.putInt("viewWidth", viewWidth);
+ bundle.putInt("viewHeight", viewHeight);
+ matrix.getValues(m);
+ bundle.putFloatArray("matrix", m);
+ bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce);
+ return bundle;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state instanceof Bundle) {
+ Bundle bundle = (Bundle) state;
+ normalizedScale = bundle.getFloat("saveScale");
+ m = bundle.getFloatArray("matrix");
+ prevMatrix.setValues(m);
+ prevMatchViewHeight = bundle.getFloat("matchViewHeight");
+ prevMatchViewWidth = bundle.getFloat("matchViewWidth");
+ prevViewHeight = bundle.getInt("viewHeight");
+ prevViewWidth = bundle.getInt("viewWidth");
+ imageRenderedAtLeastOnce = bundle.getBoolean("imageRendered");
+ super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
+ return;
+ }
+
+ super.onRestoreInstanceState(state);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ onDrawReady = true;
+ imageRenderedAtLeastOnce = true;
+ if (delayedZoomVariables != null) {
+ setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType);
+ delayedZoomVariables = null;
+ }
+ super.onDraw(canvas);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ savePreviousImageValues();
+ }
+
+ /**
+ * Get the max zoom multiplier.
+ * @return max zoom multiplier.
+ */
+ public float getMaxZoom() {
+ return maxScale;
+ }
+
+ /**
+ * Set the max zoom multiplier. Default value: 3.
+ * @param max max zoom multiplier.
+ */
+ public void setMaxZoom(float max) {
+ maxScale = max;
+ superMaxScale = SUPER_MAX_MULTIPLIER * maxScale;
+ }
+
+ /**
+ * Get the min zoom multiplier.
+ * @return min zoom multiplier.
+ */
+ public float getMinZoom() {
+ return minScale;
+ }
+
+ /**
+ * Get the current zoom. This is the zoom relative to the initial
+ * scale, not the original resource.
+ * @return current zoom multiplier.
+ */
+ public float getCurrentZoom() {
+ return normalizedScale;
+ }
+
+ /**
+ * Set the min zoom multiplier. Default value: 1.
+ * @param min min zoom multiplier.
+ */
+ public void setMinZoom(float min) {
+ minScale = min;
+ superMinScale = SUPER_MIN_MULTIPLIER * minScale;
+ }
+
+ /**
+ * Reset zoom and translation to initial state.
+ */
+ public void resetZoom() {
+ normalizedScale = 1;
+ fitImageToView();
+ }
+
+ /**
+ * Set zoom to the specified scale. Image will be centered by default.
+ * @param scale
+ */
+ public void setZoom(float scale) {
+ setZoom(scale, 0.5f, 0.5f);
+ }
+
+ /**
+ * Set zoom to the specified scale. Image will be centered around the point
+ * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
+ * as a fraction from the left and top of the view. For example, the top left
+ * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
+ * @param scale
+ * @param focusX
+ * @param focusY
+ */
+ public void setZoom(float scale, float focusX, float focusY) {
+ setZoom(scale, focusX, focusY, mScaleType);
+ }
+
+ /**
+ * Set zoom to the specified scale. Image will be centered around the point
+ * (focusX, focusY). These floats range from 0 to 1 and denote the focus point
+ * as a fraction from the left and top of the view. For example, the top left
+ * corner of the image would be (0, 0). And the bottom right corner would be (1, 1).
+ * @param scale
+ * @param focusX
+ * @param focusY
+ * @param scaleType
+ */
+ public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) {
+ //
+ // setZoom can be called before the image is on the screen, but at this point,
+ // image and view sizes have not yet been calculated in onMeasure. Thus, we should
+ // delay calling setZoom until the view has been measured.
+ //
+ if (!onDrawReady) {
+ delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType);
+ return;
+ }
+
+ if (scaleType != mScaleType) {
+ setScaleType(scaleType);
+ }
+ resetZoom();
+ scaleImage(scale, viewWidth / 2, viewHeight / 2, true);
+ matrix.getValues(m);
+ m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f));
+ m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f));
+ matrix.setValues(m);
+ fixTrans();
+ setImageMatrix(matrix);
+ }
+
+ /**
+ * Set zoom parameters equal to another TouchImageView. Including scale, position,
+ * and ScaleType.
+ * @param img
+ */
+ public void setZoom(TouchImageViewCustom img) {
+ PointF center = img.getScrollPosition();
+ setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType());
+ }
+
+ /**
+ * Return the point at the center of the zoomed image. The PointF coordinates range
+ * in value between 0 and 1 and the focus point is denoted as a fraction from the left
+ * and top of the view. For example, the top left corner of the image would be (0, 0).
+ * And the bottom right corner would be (1, 1).
+ * @return PointF representing the scroll position of the zoomed image.
+ */
+ public PointF getScrollPosition() {
+ Drawable drawable = getDrawable();
+ if (drawable == null) {
+ return null;
+ }
+ int drawableWidth = drawable.getIntrinsicWidth();
+ int drawableHeight = drawable.getIntrinsicHeight();
+
+ PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true);
+ point.x /= drawableWidth;
+ point.y /= drawableHeight;
+ return point;
+ }
+
+ /**
+ * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the
+ * left and top of the view. The focus points can range in value between 0 and 1.
+ * @param focusX
+ * @param focusY
+ */
+ public void setScrollPosition(float focusX, float focusY) {
+ setZoom(normalizedScale, focusX, focusY);
+ }
+
+ /**
+ * Performs boundary checking and fixes the image matrix if it
+ * is out of bounds.
+ */
+ private void fixTrans() {
+ matrix.getValues(m);
+ float transX = m[Matrix.MTRANS_X];
+ float transY = m[Matrix.MTRANS_Y];
+
+ float fixTransX = getFixTrans(transX, viewWidth, getImageWidth());
+ float fixTransY = getFixTrans(transY, viewHeight, getImageHeight());
+
+ if (fixTransX != 0 || fixTransY != 0) {
+ matrix.postTranslate(fixTransX, fixTransY);
+ }
+ }
+
+ /**
+ * When transitioning from zooming from focus to zoom from center (or vice versa)
+ * the image can become unaligned within the view. This is apparent when zooming
+ * quickly. When the content size is less than the view size, the content will often
+ * be centered incorrectly within the view. fixScaleTrans first calls fixTrans() and
+ * then makes sure the image is centered correctly within the view.
+ */
+ private void fixScaleTrans() {
+ fixTrans();
+ matrix.getValues(m);
+ if (getImageWidth() < viewWidth) {
+ m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2;
+ }
+
+ if (getImageHeight() < viewHeight) {
+ m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2;
+ }
+ matrix.setValues(m);
+ }
+
+ private float getFixTrans(float trans, float viewSize, float contentSize) {
+ float minTrans, maxTrans;
+
+ if (contentSize <= viewSize) {
+ minTrans = 0;
+ maxTrans = viewSize - contentSize;
+
+ } else {
+ minTrans = viewSize - contentSize;
+ maxTrans = 0;
+ }
+
+ if (trans < minTrans)
+ return -trans + minTrans;
+ if (trans > maxTrans)
+ return -trans + maxTrans;
+ return 0;
+ }
+
+ private float getFixDragTrans(float delta, float viewSize, float contentSize) {
+ if (contentSize <= viewSize) {
+ return 0;
+ }
+ return delta;
+ }
+
+ private float getImageWidth() {
+ return matchViewWidth * normalizedScale;
+ }
+
+ private float getImageHeight() {
+ return matchViewHeight * normalizedScale;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Drawable drawable = getDrawable();
+ if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
+ setMeasuredDimension(0, 0);
+ return;
+ }
+
+ int drawableWidth = drawable.getIntrinsicWidth();
+ int drawableHeight = drawable.getIntrinsicHeight();
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ viewWidth = setViewSize(widthMode, widthSize, drawableWidth);
+ viewHeight = setViewSize(heightMode, heightSize, drawableHeight);
+
+ //
+ // Set view dimensions
+ //
+ setMeasuredDimension(viewWidth, viewHeight);
+
+ //
+ // Fit content within view
+ //
+ fitImageToView();
+ }
+
+ /**
+ * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise,
+ * it is made to fit the screen according to the dimensions of the previous image matrix. This
+ * allows the image to maintain its zoom after rotation.
+ */
+ private void fitImageToView() {
+ Drawable drawable = getDrawable();
+ if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
+ return;
+ }
+ if (matrix == null || prevMatrix == null) {
+ return;
+ }
+
+ int drawableWidth = drawable.getIntrinsicWidth();
+ int drawableHeight = drawable.getIntrinsicHeight();
+
+ //
+ // Scale image for view
+ //
+ float scaleX = (float) viewWidth / drawableWidth;
+ float scaleY = (float) viewHeight / drawableHeight;
+
+ switch (mScaleType) {
+ case CENTER:
+ scaleX = scaleY = 1;
+ break;
+
+ case CENTER_CROP:
+ scaleX = scaleY = Math.max(scaleX, scaleY);
+ break;
+
+ case CENTER_INSIDE:
+ scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY));
+
+ case FIT_CENTER:
+ scaleX = scaleY = Math.min(scaleX, scaleY);
+ break;
+
+ case FIT_XY:
+ break;
+
+ default:
+ //
+ // FIT_START and FIT_END not supported
+ //
+ throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END");
+
+ }
+
+ //
+ // Center the image
+ //
+ float redundantXSpace = viewWidth - (scaleX * drawableWidth);
+ float redundantYSpace = viewHeight - (scaleY * drawableHeight);
+ matchViewWidth = viewWidth - redundantXSpace;
+ matchViewHeight = viewHeight - redundantYSpace;
+ if (!isZoomed() && !imageRenderedAtLeastOnce) {
+ //
+ // Stretch and center image to fit view
+ //
+ matrix.setScale(scaleX, scaleY);
+ matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
+ normalizedScale = 1;
+
+ } else {
+ //
+ // These values should never be 0 or we will set viewWidth and viewHeight
+ // to NaN in translateMatrixAfterRotate. To avoid this, call savePreviousImageValues
+ // to set them equal to the current values.
+ //
+ if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) {
+ savePreviousImageValues();
+ }
+
+ prevMatrix.getValues(m);
+
+ //
+ // Rescale Matrix after rotation
+ //
+ m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale;
+ m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale;
+
+ //
+ // TransX and TransY from previous matrix
+ //
+ float transX = m[Matrix.MTRANS_X];
+ float transY = m[Matrix.MTRANS_Y];
+
+ //
+ // Width
+ //
+ float prevActualWidth = prevMatchViewWidth * normalizedScale;
+ float actualWidth = getImageWidth();
+ translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth);
+
+ //
+ // Height
+ //
+ float prevActualHeight = prevMatchViewHeight * normalizedScale;
+ float actualHeight = getImageHeight();
+ translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight);
+
+ //
+ // Set the matrix to the adjusted scale and translate values.
+ //
+ matrix.setValues(m);
+ }
+ fixTrans();
+ setImageMatrix(matrix);
+ }
+
+ /**
+ * Set view dimensions based on layout params
+ *
+ * @param mode
+ * @param size
+ * @param drawableWidth
+ * @return
+ */
+ private int setViewSize(int mode, int size, int drawableWidth) {
+ int viewSize;
+ switch (mode) {
+ case MeasureSpec.EXACTLY:
+ viewSize = size;
+ break;
+
+ case MeasureSpec.AT_MOST:
+ viewSize = Math.min(drawableWidth, size);
+ break;
+
+ case MeasureSpec.UNSPECIFIED:
+ viewSize = drawableWidth;
+ break;
+
+ default:
+ viewSize = size;
+ break;
+ }
+ return viewSize;
+ }
+
+ /**
+ * After rotating, the matrix needs to be translated. This function finds the area of image
+ * which was previously centered and adjusts translations so that is again the center, post-rotation.
+ *
+ * @param axis Matrix.MTRANS_X or Matrix.MTRANS_Y
+ * @param trans the value of trans in that axis before the rotation
+ * @param prevImageSize the width/height of the image before the rotation
+ * @param imageSize width/height of the image after rotation
+ * @param prevViewSize width/height of view before rotation
+ * @param viewSize width/height of view after rotation
+ * @param drawableSize width/height of drawable
+ */
+ private void translateMatrixAfterRotate(int axis, float trans, float prevImageSize, float imageSize, int prevViewSize, int viewSize, int drawableSize) {
+ if (imageSize < viewSize) {
+ //
+ // The width/height of image is less than the view's width/height. Center it.
+ //
+ m[axis] = (viewSize - (drawableSize * m[Matrix.MSCALE_X])) * 0.5f;
+
+ } else if (trans > 0) {
+ //
+ // The image is larger than the view, but was not before rotation. Center it.
+ //
+ m[axis] = -((imageSize - viewSize) * 0.5f);
+
+ } else {
+ //
+ // Find the area of the image which was previously centered in the view. Determine its distance
+ // from the left/top side of the view as a fraction of the entire image's width/height. Use that percentage
+ // to calculate the trans in the new view width/height.
+ //
+ float percentage = (Math.abs(trans) + (0.5f * prevViewSize)) / prevImageSize;
+ m[axis] = -((percentage * imageSize) - (viewSize * 0.5f));
+ }
+ }
+
+ private void setState(State state) {
+ this.state = state;
+ }
+
+ public boolean canScrollHorizontallyFroyo(int direction) {
+ return canScrollHorizontally(direction);
+ }
+
+ @Override
+ public boolean canScrollHorizontally(int direction) {
+ matrix.getValues(m);
+ float x = m[Matrix.MTRANS_X];
+
+ if (getImageWidth() < viewWidth) {
+ return false;
+
+ } else if (x >= -1 && direction < 0) {
+ return false;
+
+ } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gesture Listener detects a single click or long click and passes that on
+ * to the view's listener.
+ * @author Ortiz
+ *
+ */
+ private class GestureListener extends GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e)
+ {
+ if(doubleTapListener != null) {
+ return doubleTapListener.onSingleTapConfirmed(e);
+ }
+ return performClick();
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e)
+ {
+ performLongClick();
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
+ {
+ if (fling != null) {
+ //
+ // If a previous fling is still active, it should be cancelled so that two flings
+ // are not run simultaenously.
+ //
+ fling.cancelFling();
+ }
+ fling = new Fling((int) velocityX, (int) velocityY);
+ compatPostOnAnimation(fling);
+ return super.onFling(e1, e2, velocityX, velocityY);
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ boolean consumed = false;
+ if(doubleTapListener != null) {
+ consumed = doubleTapListener.onDoubleTap(e);
+ }
+ if (state == State.NONE) {
+ float targetZoom = (normalizedScale == minScale) ? maxScale : minScale;
+ DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, e.getX(), e.getY(), false);
+ compatPostOnAnimation(doubleTap);
+ consumed = true;
+ }
+ return consumed;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ if(doubleTapListener != null) {
+ return doubleTapListener.onDoubleTapEvent(e);
+ }
+ return false;
+ }
+ }
+
+ public interface OnTouchImageViewListener {
+ public void onMove();
+ }
+
+ /**
+ * Responsible for all touch events. Handles the heavy lifting of drag and also sends
+ * touch events to Scale Detector and Gesture Detector.
+ * @author Ortiz
+ *
+ */
+ private class PrivateOnTouchListener implements OnTouchListener {
+
+ //
+ // Remember last point position for dragging
+ //
+ private PointF last = new PointF();
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mScaleDetector.onTouchEvent(event);
+ mGestureDetector.onTouchEvent(event);
+ PointF curr = new PointF(event.getX(), event.getY());
+
+ if (state == State.NONE || state == State.DRAG || state == State.FLING) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ last.set(curr);
+ if (fling != null)
+ fling.cancelFling();
+ setState(State.DRAG);
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ if (state == State.DRAG) {
+ float deltaX = curr.x - last.x;
+ float deltaY = curr.y - last.y;
+ float fixTransX = getFixDragTrans(deltaX, viewWidth, getImageWidth());
+ float fixTransY = getFixDragTrans(deltaY, viewHeight, getImageHeight());
+ matrix.postTranslate(fixTransX, fixTransY);
+ fixTrans();
+ last.set(curr.x, curr.y);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ setState(State.NONE);
+ break;
+ }
+ }
+
+ setImageMatrix(matrix);
+
+ //
+ // User-defined OnTouchListener
+ //
+ if(userTouchListener != null) {
+ userTouchListener.onTouch(v, event);
+ }
+
+ //
+ // OnTouchImageViewListener is set: TouchImageView dragged by user.
+ //
+ if (touchImageViewListener != null) {
+ touchImageViewListener.onMove();
+ }
+
+ //
+ // indicate event was handled
+ //
+ return true;
+ }
+ }
+
+ /**
+ * ScaleListener detects user two finger scaling and scales image.
+ * @author Ortiz
+ *
+ */
+ private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ setState(State.ZOOM);
+ return true;
+ }
+
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true);
+
+ //
+ // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user.
+ //
+ if (touchImageViewListener != null) {
+ touchImageViewListener.onMove();
+ }
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ super.onScaleEnd(detector);
+ setState(State.NONE);
+ boolean animateToZoomBoundary = false;
+ float targetZoom = normalizedScale;
+ if (normalizedScale > maxScale) {
+ targetZoom = maxScale;
+ animateToZoomBoundary = true;
+
+ } else if (normalizedScale < minScale) {
+ targetZoom = minScale;
+ animateToZoomBoundary = true;
+ }
+
+ if (animateToZoomBoundary) {
+ DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true);
+ compatPostOnAnimation(doubleTap);
+ }
+ }
+ }
+
+ private void scaleImage(double deltaScale, float focusX, float focusY, boolean stretchImageToSuper) {
+
+ float lowerScale, upperScale;
+ if (stretchImageToSuper) {
+ lowerScale = superMinScale;
+ upperScale = superMaxScale;
+
+ } else {
+ lowerScale = minScale;
+ upperScale = maxScale;
+ }
+
+ float origScale = normalizedScale;
+ normalizedScale *= deltaScale;
+ if (normalizedScale > upperScale) {
+ normalizedScale = upperScale;
+ deltaScale = upperScale / origScale;
+ } else if (normalizedScale < lowerScale) {
+ normalizedScale = lowerScale;
+ deltaScale = lowerScale / origScale;
+ }
+
+ matrix.postScale((float) deltaScale, (float) deltaScale, focusX, focusY);
+ fixScaleTrans();
+ }
+
+ /**
+ * DoubleTapZoom calls a series of runnables which apply
+ * an animated zoom in/out graphic to the image.
+ * @author Ortiz
+ *
+ */
+ private class DoubleTapZoom implements Runnable {
+
+ private long startTime;
+ private static final float ZOOM_TIME = 500;
+ private float startZoom, targetZoom;
+ private float bitmapX, bitmapY;
+ private boolean stretchImageToSuper;
+ private AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator();
+ private PointF startTouch;
+ private PointF endTouch;
+
+ DoubleTapZoom(float targetZoom, float focusX, float focusY, boolean stretchImageToSuper) {
+ setState(State.ANIMATE_ZOOM);
+ startTime = System.currentTimeMillis();
+ this.startZoom = normalizedScale;
+ this.targetZoom = targetZoom;
+ this.stretchImageToSuper = stretchImageToSuper;
+ PointF bitmapPoint = transformCoordTouchToBitmap(focusX, focusY, false);
+ this.bitmapX = bitmapPoint.x;
+ this.bitmapY = bitmapPoint.y;
+
+ //
+ // Used for translating image during scaling
+ //
+ startTouch = transformCoordBitmapToTouch(bitmapX, bitmapY);
+ endTouch = new PointF(viewWidth / 2, viewHeight / 2);
+ }
+
+ @Override
+ public void run() {
+ float t = interpolate();
+ double deltaScale = calculateDeltaScale(t);
+ scaleImage(deltaScale, bitmapX, bitmapY, stretchImageToSuper);
+ translateImageToCenterTouchPosition(t);
+ fixScaleTrans();
+ setImageMatrix(matrix);
+
+ //
+ // OnTouchImageViewListener is set: double tap runnable updates listener
+ // with every frame.
+ //
+ if (touchImageViewListener != null) {
+ touchImageViewListener.onMove();
+ }
+
+ if (t < 1f) {
+ //
+ // We haven't finished zooming
+ //
+ compatPostOnAnimation(this);
+
+ } else {
+ //
+ // Finished zooming
+ //
+ setState(State.NONE);
+ }
+ }
+
+ /**
+ * Interpolate between where the image should start and end in order to translate
+ * the image so that the point that is touched is what ends up centered at the end
+ * of the zoom.
+ * @param t
+ */
+ private void translateImageToCenterTouchPosition(float t) {
+ float targetX = startTouch.x + t * (endTouch.x - startTouch.x);
+ float targetY = startTouch.y + t * (endTouch.y - startTouch.y);
+ PointF curr = transformCoordBitmapToTouch(bitmapX, bitmapY);
+ matrix.postTranslate(targetX - curr.x, targetY - curr.y);
+ }
+
+ /**
+ * Use interpolator to get t
+ * @return
+ */
+ private float interpolate() {
+ long currTime = System.currentTimeMillis();
+ float elapsed = (currTime - startTime) / ZOOM_TIME;
+ elapsed = Math.min(1f, elapsed);
+ return interpolator.getInterpolation(elapsed);
+ }
+
+ /**
+ * Interpolate the current targeted zoom and get the delta
+ * from the current zoom.
+ * @param t
+ * @return
+ */
+ private double calculateDeltaScale(float t) {
+ double zoom = startZoom + t * (targetZoom - startZoom);
+ return zoom / normalizedScale;
+ }
+ }
+
+ /**
+ * This function will transform the coordinates in the touch event to the coordinate
+ * system of the drawable that the imageview contain
+ * @param x x-coordinate of touch event
+ * @param y y-coordinate of touch event
+ * @param clipToBitmap Touch event may occur within view, but outside image content. True, to clip return value
+ * to the bounds of the bitmap size.
+ * @return Coordinates of the point touched, in the coordinate system of the original drawable.
+ */
+ private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitmap) {
+ matrix.getValues(m);
+ float origW = getDrawable().getIntrinsicWidth();
+ float origH = getDrawable().getIntrinsicHeight();
+ float transX = m[Matrix.MTRANS_X];
+ float transY = m[Matrix.MTRANS_Y];
+ float finalX = ((x - transX) * origW) / getImageWidth();
+ float finalY = ((y - transY) * origH) / getImageHeight();
+
+ if (clipToBitmap) {
+ finalX = Math.min(Math.max(finalX, 0), origW);
+ finalY = Math.min(Math.max(finalY, 0), origH);
+ }
+
+ return new PointF(finalX , finalY);
+ }
+
+ /**
+ * Inverse of transformCoordTouchToBitmap. This function will transform the coordinates in the
+ * drawable's coordinate system to the view's coordinate system.
+ * @param bx x-coordinate in original bitmap coordinate system
+ * @param by y-coordinate in original bitmap coordinate system
+ * @return Coordinates of the point in the view's coordinate system.
+ */
+ private PointF transformCoordBitmapToTouch(float bx, float by) {
+ matrix.getValues(m);
+ float origW = getDrawable().getIntrinsicWidth();
+ float origH = getDrawable().getIntrinsicHeight();
+ float px = bx / origW;
+ float py = by / origH;
+ float finalX = m[Matrix.MTRANS_X] + getImageWidth() * px;
+ float finalY = m[Matrix.MTRANS_Y] + getImageHeight() * py;
+ return new PointF(finalX , finalY);
+ }
+
+ /**
+ * Fling launches sequential runnables which apply
+ * the fling graphic to the image. The values for the translation
+ * are interpolated by the Scroller.
+ * @author Ortiz
+ *
+ */
+ private class Fling implements Runnable {
+
+ CompatScroller scroller;
+ int currX, currY;
+
+ Fling(int velocityX, int velocityY) {
+ setState(State.FLING);
+ scroller = new CompatScroller(context);
+ matrix.getValues(m);
+
+ int startX = (int) m[Matrix.MTRANS_X];
+ int startY = (int) m[Matrix.MTRANS_Y];
+ int minX, maxX, minY, maxY;
+
+ if (getImageWidth() > viewWidth) {
+ minX = viewWidth - (int) getImageWidth();
+ maxX = 0;
+
+ } else {
+ minX = maxX = startX;
+ }
+
+ if (getImageHeight() > viewHeight) {
+ minY = viewHeight - (int) getImageHeight();
+ maxY = 0;
+
+ } else {
+ minY = maxY = startY;
+ }
+
+ scroller.fling(startX, startY, (int) velocityX, (int) velocityY, minX,
+ maxX, minY, maxY);
+ currX = startX;
+ currY = startY;
+ }
+
+ public void cancelFling() {
+ if (scroller != null) {
+ setState(State.NONE);
+ scroller.forceFinished(true);
+ }
+ }
+
+ @Override
+ public void run() {
+
+ //
+ // OnTouchImageViewListener is set: TouchImageView listener has been flung by user.
+ // Listener runnable updated with each frame of fling animation.
+ //
+ if (touchImageViewListener != null) {
+ touchImageViewListener.onMove();
+ }
+
+ if (scroller.isFinished()) {
+ scroller = null;
+ return;
+ }
+
+ if (scroller.computeScrollOffset()) {
+ int newX = scroller.getCurrX();
+ int newY = scroller.getCurrY();
+ int transX = newX - currX;
+ int transY = newY - currY;
+ currX = newX;
+ currY = newY;
+ matrix.postTranslate(transX, transY);
+ fixTrans();
+ setImageMatrix(matrix);
+ compatPostOnAnimation(this);
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.GINGERBREAD)
+ private class CompatScroller {
+ Scroller scroller;
+ OverScroller overScroller;
+ boolean isPreGingerbread;
+
+ public CompatScroller(Context context) {
+ if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
+ isPreGingerbread = true;
+ scroller = new Scroller(context);
+
+ } else {
+ isPreGingerbread = false;
+ overScroller = new OverScroller(context);
+ }
+ }
+
+ public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) {
+ if (isPreGingerbread) {
+ scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
+ } else {
+ overScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
+ }
+ }
+
+ public void forceFinished(boolean finished) {
+ if (isPreGingerbread) {
+ scroller.forceFinished(finished);
+ } else {
+ overScroller.forceFinished(finished);
+ }
+ }
+
+ public boolean isFinished() {
+ if (isPreGingerbread) {
+ return scroller.isFinished();
+ } else {
+ return overScroller.isFinished();
+ }
+ }
+
+ public boolean computeScrollOffset() {
+ if (isPreGingerbread) {
+ return scroller.computeScrollOffset();
+ } else {
+ overScroller.computeScrollOffset();
+ return overScroller.computeScrollOffset();
+ }
+ }
+
+ public int getCurrX() {
+ if (isPreGingerbread) {
+ return scroller.getCurrX();
+ } else {
+ return overScroller.getCurrX();
+ }
+ }
+
+ public int getCurrY() {
+ if (isPreGingerbread) {
+ return scroller.getCurrY();
+ } else {
+ return overScroller.getCurrY();
+ }
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ private void compatPostOnAnimation(Runnable runnable) {
+ if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+ postOnAnimation(runnable);
+
+ } else {
+ postDelayed(runnable, 1000/60);
+ }
+ }
+
+ private class ZoomVariables {
+ public float scale;
+ public float focusX;
+ public float focusY;
+ public ScaleType scaleType;
+
+ public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleType) {
+ this.scale = scale;
+ this.focusX = focusX;
+ this.focusY = focusY;
+ this.scaleType = scaleType;
+ }
+ }
+
+ private void printMatrixInfo() {
+ float[] n = new float[9];
+ matrix.getValues(n);
+ Log.d(DEBUG, "Scale: " + n[Matrix.MSCALE_X] + " TransX: " + n[Matrix.MTRANS_X] + " TransY: " + n[Matrix.MTRANS_Y]);
+ }
+}
\ No newline at end of file
-/* ownCloud Android client application
+/**
+ * ownCloud Android client application
+ *
* Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2015 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,