[submodule "actionbarsherlock"]
path = actionbarsherlock
url = git://github.com/JakeWharton/ActionBarSherlock.git
+[submodule "owncloud-android-library"]
+ path = owncloud-android-library
+ url = git@github.com:owncloud/android-library.git
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest package="com.owncloud.android"
- android:versionCode="105000"
- android:versionName="1.5.0" xmlns:android="http://schemas.android.com/apk/res/android">
+ android:versionCode="105003"
+ android:versionName="1.5.3" 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" />
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<category android:name="android.intent.category.DEFAULT" >
</category>
- <data android:mimeType="*/*" >
+ <data android:mimeType="*/*" android:scheme="content">
</data>
- </intent-filter>
+
+ <data android:mimeType="*/*" android:scheme="file">
+ </data>
+ </intent-filter>
</activity>
<activity
android:name=".ui.activity.Preferences"
<service
android:name=".syncadapter.FileSyncService"
android:exported="true"
- android:process=":sync">
+ >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
</intent-filter>
</activity>
+ <service android:name=".services.OperationsService" />
<service android:name=".files.services.FileDownloader" />
<service android:name=".files.services.FileUploader" />
<service android:name=".media.MediaService" />
### 3. Building with console/maven:
-NOTE: You must have mvn in your environment path
+NOTE: You must have mvn (version >= 3.1.1) in your environment path. Current Android 'platforms-tools' need to be installed.
-* Download/install Android plugin for Maven, then build ownCloud with mvn:
-* "cd .."
-* "git clone https://github.com/mosabua/maven-android-sdk-deployer.git"
-* "cd maven-android-sdk-deployer"
-* "mvn -pl com.simpligility.android.sdk-deployer:android-17 -am install"
-* "cd ../android"
-* Now you can create APK using "mvn package"
+Download/install Android plugin for Maven, install owncloud-android-library, then build ownCloud with mvn:
+
+* cd ..
+* git clone https://github.com/mosabua/maven-android-sdk-deployer.git
+* cd maven-android-sdk-deployer
+* mvn -pl com.simpligility.android.sdk-deployer:android-19 -am install
+* cd ../android/owncloud-android-library
+* mvn install
+* cd ..
+
+Now you can create ownCloud APK using "mvn package"
### 4. Building with Eclipse:
* Clean project and compile.
* If any error appear, check the project properties; in the 'Android' section, API Level should be greater or equal than 14.
* Make sure android/actionbarsherlock/library/bin/library.jar was created.
-* Create a new "Android Project from Existing Code". Choose android/oc_framework/library as root.
+* Create a new "Android Project from Existing Code". Choose android/owncloud-android-library as root.
* Clean project and compile.
* If any error appear, check the project properties; in the 'Android' section, API Level should be 19 or greater.
-* Make sure android/oc_framework/bin/classes.jar was created.
+* Make sure android/owncloud-android-library/bin/classes.jar was created.
* Import ownCloud Android project.
* Clean project and compile.
-* If any error appears, check the project properties; in the 'Android' section:
+* If any error appears, check the project properties of owncloud-android project; in the 'Android' section:
- API Level should be 19 or greater.
- - Two library projects should appear referred in the bottom square: actionbarsherlock/library and oc_framework. Add them if needed.
+ - Two library projects should appear referred in the bottom square: actionbarsherlock/library and owncloud-android-library. Add them if needed.
* After those actions you should be good to go. HAVE FUN!
NOTE: Even though API level is set to 19, APK also runs on older devices because in AndroidManifest.xml minSdkVersion is set to 8.
* Again, click "Edit" and set "compare:develop"
* Enter description and send pull request.
+### 6. Create another pull request:
+
+To make sure your new pull request does not contain commits which are already contained in previous PRs, create a new branch which is a clone of upstream/develop.
+
+* git fetch upstream
+* git checkout -b my_new_develop_branch upstream/develop
+* If you want to rename that branch later: "git checkout -b my_new_develop_branch_with_new_name"
+* Push branch to server: "git push -u origin name_of_local_develop_branch"
+* Use Github to issue PR
+
[0]: https://github.com/owncloud/android/blob/master/CONTRIBUTING.md
[1]: http://owncloud.org/about/contributor-agreement/
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry exported="true" kind="lib" path="/oc_framework/bin/oc_framework.jar" sourcepath="/oc_framework"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>oc_framework-test-project</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.source=1.6
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.owncloud.android.oc_framework_test_project"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-permission android:name="android.permission.USE_CREDENTIALS" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="19" />
-
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity
- android:name="com.owncloud.android.oc_framework_test_project.TestActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry combineaccessrules="false" kind="src" path="/oc_framework-test-project"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>oc_framework-test</name>
- <comment></comment>
- <projects>
- <project>oc_framework-test-project</project>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.source=1.6
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.owncloud.android.oc_framework_test_project.test"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk android:minSdkVersion="8" />
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:label="Tests for com.owncloud.android.oc_framework_test_project"
- android:targetPackage="com.owncloud.android.oc_framework_test_project" />
-
-</manifest>
\ No newline at end of file
+++ /dev/null
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="app_name">Oc_framework-testTest</string>
-
-</resources>
+++ /dev/null
-package com.owncloud.android.oc_framework_test_project.test;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework_test_project.TestActivity;
-
-import android.test.ActivityInstrumentationTestCase2;
-
-/**
- * Class to test Create Folder Operation
- * @author masensio
- *
- */
-public class CreateFolderTest extends ActivityInstrumentationTestCase2<TestActivity> {
-
- private TestActivity mActivity;
- private String mCurrentDate;
-
- public CreateFolderTest() {
- super(TestActivity.class);
-
- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
- mCurrentDate = sdf.format(new Date());
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- setActivityInitialTouchMode(false);
- mActivity = getActivity();
- }
-
- /**
- * Test Create Folder
- */
- public void testCreateFolder() {
-
- String remotePath = "/testCreateFolder" + mCurrentDate;
- boolean createFullPath = true;
-
- RemoteOperationResult result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.isSuccess() || result.getCode() == ResultCode.TIMEOUT);
-
- // Create Subfolder
- remotePath = "/testCreateFolder" + mCurrentDate + "/" + "testCreateFolder" + mCurrentDate;
- createFullPath = true;
-
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.isSuccess() || result.getCode() == ResultCode.TIMEOUT);
- }
-
-
- /**
- * Test to Create Folder with special characters: / \ < > : " | ? *
- */
- public void testCreateFolderSpecialCharacters() {
- boolean createFullPath = true;
-
- String remotePath = "/testSpecialCharacters_\\" + mCurrentDate;
- RemoteOperationResult result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_<" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_>" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_:" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_\"" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_|" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_?" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- remotePath = "/testSpecialCharacters_*" + mCurrentDate;
- result = mActivity.createFolder(remotePath, createFullPath);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
- }
-
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework_test_project.test;
-
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework_test_project.TestActivity;
-
-import android.test.ActivityInstrumentationTestCase2;
-
-public class RenameFileTest extends ActivityInstrumentationTestCase2<TestActivity> {
-
- /* Folder data to rename. This folder must exist on the account */
- private final String mOldFolderName = "folderToRename";
- private final String mOldFolderPath = "/folderToRename";
- private final String mNewFolderName = "renamedFolder";
- private final String mNewFolderPath = "/renamedFolder";
-
- /* File data to rename. This file must exist on the account */
- private final String mOldFileName = "fileToRename.png";
- private final String mOldFilePath = "/fileToRename.png";
- private final String mNewFileName = "renamedFile";
- private final String mFileExtension = ".png";
- private final String mNewFilePath ="/renamedFile.png";
-
-
- private TestActivity mActivity;
-
- public RenameFileTest() {
- super(TestActivity.class);
-
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- setActivityInitialTouchMode(false);
- mActivity = getActivity();
- }
-
- /**
- * Test Rename Folder
- */
- public void testRenameFolder() {
-
- RemoteOperationResult result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName, true);
- assertTrue(result.isSuccess());
- }
-
- /**
- * Test Rename Folder with forbidden characters : \ < > : " | ? *
- */
- public void testRenameFolderForbiddenChars() {
-
- RemoteOperationResult result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "\\", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "<", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + ">", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + ":", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "\"", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "|", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "?", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFolderName, mOldFolderPath,
- mNewFolderName + "*", true);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
- }
-
- /**
- * Test Rename File
- */
- public void testRenameFile() {
- RemoteOperationResult result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + mFileExtension, false);
- assertTrue(result.isSuccess());
- }
-
-
- /**
- * Test Rename Folder with forbidden characters: \ < > : " | ? *
- */
- public void testRenameFileForbiddenChars() {
- RemoteOperationResult result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "\\" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "<" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + ">" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + ":" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "\"" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "|" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "?" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- result = mActivity.renameFile(mOldFileName, mOldFilePath,
- mNewFileName + "*" + mFileExtension, false);
- assertTrue(result.getCode() == ResultCode.INVALID_CHARACTER_IN_NAME);
-
- }
-
-
- /**
- * Restore initial conditions
- */
- public void testRestoreInitialConditions() {
- RemoteOperationResult result = mActivity.renameFile(mNewFolderName, mNewFolderPath, mOldFolderName, true);
- assertTrue(result.isSuccess());
-
- result = mActivity.renameFile(mNewFileName + mFileExtension, mNewFilePath, mOldFileName, false);
- assertTrue(result.isSuccess());
- }
-
-}
+++ /dev/null
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library.reference.1=../oc_framework
+++ /dev/null
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".TestActivity" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/hello_world" />
-
-</RelativeLayout>
+++ /dev/null
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item
- android:id="@+id/action_settings"
- android:orderInCategory="100"
- android:showAsAction="never"
- android:title="@string/action_settings"/>
-
-</menu>
+++ /dev/null
-<resources>
-
- <!--
- Customize dimensions originally defined in res/values/dimens.xml (such as
- screen margins) for sw600dp devices (e.g. 7" tablets) here.
- -->
-
-</resources>
+++ /dev/null
-<resources>
-
- <!--
- Customize dimensions originally defined in res/values/dimens.xml (such as
- screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
- -->
- <dimen name="activity_horizontal_margin">128dp</dimen>
-
-</resources>
+++ /dev/null
-<resources>
-
- <!--
- Base application theme for API 11+. This theme completely replaces
- AppBaseTheme from res/values/styles.xml on API 11+ devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
- <!-- API 11 theme customizations can go here. -->
- </style>
-
-</resources>
+++ /dev/null
-<resources>
-
- <!--
- Base application theme for API 14+. This theme completely replaces
- AppBaseTheme from BOTH res/values/styles.xml and
- res/values-v11/styles.xml on API 14+ devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
- <!-- API 14 theme customizations can go here. -->
- </style>
-
-</resources>
+++ /dev/null
-<resources>
-
- <!-- Default screen margins, per the Android Design guidelines. -->
- <dimen name="activity_horizontal_margin">16dp</dimen>
- <dimen name="activity_vertical_margin">16dp</dimen>
-
-</resources>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <string name="app_name">oc_framework-test-project</string>
- <string name="action_settings">Settings</string>
- <string name="hello_world">Hello world!</string>
- <string name="test_account_not_found">The test account %1$s could not be found in the device</string>
-
-</resources>
+++ /dev/null
-<resources>
-
- <!--
- Base application theme, dependent on API level. This theme is replaced
- by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
- -->
- <style name="AppBaseTheme" parent="android:Theme.Light">
- <!--
- Theme customizations available in newer API levels can go in
- res/values-vXX/styles.xml, while customizations related to
- backward-compatibility can go here.
- -->
- </style>
-
- <!-- Application theme. -->
- <style name="AppTheme" parent="AppBaseTheme">
- <!-- All customizations that are NOT specific to a particular API-level can go here. -->
- </style>
-
-</resources>
+++ /dev/null
-package com.owncloud.android.oc_framework_test_project;
-
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.remote.CreateRemoteFolderOperation;
-import com.owncloud.android.oc_framework.operations.remote.RenameRemoteFileOperation;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.app.Activity;
-import android.view.Menu;
-
-/**
- * Activity to test OC framework
- * @author masensio
- * @author David A. Velasco
- */
-public class TestActivity extends Activity {
-
- // This account must exists on the simulator / device
- private static final String mServerUri = "https://beta.owncloud.com/owncloud/remote.php/webdav";
- private static final String mUser = "testandroid";
- private static final String mPass = "testandroid";
-
- //private Account mAccount = null;
- private WebdavClient mClient;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_test);
- Uri uri = Uri.parse(mServerUri);
- mClient = OwnCloudClientFactory.createOwnCloudClient(uri ,getApplicationContext(), true);
- mClient.setBasicCredentials(mUser, mPass);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.test, menu);
- return true;
- }
-
- /**
- * Access to the library method to Create a Folder
- * @param remotePath
- * @param createFullPath
- *
- * @return
- */
- public RemoteOperationResult createFolder(String remotePath, boolean createFullPath) {
-
- CreateRemoteFolderOperation createOperation = new CreateRemoteFolderOperation(remotePath, createFullPath);
- RemoteOperationResult result = createOperation.execute(mClient);
-
- return result;
- }
-
- /**
- * Access to the library method to Rename a File or Folder
- * @param oldName Old name of the file.
- * @param oldRemotePath Old remote path of the file. For folders it starts and ends by "/"
- * @param newName New name to set as the name of file.
- * @param isFolder 'true' for folder and 'false' for files
- *
- * @return
- */
-
- public RemoteOperationResult renameFile(String oldName, String oldRemotePath, String newName, boolean isFolder) {
-
- RenameRemoteFileOperation renameOperation = new RenameRemoteFileOperation(oldName, oldRemotePath, newName, isFolder);
- RemoteOperationResult result = renameOperation.execute(mClient);
-
- return result;
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>oc_framework</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
+++ /dev/null
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.source=1.6
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.owncloud.android.oc_framework"
- android:versionCode="1"
- android:versionName="1.0" >
-
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="19" />
-
-</manifest>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project name="ownCloudFramework" default="help">
-
- <!-- The local.properties file is created and updated by the 'android' tool.
- It contains the path to the SDK. It should *NOT* be checked into
- Version Control Systems. -->
- <property file="local.properties" />
-
- <!-- The ant.properties file can be created by you. It is only edited by the
- 'android' tool to add properties to it.
- This is the place to change some Ant specific build properties.
- Here are some properties you may want to change/update:
-
- source.dir
- The name of the source directory. Default is 'src'.
- out.dir
- The name of the output directory. Default is 'bin'.
-
- For other overridable properties, look at the beginning of the rules
- files in the SDK, at tools/ant/build.xml
-
- Properties related to the SDK location or the project target should
- be updated using the 'android' tool with the 'update' action.
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems.
-
- -->
- <property file="ant.properties" />
-
- <!-- if sdk.dir was not set from one of the property file, then
- get it from the ANDROID_HOME env var.
- This must be done before we load project.properties since
- the proguard config can use sdk.dir -->
- <property environment="env" />
- <condition property="sdk.dir" value="${env.ANDROID_HOME}">
- <isset property="env.ANDROID_HOME" />
- </condition>
-
- <!-- The project.properties file is created and updated by the 'android'
- tool, as well as ADT.
-
- This contains project specific properties such as project target, and library
- dependencies. Lower level build properties are stored in ant.properties
- (or in .classpath for Eclipse projects).
-
- This file is an integral part of the build system for your
- application and should be checked into Version Control Systems. -->
- <loadproperties srcFile="project.properties" />
-
- <!-- quick check on sdk.dir -->
- <fail
- message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
- unless="sdk.dir"
- />
-
- <!--
- Import per project custom build rules if present at the root of the project.
- This is the place to put custom intermediary targets such as:
- -pre-build
- -pre-compile
- -post-compile (This is typically used for code obfuscation.
- Compiled code location: ${out.classes.absolute.dir}
- If this is not done in place, override ${out.dex.input.absolute.dir})
- -post-package
- -post-build
- -pre-clean
- -->
- <import file="custom_rules.xml" optional="true" />
-
- <!-- Import the actual build file.
-
- To customize existing targets, there are two options:
- - Customize only one target:
- - copy/paste the target into this file, *before* the
- <import> task.
- - customize it to your needs.
- - Customize the whole content of build.xml
- - copy/paste the content of the rules files (minus the top node)
- into this file, replacing the <import> task.
- - customize to your needs.
-
- ***********************
- ****** IMPORTANT ******
- ***********************
- In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
- in order to avoid having your file be overridden by tools such as "android update project"
- -->
- <!-- version-tag: 1 -->
- <import file="${sdk.dir}/tools/ant/build.xml" />
-
-</project>
+++ /dev/null
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library=true
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<resources/>
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.accounts;
-
-/**
- * @author masensio
- * @author David A. Velasco
- */
-public class AccountTypeUtils {
-
- public static String getAuthTokenTypePass(String accountType) {
- return accountType + ".password";
- }
-
- public static String getAuthTokenTypeAccessToken(String accountType) {
- return accountType + ".oauth2.access_token";
- }
-
- public static String getAuthTokenTypeRefreshToken(String accountType) {
- return accountType + ".oauth2.refresh_token";
- }
-
- public static String getAuthTokenTypeSamlSessionCookie(String accountType) {
- return accountType + ".saml.web_sso.session_cookie";
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.accounts;
-
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.content.Context;
-
-public class AccountUtils {
- public static final String WEBDAV_PATH_1_2 = "/webdav/owncloud.php";
- public static final String WEBDAV_PATH_2_0 = "/files/webdav.php";
- public static final String WEBDAV_PATH_4_0 = "/remote.php/webdav";
- private static final String ODAV_PATH = "/remote.php/odav";
- private static final String SAML_SSO_PATH = "/remote.php/webdav";
- public static final String CARDDAV_PATH_2_0 = "/apps/contacts/carddav.php";
- public static final String CARDDAV_PATH_4_0 = "/remote/carddav.php";
- public static final String STATUS_PATH = "/status.php";
-
- /**
- *
- * @param version version of owncloud
- * @return webdav path for given OC version, null if OC version unknown
- */
- public static String getWebdavPath(OwnCloudVersion version, boolean supportsOAuth, boolean supportsSamlSso) {
- if (version != null) {
- if (supportsOAuth) {
- return ODAV_PATH;
- }
- if (supportsSamlSso) {
- return SAML_SSO_PATH;
- }
- if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)
- return WEBDAV_PATH_4_0;
- if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0
- || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)
- return WEBDAV_PATH_2_0;
- if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)
- return WEBDAV_PATH_1_2;
- }
- return null;
- }
-
-// /**
-// * Returns the proper URL path to access the WebDAV interface of an ownCloud server,
-// * according to its version and the authorization method used.
-// *
-// * @param version Version of ownCloud server.
-// * @param authTokenType Authorization token type, matching some of the AUTH_TOKEN_TYPE_* constants in {@link AccountAuthenticator}.
-// * @return WebDAV path for given OC version and authorization method, null if OC version is unknown.
-// */
-// public static String getWebdavPath(OwnCloudVersion version, String authTokenType) {
-// if (version != null) {
-// if (MainApp.getAuthTokenTypeAccessToken().equals(authTokenType)) {
-// return ODAV_PATH;
-// }
-// if (MainApp.getAuthTokenTypeSamlSessionCookie().equals(authTokenType)) {
-// return SAML_SSO_PATH;
-// }
-// if (version.compareTo(OwnCloudVersion.owncloud_v4) >= 0)
-// return WEBDAV_PATH_4_0;
-// if (version.compareTo(OwnCloudVersion.owncloud_v3) >= 0
-// || version.compareTo(OwnCloudVersion.owncloud_v2) >= 0)
-// return WEBDAV_PATH_2_0;
-// if (version.compareTo(OwnCloudVersion.owncloud_v1) >= 0)
-// return WEBDAV_PATH_1_2;
-// }
-// return null;
-// }
-
- /**
- * Constructs full url to host and webdav resource basing on host version
- * @param context
- * @param account
- * @return url or null on failure
- * @throws AccountNotFoundException When 'account' is unknown for the AccountManager
- */
- public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException {
- AccountManager ama = AccountManager.get(context);
- String baseurl = ama.getUserData(account, OwnCloudAccount.Constants.KEY_OC_BASE_URL);
- String strver = ama.getUserData(account, OwnCloudAccount.Constants.KEY_OC_VERSION);
- boolean supportsOAuth = (ama.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null);
- boolean supportsSamlSso = (ama.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null);
- OwnCloudVersion ver = new OwnCloudVersion(strver);
- String webdavpath = getWebdavPath(ver, supportsOAuth, supportsSamlSso);
-
- if (baseurl == null || webdavpath == null)
- throw new AccountNotFoundException(account, "Account not found", null);
-
- return baseurl + webdavpath;
- }
-
-
- public static class AccountNotFoundException extends AccountsException {
-
- /** Generated - should be refreshed every time the class changes!! */
- private static final long serialVersionUID = -9013287181793186830L;
-
- private Account mFailedAccount;
-
- public AccountNotFoundException(Account failedAccount, String message, Throwable cause) {
- super(message, cause);
- mFailedAccount = failedAccount;
- }
-
- public Account getFailedAccount() {
- return mFailedAccount;
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.accounts;
-
-import android.accounts.Account;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Account with extra information specific for ownCloud accounts.
- *
- * TODO integrate in the main app
- *
- * @author David A. Velasco
- */
-public class OwnCloudAccount extends Account {
-
- public static class Constants {
- /**
- * Value under this key should handle path to webdav php script. Will be
- * removed and usage should be replaced by combining
- * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
- * {@link com.owncloud.android.oc_framework.utils.utils.OwnCloudVersion}
- *
- * @deprecated
- */
- public static final String KEY_OC_URL = "oc_url";
- /**
- * Version should be 3 numbers separated by dot so it can be parsed by
- * {@link com.owncloud.android.oc_framework.utils.utils.OwnCloudVersion}
- */
- public static final String KEY_OC_VERSION = "oc_version";
- /**
- * Base url should point to owncloud installation without trailing / ie:
- * http://server/path or https://owncloud.server
- */
- public static final String KEY_OC_BASE_URL = "oc_base_url";
- /**
- * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
- */
- public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
- /**
- * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
- */
- public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
- }
-
- private String mAuthTokenType;
-
- public OwnCloudAccount(String name, String type, String authTokenType) {
- super(name, type);
- // TODO validate authTokentype as supported
- mAuthTokenType = authTokenType;
- }
-
- /**
- * Reconstruct from parcel
- *
- * @param source The source parcel
- */
- public OwnCloudAccount(Parcel source) {
- super(source);
- mAuthTokenType = source.readString();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(mAuthTokenType);
- }
-
-
- public String getAuthTokenType() {
- return mAuthTokenType;
- }
-
-
- public static final Parcelable.Creator<OwnCloudAccount> CREATOR = new Parcelable.Creator<OwnCloudAccount>() {
- @Override
- public OwnCloudAccount createFromParcel(Parcel source) {
- return new OwnCloudAccount(source);
- }
-
- @Override
- public OwnCloudAccount [] newArray(int size) {
- return new OwnCloudAccount[size];
- }
- };
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.UnknownHostException;
-import java.security.cert.X509Certificate;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.params.HttpConnectionParams;
-import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import android.util.Log;
-
-
-
-/**
- * AdvancedSSLProtocolSocketFactory allows to create SSL {@link Socket}s with
- * a custom SSLContext and an optional Hostname Verifier.
- *
- * @author David A. Velasco
- */
-
-public class AdvancedSslSocketFactory implements ProtocolSocketFactory {
-
- private static final String TAG = AdvancedSslSocketFactory.class.getSimpleName();
-
- private SSLContext mSslContext = null;
- private AdvancedX509TrustManager mTrustManager = null;
- private X509HostnameVerifier mHostnameVerifier = null;
-
- public SSLContext getSslContext() {
- return mSslContext;
- }
-
- /**
- * Constructor for AdvancedSSLProtocolSocketFactory.
- */
- public AdvancedSslSocketFactory(SSLContext sslContext, AdvancedX509TrustManager trustManager, X509HostnameVerifier hostnameVerifier) {
- if (sslContext == null)
- throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null SSLContext");
- if (trustManager == null)
- throw new IllegalArgumentException("AdvancedSslSocketFactory can not be created with a null Trust Manager");
- mSslContext = sslContext;
- mTrustManager = trustManager;
- mHostnameVerifier = hostnameVerifier;
- }
-
- /**
- * @see ProtocolSocketFactory#createSocket(java.lang.String,int,java.net.InetAddress,int)
- */
- public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
- Socket socket = mSslContext.getSocketFactory().createSocket(host, port, clientHost, clientPort);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
-
- /**
- * Attempts to get a new socket connection to the given host within the
- * given time limit.
- *
- * @param host the host name/IP
- * @param port the port on the host
- * @param clientHost the local host name/IP to bind the socket to
- * @param clientPort the port on the local machine
- * @param params {@link HttpConnectionParams Http connection parameters}
- *
- * @return Socket a new socket
- *
- * @throws IOException if an I/O error occurs while creating the socket
- * @throws UnknownHostException if the IP address of the host cannot be
- * determined
- */
- public Socket createSocket(final String host, final int port,
- final InetAddress localAddress, final int localPort,
- final HttpConnectionParams params) throws IOException,
- UnknownHostException, ConnectTimeoutException {
- Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port + ", local " + localAddress + ":" + localPort + ", params: " + params);
- if (params == null) {
- throw new IllegalArgumentException("Parameters may not be null");
- }
- int timeout = params.getConnectionTimeout();
- SocketFactory socketfactory = mSslContext.getSocketFactory();
- Log.d(TAG, " ... with connection timeout " + timeout + " and socket timeout " + params.getSoTimeout());
- Socket socket = socketfactory.createSocket();
- SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
- SocketAddress remoteaddr = new InetSocketAddress(host, port);
- socket.setSoTimeout(params.getSoTimeout());
- socket.bind(localaddr);
- socket.connect(remoteaddr, timeout);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
- /**
- * @see ProtocolSocketFactory#createSocket(java.lang.String,int)
- */
- public Socket createSocket(String host, int port) throws IOException,
- UnknownHostException {
- Log.d(TAG, "Creating SSL Socket with remote " + host + ":" + port);
- Socket socket = mSslContext.getSocketFactory().createSocket(host, port);
- verifyPeerIdentity(host, port, socket);
- return socket;
- }
-
- public boolean equals(Object obj) {
- return ((obj != null) && obj.getClass().equals(
- AdvancedSslSocketFactory.class));
- }
-
- public int hashCode() {
- return AdvancedSslSocketFactory.class.hashCode();
- }
-
-
- public X509HostnameVerifier getHostNameVerifier() {
- return mHostnameVerifier;
- }
-
-
- public void setHostNameVerifier(X509HostnameVerifier hostnameVerifier) {
- mHostnameVerifier = hostnameVerifier;
- }
-
- /**
- * Verifies the identity of the server.
- *
- * The server certificate is verified first.
- *
- * Then, the host name is compared with the content of the server certificate using the current host name verifier, if any.
- * @param socket
- */
- private void verifyPeerIdentity(String host, int port, Socket socket) throws IOException {
- try {
- CertificateCombinedException failInHandshake = null;
- /// 1. VERIFY THE SERVER CERTIFICATE through the registered TrustManager (that should be an instance of AdvancedX509TrustManager)
- try {
- SSLSocket sock = (SSLSocket) socket; // a new SSLSession instance is created as a "side effect"
- sock.startHandshake();
-
- } catch (RuntimeException e) {
-
- if (e instanceof CertificateCombinedException) {
- failInHandshake = (CertificateCombinedException) e;
- } else {
- Throwable cause = e.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertificateCombinedException) {
- failInHandshake = (CertificateCombinedException)cause;
- }
- }
- if (failInHandshake == null) {
- throw e;
- }
- failInHandshake.setHostInUrl(host);
-
- }
-
- /// 2. VERIFY HOSTNAME
- SSLSession newSession = null;
- boolean verifiedHostname = true;
- if (mHostnameVerifier != null) {
- if (failInHandshake != null) {
- /// 2.1 : a new SSLSession instance was NOT created in the handshake
- X509Certificate serverCert = failInHandshake.getServerCertificate();
- try {
- mHostnameVerifier.verify(host, serverCert);
- } catch (SSLException e) {
- verifiedHostname = false;
- }
-
- } else {
- /// 2.2 : a new SSLSession instance was created in the handshake
- newSession = ((SSLSocket)socket).getSession();
- if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
- verifiedHostname = mHostnameVerifier.verify(host, newSession);
- }
- }
- }
-
- /// 3. Combine the exceptions to throw, if any
- if (!verifiedHostname) {
- SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException("Names in the server certificate do not match to " + host + " in the URL");
- if (failInHandshake == null) {
- failInHandshake = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
- failInHandshake.setHostInUrl(host);
- }
- failInHandshake.setSslPeerUnverifiedException(pue);
- pue.initCause(failInHandshake);
- throw pue;
-
- } else if (failInHandshake != null) {
- SSLHandshakeException hse = new SSLHandshakeException("Server certificate could not be verified");
- hse.initCause(failInHandshake);
- throw hse;
- }
-
- } catch (IOException io) {
- try {
- socket.close();
- } catch (Exception x) {
- // NOTHING - irrelevant exception for the caller
- }
- throw io;
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network;
-
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertStoreException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-
-import android.util.Log;
-
-
-
-/**
- * @author David A. Velasco
- */
-public class AdvancedX509TrustManager implements X509TrustManager {
-
- private static final String TAG = AdvancedX509TrustManager.class.getSimpleName();
-
- private X509TrustManager mStandardTrustManager = null;
- private KeyStore mKnownServersKeyStore;
-
- /**
- * Constructor for AdvancedX509TrustManager
- *
- * @param knownServersCertStore Local certificates store with server certificates explicitly trusted by the user.
- * @throws CertStoreException When no default X509TrustManager instance was found in the system.
- */
- public AdvancedX509TrustManager(KeyStore knownServersKeyStore)
- throws NoSuchAlgorithmException, KeyStoreException, CertStoreException {
- super();
- TrustManagerFactory factory = TrustManagerFactory
- .getInstance(TrustManagerFactory.getDefaultAlgorithm());
- factory.init((KeyStore)null);
- mStandardTrustManager = findX509TrustManager(factory);
-
- mKnownServersKeyStore = knownServersKeyStore;
- }
-
-
- /**
- * Locates the first X509TrustManager provided by a given TrustManagerFactory
- * @param factory TrustManagerFactory to inspect in the search for a X509TrustManager
- * @return The first X509TrustManager found in factory.
- * @throws CertStoreException When no X509TrustManager instance was found in factory
- */
- private X509TrustManager findX509TrustManager(TrustManagerFactory factory) throws CertStoreException {
- TrustManager tms[] = factory.getTrustManagers();
- for (int i = 0; i < tms.length; i++) {
- if (tms[i] instanceof X509TrustManager) {
- return (X509TrustManager) tms[i];
- }
- }
- return null;
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],
- * String authType)
- */
- public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
- mStandardTrustManager.checkClientTrusted(certificates, authType);
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],
- * String authType)
- */
- public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {
- if (!isKnownServer(certificates[0])) {
- CertificateCombinedException result = new CertificateCombinedException(certificates[0]);
- try {
- certificates[0].checkValidity();
- } catch (CertificateExpiredException c) {
- result.setCertificateExpiredException(c);
-
- } catch (CertificateNotYetValidException c) {
- result.setCertificateNotYetException(c);
- }
-
- try {
- mStandardTrustManager.checkServerTrusted(certificates, authType);
- } catch (CertificateException c) {
- Throwable cause = c.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertPathValidatorException)) { // getCause() is not funny
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertPathValidatorException) {
- result.setCertPathValidatorException((CertPathValidatorException)cause);
- } else {
- result.setOtherCertificateException(c);
- }
- }
-
- if (result.isException())
- throw result;
-
- }
- }
-
-
- /**
- * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()
- */
- public X509Certificate[] getAcceptedIssuers() {
- return mStandardTrustManager.getAcceptedIssuers();
- }
-
-
- public boolean isKnownServer(X509Certificate cert) {
- try {
- return (mKnownServersKeyStore.getCertificateAlias(cert) != null);
- } catch (KeyStoreException e) {
- Log.d(TAG, "Fail while checking certificate in the known-servers store");
- return false;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-package com.owncloud.android.oc_framework.network;
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-import java.util.Map;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.auth.AuthChallengeParser;
-import org.apache.commons.httpclient.auth.AuthScheme;
-import org.apache.commons.httpclient.auth.AuthenticationException;
-import org.apache.commons.httpclient.auth.InvalidCredentialsException;
-import org.apache.commons.httpclient.auth.MalformedChallengeException;
-
-import android.util.Log;
-
-
-
-/**
- * Bearer authentication scheme as defined in RFC 6750.
- *
- * @author David A. Velasco
- */
-
-public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ {
-
- private static final String TAG = BearerAuthScheme.class.getSimpleName();
-
- public static final String AUTH_POLICY = "Bearer";
-
- /** Whether the bearer authentication process is complete */
- private boolean mComplete;
-
- /** Authentication parameter map */
- @SuppressWarnings("rawtypes")
- private Map mParams = null;
-
-
- /**
- * Default constructor for the bearer authentication scheme.
- */
- public BearerAuthScheme() {
- mComplete = false;
- }
-
- /**
- * Constructor for the basic authentication scheme.
- *
- * @param challenge Authentication challenge
- *
- * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
- *
- * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method
- */
- public BearerAuthScheme(final String challenge) throws MalformedChallengeException {
- processChallenge(challenge);
- mComplete = true;
- }
-
- /**
- * Returns textual designation of the bearer authentication scheme.
- *
- * @return "Bearer"
- */
- public String getSchemeName() {
- return "bearer";
- }
-
- /**
- * Processes the Bearer challenge.
- *
- * @param challenge The challenge string
- *
- * @throws MalformedChallengeException Thrown if the authentication challenge is malformed
- */
- public void processChallenge(String challenge) throws MalformedChallengeException {
- String s = AuthChallengeParser.extractScheme(challenge);
- if (!s.equalsIgnoreCase(getSchemeName())) {
- throw new MalformedChallengeException(
- "Invalid " + getSchemeName() + " challenge: " + challenge);
- }
- mParams = AuthChallengeParser.extractParams(challenge);
- mComplete = true;
- }
-
- /**
- * Tests if the Bearer authentication process has been completed.
- *
- * @return 'true' if Bearer authorization has been processed, 'false' otherwise.
- */
- public boolean isComplete() {
- return this.mComplete;
- }
-
- /**
- * Produces bearer authorization string for the given set of
- * {@link Credentials}.
- *
- * @param credentials The set of credentials to be used for authentication
- * @param method Method name is ignored by the bearer authentication scheme
- * @param uri URI is ignored by the bearer authentication scheme
- * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable
- * for this authentication scheme
- * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure
- * @return A bearer authorization string
- *
- * @deprecated Use {@link #authenticate(Credentials, HttpMethod)}
- */
- public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException {
- Log.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)");
-
- BearerCredentials bearer = null;
- try {
- bearer = (BearerCredentials) credentials;
- } catch (ClassCastException e) {
- throw new InvalidCredentialsException(
- "Credentials cannot be used for bearer authentication: "
- + credentials.getClass().getName());
- }
- return BearerAuthScheme.authenticate(bearer);
- }
-
-
- /**
- * Returns 'false'. Bearer authentication scheme is request based.
- *
- * @return 'false'.
- */
- public boolean isConnectionBased() {
- return false;
- }
-
- /**
- * Produces bearer authorization string for the given set of {@link Credentials}.
- *
- * @param credentials The set of credentials to be used for authentication
- * @param method The method being authenticated
- * @throws InvalidCredentialsException If authentication credentials are not valid or not applicable for this authentication
- * scheme.
- * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure.
- *
- * @return a basic authorization string
- */
- public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException {
- Log.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)");
-
- if (method == null) {
- throw new IllegalArgumentException("Method may not be null");
- }
- BearerCredentials bearer = null;
- try {
- bearer = (BearerCredentials) credentials;
- } catch (ClassCastException e) {
- throw new InvalidCredentialsException(
- "Credentials cannot be used for bearer authentication: "
- + credentials.getClass().getName());
- }
- return BearerAuthScheme.authenticate(
- bearer,
- method.getParams().getCredentialCharset());
- }
-
- /**
- * @deprecated Use {@link #authenticate(BearerCredentials, String)}
- *
- * Returns a bearer Authorization header value for the given
- * {@link BearerCredentials}.
- *
- * @param credentials The credentials to encode.
- *
- * @return A bearer authorization string
- */
- public static String authenticate(BearerCredentials credentials) {
- return authenticate(credentials, "ISO-8859-1");
- }
-
- /**
- * Returns a bearer Authorization header value for the given
- * {@link BearerCredentials} and charset.
- *
- * @param credentials The credentials to encode.
- * @param charset The charset to use for encoding the credentials
- *
- * @return A bearer authorization string
- *
- * @since 3.0
- */
- public static String authenticate(BearerCredentials credentials, String charset) {
- Log.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)");
-
- if (credentials == null) {
- throw new IllegalArgumentException("Credentials may not be null");
- }
- if (charset == null || charset.length() == 0) {
- throw new IllegalArgumentException("charset may not be null or empty");
- }
- StringBuffer buffer = new StringBuffer();
- buffer.append(credentials.getAccessToken());
-
- //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset));
- return "Bearer " + buffer.toString();
- }
-
- /**
- * Returns a String identifying the authentication challenge. This is
- * used, in combination with the host and port to determine if
- * authorization has already been attempted or not. Schemes which
- * require multiple requests to complete the authentication should
- * return a different value for each stage in the request.
- *
- * Additionally, the ID should take into account any changes to the
- * authentication challenge and return a different value when appropriate.
- * For example when the realm changes in basic authentication it should be
- * considered a different authentication attempt and a different value should
- * be returned.
- *
- * This method simply returns the realm for the challenge.
- *
- * @return String a String identifying the authentication challenge.
- *
- * @deprecated no longer used
- */
- @Override
- public String getID() {
- return getRealm();
- }
-
- /**
- * Returns authentication parameter with the given name, if available.
- *
- * @param name The name of the parameter to be returned
- *
- * @return The parameter with the given name
- */
- @Override
- public String getParameter(String name) {
- if (name == null) {
- throw new IllegalArgumentException("Parameter name may not be null");
- }
- if (mParams == null) {
- return null;
- }
- return (String) mParams.get(name.toLowerCase());
- }
-
- /**
- * Returns authentication realm. The realm may not be null.
- *
- * @return The authentication realm
- */
- @Override
- public String getRealm() {
- return getParameter("realm");
- }
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework.network;
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.util.LangUtils;
-
-/**
- * Bearer token {@link Credentials}
- *
- * @author David A. Velasco
- */
-public class BearerCredentials implements Credentials {
-
-
- private String mAccessToken;
-
-
- /**
- * The constructor with the bearer token
- *
- * @param token The bearer token
- */
- public BearerCredentials(String token) {
- /*if (token == null) {
- throw new IllegalArgumentException("Bearer token may not be null");
- }*/
- mAccessToken = (token == null) ? "" : token;
- }
-
-
- /**
- * Returns the access token
- *
- * @return The access token
- */
- public String getAccessToken() {
- return mAccessToken;
- }
-
-
- /**
- * Get this object string.
- *
- * @return The access token
- */
- public String toString() {
- return mAccessToken;
- }
-
- /**
- * Does a hash of the access token.
- *
- * @return The hash code of the access token
- */
- public int hashCode() {
- int hash = LangUtils.HASH_SEED;
- hash = LangUtils.hashCode(hash, mAccessToken);
- return hash;
- }
-
- /**
- * These credentials are assumed equal if accessToken is the same.
- *
- * @param o The other object to compare with.
- *
- * @return 'True' if the object is equivalent.
- */
- public boolean equals(Object o) {
- if (o == null) return false;
- if (this == o) return true;
- if (this.getClass().equals(o.getClass())) {
- BearerCredentials that = (BearerCredentials) o;
- if (LangUtils.equals(mAccessToken, that.mAccessToken)) {
- return true;
- }
- }
- return false;
- }
-
-}
-
+++ /dev/null
-package com.owncloud.android.oc_framework.network;
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-import java.security.cert.CertPathValidatorException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-/**
- * Exception joining all the problems that {@link AdvancedX509TrustManager} can find in
- * a certificate chain for a server.
- *
- * This was initially created as an extension of CertificateException, but some
- * implementations of the SSL socket layer in existing devices are REPLACING the CertificateException
- * instances thrown by {@link javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[], String)}
- * with SSLPeerUnverifiedException FORGETTING THE CAUSING EXCEPTION instead of wrapping it.
- *
- * Due to this, extending RuntimeException is necessary to get that the CertificateCombinedException
- * instance reaches {@link AdvancedSslSocketFactory#verifyPeerIdentity}.
- *
- * BE CAREFUL. As a RuntimeException extensions, Java compilers do not require to handle it
- * in client methods. Be sure to use it only when you know exactly where it will go.
- *
- * @author David A. Velasco
- */
-public class CertificateCombinedException extends RuntimeException {
-
- /** Generated - to refresh every time the class changes */
- private static final long serialVersionUID = -8875782030758554999L;
-
- private X509Certificate mServerCert = null;
- private String mHostInUrl;
-
- private CertificateExpiredException mCertificateExpiredException = null;
- private CertificateNotYetValidException mCertificateNotYetValidException = null;
- private CertPathValidatorException mCertPathValidatorException = null;
- private CertificateException mOtherCertificateException = null;
- private SSLPeerUnverifiedException mSslPeerUnverifiedException = null;
-
- public CertificateCombinedException(X509Certificate x509Certificate) {
- mServerCert = x509Certificate;
- }
-
- public X509Certificate getServerCertificate() {
- return mServerCert;
- }
-
- public String getHostInUrl() {
- return mHostInUrl;
- }
-
- public void setHostInUrl(String host) {
- mHostInUrl = host;
- }
-
- public CertificateExpiredException getCertificateExpiredException() {
- return mCertificateExpiredException;
- }
-
- public void setCertificateExpiredException(CertificateExpiredException c) {
- mCertificateExpiredException = c;
- }
-
- public CertificateNotYetValidException getCertificateNotYetValidException() {
- return mCertificateNotYetValidException;
- }
-
- public void setCertificateNotYetException(CertificateNotYetValidException c) {
- mCertificateNotYetValidException = c;
- }
-
- public CertPathValidatorException getCertPathValidatorException() {
- return mCertPathValidatorException;
- }
-
- public void setCertPathValidatorException(CertPathValidatorException c) {
- mCertPathValidatorException = c;
- }
-
- public CertificateException getOtherCertificateException() {
- return mOtherCertificateException;
- }
-
- public void setOtherCertificateException(CertificateException c) {
- mOtherCertificateException = c;
- }
-
- public SSLPeerUnverifiedException getSslPeerUnverifiedException() {
- return mSslPeerUnverifiedException ;
- }
-
- public void setSslPeerUnverifiedException(SSLPeerUnverifiedException s) {
- mSslPeerUnverifiedException = s;
- }
-
- public boolean isException() {
- return (mCertificateExpiredException != null ||
- mCertificateNotYetValidException != null ||
- mCertPathValidatorException != null ||
- mOtherCertificateException != null ||
- mSslPeerUnverifiedException != null);
- }
-
- public boolean isRecoverable() {
- return (mCertificateExpiredException != null ||
- mCertificateNotYetValidException != null ||
- mCertPathValidatorException != null ||
- mSslPeerUnverifiedException != null);
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.oc_framework.network;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.GeneralSecurityException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-
-import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
-import org.apache.commons.httpclient.protocol.Protocol;
-import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
-import org.apache.http.conn.ssl.X509HostnameVerifier;
-
-import android.content.Context;
-import android.util.Log;
-
-public class NetworkUtils {
-
- final private static String TAG = NetworkUtils.class.getSimpleName();
-
- /** Default timeout for waiting data from the server */
- public static final int DEFAULT_DATA_TIMEOUT = 60000;
-
- /** Default timeout for establishing a connection */
- public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
-
- /** Connection manager for all the WebdavClients */
- private static MultiThreadedHttpConnectionManager mConnManager = null;
-
- private static Protocol mDefaultHttpsProtocol = null;
-
- private static AdvancedSslSocketFactory mAdvancedSslSocketFactory = null;
-
- private static X509HostnameVerifier mHostnameVerifier = null;
-
-
- /**
- * Registers or unregisters the proper components for advanced SSL handling.
- * @throws IOException
- */
- public static void registerAdvancedSslContext(boolean register, Context context) throws GeneralSecurityException, IOException {
- Protocol pr = null;
- try {
- pr = Protocol.getProtocol("https");
- if (pr != null && mDefaultHttpsProtocol == null) {
- mDefaultHttpsProtocol = pr;
- }
- } catch (IllegalStateException e) {
- // nothing to do here; really
- }
- boolean isRegistered = (pr != null && pr.getSocketFactory() instanceof AdvancedSslSocketFactory);
- if (register && !isRegistered) {
- Protocol.registerProtocol("https", new Protocol("https", getAdvancedSslSocketFactory(context), 443));
-
- } else if (!register && isRegistered) {
- if (mDefaultHttpsProtocol != null) {
- Protocol.registerProtocol("https", mDefaultHttpsProtocol);
- }
- }
- }
-
- public static AdvancedSslSocketFactory getAdvancedSslSocketFactory(Context context) throws GeneralSecurityException, IOException {
- if (mAdvancedSslSocketFactory == null) {
- KeyStore trustStore = getKnownServersStore(context);
- AdvancedX509TrustManager trustMgr = new AdvancedX509TrustManager(trustStore);
- TrustManager[] tms = new TrustManager[] { trustMgr };
-
- SSLContext sslContext = SSLContext.getInstance("TLS");
- sslContext.init(null, tms, null);
-
- mHostnameVerifier = new BrowserCompatHostnameVerifier();
- mAdvancedSslSocketFactory = new AdvancedSslSocketFactory(sslContext, trustMgr, mHostnameVerifier);
- }
- return mAdvancedSslSocketFactory;
- }
-
-
- private static String LOCAL_TRUSTSTORE_FILENAME = "knownServers.bks";
-
- private static String LOCAL_TRUSTSTORE_PASSWORD = "password";
-
- private static KeyStore mKnownServersStore = null;
-
- /**
- * Returns the local store of reliable server certificates, explicitly accepted by the user.
- *
- * Returns a KeyStore instance with empty content if the local store was never created.
- *
- * Loads the store from the storage environment if needed.
- *
- * @param context Android context where the operation is being performed.
- * @return KeyStore instance with explicitly-accepted server certificates.
- * @throws KeyStoreException When the KeyStore instance could not be created.
- * @throws IOException When an existing local trust store could not be loaded.
- * @throws NoSuchAlgorithmException When the existing local trust store was saved with an unsupported algorithm.
- * @throws CertificateException When an exception occurred while loading the certificates from the local trust store.
- */
- private static KeyStore getKnownServersStore(Context context) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
- if (mKnownServersStore == null) {
- //mKnownServersStore = KeyStore.getInstance("BKS");
- mKnownServersStore = KeyStore.getInstance(KeyStore.getDefaultType());
- File localTrustStoreFile = new File(context.getFilesDir(), LOCAL_TRUSTSTORE_FILENAME);
- Log.d(TAG, "Searching known-servers store at " + localTrustStoreFile.getAbsolutePath());
- if (localTrustStoreFile.exists()) {
- InputStream in = new FileInputStream(localTrustStoreFile);
- try {
- mKnownServersStore.load(in, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
- } finally {
- in.close();
- }
- } else {
- mKnownServersStore.load(null, LOCAL_TRUSTSTORE_PASSWORD.toCharArray()); // necessary to initialize an empty KeyStore instance
- }
- }
- return mKnownServersStore;
- }
-
-
- public static void addCertToKnownServersStore(Certificate cert, Context context) throws KeyStoreException, NoSuchAlgorithmException,
- CertificateException, IOException {
- KeyStore knownServers = getKnownServersStore(context);
- knownServers.setCertificateEntry(Integer.toString(cert.hashCode()), cert);
- FileOutputStream fos = null;
- try {
- fos = context.openFileOutput(LOCAL_TRUSTSTORE_FILENAME, Context.MODE_PRIVATE);
- knownServers.store(fos, LOCAL_TRUSTSTORE_PASSWORD.toCharArray());
- } finally {
- fos.close();
- }
- }
-
-
- static public MultiThreadedHttpConnectionManager getMultiThreadedConnManager() {
- if (mConnManager == null) {
- mConnManager = new MultiThreadedHttpConnectionManager();
- mConnManager.getParams().setDefaultMaxConnectionsPerHost(5);
- mConnManager.getParams().setMaxTotalConnections(5);
- }
- return mConnManager;
- }
-
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework.network;
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-
-
-import java.util.Collection;
-
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-
-
-public interface ProgressiveDataTransferer {
-
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener);
-
- public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners);
-
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener);
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network.webdav;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.apache.commons.httpclient.methods.RequestEntity;
-
-import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer;
-
-import android.util.Log;
-
-
-/**
- * A RequestEntity that represents a PIECE of a file.
- *
- * @author David A. Velasco
- */
-public class ChunkFromFileChannelRequestEntity implements RequestEntity, ProgressiveDataTransferer {
-
- private static final String TAG = ChunkFromFileChannelRequestEntity.class.getSimpleName();
-
- //private final File mFile;
- private final FileChannel mChannel;
- private final String mContentType;
- private final long mChunkSize;
- private final File mFile;
- private long mOffset;
- private long mTransferred;
- Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
- private ByteBuffer mBuffer = ByteBuffer.allocate(4096);
-
- public ChunkFromFileChannelRequestEntity(final FileChannel channel, final String contentType, long chunkSize, final File file) {
- super();
- if (channel == null) {
- throw new IllegalArgumentException("File may not be null");
- }
- if (chunkSize <= 0) {
- throw new IllegalArgumentException("Chunk size must be greater than zero");
- }
- mChannel = channel;
- mContentType = contentType;
- mChunkSize = chunkSize;
- mFile = file;
- mOffset = 0;
- mTransferred = 0;
- }
-
- public void setOffset(long offset) {
- mOffset = offset;
- }
-
- public long getContentLength() {
- try {
- return Math.min(mChunkSize, mChannel.size() - mChannel.position());
- } catch (IOException e) {
- return mChunkSize;
- }
- }
-
- public String getContentType() {
- return mContentType;
- }
-
- public boolean isRepeatable() {
- return true;
- }
-
- @Override
- public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.add(listener);
- }
- }
-
- @Override
- public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.addAll(listeners);
- }
- }
-
- @Override
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.remove(listener);
- }
- }
-
-
- public void writeRequest(final OutputStream out) throws IOException {
- int readCount = 0;
- Iterator<OnDatatransferProgressListener> it = null;
-
- try {
- mChannel.position(mOffset);
- long size = mFile.length();
- if (size == 0) size = -1;
- long maxCount = Math.min(mOffset + mChunkSize, mChannel.size());
- while (mChannel.position() < maxCount) {
- readCount = mChannel.read(mBuffer);
- out.write(mBuffer.array(), 0, readCount);
- mBuffer.clear();
- if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks
- mTransferred += readCount;
- }
- synchronized (mDataTransferListeners) {
- it = mDataTransferListeners.iterator();
- while (it.hasNext()) {
- it.next().onTransferProgress(readCount, mTransferred, size, mFile.getName());
- }
- }
- }
-
- } catch (IOException io) {
- Log.e(TAG, io.getMessage());
- throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);
-
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network.webdav;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import org.apache.commons.httpclient.methods.RequestEntity;
-
-import android.util.Log;
-
-import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer;
-
-
-/**
- * A RequestEntity that represents a File.
- *
- */
-public class FileRequestEntity implements RequestEntity, ProgressiveDataTransferer {
-
- final File mFile;
- final String mContentType;
- Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
-
- public FileRequestEntity(final File file, final String contentType) {
- super();
- this.mFile = file;
- this.mContentType = contentType;
- if (file == null) {
- throw new IllegalArgumentException("File may not be null");
- }
- }
-
- @Override
- public long getContentLength() {
- return mFile.length();
- }
-
- @Override
- public String getContentType() {
- return mContentType;
- }
-
- @Override
- public boolean isRepeatable() {
- return true;
- }
-
- @Override
- public void addDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.add(listener);
- }
- }
-
- @Override
- public void addDatatransferProgressListeners(Collection<OnDatatransferProgressListener> listeners) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.addAll(listeners);
- }
- }
-
- @Override
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.remove(listener);
- }
- }
-
-
- @Override
- public void writeRequest(final OutputStream out) throws IOException {
- //byte[] tmp = new byte[4096];
- ByteBuffer tmp = ByteBuffer.allocate(4096);
- int readResult = 0;
-
- // TODO(bprzybylski): each mem allocation can throw OutOfMemoryError we need to handle it
- // globally in some fashionable manner
- RandomAccessFile raf = new RandomAccessFile(mFile, "r");
- FileChannel channel = raf.getChannel();
- Iterator<OnDatatransferProgressListener> it = null;
- long transferred = 0;
- long size = mFile.length();
- if (size == 0) size = -1;
- try {
- while ((readResult = channel.read(tmp)) >= 0) {
- out.write(tmp.array(), 0, readResult);
- tmp.clear();
- transferred += readResult;
- synchronized (mDataTransferListeners) {
- it = mDataTransferListeners.iterator();
- while (it.hasNext()) {
- it.next().onTransferProgress(readResult, transferred, size, mFile.getName());
- }
- }
- }
-
- } catch (IOException io) {
- Log.e("FileRequestException", io.getMessage());
- throw new RuntimeException("Ugly solution to workaround the default policy of retries when the server falls while uploading ; temporal fix; really", io);
-
- } finally {
- channel.close();
- raf.close();
- }
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network.webdav;
-
-public interface OnDatatransferProgressListener {
- public void onTransferProgress(long progressRate);
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName);
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.oc_framework.network.webdav;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-
-import com.owncloud.android.oc_framework.accounts.AccountTypeUtils;
-import com.owncloud.android.oc_framework.accounts.AccountUtils;
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
-import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException;
-import com.owncloud.android.oc_framework.network.NetworkUtils;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerFuture;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.app.Activity;
-import android.content.Context;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-public class OwnCloudClientFactory {
-
- final private static String TAG = OwnCloudClientFactory.class.getSimpleName();
-
- /** Default timeout for waiting data from the server */
- public static final int DEFAULT_DATA_TIMEOUT = 60000;
-
- /** Default timeout for establishing a connection */
- public static final int DEFAULT_CONNECTION_TIMEOUT = 60000;
-
-
- /**
- * Creates a WebdavClient setup for an ownCloud account
- *
- * Do not call this method from the main thread.
- *
- * @param account The ownCloud account
- * @param appContext Android application context
- * @return A WebdavClient object ready to be used
- * @throws AuthenticatorException If the authenticator failed to get the authorization token for the account.
- * @throws OperationCanceledException If the authenticator operation was cancelled while getting the authorization token for the account.
- * @throws IOException If there was some I/O error while getting the authorization token for the account.
- * @throws AccountNotFoundException If 'account' is unknown for the AccountManager
- */
- public static WebdavClient createOwnCloudClient (Account account, Context appContext) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
- //Log_OC.d(TAG, "Creating WebdavClient associated to " + account.name);
-
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
- AccountManager am = AccountManager.get(appContext);
- boolean isOauth2 = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
- boolean isSamlSso = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
- WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
- if (isOauth2) {
- String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), false);
- client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
-
- } else if (isSamlSso) { // TODO avoid a call to getUserData here
- String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), false);
- client.setSsoSessionCookie(accessToken);
-
- } else {
- String username = account.name.substring(0, account.name.lastIndexOf('@'));
- //String password = am.getPassword(account);
- String password = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), false);
- client.setBasicCredentials(username, password);
- }
-
- return client;
- }
-
-
- public static WebdavClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException {
- Uri uri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account));
- AccountManager am = AccountManager.get(appContext);
- boolean isOauth2 = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here
- boolean isSamlSso = am.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null;
- WebdavClient client = createOwnCloudClient(uri, appContext, !isSamlSso);
-
- if (isOauth2) { // TODO avoid a call to getUserData here
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
- if (accessToken == null) throw new AuthenticatorException("WTF!");
- client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token
-
- } else if (isSamlSso) { // TODO avoid a call to getUserData here
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
- if (accessToken == null) throw new AuthenticatorException("WTF!");
- client.setSsoSessionCookie(accessToken);
-
- } else {
- String username = account.name.substring(0, account.name.lastIndexOf('@'));
- //String password = am.getPassword(account);
- //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false);
- AccountManagerFuture<Bundle> future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), null, currentActivity, null, null);
- Bundle result = future.getResult();
- String password = result.getString(AccountManager.KEY_AUTHTOKEN);
- client.setBasicCredentials(username, password);
- }
-
- return client;
- }
-
- /**
- * Creates a WebdavClient to access a URL and sets the desired parameters for ownCloud client connections.
- *
- * @param uri URL to the ownCloud server
- * @param context Android context where the WebdavClient is being created.
- * @return A WebdavClient object ready to be used
- */
- public static WebdavClient createOwnCloudClient(Uri uri, Context context, boolean followRedirects) {
- try {
- NetworkUtils.registerAdvancedSslContext(true, context);
- } catch (GeneralSecurityException e) {
- Log.e(TAG, "Advanced SSL Context could not be loaded. Default SSL management in the system will be used for HTTPS connections", e);
-
- } catch (IOException e) {
- Log.e(TAG, "The local server truststore could not be read. Default SSL management in the system will be used for HTTPS connections", e);
- }
-
- WebdavClient client = new WebdavClient(NetworkUtils.getMultiThreadedConnManager());
-
- client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT);
- client.setBaseUri(uri);
- client.setFollowRedirects(followRedirects);
-
- return client;
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network.webdav;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.commons.httpclient.Credentials;
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpClient;
-import org.apache.commons.httpclient.HttpConnectionManager;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.HttpMethod;
-import org.apache.commons.httpclient.HttpMethodBase;
-import org.apache.commons.httpclient.HttpVersion;
-import org.apache.commons.httpclient.URI;
-import org.apache.commons.httpclient.UsernamePasswordCredentials;
-import org.apache.commons.httpclient.auth.AuthPolicy;
-import org.apache.commons.httpclient.auth.AuthScope;
-import org.apache.commons.httpclient.cookie.CookiePolicy;
-import org.apache.commons.httpclient.methods.HeadMethod;
-import org.apache.commons.httpclient.params.HttpMethodParams;
-import org.apache.http.HttpStatus;
-import org.apache.http.params.CoreProtocolPNames;
-
-import com.owncloud.android.oc_framework.network.BearerAuthScheme;
-import com.owncloud.android.oc_framework.network.BearerCredentials;
-
-import android.net.Uri;
-import android.util.Log;
-
-public class WebdavClient extends HttpClient {
- private static final int MAX_REDIRECTIONS_COUNT = 3;
-
- private Uri mUri;
- private Credentials mCredentials;
- private boolean mFollowRedirects;
- private String mSsoSessionCookie;
- final private static String TAG = WebdavClient.class.getSimpleName();
- public static final String USER_AGENT = "Android-ownCloud";
-
- static private byte[] sExhaustBuffer = new byte[1024];
-
- /**
- * Constructor
- */
- public WebdavClient(HttpConnectionManager connectionMgr) {
- super(connectionMgr);
- Log.d(TAG, "Creating WebdavClient");
- getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);
- getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
- mFollowRedirects = true;
- mSsoSessionCookie = null;
- }
-
- public void setBearerCredentials(String accessToken) {
- AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class);
-
- List<String> authPrefs = new ArrayList<String>(1);
- authPrefs.add(BearerAuthScheme.AUTH_POLICY);
- getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
-
- mCredentials = new BearerCredentials(accessToken);
- getState().setCredentials(AuthScope.ANY, mCredentials);
- mSsoSessionCookie = null;
- }
-
- public void setBasicCredentials(String username, String password) {
- List<String> authPrefs = new ArrayList<String>(1);
- authPrefs.add(AuthPolicy.BASIC);
- getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
-
- getParams().setAuthenticationPreemptive(true);
- mCredentials = new UsernamePasswordCredentials(username, password);
- getState().setCredentials(AuthScope.ANY, mCredentials);
- mSsoSessionCookie = null;
- }
-
- public void setSsoSessionCookie(String accessToken) {
- getParams().setAuthenticationPreemptive(false);
- getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
- mSsoSessionCookie = accessToken;
- mCredentials = null;
- }
-
-
- /**
- * Check if a file exists in the OC server
- *
- * TODO replace with ExistenceOperation
- *
- * @return 'true' if the file exists; 'false' it doesn't exist
- * @throws Exception When the existence could not be determined
- */
- public boolean existsFile(String path) throws IOException, HttpException {
- HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path));
- try {
- int status = executeMethod(head);
- Log.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":""));
- exhaustResponse(head.getResponseBodyAsStream());
- return (status == HttpStatus.SC_OK);
-
- } finally {
- head.releaseConnection(); // let the connection available for other methods
- }
- }
-
- /**
- * Requests the received method with the received timeout (milliseconds).
- *
- * Executes the method through the inherited HttpClient.executedMethod(method).
- *
- * Sets the socket and connection timeouts only for the method received.
- *
- * The timeouts are both in milliseconds; 0 means 'infinite'; < 0 means 'do not change the default'
- *
- * @param method HTTP method request.
- * @param readTimeout Timeout to set for data reception
- * @param conntionTimout Timeout to set for connection establishment
- */
- public int executeMethod(HttpMethodBase method, int readTimeout, int connectionTimeout) throws HttpException, IOException {
- int oldSoTimeout = getParams().getSoTimeout();
- int oldConnectionTimeout = getHttpConnectionManager().getParams().getConnectionTimeout();
- try {
- if (readTimeout >= 0) {
- method.getParams().setSoTimeout(readTimeout); // this should be enough...
- getParams().setSoTimeout(readTimeout); // ... but this looks like necessary for HTTPS
- }
- if (connectionTimeout >= 0) {
- getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
- }
- return executeMethod(method);
- } finally {
- getParams().setSoTimeout(oldSoTimeout);
- getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout);
- }
- }
-
-
- @Override
- public int executeMethod(HttpMethod method) throws IOException, HttpException {
- boolean customRedirectionNeeded = false;
- try {
- method.setFollowRedirects(mFollowRedirects);
- } catch (Exception e) {
- //if (mFollowRedirects) Log_OC.d(TAG, "setFollowRedirects failed for " + method.getName() + " method, custom redirection will be used if needed");
- customRedirectionNeeded = mFollowRedirects;
- }
- if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {
- method.setRequestHeader("Cookie", mSsoSessionCookie);
- }
- int status = super.executeMethod(method);
- int redirectionsCount = 0;
- while (customRedirectionNeeded &&
- redirectionsCount < MAX_REDIRECTIONS_COUNT &&
- ( status == HttpStatus.SC_MOVED_PERMANENTLY ||
- status == HttpStatus.SC_MOVED_TEMPORARILY ||
- status == HttpStatus.SC_TEMPORARY_REDIRECT)
- ) {
-
- Header location = method.getResponseHeader("Location");
- if (location != null) {
- Log.d(TAG, "Location to redirect: " + location.getValue());
- method.setURI(new URI(location.getValue(), true));
- status = super.executeMethod(method);
- redirectionsCount++;
-
- } else {
- Log.d(TAG, "No location to redirect!");
- status = HttpStatus.SC_NOT_FOUND;
- }
- }
-
- return status;
- }
-
-
- /**
- * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
- *
- * @param responseBodyAsStream InputStream with the HTTP response to exhaust.
- */
- public void exhaustResponse(InputStream responseBodyAsStream) {
- if (responseBodyAsStream != null) {
- try {
- while (responseBodyAsStream.read(sExhaustBuffer) >= 0);
- responseBodyAsStream.close();
-
- } catch (IOException io) {
- Log.e(TAG, "Unexpected exception while exhausting not interesting HTTP response; will be IGNORED", io);
- }
- }
- }
-
- /**
- * Sets the connection and wait-for-data timeouts to be applied by default to the methods performed by this client.
- */
- public void setDefaultTimeouts(int defaultDataTimeout, int defaultConnectionTimeout) {
- getParams().setSoTimeout(defaultDataTimeout);
- getHttpConnectionManager().getParams().setConnectionTimeout(defaultConnectionTimeout);
- }
-
- /**
- * Sets the base URI for the helper methods that receive paths as parameters, instead of full URLs
- * @param uri
- */
- public void setBaseUri(Uri uri) {
- mUri = uri;
- }
-
- public Uri getBaseUri() {
- return mUri;
- }
-
- public final Credentials getCredentials() {
- return mCredentials;
- }
-
- public final String getSsoSessionCookie() {
- return mSsoSessionCookie;
- }
-
- public void setFollowRedirects(boolean followRedirects) {
- mFollowRedirects = followRedirects;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud
- *
- * 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.oc_framework.network.webdav;
-
-import java.util.Date;
-
-import org.apache.jackrabbit.webdav.MultiStatusResponse;
-import org.apache.jackrabbit.webdav.property.DavProperty;
-import org.apache.jackrabbit.webdav.property.DavPropertyName;
-import org.apache.jackrabbit.webdav.property.DavPropertySet;
-
-
-
-import android.net.Uri;
-import android.util.Log;
-
-public class WebdavEntry {
- private String mName, mPath, mUri, mContentType, mEtag;
- private long mContentLength, mCreateTimestamp, mModifiedTimestamp;
-
- public WebdavEntry(MultiStatusResponse ms, String splitElement) {
- resetData();
- if (ms.getStatus().length != 0) {
- mUri = ms.getHref();
-
- mPath = mUri.split(splitElement, 2)[1];
-
- int status = ms.getStatus()[0].getStatusCode();
- DavPropertySet propSet = ms.getProperties(status);
- @SuppressWarnings("rawtypes")
- DavProperty prop = propSet.get(DavPropertyName.DISPLAYNAME);
- if (prop != null) {
- mName = (String) prop.getName().toString();
- mName = mName.substring(1, mName.length()-1);
- }
- else {
- String[] tmp = mPath.split("/");
- if (tmp.length > 0)
- mName = tmp[tmp.length - 1];
- }
-
- // use unknown mimetype as default behavior
- mContentType = "application/octet-stream";
- prop = propSet.get(DavPropertyName.GETCONTENTTYPE);
- if (prop != null) {
- mContentType = (String) prop.getValue();
- // dvelasco: some builds of ownCloud server 4.0.x added a trailing ';' to the MIME type ; if looks fixed, but let's be cautious
- if (mContentType.indexOf(";") >= 0) {
- mContentType = mContentType.substring(0, mContentType.indexOf(";"));
- }
- }
-
- // check if it's a folder in the standard way: see RFC2518 12.2 . RFC4918 14.3
- prop = propSet.get(DavPropertyName.RESOURCETYPE);
- if (prop!= null) {
- Object value = prop.getValue();
- if (value != null) {
- mContentType = "DIR"; // a specific attribute would be better, but this is enough; unless while we have no reason to distinguish MIME types for folders
- }
- }
-
- prop = propSet.get(DavPropertyName.GETCONTENTLENGTH);
- if (prop != null)
- mContentLength = Long.parseLong((String) prop.getValue());
-
- prop = propSet.get(DavPropertyName.GETLASTMODIFIED);
- if (prop != null) {
- Date d = WebdavUtils
- .parseResponseDate((String) prop.getValue());
- mModifiedTimestamp = (d != null) ? d.getTime() : 0;
- }
-
- prop = propSet.get(DavPropertyName.CREATIONDATE);
- if (prop != null) {
- Date d = WebdavUtils
- .parseResponseDate((String) prop.getValue());
- mCreateTimestamp = (d != null) ? d.getTime() : 0;
- }
-
- prop = propSet.get(DavPropertyName.GETETAG);
- if (prop != null) {
- mEtag = (String) prop.getValue();
- mEtag = mEtag.substring(1, mEtag.length()-1);
- }
-
- } else {
- Log.e("WebdavEntry",
- "General fuckup, no status for webdav response");
- }
- }
-
- public String path() {
- return mPath;
- }
-
- public String decodedPath() {
- return Uri.decode(mPath);
- }
-
- public String name() {
- return mName;
- }
-
- public boolean isDirectory() {
- return mContentType.equals("DIR");
- }
-
- public String contentType() {
- return mContentType;
- }
-
- public String uri() {
- return mUri;
- }
-
- public long contentLength() {
- return mContentLength;
- }
-
- public long createTimestamp() {
- return mCreateTimestamp;
- }
-
- public long modifiedTimestamp() {
- return mModifiedTimestamp;
- }
-
- public String etag() {
- return mEtag;
- }
-
- private void resetData() {
- mName = mUri = mContentType = null;
- mContentLength = mCreateTimestamp = mModifiedTimestamp = 0;
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.network.webdav;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-import android.net.Uri;
-
-public class WebdavUtils {
- public static final SimpleDateFormat DISPLAY_DATE_FORMAT = new SimpleDateFormat(
- "dd.MM.yyyy hh:mm");
- private static final SimpleDateFormat DATETIME_FORMATS[] = {
- new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sss'Z'", Locale.US),
- new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
- new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) };
-
- public static String prepareXmlForPropFind() {
- String ret = "<?xml version=\"1.0\" ?><D:propfind xmlns:D=\"DAV:\"><D:allprop/></D:propfind>";
- return ret;
- }
-
- public static String prepareXmlForPatch() {
- return "<?xml version=\"1.0\" ?><D:propertyupdate xmlns:D=\"DAV:\"></D:propertyupdate>";
- }
-
- public static Date parseResponseDate(String date) {
- Date returnDate = null;
- for (int i = 0; i < DATETIME_FORMATS.length; ++i) {
- try {
- returnDate = DATETIME_FORMATS[i].parse(date);
- return returnDate;
- } catch (ParseException e) {
- }
- }
- return null;
- }
-
- /**
- * Encodes a path according to URI RFC 2396.
- *
- * If the received path doesn't start with "/", the method adds it.
- *
- * @param remoteFilePath Path
- * @return Encoded path according to RFC 2396, always starting with "/"
- */
- public static String encodePath(String remoteFilePath) {
- String encodedPath = Uri.encode(remoteFilePath, "/");
- if (!encodedPath.startsWith("/"))
- encodedPath = "/" + encodedPath;
- return encodedPath;
- }
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.operations;
-
-public interface OnRemoteOperationListener {
-
- void onRemoteOperationFinish(RemoteOperation caller, RemoteOperationResult result);
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.operations;
-
-public class OperationCancelledException extends Exception {
-
- /**
- * Generated serial version - to avoid Java warning
- */
- private static final long serialVersionUID = -6350981497740424983L;
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.oc_framework.operations;
-
-import java.io.IOException;
-
-import org.apache.commons.httpclient.Credentials;
-
-import com.owncloud.android.oc_framework.network.BearerCredentials;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-
-
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountsException;
-import android.app.Activity;
-import android.content.Context;
-import android.os.Handler;
-import android.util.Log;
-
-
-/**
- * Operation which execution involves one or several interactions with an ownCloud server.
- *
- * Provides methods to execute the operation both synchronously or asynchronously.
- *
- * @author David A. Velasco
- */
-public abstract class RemoteOperation implements Runnable {
-
- private static final String TAG = RemoteOperation.class.getSimpleName();
-
- /** ownCloud account in the remote ownCloud server to operate */
- private Account mAccount = null;
-
- /** Android Application context */
- private Context mContext = null;
-
- /** Object to interact with the remote server */
- private WebdavClient mClient = null;
-
- /** Callback object to notify about the execution of the remote operation */
- private OnRemoteOperationListener mListener = null;
-
- /** Handler to the thread where mListener methods will be called */
- private Handler mListenerHandler = null;
-
- /** Activity */
- private Activity mCallerActivity;
-
-
- /**
- * Abstract method to implement the operation in derived classes.
- */
- protected abstract RemoteOperationResult run(WebdavClient client);
-
-
- /**
- * Synchronously executes the remote operation on the received ownCloud account.
- *
- * Do not call this method from the main thread.
- *
- * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
- *
- * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
- * @param context Android context for the component calling the method.
- * @return Result of the operation.
- */
- public final RemoteOperationResult execute(Account account, Context context) {
- if (account == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
- if (context == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
- mAccount = account;
- mContext = context.getApplicationContext();
- try {
- mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext);
- } catch (Exception e) {
- Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
- return new RemoteOperationResult(e);
- }
- return run(mClient);
- }
-
-
- /**
- * Synchronously executes the remote operation
- *
- * Do not call this method from the main thread.
- *
- * @param client Client object to reach an ownCloud server during the execution of the operation.
- * @return Result of the operation.
- */
- public final RemoteOperationResult execute(WebdavClient client) {
- if (client == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
- mClient = client;
- return run(client);
- }
-
-
- /**
- * Asynchronously executes the remote operation
- *
- * This method should be used whenever an ownCloud account is available, instead of {@link #execute(WebdavClient)}.
- *
- * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
- * @param context Android context for the component calling the method.
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
- if (account == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Account");
- if (context == null)
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL Context");
- mAccount = account;
- mContext = context.getApplicationContext();
- mCallerActivity = callerActivity;
- mClient = null; // the client instance will be created from mAccount and mContext in the runnerThread to create below
-
- mListener = listener;
-
- mListenerHandler = listenerHandler;
-
- Thread runnerThread = new Thread(this);
- runnerThread.start();
- return runnerThread;
- }
-
-
- /**
- * Asynchronously executes the remote operation
- *
- * @param client Client object to reach an ownCloud server during the execution of the operation.
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread execute(WebdavClient client, OnRemoteOperationListener listener, Handler listenerHandler) {
- if (client == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation with a NULL WebdavClient");
- }
- mClient = client;
-
- if (listener == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a listener to notiy the result");
- }
- mListener = listener;
-
- if (listenerHandler == null) {
- throw new IllegalArgumentException("Trying to execute a remote operation asynchronously without a handler to the listener's thread");
- }
- mListenerHandler = listenerHandler;
-
- Thread runnerThread = new Thread(this);
- runnerThread.start();
- return runnerThread;
- }
-
- /**
- * Synchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient)}
- *
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final RemoteOperationResult retry() {
- return execute(mClient);
- }
-
- /**
- * Asynchronously retries the remote operation using the same WebdavClient in the last call to {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)}
- *
- * @param listener Listener to be notified about the execution of the operation.
- * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
- * @return Thread were the remote operation is executed.
- */
- public final Thread retry(OnRemoteOperationListener listener, Handler listenerHandler) {
- return execute(mClient, listener, listenerHandler);
- }
-
-
- /**
- * Asynchronous execution of the operation
- * started by {@link RemoteOperation#execute(WebdavClient, OnRemoteOperationListener, Handler)},
- * and result posting.
- *
- * TODO refactor && clean the code; now it's a mess
- */
- @Override
- public final void run() {
- RemoteOperationResult result = null;
- boolean repeat = false;
- do {
- try{
- if (mClient == null) {
- if (mAccount != null && mContext != null) {
- if (mCallerActivity != null) {
- mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext, mCallerActivity);
- } else {
- mClient = OwnCloudClientFactory.createOwnCloudClient(mAccount, mContext);
- }
- } else {
- throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account");
- }
- }
-
- } catch (IOException e) {
- Log.e(TAG, "Error while trying to access to " + mAccount.name, new AccountsException("I/O exception while trying to authorize the account", e));
- result = new RemoteOperationResult(e);
-
- } catch (AccountsException e) {
- Log.e(TAG, "Error while trying to access to " + mAccount.name, e);
- result = new RemoteOperationResult(e);
- }
-
- if (result == null)
- result = run(mClient);
-
- repeat = false;
- if (mCallerActivity != null && mAccount != null && mContext != null && !result.isSuccess() &&
-// (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) {
- (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) {
- /// possible fail due to lack of authorization in an operation performed in foreground
- Credentials cred = mClient.getCredentials();
- String ssoSessionCookie = mClient.getSsoSessionCookie();
- if (cred != null || ssoSessionCookie != null) {
- /// confirmed : unauthorized operation
- AccountManager am = AccountManager.get(mContext);
- boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials);
- boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null);
- if (bearerAuthorization) {
- am.invalidateAuthToken(mAccount.type, ((BearerCredentials)cred).getAccessToken());
- } else if (samlBasedSsoAuthorization ) {
- am.invalidateAuthToken(mAccount.type, ssoSessionCookie);
- } else {
- am.clearPassword(mAccount);
- }
- mClient = null;
- repeat = true; // when repeated, the creation of a new OwnCloudClient after erasing the saved credentials will trigger the login activity
- result = null;
- }
- }
- } while (repeat);
-
- final RemoteOperationResult resultToSend = result;
- if (mListenerHandler != null && mListener != null) {
- mListenerHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
- }
- });
- }
- }
-
-
- /**
- * Returns the current client instance to access the remote server.
- *
- * @return Current client instance to access the remote server.
- */
- public final WebdavClient getClient() {
- return mClient;
- }
-
-
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.operations;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.net.MalformedURLException;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-
-import javax.net.ssl.SSLException;
-
-import org.apache.commons.httpclient.ConnectTimeoutException;
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.DavException;
-
-import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException;
-import com.owncloud.android.oc_framework.network.CertificateCombinedException;
-
-import android.accounts.Account;
-import android.accounts.AccountsException;
-import android.util.Log;
-
-
-/**
- * The result of a remote operation required to an ownCloud server.
- *
- * Provides a common classification of remote operation results for all the
- * application.
- *
- * @author David A. Velasco
- */
-public class RemoteOperationResult implements Serializable {
-
- /** Generated - should be refreshed every time the class changes!! */
- private static final long serialVersionUID = -4415103901492836870L;
-
-
-
- private static final String TAG = "RemoteOperationResult";
-
- public enum ResultCode {
- OK,
- OK_SSL,
- OK_NO_SSL,
- UNHANDLED_HTTP_CODE,
- UNAUTHORIZED,
- FILE_NOT_FOUND,
- INSTANCE_NOT_CONFIGURED,
- UNKNOWN_ERROR,
- WRONG_CONNECTION,
- TIMEOUT,
- INCORRECT_ADDRESS,
- HOST_NOT_AVAILABLE,
- NO_NETWORK_CONNECTION,
- SSL_ERROR,
- SSL_RECOVERABLE_PEER_UNVERIFIED,
- BAD_OC_VERSION,
- CANCELLED,
- INVALID_LOCAL_FILE_NAME,
- INVALID_OVERWRITE,
- CONFLICT,
- OAUTH2_ERROR,
- SYNC_CONFLICT,
- LOCAL_STORAGE_FULL,
- LOCAL_STORAGE_NOT_MOVED,
- LOCAL_STORAGE_NOT_COPIED,
- OAUTH2_ERROR_ACCESS_DENIED,
- QUOTA_EXCEEDED,
- ACCOUNT_NOT_FOUND,
- ACCOUNT_EXCEPTION,
- ACCOUNT_NOT_NEW,
- ACCOUNT_NOT_THE_SAME,
- INVALID_CHARACTER_IN_NAME
- }
-
- private boolean mSuccess = false;
- private int mHttpCode = -1;
- private Exception mException = null;
- private ResultCode mCode = ResultCode.UNKNOWN_ERROR;
- private String mRedirectedLocation;
-
- public RemoteOperationResult(ResultCode code) {
- mCode = code;
- mSuccess = (code == ResultCode.OK || code == ResultCode.OK_SSL || code == ResultCode.OK_NO_SSL);
- }
-
- private RemoteOperationResult(boolean success, int httpCode) {
- mSuccess = success;
- mHttpCode = httpCode;
-
- if (success) {
- mCode = ResultCode.OK;
-
- } else if (httpCode > 0) {
- switch (httpCode) {
- case HttpStatus.SC_UNAUTHORIZED:
- mCode = ResultCode.UNAUTHORIZED;
- break;
- case HttpStatus.SC_NOT_FOUND:
- mCode = ResultCode.FILE_NOT_FOUND;
- break;
- case HttpStatus.SC_INTERNAL_SERVER_ERROR:
- mCode = ResultCode.INSTANCE_NOT_CONFIGURED;
- break;
- case HttpStatus.SC_CONFLICT:
- mCode = ResultCode.CONFLICT;
- break;
- case HttpStatus.SC_INSUFFICIENT_STORAGE:
- mCode = ResultCode.QUOTA_EXCEEDED;
- break;
- default:
- mCode = ResultCode.UNHANDLED_HTTP_CODE;
- Log.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
- }
- }
- }
-
- public RemoteOperationResult(boolean success, int httpCode, Header[] headers) {
- this(success, httpCode);
- if (headers != null) {
- Header current;
- for (int i=0; i<headers.length; i++) {
- current = headers[i];
- if ("Location".equals(current.getName())) {
- mRedirectedLocation = current.getValue();
- break;
- }
- }
- }
- }
-
- public RemoteOperationResult(Exception e) {
- mException = e;
-
- if (e instanceof OperationCancelledException) {
- mCode = ResultCode.CANCELLED;
-
- } else if (e instanceof SocketException) {
- mCode = ResultCode.WRONG_CONNECTION;
-
- } else if (e instanceof SocketTimeoutException) {
- mCode = ResultCode.TIMEOUT;
-
- } else if (e instanceof ConnectTimeoutException) {
- mCode = ResultCode.TIMEOUT;
-
- } else if (e instanceof MalformedURLException) {
- mCode = ResultCode.INCORRECT_ADDRESS;
-
- } else if (e instanceof UnknownHostException) {
- mCode = ResultCode.HOST_NOT_AVAILABLE;
-
- } else if (e instanceof AccountNotFoundException) {
- mCode = ResultCode.ACCOUNT_NOT_FOUND;
-
- } else if (e instanceof AccountsException) {
- mCode = ResultCode.ACCOUNT_EXCEPTION;
-
- } else if (e instanceof SSLException || e instanceof RuntimeException) {
- CertificateCombinedException se = getCertificateCombinedException(e);
- if (se != null) {
- mException = se;
- if (se.isRecoverable()) {
- mCode = ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
- }
- } else if (e instanceof RuntimeException) {
- mCode = ResultCode.HOST_NOT_AVAILABLE;
-
- } else {
- mCode = ResultCode.SSL_ERROR;
- }
-
- } else {
- mCode = ResultCode.UNKNOWN_ERROR;
- }
-
- }
-
- public boolean isSuccess() {
- return mSuccess;
- }
-
- public boolean isCancelled() {
- return mCode == ResultCode.CANCELLED;
- }
-
- public int getHttpCode() {
- return mHttpCode;
- }
-
- public ResultCode getCode() {
- return mCode;
- }
-
- public Exception getException() {
- return mException;
- }
-
- public boolean isSslRecoverableException() {
- return mCode == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED;
- }
-
- private CertificateCombinedException getCertificateCombinedException(Exception e) {
- CertificateCombinedException result = null;
- if (e instanceof CertificateCombinedException) {
- return (CertificateCombinedException) e;
- }
- Throwable cause = mException.getCause();
- Throwable previousCause = null;
- while (cause != null && cause != previousCause && !(cause instanceof CertificateCombinedException)) {
- previousCause = cause;
- cause = cause.getCause();
- }
- if (cause != null && cause instanceof CertificateCombinedException) {
- result = (CertificateCombinedException) cause;
- }
- return result;
- }
-
- public String getLogMessage() {
-
- if (mException != null) {
- if (mException instanceof OperationCancelledException) {
- return "Operation cancelled by the caller";
-
- } else if (mException instanceof SocketException) {
- return "Socket exception";
-
- } else if (mException instanceof SocketTimeoutException) {
- return "Socket timeout exception";
-
- } else if (mException instanceof ConnectTimeoutException) {
- return "Connect timeout exception";
-
- } else if (mException instanceof MalformedURLException) {
- return "Malformed URL exception";
-
- } else if (mException instanceof UnknownHostException) {
- return "Unknown host exception";
-
- } else if (mException instanceof CertificateCombinedException) {
- if (((CertificateCombinedException) mException).isRecoverable())
- return "SSL recoverable exception";
- else
- return "SSL exception";
-
- } else if (mException instanceof SSLException) {
- return "SSL exception";
-
- } else if (mException instanceof DavException) {
- return "Unexpected WebDAV exception";
-
- } else if (mException instanceof HttpException) {
- return "HTTP violation";
-
- } else if (mException instanceof IOException) {
- return "Unrecovered transport exception";
-
- } else if (mException instanceof AccountNotFoundException) {
- Account failedAccount = ((AccountNotFoundException)mException).getFailedAccount();
- return mException.getMessage() + " (" + (failedAccount != null ? failedAccount.name : "NULL") + ")";
-
- } else if (mException instanceof AccountsException) {
- return "Exception while using account";
-
- } else {
- return "Unexpected exception";
- }
- }
-
- if (mCode == ResultCode.INSTANCE_NOT_CONFIGURED) {
- return "The ownCloud server is not configured!";
-
- } else if (mCode == ResultCode.NO_NETWORK_CONNECTION) {
- return "No network connection";
-
- } else if (mCode == ResultCode.BAD_OC_VERSION) {
- return "No valid ownCloud version was found at the server";
-
- } else if (mCode == ResultCode.LOCAL_STORAGE_FULL) {
- return "Local storage full";
-
- } else if (mCode == ResultCode.LOCAL_STORAGE_NOT_MOVED) {
- return "Error while moving file to final directory";
-
- } else if (mCode == ResultCode.ACCOUNT_NOT_NEW) {
- return "Account already existing when creating a new one";
-
- } else if (mCode == ResultCode.ACCOUNT_NOT_THE_SAME) {
- return "Authenticated with a different account than the one updating";
- } else if (mCode == ResultCode.INVALID_CHARACTER_IN_NAME) {
- return "The file name contains an forbidden character";
- }
-
- return "Operation finished with HTTP status code " + mHttpCode + " (" + (isSuccess() ? "success" : "fail") + ")";
-
- }
-
- public boolean isServerFail() {
- return (mHttpCode >= HttpStatus.SC_INTERNAL_SERVER_ERROR);
- }
-
- public boolean isException() {
- return (mException != null);
- }
-
- public boolean isTemporalRedirection() {
- return (mHttpCode == 302 || mHttpCode == 307);
- }
-
- public String getRedirectedLocation() {
- return mRedirectedLocation;
- }
-
- public boolean isIdPRedirection() {
- return (mRedirectedLocation != null &&
- (mRedirectedLocation.toUpperCase().contains("SAML") ||
- mRedirectedLocation.toLowerCase().contains("wayf")));
- }
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework.operations.remote;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
-
-import android.util.Log;
-
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework.utils.FileUtils;
-
-
-
-/**
- * Remote operation performing the creation of a new folder in the ownCloud server.
- *
- * @author David A. Velasco
- * @author masensio
- *
- */
-public class CreateRemoteFolderOperation extends RemoteOperation {
-
- private static final String TAG = CreateRemoteFolderOperation.class.getSimpleName();
-
- private static final int READ_TIMEOUT = 10000;
- private static final int CONNECTION_TIMEOUT = 5000;
-
-
- protected String mRemotePath;
- protected boolean mCreateFullPath;
-
- /**
- * Constructor
- *
- * @param remotePath Full path to the new directory to create in the remote server.
- * @param createFullPath 'True' means that all the ancestor folders should be created if don't exist yet.
- */
- public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) {
- mRemotePath = remotePath;
- mCreateFullPath = createFullPath;
- }
-
- /**
- * Performs the operation
- *
- * @param client Client object to communicate with the remote ownCloud server.
- */
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
- MkColMethod mkcol = null;
-
- boolean noInvalidChars = FileUtils.isValidPath(mRemotePath);
- if (noInvalidChars) {
- try {
- mkcol = new MkColMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
- int status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT);
- if (!mkcol.succeeded() && mkcol.getStatusCode() == HttpStatus.SC_CONFLICT && mCreateFullPath) {
- result = createParentFolder(FileUtils.getParentPath(mRemotePath), client);
- status = client.executeMethod(mkcol, READ_TIMEOUT, CONNECTION_TIMEOUT); // second (and last) try
- }
-
- result = new RemoteOperationResult(mkcol.succeeded(), status, mkcol.getResponseHeaders());
- Log.d(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage());
- client.exhaustResponse(mkcol.getResponseBodyAsStream());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log.e(TAG, "Create directory " + mRemotePath + ": " + result.getLogMessage(), e);
-
- } finally {
- if (mkcol != null)
- mkcol.releaseConnection();
- }
- } else {
- result = new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME);
- }
-
- return result;
- }
-
-
- private RemoteOperationResult createParentFolder(String parentPath, WebdavClient client) {
- RemoteOperation operation = new CreateRemoteFolderOperation(parentPath,
- mCreateFullPath);
- return operation.execute(client);
- }
-
-
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework.operations.remote;
-
-import java.io.File;
-
-import org.apache.jackrabbit.webdav.client.methods.DavMethodBase;
-
-import android.util.Log;
-
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework.utils.FileUtils;
-
-
-/**
- * Remote operation performing the rename of a remote file or folder in the ownCloud server.
- *
- * @author David A. Velasco
- * @author masensio
- */
-public class RenameRemoteFileOperation extends RemoteOperation {
-
- private static final String TAG = RenameRemoteFileOperation.class.getSimpleName();
-
- private static final int RENAME_READ_TIMEOUT = 10000;
- private static final int RENAME_CONNECTION_TIMEOUT = 5000;
-
- private String mOldName;
- private String mOldRemotePath;
- private String mNewName;
- private String mNewRemotePath;
-
-
- /**
- * Constructor
- *
- * @param oldName Old name of the file.
- * @param oldRemotePath Old remote path of the file.
- * @param newName New name to set as the name of file.
- * @param isFolder 'true' for folder and 'false' for files
- */
- public RenameRemoteFileOperation(String oldName, String oldRemotePath, String newName, boolean isFolder) {
- mOldName = oldName;
- mOldRemotePath = oldRemotePath;
- mNewName = newName;
-
- String parent = (new File(mOldRemotePath)).getParent();
- parent = (parent.endsWith(FileUtils.PATH_SEPARATOR)) ? parent : parent + FileUtils.PATH_SEPARATOR;
- mNewRemotePath = parent + mNewName;
- if (isFolder) {
- mNewRemotePath += FileUtils.PATH_SEPARATOR;
- }
- }
-
- /**
- * Performs the rename operation.
- *
- * @param client Client object to communicate with the remote ownCloud server.
- */
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- RemoteOperationResult result = null;
-
- LocalMoveMethod move = null;
-
- boolean noInvalidChars = FileUtils.isValidPath(mNewRemotePath);
-
- if (noInvalidChars) {
- try {
-
- if (mNewName.equals(mOldName)) {
- return new RemoteOperationResult(ResultCode.OK);
- }
-
-
- // check if a file with the new name already exists
- if (client.existsFile(mNewRemotePath)) {
- return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE);
- }
-
- move = new LocalMoveMethod( client.getBaseUri() + WebdavUtils.encodePath(mOldRemotePath),
- client.getBaseUri() + WebdavUtils.encodePath(mNewRemotePath));
- int status = client.executeMethod(move, RENAME_READ_TIMEOUT, RENAME_CONNECTION_TIMEOUT);
-
- move.getResponseBodyAsString(); // exhaust response, although not interesting
- result = new RemoteOperationResult(move.succeeded(), status, move.getResponseHeaders());
- Log.i(TAG, "Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log.e(TAG, "Rename " + mOldRemotePath + " to " + ((mNewRemotePath==null) ? mNewName : mNewRemotePath) + ": " + result.getLogMessage(), e);
-
- } finally {
- if (move != null)
- move.releaseConnection();
- }
- } else {
- result = new RemoteOperationResult(ResultCode.INVALID_CHARACTER_IN_NAME);
- }
-
- return result;
- }
-
- /**
- * Move operation
- *
- */
- private class LocalMoveMethod extends DavMethodBase {
-
- public LocalMoveMethod(String uri, String dest) {
- super(uri);
- addRequestHeader(new org.apache.commons.httpclient.Header("Destination", dest));
- }
-
- @Override
- public String getName() {
- return "MOVE";
- }
-
- @Override
- protected boolean isSuccess(int status) {
- return status == 201 || status == 204;
- }
-
- }
-
-}
+++ /dev/null
-package com.owncloud.android.oc_framework.utils;
-
-import java.io.File;
-
-import android.util.Log;
-
-public class FileUtils {
-
- public static final String PATH_SEPARATOR = "/";
-
-
- public static String getParentPath(String remotePath) {
- String parentPath = new File(remotePath).getParent();
- parentPath = parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR;
- return parentPath;
- }
-
- /**
- * Validate the fileName to detect if contains any forbidden character: / , \ , < , > , : , " , | , ? , *
- * @param fileName
- * @return
- */
- public static boolean isValidName(String fileName) {
- boolean result = true;
-
- Log.d("FileUtils", "fileName =======" + fileName);
- if (fileName.contains(PATH_SEPARATOR) ||
- fileName.contains("\\") || fileName.contains("<") || fileName.contains(">") ||
- fileName.contains(":") || fileName.contains("\"") || fileName.contains("|") ||
- fileName.contains("?") || fileName.contains("*")) {
- result = false;
- }
- return result;
- }
-
- /**
- * Validate the path to detect if contains any forbidden character: \ , < , > , : , " , | , ? , *
- * @param path
- * @return
- */
- public static boolean isValidPath(String path) {
- boolean result = true;
-
- Log.d("FileUtils", "path ....... " + path);
- if (path.contains("\\") || path.contains("<") || path.contains(">") ||
- path.contains(":") || path.contains("\"") || path.contains("|") ||
- path.contains("?") || path.contains("*")) {
- result = false;
- }
- return result;
- }
-}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.oc_framework.utils;
-
-public class OwnCloudVersion implements Comparable<OwnCloudVersion> {
- public static final OwnCloudVersion owncloud_v1 = new OwnCloudVersion(
- 0x010000);
- public static final OwnCloudVersion owncloud_v2 = new OwnCloudVersion(
- 0x020000);
- public static final OwnCloudVersion owncloud_v3 = new OwnCloudVersion(
- 0x030000);
- public static final OwnCloudVersion owncloud_v4 = new OwnCloudVersion(
- 0x040000);
- public static final OwnCloudVersion owncloud_v4_5 = new OwnCloudVersion(
- 0x040500);
-
- // format is in version
- // 0xAABBCC
- // for version AA.BB.CC
- // ie version 2.0.3 will be stored as 0x030003
- private int mVersion;
- private boolean mIsValid;
-
- public OwnCloudVersion(int version) {
- mVersion = version;
- mIsValid = true;
- }
-
- public OwnCloudVersion(String version) {
- mVersion = 0;
- mIsValid = false;
- parseVersionString(version);
- }
-
- public String toString() {
- return ((mVersion >> 16) % 256) + "." + ((mVersion >> 8) % 256) + "."
- + ((mVersion) % 256);
- }
-
- public boolean isVersionValid() {
- return mIsValid;
- }
-
- @Override
- public int compareTo(OwnCloudVersion another) {
- return another.mVersion == mVersion ? 0
- : another.mVersion < mVersion ? 1 : -1;
- }
-
- private void parseVersionString(String version) {
- try {
- String[] nums = version.split("\\.");
- if (nums.length > 0) {
- mVersion += Integer.parseInt(nums[0]);
- }
- mVersion = mVersion << 8;
- if (nums.length > 1) {
- mVersion += Integer.parseInt(nums[1]);
- }
- mVersion = mVersion << 8;
- if (nums.length > 2) {
- mVersion += Integer.parseInt(nums[2]);
- }
- mIsValid = true;
- } catch (Exception e) {
- mIsValid = false;
- }
- }
-}
<?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="0100010"
- android:versionName="1.0.10" >
+ android:versionCode="0100012"
+ android:versionName="1.0.12" >
<uses-sdk
android:minSdkVersion="16"
--- /dev/null
+Subproject commit 6992df89b04c3c35bf906c3f810fa05daad7b43f
<modelVersion>4.0.0</modelVersion>
<groupId>com.owncloud.android</groupId>
<artifactId>owncloud</artifactId>
- <version>1.3.21-SNAPSHOT</version>
+ <version>${owncloud.version}</version>
<packaging>apk</packaging>
<name>Owncloud Android</name>
<properties>
+ <owncloud.version>1.5.1-SNAPSHOT</owncloud.version>
<java-version>1.6</java-version>
- <google.android-version>4.2.2_r2</google.android-version>
+ <!-- Given by maven-android-sdk-deployer -->
+ <google.android-version>4.4.2_r2</google.android-version>
+ <!-- Usually the latest Android API -->
+ <google.android-api>19</google.android-api>
<actionbarsherlock-version>4.2.0</actionbarsherlock-version>
</properties>
<developerConnection>scm:git:git@github.com:owncloud/android.git</developerConnection>
<url>https://github.com/owncloud/android</url>
</scm>
-
+
<dependencies>
<dependency>
<type>apklib</type>
</dependency>
+ <!-- MUST BE INSTALLED FIRST: cd owncloud-android-library; mvn install -->
<dependency>
- <groupId>org.apache.jackrabbit</groupId>
- <artifactId>jackrabbit-webdav</artifactId>
- <version>2.5.2</version>
- </dependency>
+ <groupId>com.owncloud.android</groupId>
+ <artifactId>owncloud-android-library</artifactId>
+ <version>${owncloud.version}</version>
+ </dependency>
</dependencies>
<plugin>
<groupId>com.jayway.maven.plugins.android.generation2</groupId>
<artifactId>android-maven-plugin</artifactId>
- <version>3.5.0</version>
+ <version>3.8.0</version>
<configuration>
<sdk>
- <!-- platform or api level (api level 4 = platform 1.6)-->
<path>${env.ANDROID_HOME}</path>
- <platform>17</platform>
+ <platform>${google.android-api}</platform>
</sdk>
</configuration>
<extensions>true</extensions>
# Project target.
target=android-19
android.library.reference.1=actionbarsherlock/library
-android.library.reference.2=oc_framework
+android.library.reference.2=owncloud-android-library
<LinearLayout\r
android:layout_width="match_parent"\r
android:layout_height="wrap_content"\r
- android:layout_above="@id/buttonOK"\r
+ android:layout_above="@+id/bottom_block"\r
android:layout_alignParentTop="true"\r
android:orientation="horizontal" >\r
\r
\r
</LinearLayout>\r
\r
- <Button\r
- android:id="@id/buttonOK"\r
- android:layout_width="match_parent"\r
- android:layout_height="wrap_content"\r
- android:layout_above="@+id/welcome_link"\r
- android:layout_centerHorizontal="true"\r
- android:enabled="false"\r
- android:onClick="onOkClick"\r
- android:text="@string/setup_btn_connect" />\r
-\r
- <Button\r
- android:id="@id/welcome_link"\r
- android:layout_width="wrap_content"\r
- android:layout_height="wrap_content"\r
- android:layout_alignParentBottom="true"\r
- android:layout_centerHorizontal="true"\r
- android:background="@android:color/transparent"\r
- android:onClick="onRegisterClick"\r
- android:paddingBottom="5dp"\r
- android:paddingTop="5dp"\r
- android:text="@string/auth_register"\r
- android:textColor="#0000FF"/>\r
- \r
+ <LinearLayout\r
+ android:id="@id/bottom_block"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content"\r
+ android:layout_alignParentBottom="true"\r
+ android:orientation="vertical" >\r
+ \r
+ <Button\r
+ android:id="@+id/buttonOK"\r
+ android:layout_width="match_parent"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center_horizontal"\r
+ android:enabled="false"\r
+ android:onClick="onOkClick"\r
+ android:text="@string/setup_btn_connect" />\r
+ \r
+ <Button\r
+ android:id="@+id/welcome_link"\r
+ android:layout_width="wrap_content"\r
+ android:layout_height="wrap_content"\r
+ android:layout_gravity="center_horizontal"\r
+ android:background="@android:color/transparent"\r
+ android:onClick="onRegisterClick"\r
+ android:paddingBottom="5dp"\r
+ android:paddingTop="5dp"\r
+ android:text="@string/auth_register"\r
+ android:textColor="#0000FF"/>\r
+ </LinearLayout>\r
+ \r
</RelativeLayout>\r
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2012-2014 ownCloud Inc.
+
+ 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/list_item"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:paddingStart="16dip"
+ android:paddingEnd="16dip"
+ android:paddingRight="16dip"
+ android:paddingLeft="16dip"
+ android:minWidth="196dip"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:duplicateParentState="true" >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="40dip"
+ android:layout_height="40dip"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="8dip"
+ android:layout_marginRight="8dip"
+ android:duplicateParentState="true" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+ android:duplicateParentState="true"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ownCloud Android client application
+
+ Copyright (C) 2012-2014 ownCloud Inc.
+
+ 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/list_item"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:paddingRight="16dip"
+ android:paddingLeft="16dip"
+ android:minWidth="196dip"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:duplicateParentState="true" >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="40dip"
+ android:layout_height="40dip"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="8dip"
+ android:duplicateParentState="true" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:duplicateParentState="true"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
android:src="@drawable/ic_favorite" />\r
\r
</FrameLayout>\r
-\r\r\r\r
+\r
<LinearLayout\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\r
+\r
<TextView\r
android:id="@+id/Filename"\r
android:layout_width="wrap_content"\r
</LinearLayout>\r
\r
</LinearLayout>\r
-\r\r\r\r
+\r
+ <ImageView\r
+ android:id="@+id/shareIcon"\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:src="@drawable/sharedlink" />\r
+\r
<ImageView\r
android:id="@+id/custom_checkbox"\r
android:layout_width="wrap_content"\r
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/action_open_file_with" android:title="@string/actionbar_open_with" android:icon="@android:drawable/ic_menu_edit" android:orderInCategory="1" />
+ <item android:id="@+id/action_share_file" android:title="@string/action_share_file" android:icon="@android:drawable/ic_menu_share" android:orderInCategory="1" />
+ <item android:id="@+id/action_unshare_file" android:title="@string/action_unshare_file" android:icon="@android:drawable/ic_menu_share" android:orderInCategory="1" />
+ <item android:id="@+id/action_open_file_with" android:title="@string/actionbar_open_with" android:icon="@android:drawable/ic_menu_edit" android:orderInCategory="1" />
<item android:id="@+id/action_download_file" android:title="@string/filedetails_download" android:icon="@drawable/ic_action_download" android:orderInCategory="1" />
<item android:id="@+id/action_sync_file" android:title="@string/filedetails_sync_file" android:icon="@drawable/ic_action_refresh" android:orderInCategory="1" />
<item android:id="@+id/action_cancel_download" android:title="@string/common_cancel_download" android:icon="@android:drawable/ic_menu_close_clear_cancel" android:orderInCategory="1" />
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<string name="actionbar_upload_files">الملفات</string>
<string name="actionbar_mkdir">إنشاء دليل</string>
<string name="actionbar_settings">تعديلات</string>
+ <string name="actionbar_see_details">تفاصيل</string>
<string name="prefs_category_general">عام</string>
<string name="prefs_category_more">المزيد</string>
<string name="prefs_accounts">حسابات</string>
<string name="prefs_help">المساعدة</string>
<string name="prefs_imprint">الدمغة.</string>
- <string name="auth_host_url">عنوان الخادم</string>
<string name="auth_username">إسم المستخدم</string>
<string name="auth_password">كلمات السر</string>
<string name="auth_register">جديد لـ %1$s ؟</string>
<string name="filedetails_type">النوع</string>
<string name="filedetails_modified">عُدل</string>
<string name="filedetails_download">انزال</string>
+ <string name="filedetails_sync_file">تحديث ملف</string>
<string name="filedetails_renamed_in_upload_msg">تم تغيير اسم الملف إلى %1$s أثناء الرفع</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">إلغاء رفع الملفات</string>
<string name="common_cancel">الغاء</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="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_not_downloaded_yet">لم يتم تحميلها بعد</string>
<string name="common_choose_account">اختر حساب</string>
<string name="sync_fail_content">تعذر إكمال التزامن لـ %1$s </string>
<string name="sync_fail_content_unauthorized">كلمة السر غير صالحة لـ %1$s</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_folder_local">المحتويات المحلية فقط</string>
<string name="confirmation_remove_remote">حذف من الخادم</string>
<string name="remove_success_msg">يتم الحذف بنجاح</string>
<string name="remove_fail_msg">لقد فشل الحذف</string>
+ <string name="rename_dialog_title">أدخل اسما جديدا</string>
<string name="wait_a_moment">فضلاً, انتظر</string>
<string name="filedisplay_no_file_selected">لم يتم اختيار أي ملف</string>
+ <string name="ssl_validator_btn_details_see">تفاصيل</string>
+ <string name="ssl_validator_btn_details_hide">إخفاء</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">البلد:</string>
+ <string name="ssl_validator_label_L">المكان:</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="placeholder_filesize">389 KB</string>
+ <string name="placeholder_media_time">12:23:45</string>
+ <string name="preview_image_description">معاينة الصورة</string>
+ <string name="preview_image_error_unknown_format">هذه الصورة لا يمكن أن تظهر</string>
+ <string name="failed_upload_all_cb">تحديد الكل</string>
</resources>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<?xml version='1.0' encoding='UTF-8'?>
-<resources/>
+<resources>
+ <string name="actionbar_settings">Налады</string>
+ <string name="common_yes">Так</string>
+ <string name="common_no">Не</string>
+ <string name="common_error">Памылка</string>
+</resources>
<string name="prefs_accounts">Профили</string>
<string name="prefs_instant_upload_summary">Своевременно качване на снимки направени с камерата</string>
<string name="prefs_help">Помощ</string>
- <string name="auth_host_url">Адрес на сървъра</string>
<string name="auth_username">Потребител</string>
<string name="auth_password">Парола</string>
<string name="sync_string_files">Файлове</string>
<string name="prefs_category_more">বেশী</string>
<string name="prefs_accounts">একাউন্ট</string>
<string name="prefs_help">সহায়িকা</string>
- <string name="auth_host_url">সার্ভার ঠিকানা</string>
<string name="auth_username">ব্যবহারকারি</string>
<string name="auth_password">কূটশব্দ</string>
<string name="sync_string_files">ফাইল</string>
<string name="recommend_subject">Proveu %1$s a un telèfon avançat!</string>
<string name="recommend_text">Vull convidar-te a usar l\'aplicació %1$s al teu telèfon avançat!\nBaixa\'l aquí: %2$s</string>
<string name="auth_check_server">Comprova el servidor</string>
- <string name="auth_host_url">Adreça del servidor</string>
+ <string name="auth_host_url">Adreça del servidor https://…</string>
<string name="auth_username">Nom d\'usuari</string>
<string name="auth_password">Contrasenya</string>
<string name="auth_register">Nou a %1$s?</string>
<string name="filedetails_download">Baixa</string>
<string name="filedetails_sync_file">Actualitza el fitxer</string>
<string name="filedetails_renamed_in_upload_msg">L\'arxiu s\'ha canviat de nom a %1$s durant la càrrega</string>
+ <string name="action_share_file">Enllaç de compartició</string>
<string name="common_yes">Sí</string>
<string name="common_no">No</string>
<string name="common_ok">D\'acord</string>
<string name="sync_file_fail_msg">L\'arxiu remot no ha pogut ser comprovat</string>
<string name="sync_file_nothing_to_do_msg">Contingut de l\'arxiu ja sincronitzat</string>
<string name="create_dir_fail_msg">La carpeta no s\'ha pogut crear</string>
+ <string name="filename_forbidden_characters">Caràcters no permesos: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Espereu</string>
<string name="filedisplay_unexpected_bad_get_content">S\'ha produït un problema inesperat; proveu una altra aplicació per seleccionar el fitxer</string>
<string name="filedisplay_no_file_selected">No heu seleccionat cap fitxer</string>
<string name="preview_image_description">Visualització prèvia d\'imatge</string>
<string name="preview_image_error_unknown_format">Auquesta imatge no es pot mostrar</string>
<string name="error__upload__local_file_not_copied">%1$s no s\'ha pogut copiar a la carpeta local %2$s</string>
+ <string name="actionbar_failed_instant_upload">La pujada instantània ha fallat</string>
<string name="failed_upload_headline_text">Fallada de pujades instantànies</string>
<string name="failed_upload_headline_hint">Resum de totes les pujades instantànies que han fallat</string>
<string name="failed_upload_all_cb">selecciona-ho tot</string>
<string name="prefs_log_summary_history">Zobrazuje zaznamenané logy</string>
<string name="prefs_log_delete_history_button">Smazat historii</string>
<string name="prefs_help">Nápověda</string>
+ <string name="prefs_recommend">Doporučit příteli</string>
<string name="prefs_feedback">Odezva</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="recommend_subject">Zkuste %1$s na vašem smartphonu!</string>
+ <string name="recommend_text">Chtěl bych vás pozvat k používání %1$s na vašem smartphonu.\nKe stažení zde: %2$s</string>
<string name="auth_check_server">Zkontrolovat server</string>
- <string name="auth_host_url">Adresa serveru</string>
+ <string name="auth_host_url">Adresa serveru https://...</string>
<string name="auth_username">Uživatelské jméno</string>
<string name="auth_password">Heslo</string>
<string name="auth_register">Nováček s %1$s?</string>
<string name="filedetails_download">Stáhnout</string>
<string name="filedetails_sync_file">Obnovit soubor</string>
<string name="filedetails_renamed_in_upload_msg">Soubor byl v průběhu odesílání přejmenován na %1$s</string>
+ <string name="action_share_file">Sdílet odkaz</string>
+ <string name="action_unshare_file">Zrušit sdílení odkazu</string>
<string name="common_yes">Ano</string>
<string name="common_no">Ne</string>
<string name="common_ok">OK</string>
<string name="sync_foreign_files_forgotten_ticker">Některé místní soubory byly zapomenuty</string>
<string name="sync_foreign_files_forgotten_content">%1$d souborů z adresáře %2$s nelze zkopírovat do</string>
<string name="sync_foreign_files_forgotten_explanation">Od verze 1.3.16 jsou soubory odeslané z tohoto zařízení, pro ochranu proti ztrátě dat při synchronizaci z více účtů, nahrány do místní složky %1$s.\n\nVšechny soubory odeslané předchozími verzemi byly kvůli této změně přesunuty do složky %2$s. Bohužel chyba zabránila dokončení této operace při synchronizaci účtu. Můžete nyní ponechat soubory ve stávajícím stavu a smazat odkaz na %3$s nebo přesunout soubory do adresáře %1$s a zachovat odkazy na %4$s.\n\nNásleduje seznam místních souborů a jejich odkazů na vzdálené soubory v %5$s.</string>
+ <string name="sync_current_folder_was_removed">Složka %1$s již neexistuje</string>
<string name="foreign_files_move">Přesunout vše</string>
<string name="foreign_files_success">Všechny soubory byly přesunuty</string>
<string name="foreign_files_fail">Některé soubory nebylo možno přesunout</string>
<string name="media_err_unsupported">Nepodporovaný kodek</string>
<string name="media_err_io">Multimediální soubor nelze přečíst</string>
<string name="media_err_malformed">Multimediální soubor není správně kódován</string>
+ <string name="media_err_timeout">Vypršel čas při pokusu o přehrání</string>
<string name="media_err_invalid_progressive_playback">Multimediální soubor nelze proudově odesílat</string>
<string name="media_err_unknown">Multimediální soubor nemůže být přehrán s výchozím přehrávačem</string>
<string name="media_err_security_ex">Chyba zabezpečení při pokusu o přehrání %1$s</string>
<string name="auth_connection_established">Spojení navázáno</string>
<string name="auth_testing_connection">Zkouším spojení...</string>
<string name="auth_not_configured_title">Neplatné nastavení serveru</string>
+ <string name="auth_account_not_new">Účet pro stejného uživatele a server již v zařízení existuje</string>
+ <string name="auth_account_not_the_same">Zadaný uživatel neodpovídá uživateli tohoto účtu</string>
<string name="auth_unknown_error_title">Nastala neznámá chyba</string>
<string name="auth_unknown_host_title">Nelze najít hostitele</string>
<string name="auth_incorrect_path_title">Instance serveru nenalezena</string>
<string name="auth_timeout_title">Serveru trvalo příliš dlouho odpovědět</string>
<string name="auth_incorrect_address_title">Neplatné URL</string>
<string name="auth_ssl_general_error_title">Inicializace SSL selhala</string>
+ <string name="auth_ssl_unverified_server_title">Nemohu ověřit SSL identitu serveru</string>
<string name="auth_bad_oc_version_title">Nerozpoznaná verze serveru</string>
<string name="auth_wrong_connection_title">Nemohu navázat spojení</string>
<string name="auth_secure_connection">Zabezpečené spojení navázáno</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_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_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="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="sync_file_fail_msg">Vzdálený soubor nemohl být zkontrolován</string>
<string name="sync_file_nothing_to_do_msg">Obsah souboru je již synchronizován</string>
<string name="create_dir_fail_msg">Adresář nelze vytvořit</string>
+ <string name="filename_forbidden_characters">Zakázané znaky: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Počkejte chvíli</string>
<string name="filedisplay_unexpected_bad_get_content">Neočekávaný problém - zkuste zvolit soubor jinou aplikací</string>
<string name="filedisplay_no_file_selected">Žádný soubor nebyl vybrán</string>
+ <string name="activity_chooser_title">Odeslat odkaz ...</string>
+ <string name="oauth_check_onoff">Přihlásit se s oAuth2</string>
<string name="oauth_login_connection">Připojuji se k oAuth2 serveru...</string>
<string name="ssl_validator_header">Identitu stránky nelze ověřit</string>
<string name="ssl_validator_reason_cert_not_trusted">- Certifikát serveru je nedůvěryhodný</string>
<string name="preview_image_description">Náhled obrázku</string>
<string name="preview_image_error_unknown_format">Obrázek nemůže být zobrazen</string>
<string name="error__upload__local_file_not_copied">%1$s nelze zkopírovat do místního adresáře %2$s</string>
+ <string name="actionbar_failed_instant_upload">Selhalo Okamžité odeslání</string>
<string name="failed_upload_headline_text">Selhaná okamžitá odeslání</string>
<string name="failed_upload_headline_hint">Souhrn všech selhaných okamžitých odeslání</string>
<string name="failed_upload_all_cb">vybrat vše</string>
<string name="failed_upload_retry_do_nothing_text">nic nedělat nejste připojeni pro okamžité odeslání</string>
<string name="failed_upload_failure_text">Chybová zpráva:</string>
<string name="failed_upload_quota_exceeded_text">Zkontrolujte prosím nastavení vašeho serveru, možná jste překročili kvótu.</string>
+ <string name="share_link_no_support_share_api">Je nám líto, ale sdílení není na vašem serveru povoleno. Kontaktujte vašeho administrátora.</string>
+ <string name="share_link_file_no_exist">Nepodařilo se sdílet tento soubor či složku. Ujistěte se, že existuje.</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_error">Při pokusu o zrušení sdílení tohoto souboru či složky nastala chyba</string>
</resources>
<string name="prefs_log_summary_history">Dette viser de optagne logger</string>
<string name="prefs_log_delete_history_button">Slet Historik</string>
<string name="prefs_help">Hjælp</string>
+ <string name="prefs_recommend">Anbefal til en ven</string>
+ <string name="prefs_feedback">Feedback</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="recommend_subject">Prøv %1$s på din smartphone!</string>
+ <string name="recommend_text">Jeg ønsker at invitere dig til at bruge %1$s på din smartphone!\nHent den her: %2$s</string>
<string name="auth_check_server">Check Server</string>
- <string name="auth_host_url">Serveradresse</string>
+ <string name="auth_host_url">Server addresse https://…</string>
<string name="auth_username">Brugernavn</string>
<string name="auth_password">Kodeord</string>
<string name="auth_register">Uvant med %1$s</string>
<string name="filedetails_download">Hent</string>
<string name="filedetails_sync_file">Opdater fil</string>
<string name="filedetails_renamed_in_upload_msg">Filen blev omdøbt til %1$s under upload</string>
+ <string name="action_share_file">Del link</string>
+ <string name="action_unshare_file">Ophæv deling</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nej</string>
<string name="common_ok">OK</string>
<string name="sync_foreign_files_forgotten_ticker">Visse lokale filer blev glemt</string>
<string name="sync_foreign_files_forgotten_content">%1$d filer ud af %2$s mappe kunne ikke kopieres ind i</string>
<string name="sync_foreign_files_forgotten_explanation">Fra version 1.3.16 bliver filer uploadet fra denne enhed kopieret til mappen %1$s for at forhindre datatab når en enkelt fil synkroniseres med flere konti.\n\nPå grund af denne ændring blev alle filer uploadet i tidligere versioner af denne app kopieret til mappen %2$s. Imidlertid forhindrede en fejl færdiggørelsen af denne operation under konto-synkronisering. Du kan enten lade filerne være som de er og fjerne linket til %3$s eller flytte filerne til mappen %1$s og beholde linket til %4$s.\n\nHerunder er en liste med de lokale filer og de eksterne filer i %5$s, som de var knyttet til.</string>
+ <string name="sync_current_folder_was_removed">Mappen %1$s eksistere ikke længere</string>
<string name="foreign_files_move">Flyt alle</string>
<string name="foreign_files_success">Alle filer blev flyttet</string>
<string name="foreign_files_fail">Visse filer kunne ikke flyttes</string>
<string name="media_err_unsupported">Ikke-understøttet medie codec</string>
<string name="media_err_io">Mediefilen kunne ikke læses</string>
<string name="media_err_malformed">Mediefilen er ikke korrekt kodet</string>
+ <string name="media_err_timeout">Tiden udløb under forsøg på at afspille</string>
<string name="media_err_invalid_progressive_playback">Mediefilen kan ikke streames</string>
<string name="media_err_unknown">Mediefil kan ikke afspilles med tilgængelige medieafspiller</string>
<string name="media_err_security_ex">Sikkerhedsfejl ved forsøg på afspilning af </string>
<string name="auth_connection_established">Forbindelse oprettet</string>
<string name="auth_testing_connection">Afprøver forbindelse ...</string>
<string name="auth_not_configured_title">Misdannet server konfiguration</string>
+ <string name="auth_account_not_new">En konto for den samme bruger og server eksisterer allerede på enheden</string>
+ <string name="auth_account_not_the_same">Den indtastede bruger passer ikke til brugeren for denne konto</string>
<string name="auth_unknown_error_title">Ukendt fejl opstod!</string>
<string name="auth_unknown_host_title">Kunne ikke finde host</string>
<string name="auth_incorrect_path_title">Server instans blev ikke fundet</string>
<string name="auth_timeout_title">Serveren var for længe om at svare</string>
<string name="auth_incorrect_address_title">Deform URL</string>
<string name="auth_ssl_general_error_title">SSL initialisering fejlede</string>
+ <string name="auth_ssl_unverified_server_title">Kunne ikke bekræfte SSl-serverens identitet</string>
<string name="auth_bad_oc_version_title">Ikke genkendt server version</string>
<string name="auth_wrong_connection_title">Ikke ikke oprette forbindelse</string>
<string name="auth_secure_connection">Sikker forbindelse oprettet</string>
<string name="auth_oauth_error">Mislykket godkendelse</string>
<string name="auth_oauth_error_access_denied">Adgang afvist af autorisationsserver</string>
<string name="auth_wtf_reenter_URL">Uventet tilstand; angiv server-URL\'en igen</string>
+ <string name="auth_expired_oauth_token_toast">Din godkendelse udløb. Gentag godkendelse</string>
<string name="auth_expired_basic_auth_toast">Indtast venligst dit nuværende kodeord</string>
+ <string name="auth_expired_saml_sso_token_toast">Din session udløb. Forbind venligst igen</string>
+ <string name="auth_connecting_auth_server">Forbinder til godkendelsesserver ...</string>
+ <string name="auth_unsupported_auth_method">Serveren understøtter ikke denne godkendelsesmetode</string>
+ <string name="auth_unsupported_multiaccount">%1$s understøtter ikke multiple konti</string>
<string name="fd_keep_in_sync">Hold fil opdateret</string>
<string name="common_rename">Omdøb</string>
<string name="common_remove">Fjern</string>
<string name="sync_file_fail_msg">Ekstern fil kunne ikke kontrolleres</string>
<string name="sync_file_nothing_to_do_msg">Filindholdet allerede synkroniseret</string>
<string name="create_dir_fail_msg">Mappe kunne ikke oprettes</string>
+ <string name="filename_forbidden_characters">Ugyldige tegn: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Vent et øjeblik</string>
<string name="filedisplay_unexpected_bad_get_content">Uforventet problem; prøv venligst anden applikation til at vælge filen</string>
<string name="filedisplay_no_file_selected">Ingen fil blev valgt</string>
+ <string name="activity_chooser_title">Send link til ...</string>
+ <string name="oauth_check_onoff">Log på med oAuth2</string>
<string name="oauth_login_connection">Forbinder til oAuth2 server...</string>
<string name="ssl_validator_header">Sidens identitet kunne ikke verificeres</string>
<string name="ssl_validator_reason_cert_not_trusted">- Serverens certifikat er ikke troværdigt</string>
<string name="preview_image_description">Billede preview</string>
<string name="preview_image_error_unknown_format">Dette billede kan ikke vises</string>
<string name="error__upload__local_file_not_copied">%1$s kunne ikke kopieres til %2$s lokale mappe</string>
+ <string name="actionbar_failed_instant_upload">Fejlede Umiddelbar upload</string>
<string name="failed_upload_headline_text">Øjeblikkelige uploads mislykkedes</string>
<string name="failed_upload_headline_hint">Sammenfatning af alle mislykkede øjeblikkelige uploads</string>
<string name="failed_upload_all_cb">Vælg alle</string>
<string name="failed_upload_retry_do_nothing_text">gør intet, du er ikke online til øjeblikkelig upload</string>
<string name="failed_upload_failure_text">Fejlmeddelelse</string>
<string name="failed_upload_quota_exceeded_text">Tjek din serverkonfiguration, måske er din kvota overskredet.</string>
+ <string name="share_link_no_support_share_api">Beklager, deling er ikke slået til på din server. Kontakt venligst din administrator.</string>
+ <string name="share_link_file_no_exist">Kan ikke dele denne fil eller mappe. Find venligst ud af om den eksisterer</string>
+ <string name="share_link_file_error">Der opstod en fejl ved deling af denne fil eller mappe</string>
+ <string name="unshare_link_file_error">Der opstod en fejl ved stopning af deling af denne mappe.</string>
</resources>
<string name="prefs_feedback">Rückmeldungen</string>
<string name="prefs_imprint">Impressum</string>
<string name="auth_check_server">Server überprüfen</string>
- <string name="auth_host_url">Adresse des Servers</string>
<string name="auth_username">Benutzername</string>
<string name="auth_password">Passwort</string>
<string name="auth_register">Ist %1$s neu für Sie?</string>
<string name="recommend_subject">Probieren Sie %1$s auf Ihrem Smartphone!</string>
<string name="recommend_text">Ich möchte Sie zum Benutzen von %1$s auf Ihrem Smartphone einladen!\nLaden Sie es hier herunter: %2$s</string>
<string name="auth_check_server">Server überprüfen</string>
- <string name="auth_host_url">Adresse des Servers</string>
+ <string name="auth_host_url">Server-Adresse https://…</string>
<string name="auth_username">Benutzername</string>
<string name="auth_password">Passwort</string>
<string name="auth_register">Ist %1$s neu für Sie?</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_unshare_file">Link nicht mehr freigeben</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nein</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Die entfernte Datei konnte nicht überprüft werden</string>
<string name="sync_file_nothing_to_do_msg">Dateiinhalte bereits synchronisiert</string>
<string name="create_dir_fail_msg">Das Verzeichnis konnte nicht erstellt werden.</string>
+ <string name="filename_forbidden_characters">Verbotene Zeichen: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Bitte warten Sie einen Moment.</string>
<string name="filedisplay_unexpected_bad_get_content">Ein unerwartetes Problem ist aufgetreten. Bitte versuchen Sie, 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="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="preview_image_description">Bildvorschau</string>
<string name="preview_image_error_unknown_format">Dieses Bild kann nicht angezeigt werden</string>
<string name="error__upload__local_file_not_copied">%1$s konnte nicht in den lokalen %2$s Ordner kopiert werden</string>
+ <string name="actionbar_failed_instant_upload">Sofort-Upload fehlgeschlagen</string>
<string name="failed_upload_headline_text">Sofortige Uploads fehlgeschlagen</string>
<string name="failed_upload_headline_hint">Zusammenfassung aller fehlgeschlagenen Uploads</string>
<string name="failed_upload_all_cb">Alle auswählen</string>
<string name="failed_upload_retry_do_nothing_text">Nicht durchgeführt - Nicht online für sofortigen Upload</string>
<string name="failed_upload_failure_text">Fehlermeldung:</string>
<string name="failed_upload_quota_exceeded_text">Bitte überprüfen Sie Ihre Serverkonfiguration. Vielleicht ist Ihr Nutzungslimit überschritten.</string>
+ <string name="share_link_no_support_share_api">Entschuldigung, Freigaben sind auf Ihrem Server nicht aktiviert. Bitte kontaktieren Sie Ihren Administrator.</string>
+ <string name="share_link_file_no_exist">Die Freigabe der Datei oder des Ordners ist nicht möglich. Bitte stellen Sie sicher, dass diese existiert.</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_error">Es ist ein Fehler beim Entfernen der Freigabe für diese Datei oder den Ordner aufgetreten.</string>
</resources>
<string name="prefs_feedback">Rückmeldungen</string>
<string name="prefs_imprint">Impressum</string>
<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="recommend_text">Ich möchte Dich zu %1$s für Dein Smartphone einladen!\nLade es hier herunter: %2$s</string>
<string name="auth_check_server">Überprüfe den Server</string>
- <string name="auth_host_url">Adresse des Servers</string>
+ <string name="auth_host_url">Server-Adresse 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="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_unshare_file">Link nicht mehr freigeben</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nein</string>
<string name="common_ok">OK</string>
<string name="sync_fail_in_favourites_content">Inhalte von %1$d konnte nicht synchronisiert werden (%2$d Konflikte)</string>
<string name="sync_foreign_files_forgotten_ticker">Einige lokale Dateien wurden vergessen</string>
<string name="sync_foreign_files_forgotten_content">%1$d Dateien aus dem Verzeichnis %2$s konnten nicht kopiert werden nach</string>
- <string name="sync_foreign_files_forgotten_explanation">\"Mit Version 1.3.16 werden Dateien die von diesem Gerät aus hochgeladen werden in den lokalen Ordner %1$s kopiert um Datenverlust zu vermeiden, wenn eine einzelne Datei mit mehreren Accounts synchronisiert wird.\n\nInfolge dieser Änderung wurden alle Dateien, die mit vorherigen Versionen dieser App hochgeladen wurden, in den Ordner %2$s verschoben. Jedoch ist während der Account-Synchronisation ein Fehler aufgetreten, der das Abschließen dieses Vorgangs verhindert. Du kannst die Datei(en) entweder wie sie sind belassen und den Link zu %3$s entfernen oder die Datei(en) in den %1$s Ordner verschieben und den Link zu %4$s beibehalten.\n\nUnten befindet sich eine Liste der lokalen Datei(en) und der mit ihnen verbundenen Remote-Datei(en) in %5$s.</string>
+ <string name="sync_foreign_files_forgotten_explanation">\"Mit Version 1.3.16 werden Dateien die von diesem Gerät aus hochgeladen werden in den lokalen Ordner %1$s kopiert, um Datenverlust zu vermeiden, wenn eine einzelne Datei mit mehreren Accounts synchronisiert wird.\n\nInfolge dieser Änderung wurden alle Dateien, die mit vorherigen Versionen dieser App hochgeladen wurden, in den Ordner %2$s verschoben. Jedoch ist während der Account-Synchronisation ein Fehler aufgetreten, der das Abschließen dieses Vorgangs verhindert. Du kannst die Datei(en) entweder wie sie sind belassen und den Link zu %3$s entfernen, oder die Datei(en) in den %1$s Ordner verschieben, und den Link zu %4$s beibehalten.\n\nUnten befindet sich eine Liste der lokalen Datei(en) und der mit ihnen verbundenen Remote-Datei(en) in %5$s.</string>
<string name="sync_current_folder_was_removed">Das Verzeichnis %1$s existiert nicht mehr</string>
<string name="foreign_files_move">Verschiebe alle</string>
<string name="foreign_files_success">Alle Dateien wurden verschoben</string>
<string name="auth_oauth_error">Autorisierung nicht erfolgreich</string>
<string name="auth_oauth_error_access_denied">Zugriff durch den Autorisierungsserver abgelehnt</string>
<string name="auth_wtf_reenter_URL">Unerwarteter Zustand; bitte gib die URL des Servers nochmals ein</string>
- <string name="auth_expired_oauth_token_toast">Ihre Autorisierung ist abgelaufen. Bitte Autorisierung nochmals durchführen</string>
+ <string name="auth_expired_oauth_token_toast">Deine Autorisierung ist abgelaufen. Bitte Autorisierung nochmals durchführen</string>
<string name="auth_expired_basic_auth_toast">Bitte gib dein aktuelles Passwort ein</string>
- <string name="auth_expired_saml_sso_token_toast">Ihre Sitzung ist abgelaufen. Bitte Anmeldung nochmals durchführen</string>
+ <string name="auth_expired_saml_sso_token_toast">Deine Sitzung ist abgelaufen. Bitte Anmeldung nochmals durchführen</string>
<string name="auth_connecting_auth_server">Verbinde mit dem Authentifizierung-Server…</string>
<string name="auth_unsupported_auth_method">Der Server unterstützt diese Authentifizierung-Methode nicht</string>
<string name="auth_unsupported_multiaccount">%1$s unterstützt nicht mehrere Benutzerkonten</string>
<string name="sync_file_fail_msg">Die entfernte Datei konnte nicht überprüft werden</string>
<string name="sync_file_nothing_to_do_msg">Dateiinhalte bereits synchronisiert</string>
<string name="create_dir_fail_msg">Das Verzeichnis konnte nicht erstellt werden.</string>
+ <string name="filename_forbidden_characters">Verbotene Zeichen: / \\ < > : \" | ? *</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="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="preview_image_description">Bildvorschau</string>
<string name="preview_image_error_unknown_format">Dieses Bild kann nicht angezeigt werden</string>
<string name="error__upload__local_file_not_copied">%1$s konnte nicht in den lokalen %2$s Ordner kopiert werden</string>
+ <string name="actionbar_failed_instant_upload">Sofort-Upload fehlgeschlagen</string>
<string name="failed_upload_headline_text">SofortUpload fehlgeschlagen</string>
<string name="failed_upload_headline_hint">Übersicht aller fehlgeschlagenen SofortUploads</string>
<string name="failed_upload_all_cb">Alles auswählen</string>
<string name="failed_upload_retry_do_nothing_text">Nicht durchgeführt - Nicht online für sofortigen Upload</string>
<string name="failed_upload_failure_text">Fehlermeldung:</string>
<string name="failed_upload_quota_exceeded_text">Bitte überprüfe Deine Servereinstellungen. Eventuell ist Dein Nutzungslimit überschritten.</string>
+ <string name="share_link_no_support_share_api">Entschuldigung, Freigaben sind auf Deinem Server nicht aktiviert. Bitte kontaktiere Deinen Administrator.</string>
+ <string name="share_link_file_no_exist">Die Freigabe der Datei oder des Ordners ist nicht möglich. Bitte stelle sicher, dass diese existiert.</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_error">Es ist ein Fehler beim Entfernen der Freigabe für diese Datei oder den Ordner aufgetreten.</string>
</resources>
<resources>
<string name="about_android">%1$s Εφαρμογή για Android</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="prefs_pincode_summary">Προστατέψτε την εφαρμογή</string>
<string name="prefs_instant_upload">Ενεργοποιήστε την άμεση μεταφόρτωση</string>
<string name="prefs_instant_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="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">Διεύθυνση εξυπηρέτη</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="uploader_btn_upload_text">Μεταφόρτωση</string>
<string name="filedetails_download">Λήψη</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="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_not_downloaded_yet">Δεν έχει κατέβει ακόμα</string>
<string name="common_choose_account">Επιλογή λογαριασμού</string>
<string name="sync_fail_ticker">Ο συγχρονισμός απέτυχε</string>
<string name="sync_fail_content">Ο συγχρονισμός του %1$s δεν μπόρεσε να ολοκληρωθεί</string>
<string name="sync_fail_in_favourites_content">Τα περιεχόμενα των %1$d αρχείων δεν μπόρεσαν να συγχρονιστούν (%2$d διενέξεις)</string>
<string name="sync_foreign_files_forgotten_ticker">Ορισμένα τοπικά αρχεία ξεχάστηκαν</string>
<string name="sync_foreign_files_forgotten_content">%1$d αρχεια απο τον %2$s χωρο αποθηκευσης δεν μπορουν να αντιγραφθουν σε</string>
+ <string name="sync_foreign_files_forgotten_explanation">Από την έκδοση 1.3.16 και μετά, αρχεία που μεταφορτώνονται από αυτήν τη συσκευή αντιγράφονται στον τοπικό φάκελο %1$s για να αποτραπεί η απώλεια δεδομένων όταν ένα αρχείο είναι συγχρονισμένο με πολλαπλούς λογαριασμούς.\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="pincode_wrong">Εσφαλμένο PIN της εφαρμογής</string>
<string name="pincode_removed">Αφαιρέθηκε το PIN της εφαρμογής</string>
<string name="pincode_stored">Το PIN της εφαρμογής αποθηκεύτηκε</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_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_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">Δεν βρέθηκε στιγμιότυπο server σας</string>
<string name="auth_timeout_title">Ο εξυπηρετητής αργεί πολύ να απαντήσει</string>
<string name="auth_incorrect_address_title">Κακώς διατυπωμένο URL</string>
<string name="auth_ssl_general_error_title">Η αρχικοποίηση του SLL απέτυχε</string>
+ <string name="auth_ssl_unverified_server_title">Αδυναμία επιβεβαίωσης την ταυτότητα SSL του διακομιστή</string>
<string name="auth_bad_oc_version_title">Μη αναγνωρίσιμη έκδοση διακομιστή server σας</string>
<string name="auth_wrong_connection_title">Δεν ήταν δυνατή η σύνδεση</string>
<string name="auth_secure_connection">Επιτεύχθηκε ασφαλής σύνδεση</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">Απρόοπτη κατάσταση - παρακαλώ εισάγετε τη διεύθυνση 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="fd_keep_in_sync">Διατήρηση αρχείου ενημερωμένo</string>
<string name="common_rename">Μετονομασία</string>
<string name="common_remove">Αφαίρεση</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="wait_a_moment">Παρακαλούμε περιμένετε</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">- Το πιστοποιητικό του διακομιστή δεν είναι αξιόπιστο</string>
<string name="ssl_validator_reason_cert_expired">- Το πιστοποιητικό του διακομιστή έχει λήξει</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="actionbar_failed_instant_upload">Αποτυχημένη στιγμιαία φόρτωση</string>
+ <string name="failed_upload_headline_text">Αποτυχημένες στιγμιαίες φορτώσεις</string>
+ <string name="failed_upload_headline_hint">Σύνοψη όλων των αποτυχημένων φορτώσεων</string>
<string name="failed_upload_all_cb">επιλογή όλων</string>
+ <string name="failed_upload_headline_retryall_btn">επανάληψη για όλα τα επιλεγμένα</string>
+ <string name="failed_upload_headline_delete_all_btn">διαγραφή όλων των επιλεγμένων από τη λίστα προς μεταφόρτωση</string>
+ <string name="failed_upload_retry_text">επανάληψη προσπάθειας μεταφόρτωσης της εικόνας:</string>
<string name="failed_upload_load_more_images">Φόρτωση περισσότερων εικόνων</string>
<string name="failed_upload_failure_text">Μήνυμα Αποτυχίας:</string>
+ <string name="failed_upload_quota_exceeded_text">Παρακαλώ ελέγξτε τις ρυθμίσεις του διακομιστή σας, ίσως έχετε υπερβεί τη διαθέσιμη μερίδα σας.</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_error">Ένα σφάλμα προέκυψε κατά τη διάρκεια ακύρωσης διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
</resources>
<string name="recommend_subject">Try %1$s on your smartphone!</string>
<string name="recommend_text">I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s</string>
<string name="auth_check_server">Check Server</string>
- <string name="auth_host_url">Server address</string>
+ <string name="auth_host_url">Server address https://…</string>
<string name="auth_username">Username</string>
<string name="auth_password">Password</string>
<string name="auth_register">New to %1$s?</string>
<string name="filedetails_download">Download</string>
<string name="filedetails_sync_file">Refresh file</string>
<string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
+ <string name="action_share_file">Share link</string>
+ <string name="action_unshare_file">Unshare link</string>
<string name="common_yes">Yes</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
<string name="wait_a_moment">Wait a moment</string>
<string name="filedisplay_unexpected_bad_get_content">Unexpected problem; please select the file from a different app</string>
<string name="filedisplay_no_file_selected">No file was selected</string>
+ <string name="activity_chooser_title">Send link to …</string>
<string name="oauth_check_onoff">Log in with oAuth2</string>
<string name="oauth_login_connection">Connecting to oAuth2 server…</string>
<string name="ssl_validator_header">The identity of the site could not be verified</string>
<string name="failed_upload_retry_do_nothing_text">do nothing you are not online for instant upload</string>
<string name="failed_upload_failure_text">Failure Message: </string>
<string name="failed_upload_quota_exceeded_text">Please check your server configuration, perhaps your quota is exceeded.</string>
+ <string name="share_link_no_support_share_api">Sorry, sharing is not enabled on your server. Please contact your administrator.</string>
+ <string name="share_link_file_no_exist">Unable to share this file or folder. Please, make sure it exists</string>
+ <string name="share_link_file_error">An error occurred while trying to share this file or folder</string>
+ <string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
</resources>
<string name="prefs_instant_upload">Kapabligi tujan alŝuton</string>
<string name="prefs_instant_upload_summary">Tuje alŝuti fotojn faritajn per fotilo</string>
<string name="prefs_help">Helpo</string>
- <string name="auth_host_url">Servila adreso</string>
<string name="auth_username">Uzantonomo</string>
<string name="auth_password">Pasvorto</string>
<string name="sync_string_files">Dosieroj</string>
<string name="filedetails_modified">Modifita je:</string>
<string name="filedetails_download">Elŝuti</string>
<string name="filedetails_renamed_in_upload_msg">La dosiero alinomiĝis al %1$s dum alŝuto</string>
+ <string name="action_share_file">Konhavigi ligilon</string>
<string name="common_yes">Jes</string>
<string name="common_no">Ne</string>
<string name="common_ok">Akcepti</string>
<string name="prefs_log_summary_history">Esto muestra los registros grabados</string>
<string name="prefs_log_delete_history_button">Eliminar Historial</string>
<string name="prefs_help">Ayuda</string>
+ <string name="prefs_recommend">Recomendar a un amigo</string>
<string name="prefs_feedback">Sugerencias</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="recommend_subject">¡Intento %1$s en tu teléfono inteligente!</string>
+ <string name="recommend_text">¡Quisiera invitarte a usar %1$s en tu smartphone!\nDescárgalo aquí: %2$s</string>
<string name="auth_check_server">Verificar Servidor</string>
- <string name="auth_host_url">Dirección del servidor</string>
+ <string name="auth_host_url">Dirección del servidor https://...</string>
<string name="auth_username">Nombre de usuario</string>
<string name="auth_password">Contraseña</string>
<string name="auth_register">¿Sos nuevo para %1$s?</string>
<string name="filedetails_download">Descargar</string>
<string name="filedetails_sync_file">Actualizar archivo</string>
<string name="filedetails_renamed_in_upload_msg">El archivo fue renombrado como %1$s durante la subida</string>
+ <string name="action_share_file">Compartir vínculo</string>
<string name="common_yes">Sí</string>
<string name="common_no">No</string>
<string name="common_ok">Aceptar</string>
<string name="sync_foreign_files_forgotten_ticker">Algunos archivos locales fueron olvidados</string>
<string name="sync_foreign_files_forgotten_content">%1$d archivos del directorio %2$s no pudieron ser copiados en</string>
<string name="sync_foreign_files_forgotten_explanation">Desde la versión 1.3.16, los archivos subidos desde este dispositivo se copian a un directorio local %1$s para prevenir la pérdida de datos cuando un único archivo es sincronizado con múltiples cuentas. \n\nDebido a este cambio, todos los archivos subidos en versiones previas de esta aplicación fueron copiados al directorio %2$s. No obstante, debido a un error, esta operación no pudo ser completada durante la sincronización de la cuenta. Podés dejar los archivos así y borrar el enlace a %3$s, o podés mover el/los archivos al directorio %1$s y conservar el enlace a %4$s. \n\nAbajo, encontrás la lista con los enlaces a los archivos locales y remotos en %5$s a los que están enlazados.</string>
+ <string name="sync_current_folder_was_removed">El directorio %1$s ya no existe</string>
<string name="foreign_files_move">Mover todos</string>
<string name="foreign_files_success">Todos los archivos fueron movidos</string>
<string name="foreign_files_fail">Algunos archivos no pudieron ser movidos</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 está correctamente codificado</string>
+ <string name="media_err_timeout">Tiempo expirado mientras se intentaba reproducir</string>
<string name="media_err_invalid_progressive_playback">Archivo de medios no puede ser transmitido</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="auth_connection_established">Conexión establecida</string>
<string name="auth_testing_connection">Probando conexión...</string>
<string name="auth_not_configured_title">Configuración de servidor en formato incorrecto</string>
+ <string name="auth_account_not_new">Una cuenta para el mismo usuario y servidor ya existe en el dispositivo</string>
+ <string name="auth_account_not_the_same">El usuario ingresado no concuerda con el usuario de esta cuenta</string>
<string name="auth_unknown_error_title">¡Ocurrió un error desconocido!</string>
<string name="auth_unknown_host_title">No fue posible encontrar la dirección</string>
<string name="auth_incorrect_path_title">Instancia de servidor no encontrada</string>
<string name="auth_timeout_title">El servidor tardó demasiado en responder</string>
<string name="auth_incorrect_address_title">URL no válida</string>
<string name="auth_ssl_general_error_title">Error al inicializar el SSL</string>
+ <string name="auth_ssl_unverified_server_title">No se ha podido verifica la identidad SSL del servidor</string>
<string name="auth_bad_oc_version_title">No es posible reconocer la versión del servidor</string>
<string name="auth_wrong_connection_title">No fue posible establecer la conexión</string>
<string name="auth_secure_connection">Conexión segura establecida</string>
<string name="auth_oauth_error">Autorización no satisfactoria</string>
<string name="auth_oauth_error_access_denied">Acceso denegado por el servidor de autorización</string>
<string name="auth_wtf_reenter_URL">Estado inesperado; por favor, introducí la URL del servidor de nuevo</string>
+ <string name="auth_expired_oauth_token_toast">Tu autorización ha expirado. Por favor, obtenga una nueva autorización</string>
<string name="auth_expired_basic_auth_toast">Por favor, ingresá tu contraseña actual</string>
+ <string name="auth_expired_saml_sso_token_toast">Su sesión ha expirado. Por favor, conéctese de nuevo</string>
+ <string name="auth_connecting_auth_server">Conectando al servidor de autenticación...</string>
+ <string name="auth_unsupported_auth_method">El servidor no soporta este método de autenticación</string>
+ <string name="auth_unsupported_multiaccount">%1$s no soporta múltiples cuentas</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="sync_file_fail_msg">No pudo comprobarse el archivo remoto</string>
<string name="sync_file_nothing_to_do_msg">Ya está sincronizado</string>
<string name="create_dir_fail_msg">No fue posible crear el directorio</string>
+ <string name="filename_forbidden_characters">Caracteres prohibidos: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Esperá un momento</string>
<string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, intentá con otra aplicación para abrir el archivo</string>
<string name="filedisplay_no_file_selected">No se seleccionó ningún archivo</string>
+ <string name="oauth_check_onoff">Iniciando sesión con oAuth2</string>
<string name="oauth_login_connection">Conectando al servidor oAuth2...</string>
<string name="ssl_validator_header">La identidad del sitio no pudo ser verificada</string>
<string name="ssl_validator_reason_cert_not_trusted">- El certificado del servidor no es confiable</string>
<string name="preview_image_description">Previsualización de imagen</string>
<string name="preview_image_error_unknown_format">No se puede mostrar la imagen</string>
<string name="error__upload__local_file_not_copied">%1$s no pudo ser copiado al directorio local %2$s</string>
+ <string name="actionbar_failed_instant_upload">Falló InstantUpload</string>
<string name="failed_upload_headline_text">Error en Cargas instantánea </string>
<string name="failed_upload_headline_hint">Resumen de todas las cargas instantáneas con error</string>
<string name="failed_upload_all_cb">Seleccionar todos</string>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="actionbar_upload">Subir</string>
+ <string name="actionbar_upload_files">Archivos</string>
+ <string name="actionbar_mkdir">Crear directorio</string>
+ <string name="actionbar_settings">Configuración</string>
+ <string name="prefs_category_general">General</string>
+ <string name="prefs_accounts">Cuentas</string>
+ <string name="auth_username">Usuario</string>
+ <string name="auth_password">Clave</string>
+ <string name="sync_string_files">Archivos</string>
+ <string name="setup_btn_connect">Conectar</string>
+ <string name="uploader_btn_upload_text">Subir</string>
+ <string name="uploader_wrn_no_account_title">No se encuentra la cuenta</string>
+ <string name="uploader_info_uploading">Subiendo</string>
+ <string name="filedetails_select_file">Seleccione un archivo para desplegar información adicional.</string>
+ <string name="filedetails_size">Tamaño:</string>
+ <string name="filedetails_type">Tipo:</string>
+ <string name="filedetails_created">Creado:</string>
+ <string name="filedetails_modified">Modificado:</string>
+ <string name="filedetails_download">Descargar</string>
+ <string name="common_yes">Si</string>
+ <string name="common_no">No</string>
+ <string name="common_ok">OK</string>
+ <string name="common_cancel">Cancelar</string>
+ <string name="common_error">Error</string>
+ <string name="uploader_info_dirname">Nombre del directorio</string>
+ <string name="common_choose_account">Elija una cuenta</string>
+ <string name="pincode_enter_pin_code">Por favor, ingreses su PIN de aplicación</string>
+ <string name="pincode_configure_your_pin">Ingrese su PIN de aplicación</string>
+ <string name="pincode_reenter_your_pincode">Por favor, reingrese su PIN de aplicación</string>
+</resources>
<?xml version='1.0' encoding='UTF-8'?>
-<resources/>
+<resources>
+ <string name="about_android">App Android %1$s</string>
+ <string name="about_version">versión %1$s</string>
+ <string name="actionbar_sync">Actualizar cuenta</string>
+ <string name="actionbar_upload">Subir archivo</string>
+ <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">Crear directorio</string>
+ <string name="actionbar_settings">Ajustes</string>
+ <string name="actionbar_see_details">Detalles</string>
+ <string name="prefs_category_general">General</string>
+ <string name="prefs_category_more">Más</string>
+ <string name="prefs_accounts">Cuentas</string>
+ <string name="prefs_manage_accounts">Gestionar cuentas</string>
+ <string name="prefs_pincode">PIN de aplicación </string>
+ <string name="prefs_pincode_summary">Proteja su cliente</string>
+ <string name="prefs_instant_upload">Habilita la subida instantánea</string>
+ <string name="prefs_instant_upload_summary">Subir instantáneamente las fotos tomadas por la cámara</string>
+ <string name="prefs_log_title">Habilitar registro</string>
+ <string name="prefs_log_summary">Esto es usado para registrar problemas</string>
+ <string name="prefs_log_title_history">Historia del Registro</string>
+ <string name="prefs_log_summary_history">Esto muestra los registros grabados</string>
+ <string name="prefs_log_delete_history_button">Eliminar Historial</string>
+ <string name="prefs_help">Ayuda</string>
+ <string name="prefs_recommend">Recomendar a un amigo</string>
+ <string name="prefs_feedback">Mensajes de retroalimentación</string>
+ <string name="prefs_imprint">Imprint</string>
+ <string name="recommend_subject">Prueba %1$s en tu smarthphone!</string>
+ <string name="recommend_text">Quiero invitarte a usar %1$s en tu smarthphone!⏎\nDescargalo 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="auth_username">Nombre de usuario</string>
+ <string name="auth_password">Contraseña</string>
+ <string name="auth_register">New to %1$s?</string>
+ <string name="sync_string_files">Archivos</string>
+ <string name="setup_btn_connect">Conectar</string>
+ <string name="uploader_btn_upload_text">Subir</string>
+ <string name="uploader_top_message">Escoja el directorio de carga:</string>
+ <string name="uploader_wrn_no_account_title">No se encontraron cuentas</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_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="uploader_wrn_no_content_text">Ningún contenido ha sido recibido. No hay nada que subir.</string>
+ <string name="uploader_error_forbidden_content">%1$s no está autorizado para acceder al contenido compartido</string>
+ <string name="uploader_info_uploading">Enviando</string>
+ <string name="file_list_empty">No hay archivos en esta carpeta.\nPuedes añadir nuevos archivos con la opción \"Subir\" del menú.</string>
+ <string name="filedetails_select_file">Pulsa sobre un archivo para mostrar información adicional.</string>
+ <string name="filedetails_size">Tamaño:</string>
+ <string name="filedetails_type">Tipo:</string>
+ <string name="filedetails_created">Creado:</string>
+ <string name="filedetails_modified">Modificado:</string>
+ <string name="filedetails_download">Descargar</string>
+ <string name="filedetails_sync_file">Actualizar archivo</string>
+ <string name="filedetails_renamed_in_upload_msg">El archivo fue renombrado como %1$s durante la subida</string>
+ <string name="action_share_file">Enlace compartido</string>
+ <string name="common_yes">Sí</string>
+ <string name="common_no">No</string>
+ <string name="common_ok">Aceptar</string>
+ <string name="common_cancel_download">Cancelar descarga</string>
+ <string name="common_cancel_upload">Cancelar subida</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_error_unknown">Error desconocido</string>
+ <string name="about_title">Acerca de</string>
+ <string name="change_password">Cambiar contraseña</string>
+ <string name="delete_account">Eliminar cuenta</string>
+ <string name="create_account">Crear cuenta</string>
+ <string name="upload_chooser_title">Subir</string>
+ <string name="uploader_info_dirname">Nombre de directorio</string>
+ <string name="uploader_upload_in_progress_ticker">Subiendo...</string>
+ <string name="uploader_upload_in_progress_content">%1$d%% Subiendo %2$s</string>
+ <string name="uploader_upload_succeeded_ticker">Subido con éxito</string>
+ <string name="uploader_upload_succeeded_content_single">%1$s se ha subido con éxito</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="downloader_download_in_progress_ticker">Descargando ...</string>
+ <string name="downloader_download_in_progress_content">%1$s Descargada 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_ticker">Falló la descarga</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="common_choose_account">Elige una cuenta</string>
+ <string name="sync_fail_ticker">Falló la sincronización</string>
+ <string name="sync_fail_content">La sincronización de %1$s s no se pudo completar</string>
+ <string name="sync_fail_content_unauthorized">Contraseña no válida para %1$s</string>
+ <string name="sync_conflicts_in_favourites_ticker">Se encontraron conflictos</string>
+ <string name="sync_conflicts_in_favourites_content">Falló la sincronización de contenidos de %1$d archivos</string>
+ <string name="sync_fail_in_favourites_ticker">Fallos en la sincronización de contenidos</string>
+ <string name="sync_fail_in_favourites_content">Los contenidos de %1$d archivos no fueron sincronizados (%2$d conflictos)</string>
+ <string name="sync_foreign_files_forgotten_ticker">Algunos archivos locales se han perdido</string>
+ <string name="sync_foreign_files_forgotten_content">%1$d archivos de %2$s no han podido ser copiados</string>
+ <string name="sync_foreign_files_forgotten_explanation">Como versión 1.3.16, los archivos subidos desde este dispositivo son copiados a un archivo %1$s local para prevenir perdida de datos cuando un simple archivo es sincronizado con multiples cuentas.\n\nDebido a este cambio, todos los archivos subidos en versiones previas de esta aplicación han sido copiados a la carpeta %2$s. No obtante, un error previno el completado de esta operación durante la sincronización de cuenta. Debería dejar el o los archivos así y eliminar el enlace a %3$s o mover el o los archivos al %1$s directorio y conservar el enlace a %4$s.\n\nListado abajo tiene los enlaces a los archivos locales y archivos remotos en %5$s </string>
+ <string name="sync_current_folder_was_removed">La carpeta local %1$s no existe.</string>
+ <string name="foreign_files_move">Mover todo</string>
+ <string name="foreign_files_success">Todos los archivos fueron movidos</string>
+ <string name="foreign_files_fail">Algunos archivos no han podido ser movidos</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 en la carpeta %1$s. ¿Quiere moverlos en lugar de copiarlos?</string>
+ <string name="pincode_enter_pin_code">Por favor, inserta tu 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="pincode_remove_your_pincode">Borre su PIN de aplicación</string>
+ <string name="pincode_mismatch">Los PIN introducidos no son iguales</string>
+ <string name="pincode_wrong">PIN de aplicación incorrecto</string>
+ <string name="pincode_removed">PIN de aplicación borrado</string>
+ <string name="pincode_stored">PIN de aplicación guardado</string>
+ <string name="media_notif_ticker">Reproductor de música %1$s</string>
+ <string name="media_state_playing">%1$s (reproduciendo)</string>
+ <string name="media_state_loading">%1$s (cargando)</string>
+ <string name="media_event_done">%1$s reproducción finalizada</string>
+ <string name="media_err_nothing_to_play">No se encuentra archivo de medio</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_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_invalid_progressive_playback">Archivo de medio no puede ser transmitido</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_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_trying_to_login">Intentado iniciar sesión...</string>
+ <string name="auth_no_net_conn_title">Sin conexión de red</string>
+ <string name="auth_nossl_plain_ok_title">Conexión segura no disponible.</string>
+ <string name="auth_connection_established">Conexión establecida</string>
+ <string name="auth_testing_connection">Probando conexión...</string>
+ <string name="auth_not_configured_title">Configuración de servidor en formato incorrecto</string>
+ <string name="auth_account_not_new">Una cuenta para el mismo usuario y servidor ya existen en el dispositivo</string>
+ <string name="auth_account_not_the_same">El usuario introducido no concuerda con el usuario de esta cuenta</string>
+ <string name="auth_unknown_error_title">Ocurrió un error desconocido</string>
+ <string name="auth_unknown_host_title">No se pudo encontrar la dirección</string>
+ <string name="auth_incorrect_path_title">Instancia de servidor no encontrada</string>
+ <string name="auth_timeout_title">El servidor ha tardado demasiado en responder</string>
+ <string name="auth_incorrect_address_title">URL no válida</string>
+ <string name="auth_ssl_general_error_title">Falló la inicialización SSL</string>
+ <string name="auth_ssl_unverified_server_title">No fue posible verificar la identidad del servidor SLL</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_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_expired_oauth_token_toast">Su autorización ha expirado. Por favor, autorice de nuevo</string>
+ <string name="auth_expired_basic_auth_toast">Por favor, introduzca la contraseña actual.</string>
+ <string name="auth_expired_saml_sso_token_toast">Su sesión ha expirado. Favor de conectarse de nuevo</string>
+ <string name="auth_connecting_auth_server">Conectando al servidor de autenticación...</string>
+ <string name="auth_unsupported_auth_method">El servidor no soporta este método de autenticación</string>
+ <string name="auth_unsupported_multiaccount">%1$s no soporta cuentas múltiples</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="confirmation_remove_alert">¿Está seguro que desea borrar %1$s ?</string>
+ <string name="confirmation_remove_folder_alert">¿Desea elimiar %1$s y sus descendientes?</string>
+ <string name="confirmation_remove_local">Sólo local</string>
+ <string name="confirmation_remove_folder_local">Sólo archivos locales</string>
+ <string name="confirmation_remove_remote">Eliminar del servidor</string>
+ <string name="confirmation_remove_remote_and_local">Tanto remoto como local</string>
+ <string name="remove_success_msg">Borrado correctamente</string>
+ <string name="remove_fail_msg">El borrado no pudo ser completado</string>
+ <string name="rename_dialog_title">Introduzca un nombre nuevo</string>
+ <string name="rename_local_fail_msg">No se pudo cambiar el nombre de la copia local, trata con un nombre differente</string>
+ <string name="rename_server_fail_msg">No se pudo cambiar el nombre</string>
+ <string name="sync_file_fail_msg">No pudo comprobarse el archivo remoto</string>
+ <string name="sync_file_nothing_to_do_msg">Ya está sincronizado</string>
+ <string name="create_dir_fail_msg">El directorio no pudo ser creado</string>
+ <string name="filename_forbidden_characters">Carácteres ilegales: / \\ < > : \" | ? *</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_no_file_selected">No fué seleccionado ningún archivo</string>
+ <string name="oauth_check_onoff">Ingresar con oAuth2</string>
+ <string name="oauth_login_connection">Conectando al servidor oAuth2...</string>
+ <string name="ssl_validator_header">La identidad del sitio no puede ser verificada</string>
+ <string name="ssl_validator_reason_cert_not_trusted">- El certificado del servidor no es de confianza</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 demasiado reciente</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_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="ssl_validator_label_subject">Emitido para:</string>
+ <string name="ssl_validator_label_issuer">Emitido por:</string>
+ <string name="ssl_validator_label_CN">Nombre común:</string>
+ <string name="ssl_validator_label_O">Organización:</string>
+ <string name="ssl_validator_label_OU">Unidad organizativa</string>
+ <string name="ssl_validator_label_C">Pais:</string>
+ <string name="ssl_validator_label_ST">Estado:</string>
+ <string name="ssl_validator_label_L">Ubicación:</string>
+ <string name="ssl_validator_label_validity">Validez:</string>
+ <string name="ssl_validator_label_validity_from">De:</string>
+ <string name="ssl_validator_label_validity_to">A:</string>
+ <string name="ssl_validator_label_signature">Firma:</string>
+ <string name="ssl_validator_label_signature_algorithm">Algoritmo:</string>
+ <string name="placeholder_sentence">Esto es un marcador de posición</string>
+ <string name="placeholder_filename">marcadordeposición.txt</string>
+ <string name="placeholder_filetype">Imagen PNG</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">Subir imágenes sólo via WiFi</string>
+ <string name="instant_upload_path">/SubidasInstantáneas</string>
+ <string name="conflict_title">Conflicto en la actualización</string>
+ <string name="conflict_message">El archivo remoto %s no está sincronizado con el archivo local. Si continúa, se reemplazará el contenido del archivo en el servidor.</string>
+ <string name="conflict_keep_both">Mantener ambas</string>
+ <string name="conflict_overwrite">Sobrescribir</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">No se puede mostrar la imagen</string>
+ <string name="error__upload__local_file_not_copied">%1$s no puede ser copiado al %2$s directorio local </string>
+ <string name="actionbar_failed_instant_upload">Carga instantánea fallida</string>
+ <string name="failed_upload_headline_text">Cargas instantáneas fallidas</string>
+ <string name="failed_upload_headline_hint">Resumen de todas las cargas instantáneas fallidas</string>
+ <string name="failed_upload_all_cb">Seleccionar todos</string>
+ <string name="failed_upload_headline_retryall_btn">Reintentar todos los seleccionados</string>
+ <string name="failed_upload_headline_delete_all_btn">Eliminar todo de la cola de subida</string>
+ <string name="failed_upload_retry_text">Reintentar subida de imagen:</string>
+ <string name="failed_upload_load_more_images">Cargar mas imágenes</string>
+ <string name="failed_upload_retry_do_nothing_text">No hacer nada no está conectado para subida instantánea</string>
+ <string name="failed_upload_failure_text">Mensaje de error:</string>
+ <string name="failed_upload_quota_exceeded_text">Por favor revise su configuración de servidor, posiblemente su cuota se haya excedido.</string>
+</resources>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
- <string name="about_android">App Android %1$s</string>
+ <string name="about_android">%1$s para Android</string>
<string name="about_version">versión %1$s</string>
<string name="actionbar_sync">Actualizar cuenta</string>
- <string name="actionbar_upload">Subir archivo</string>
+ <string name="actionbar_upload">Subir</string>
<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">Crear directorio</string>
- <string name="actionbar_settings">Ajustes</string>
+ <string name="actionbar_settings">Configuración</string>
<string name="actionbar_see_details">Detalles</string>
<string name="prefs_category_general">General</string>
<string name="prefs_category_more">Más</string>
<string name="prefs_manage_accounts">Gestionar cuentas</string>
<string name="prefs_pincode">PIN de aplicación </string>
<string name="prefs_pincode_summary">Proteja su cliente</string>
- <string name="prefs_instant_upload">Habilita la subida instantánea</string>
+ <string name="prefs_instant_upload">Activar la subida instantánea</string>
<string name="prefs_instant_upload_summary">Subir instantáneamente las fotos tomadas por la cámara</string>
<string name="prefs_log_title">Habilitar registro</string>
<string name="prefs_log_summary">Esto es usado para registrar problemas</string>
<string name="recommend_subject">Prueba %1$s en tu smarthphone!</string>
<string name="recommend_text">Quiero invitarte a usar %1$s en tu smarthphone!⏎\nDescargalo aquí: %2$s</string>
<string name="auth_check_server">Compruebe el servidor.</string>
- <string name="auth_host_url">Dirección del servidor</string>
+ <string name="auth_host_url">Dirección del servidor https://…</string>
<string name="auth_username">Nombre de usuario</string>
<string name="auth_password">Contraseña</string>
<string name="auth_register">New to %1$s?</string>
<string name="setup_btn_connect">Conectar</string>
<string name="uploader_btn_upload_text">Subir</string>
<string name="uploader_top_message">Escoja el directorio de carga:</string>
- <string name="uploader_wrn_no_account_title">No se encontraron cuentas</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_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="uploader_wrn_no_content_text">Ningún contenido ha sido recibido. No hay nada que subir.</string>
<string name="uploader_error_forbidden_content">%1$s no está autorizado para acceder al contenido compartido</string>
- <string name="uploader_info_uploading">Enviando</string>
+ <string name="uploader_info_uploading">Subiendo...</string>
<string name="file_list_empty">No hay archivos en esta carpeta.\nPuedes añadir nuevos archivos con la opción \"Subir\" del menú.</string>
<string name="filedetails_select_file">Pulsa sobre un archivo para mostrar información adicional.</string>
<string name="filedetails_size">Tamaño:</string>
<string name="filedetails_download">Descargar</string>
<string name="filedetails_sync_file">Actualizar archivo</string>
<string name="filedetails_renamed_in_upload_msg">El fichero fue renombrado como %1$s durante la subida</string>
+ <string name="action_share_file">Enlace compartido</string>
+ <string name="action_unshare_file">Ya no compartir enlace</string>
<string name="common_yes">Sí</string>
<string name="common_no">No</string>
<string name="common_ok">Aceptar</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="downloader_download_in_progress_ticker">Descargando ...</string>
- <string name="downloader_download_in_progress_content">%1$s Descargada de %2$s</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_ticker">Falló la descarga</string>
<string name="filename_forbidden_characters">Carácteres ilegales: / \\ < > : \" | ? *</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_no_file_selected">No fué seleccionado ningún 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="oauth_login_connection">Conectando al servidor oAuth2...</string>
<string name="ssl_validator_header">La identidad del sitio no puede ser verificada</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">Subir imágenes sólo via WiFi</string>
+ <string name="instant_upload_on_wifi">Subir imágenes sólo cuando hay WiFi</string>
<string name="instant_upload_path">/SubidasInstantáneas</string>
<string name="conflict_title">Conflicto en la actualización</string>
<string name="conflict_message">El archivo remoto %s no está sincronizado con el archivo local. Si continúa, se reemplazará el contenido del archivo en el servidor.</string>
<string name="preview_image_description">Previsualización de imagen</string>
<string name="preview_image_error_unknown_format">No se puede mostrar la imagen</string>
<string name="error__upload__local_file_not_copied">%1$s no puede ser copiado al %2$s directorio local </string>
+ <string name="actionbar_failed_instant_upload">Carga instantánea fallida</string>
<string name="failed_upload_headline_text">Cargas instantáneas fallidas</string>
<string name="failed_upload_headline_hint">Resumen de todas las cargas instantáneas fallidas</string>
<string name="failed_upload_all_cb">Seleccionar todos</string>
<string name="failed_upload_retry_do_nothing_text">No hacer nada no está conectado para subida instantánea</string>
<string name="failed_upload_failure_text">Mensaje de error:</string>
<string name="failed_upload_quota_exceeded_text">Por favor revise su configuración de servidor, posiblemente su cuota se haya excedido.</string>
+ <string name="share_link_no_support_share_api">Compartir archivos no está activado en su servidor. Sírvase contactar a su administrador de sistema.</string>
+ <string name="share_link_file_no_exist">No es posible compartir este archivo o carpeta. Asegúrese de que existe.</string>
+ <string name="share_link_file_error">Ocurrió un error al tratar de compartir este archivo o carpeta</string>
+ <string name="unshare_link_file_error">Ocurrió un error al tratar de ya no compartir este archivo o carpeta</string>
</resources>
<string name="recommend_subject">Proovi oma nutitelefonil rakendust %1$s!</string>
<string name="recommend_text">Soovin sind kutsuda kasutama oma nutitelefonil rakendust %1$s!\nLae alla siit: %2$s</string>
<string name="auth_check_server">Kontrolli serverit</string>
- <string name="auth_host_url">Serveri aadress</string>
+ <string name="auth_host_url">Serveri aadress https://...</string>
<string name="auth_username">Kasutajanimi</string>
<string name="auth_password">Parool</string>
<string name="auth_register">Uus %1$s kasutaja?</string>
<string name="filedetails_download">Lae alla</string>
<string name="filedetails_sync_file">Värskenda faili</string>
<string name="filedetails_renamed_in_upload_msg">Fail nimetati üleslaadimise käigus ümber %1$ </string>
+ <string name="action_share_file">Jaga linki</string>
<string name="common_yes">Jah</string>
<string name="common_no">Ei</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Mujaloleva faili kontrollimine ebaõnnestus</string>
<string name="sync_file_nothing_to_do_msg">Faili sisu on juba sünkroniseeritud</string>
<string name="create_dir_fail_msg">Kausta loomine ebaõnnestus</string>
+ <string name="filename_forbidden_characters">Keelatud sümbolid: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Oota hetk</string>
<string name="filedisplay_unexpected_bad_get_content">Ootamatu tõrge ; palun kasuta faili valimiseks mõnda teist rakendust</string>
<string name="filedisplay_no_file_selected">Ühtegi faili pole valitud</string>
+ <string name="activity_chooser_title">Saada link</string>
<string name="oauth_check_onoff">Logi sisse oAuth2-ga</string>
<string name="oauth_login_connection">oAuth2 serveriga ühendumine...</string>
<string name="ssl_validator_header">Saidi identiteeti ei suudetud kinnitada</string>
<string name="preview_image_description">Pildi eelvaade</string>
<string name="preview_image_error_unknown_format">Seda pilti ei saa näidata</string>
<string name="error__upload__local_file_not_copied">%1$s ei suudetud kopeerida kohalikku kataloogi failina %2$s</string>
+ <string name="actionbar_failed_instant_upload">Ebaõnnestunud kohene üleslaadimine</string>
<string name="failed_upload_headline_text">Ebaõnnestunud kohesed üleslaadimised</string>
<string name="failed_upload_headline_hint">Kõikide ebaõnnestunud üleslaadimiste kokkuvõte</string>
<string name="failed_upload_all_cb">vali kõik</string>
<string name="failed_upload_retry_do_nothing_text">ära tee midagi, sa pole võrku ühendatud koheseks üleslaadimiseks</string>
<string name="failed_upload_failure_text">Veateade:</string>
<string name="failed_upload_quota_exceeded_text">Palun kontrolli oma serveri seadeid, võib-olla on mahulimiit ületatud.</string>
+ <string name="share_link_file_error">Faili või kausta jagamisel esines viga</string>
+ <string name="unshare_link_file_error">Faili või kausta jagamise tühistamisel esines viga</string>
</resources>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <string name="filedetails_download">Deskargatu</string>
+ <string name="common_cancel">Ezeztatu</string>
+</resources>
<string name="prefs_log_summary_history">Honek gordetako erregistroak bistaratzen ditu.</string>
<string name="prefs_log_delete_history_button">Ezabatu historia</string>
<string name="prefs_help">Laguntza</string>
+ <string name="prefs_recommend">Lagun bati aholkatu</string>
+ <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</string>
+ <string name="auth_host_url">Zerbitzariaren helbidea https://</string>
<string name="auth_username">Erabiltzaile izena</string>
<string name="auth_password">Pasahitza</string>
<string name="auth_register">Berria %1$s-n?</string>
<string name="filedetails_download">Deskargatu</string>
<string name="filedetails_sync_file">Freskatu fitxaegia</string>
<string name="filedetails_renamed_in_upload_msg">Fitxategiaren izena %1$sra aldatu da igotzean</string>
+ <string name="action_share_file">Elkarbanatu lotura</string>
<string name="common_yes">Bai</string>
<string name="common_no">Ez</string>
<string name="common_ok">Ados</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 ekiditzeko fitxategi bat kontu ezberdinekin sinkronizatzen denean.\n\nAldaketa 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="foreign_files_fail">Fitxategi batzuk ezin dira mugitu</string>
<string name="media_err_unsupported">Onartzen ez de euskarri kodeka</string>
<string name="media_err_io">Euskarri fitxategia ezin da bihurtu</string>
<string name="media_err_malformed">Euskarri fitxategia ezin da kodetu</string>
+ <string name="media_err_timeout">Erreproduzitzen saiatzean denbora iraungitu da</string>
<string name="media_err_invalid_progressive_playback">Euskarri fitxategia ezin da jariotu</string>
<string name="media_err_unknown">Euskarri fitxategia ezin erreproduzitu stock euskarri erreproduzigailuarekin</string>
<string name="media_err_security_ex">Segurtasun errorea %1$s erreproduzitzen saiatzean</string>
<string name="auth_connection_established">Konexioa ezarri da</string>
<string name="auth_testing_connection">Konexioa probatzen...</string>
<string name="auth_not_configured_title">gaizki egindako server konfigurazioa</string>
+ <string name="auth_account_not_new">Erabiltzaile eta zerbitzari hauendako dagoeneko kontu bat existitzen da gailu honetan</string>
+ <string name="auth_account_not_the_same">Sartutako erabiltzaileak ez du bat egiten kontu honetako erabiltzailearekin</string>
<string name="auth_unknown_error_title">Errore ezezagun bat gertatu da</string>
<string name="auth_unknown_host_title">Ezin izan da hostalaria aurkitu</string>
<string name="auth_incorrect_path_title">ez da serveren instalaziorik aurkitu</string>
<string name="auth_timeout_title">Zerbitzariak denbora asko hartu du erantzuteko</string>
<string name="auth_incorrect_address_title">Gaizki sortutako URLa</string>
<string name="auth_ssl_general_error_title">SSL abiaratzeak huts egin du</string>
+ <string name="auth_ssl_unverified_server_title">Ezin izan da SSL zerbitzariaren identitaea egiaztatu</string>
<string name="auth_bad_oc_version_title">server zerbitzari bertsio ezezaguna</string>
<string name="auth_wrong_connection_title">Ezin izan da konexioa egin</string>
<string name="auth_secure_connection">Konexio segurua ezarri da</string>
<string name="auth_oauth_error">Baimena ez da lortu</string>
<string name="auth_oauth_error_access_denied">Sarrera autorizazio zerbitzariak ukatua</string>
<string name="auth_wtf_reenter_URL">Egoera esperogabea, mesedez idatzi berriz zerbitzari URLa</string>
+ <string name="auth_expired_oauth_token_toast">Zure baimena iraungitu da.\nMesedez, baimendu berriz</string>
<string name="auth_expired_basic_auth_toast">Mesedez, sartu oraingo pasahitza</string>
+ <string name="auth_expired_saml_sso_token_toast">Zure saioa iraungitu da. Mesdez konektatu berriro</string>
+ <string name="auth_connecting_auth_server">Konektatzen autentikazio zerbitzarira...</string>
+ <string name="auth_unsupported_auth_method">Zerbitzariak ez du autentikazio metodo hau onartzen</string>
+ <string name="auth_unsupported_multiaccount">%1$s ez du kontu anitzak onartzen</string>
<string name="fd_keep_in_sync">Mantendu fitxategia eguneratuta</string>
<string name="common_rename">Berrizendatu</string>
<string name="common_remove">Ezabatu</string>
<string name="sync_file_fail_msg">Urruneko fitxategia ezin izan da arakatu</string>
<string name="sync_file_nothing_to_do_msg">Fitxategi edukiak dagoeneko sinkronizaturik</string>
<string name="create_dir_fail_msg">Karpeta ezin da sortu</string>
+ <string name="filename_forbidden_characters">Debekatutako karaktereak: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Itxaron momentu bat</string>
<string name="filedisplay_unexpected_bad_get_content">Ezusteko arazoa; mesedez, saiatu beste app batekin fitxategia hautatzeko</string>
<string name="filedisplay_no_file_selected">Ez da fitxategirik hautatu</string>
+ <string name="oauth_check_onoff">Saioa hasi oAuth2-rekin</string>
<string name="oauth_login_connection">Konektatzen oAuth2 zerbitzarira...</string>
<string name="ssl_validator_header">Lekuaren identitatea ezin da egiaztatu</string>
<string name="ssl_validator_reason_cert_not_trusted">- Zerbitzariaren ziurtagiria ez da fidagarria</string>
<string name="preview_image_description">Irudi aurreikuspena</string>
<string name="preview_image_error_unknown_format">Ezin da irudi hau erakutsi</string>
<string name="error__upload__local_file_not_copied">%1$s ezin da %2$s bertako karpetara kopiatu</string>
+ <string name="actionbar_failed_instant_upload">UnekoIgoerak huts egin du</string>
<string name="failed_upload_headline_text">Uneko igoerek huts egin dute</string>
<string name="failed_upload_headline_hint">Huts egindako igoeren laburpena</string>
<string name="failed_upload_all_cb">Hautatu dena</string>
<string name="prefs_help">راهنما</string>
<string name="prefs_feedback">باز خورد</string>
<string name="prefs_imprint">مهر زدن</string>
- <string name="auth_host_url">آدرس سرور</string>
<string name="auth_username">نام کاربری</string>
<string name="auth_password">رمز عبور</string>
<string name="sync_string_files">پروندهها</string>
<string name="recommend_subject">Kokeile %1$sia älypuhelimellasi!</string>
<string name="recommend_text">Ota %1$s käyttöösi älypuhelimessa!\nLataa tästä: %2$s</string>
<string name="auth_check_server">Tarkista palvelin</string>
- <string name="auth_host_url">Palvelimen osoite</string>
+ <string name="auth_host_url">Palvelinosoite https://…</string>
<string name="auth_username">Käyttäjätunnus</string>
<string name="auth_password">Salasana</string>
<string name="auth_register">Onko %1$s uusi tuttavuus sinulle?</string>
<string name="filedetails_download">Lataa</string>
<string name="filedetails_sync_file">Päivitä tiedosto</string>
<string name="filedetails_renamed_in_upload_msg">Tiedoston nimeksi muutettiin %1$s siirron yhteydessä</string>
+ <string name="action_share_file">Jaa linkki</string>
<string name="common_yes">Kyllä</string>
<string name="common_no">Ei</string>
<string name="common_ok">OK</string>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<string name="about_android">%1$s Android App</string>
<string name="about_version">version %1$s</string>
<string name="actionbar_sync">Actualiser le compte</string>
- <string name="actionbar_upload">Téléverser</string>
+ <string name="actionbar_upload">Charger</string>
<string name="actionbar_upload_from_apps">Contenu d\'une autre application</string>
<string name="actionbar_upload_files">Fichiers</string>
<string name="actionbar_open_with">Ouvrir avec</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 !\nTéléchargez-le ici : %2$s</string>
<string name="auth_check_server">Vérifier le serveur</string>
- <string name="auth_host_url">Adresse du serveur</string>
+ <string name="auth_host_url">Adresse du serveur https://...</string>
<string name="auth_username">Nom d\'utilisateur</string>
<string name="auth_password">Mot de passe</string>
<string name="auth_register">Nouveau dans %1$s ?</string>
<string name="filedetails_download">Télécharger</string>
<string name="filedetails_sync_file">Actualiser le fichier</string>
<string name="filedetails_renamed_in_upload_msg">Le fichier a été renommé en %s pendant le téléversement</string>
+ <string name="action_share_file">Partager le lien</string>
+ <string name="action_unshare_file">Ne plus partager ce lien</string>
<string name="common_yes">Oui</string>
<string name="common_no">Non</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Le fichier distant n\'a pu être vérifié</string>
<string name="sync_file_nothing_to_do_msg">Le contenu des fichiers est déjà synchronisé</string>
<string name="create_dir_fail_msg">Création du répertoire impossible</string>
+ <string name="filename_forbidden_characters">Caractères interdits : / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Veuillez patienter</string>
<string name="filedisplay_unexpected_bad_get_content">Problème inattendu ; veuillez essayer une autre app pour la sélection du fichier</string>
<string name="filedisplay_no_file_selected">Aucun fichier sélectionné</string>
+ <string name="activity_chooser_title">Envoyer un lien à…</string>
<string name="oauth_check_onoff">Connexion avec aAuth2.</string>
<string name="oauth_login_connection">Connexion au serveur aAuth2...</string>
<string name="ssl_validator_header">L\'identité du site ne peut être vérifiée</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é vers le dossier local suivant %2$s</string>
+ <string name="actionbar_failed_instant_upload">Échec du téléversement instantané</string>
<string name="failed_upload_headline_text">Téléchargements instantanés échoués</string>
<string name="failed_upload_headline_hint">Résumé de tous les téléchargements instantanés échoués</string>
<string name="failed_upload_all_cb">Tous sélectionner</string>
<string name="failed_upload_retry_do_nothing_text">Ne rien faire vous n\'êtes pas connecté pour le téléchargement instantané</string>
<string name="failed_upload_failure_text">Message d\'échec:</string>
<string name="failed_upload_quota_exceeded_text">Veuillez vérifier la configuration de votre serveur, peut-être que votre quota est dépassé.</string>
+ <string name="share_link_no_support_share_api">Désolé, la fonctionnalité de partage n’est pas activée sur ce serveur. Veuillez contacter votre administrateur.</string>
+ <string name="share_link_file_no_exist">Impossible de partager ce fichier ou répertoire. Vérifiez qu’il 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_error">Une erreur est survenue lors de la tentative d’annulation du partage de ce fichier ou répertoire</string>
</resources>
<string name="recommend_subject">Tente %1$s no seu teléfono intelixente!</string>
<string name="recommend_text">Quero convidalo a empregar %1$s no seu teléfono intelixente!⏎\nDescárgueo de aquí:%2$s</string>
<string name="auth_check_server">Comprobar o servidor</string>
- <string name="auth_host_url">Enderezo do servidor</string>
+ <string name="auth_host_url">Enderezo do servidor https://…</string>
<string name="auth_username">Nome de usuario</string>
<string name="auth_password">Contrasinal</string>
<string name="auth_register">Novo en %1$s?</string>
<string name="filedetails_download">Descargar</string>
<string name="filedetails_sync_file">Actualizar o ficheiro</string>
<string name="filedetails_renamed_in_upload_msg">O ficheiro foi renomeado a %1$s durante o envío</string>
+ <string name="action_share_file">Ligazón para compartir</string>
+ <string name="action_unshare_file">Deixar de compartir a ligazón</string>
<string name="common_yes">Si</string>
<string name="common_no">Non</string>
<string name="common_ok">Aceptar</string>
<string name="sync_file_fail_msg">Non foi posíbel comprobar o ficheiro remoto</string>
<string name="sync_file_nothing_to_do_msg">Os contidos do ficheiro xa están sincronizados</string>
<string name="create_dir_fail_msg">Non foi posíbel crear o directorio</string>
+ <string name="filename_forbidden_characters">Caracteres non permitidos: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Agarde un chisco</string>
<string name="filedisplay_unexpected_bad_get_content">Produciuse un erro non agardado. Seleccione o ficheiro con outro aplicativo diferente</string>
<string name="filedisplay_no_file_selected">Non se escolleu ningún ficheiro</string>
+ <string name="activity_chooser_title">Enviar a ligazón a ...</string>
<string name="oauth_check_onoff">Acceder con oAuth2</string>
<string name="oauth_login_connection">Conectando co servidor oAuth2…</string>
<string name="ssl_validator_header">Non foi posíbel verificar a identidade do sitio</string>
<string name="preview_image_description">Vista previa da imaxe</string>
<string name="preview_image_error_unknown_format">Esta imaxe non pode ser amosada</string>
<string name="error__upload__local_file_not_copied">Non foi posíbel copiar %1$s no directorio local %2$s</string>
+ <string name="actionbar_failed_instant_upload">produciuse un fallo de EnvíoInstantáneo</string>
<string name="failed_upload_headline_text">Envíos instantáneos fallados</string>
<string name="failed_upload_headline_hint">Resumo de todos os envíos instantáneos fallados</string>
<string name="failed_upload_all_cb">seleccionar todo</string>
<string name="failed_upload_retry_do_nothing_text">non facer nada que non estea en liña para o envío instantáneo</string>
<string name="failed_upload_failure_text">Mensaxe de fallo:</string>
<string name="failed_upload_quota_exceeded_text">Comprobe a configuración do seu servidor. é probábel que xa excedera a cota.</string>
+ <string name="share_link_no_support_share_api">O seu servidor non ten activada a opción de compartir. Póñase en contacto co administrador.</string>
+ <string name="share_link_file_no_exist">Non foi posíbel compartir este ficheiro ou cartafol. Asegurese de que existe.</string>
+ <string name="share_link_file_error">Produciuse un erro ao tentar compartir este ficheiro ou cartafol.</string>
+ <string name="unshare_link_file_error">Produciuse un erro ao tentar deixar de compartir este ficheiro ou cartafol</string>
</resources>
<string name="prefs_instant_upload">הפעלת העלאות מהירות</string>
<string name="prefs_instant_upload_summary">העלאה מהירה של תמונות שמצולמות במצלמה שלך</string>
<string name="prefs_help">עזרה</string>
- <string name="auth_host_url">כתובת שרת</string>
<string name="auth_username">שם משתמש</string>
<string name="auth_password">ססמה</string>
<string name="sync_string_files">קבצים</string>
<string name="recommend_subject">Próbálja ki %1$s-t az okostelefonján!</string>
<string name="recommend_text">Kérem próbálja ki %1$s-t az okostelefonján!\nInnen tölthető le: %2$s</string>
<string name="auth_check_server">Szerver állapot ellenörzés</string>
- <string name="auth_host_url">A kiszolgáló címe</string>
+ <string name="auth_host_url">Kiszolgáló címe https://...</string>
<string name="auth_username">Felhasználói név</string>
<string name="auth_password">Jelszó</string>
<string name="auth_register">Új vagy a %1$s területen?</string>
<string name="filedetails_download">Letöltés</string>
<string name="filedetails_sync_file">File frissítése</string>
<string name="filedetails_renamed_in_upload_msg">A feltöltés során az állmányt erre neveztük át: %1$s</string>
+ <string name="action_share_file">Megosztás hivatkozással</string>
<string name="common_yes">Igen</string>
<string name="common_no">Nem</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">A távoli fájl nem volt ellenőrizhető</string>
<string name="sync_file_nothing_to_do_msg">Az állományok már szinkonizálva vannak</string>
<string name="create_dir_fail_msg">A mappa nem hozható létre</string>
+ <string name="filename_forbidden_characters">Nem megendedett karakterek: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Egy pillanat...</string>
<string name="filedisplay_unexpected_bad_get_content">Váratlan hiba; válassza ki a fájlt más programból</string>
<string name="filedisplay_no_file_selected">Egy fájl sincs kiválasztva</string>
<string name="preview_image_description">Előnézeti kép</string>
<string name="preview_image_error_unknown_format">Ez a kép nem jelenik meg</string>
<string name="error__upload__local_file_not_copied">%1$s-t nem sikerült átmásolni ide: %2$s </string>
+ <string name="actionbar_failed_instant_upload">Sikertelen azonnali feltöltés\"</string>
<string name="failed_upload_headline_text">Sikertelen Azonnali feltöltés</string>
<string name="failed_upload_headline_hint">Összefoglaló az összes sikertelen instant feltöltésről</string>
<string name="failed_upload_all_cb">Összes kijelölése</string>
<string name="actionbar_upload">Incargar</string>
<string name="actionbar_upload_files">Files</string>
<string name="actionbar_settings">Configurationes</string>
+ <string name="prefs_category_general">General</string>
<string name="prefs_category_more">Plus</string>
<string name="prefs_help">Adjuta</string>
<string name="auth_username">Nomine de usator</string>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
+ <string name="about_android">%1$s Apl Android</string>
+ <string name="about_version">versi %1$s</string>
+ <string name="actionbar_sync">Segarkan akun</string>
<string name="actionbar_upload">Unggah</string>
+ <string name="actionbar_upload_from_apps">Konten dari apl lain</string>
<string name="actionbar_upload_files">Berkas</string>
+ <string name="actionbar_open_with">Bukan dengan</string>
+ <string name="actionbar_mkdir">Buat folder</string>
<string name="actionbar_settings">pengaturan</string>
+ <string name="actionbar_see_details">Rincian</string>
<string name="prefs_category_general">umum</string>
<string name="prefs_category_more">Lainnya</string>
+ <string name="prefs_accounts">Akun</string>
+ <string name="prefs_manage_accounts">Kelola Akun</string>
+ <string name="prefs_pincode">PIN Apl</string>
+ <string name="prefs_pincode_summary">Lindungi klien Anda</string>
+ <string name="prefs_instant_upload">Aktifkan unggah instan</string>
+ <string name="prefs_instant_upload_summary">Unggah langsung foto yang diambil oleh kamera</string>
+ <string name="prefs_log_title">Aktifkan Pencatatan</string>
+ <string name="prefs_log_summary">Ini digunakan untuk mencatat masalah</string>
+ <string name="prefs_log_title_history">Riwayat Catatan</string>
+ <string name="prefs_log_summary_history">Ini menampilkan catatan yang disimpan</string>
+ <string name="prefs_log_delete_history_button">Riwayat Hapus</string>
<string name="prefs_help">Bantuan</string>
+ <string name="prefs_recommend">Rekomendasikan ke teman</string>
+ <string name="prefs_feedback">Umpan balik</string>
<string name="prefs_imprint">Imprint</string>
- <string name="auth_host_url">alamat server</string>
- <string name="auth_username">nama pengguna</string>
- <string name="auth_password">kata kunci</string>
+ <string name="recommend_subject">Coba %1$s pada smartphone Anda!</string>
+ <string name="recommend_text">Saya mengundang Anda untuk menggunakan %1$s pada smartphone Anda!\nUnduh di sini: %2$s</string>
+ <string name="auth_check_server">Periksa Server</string>
+ <string name="auth_host_url">Alamat server https://…</string>
+ <string name="auth_username">Nama Pengguna</string>
+ <string name="auth_password">Sandi</string>
+ <string name="auth_register">Baru di %1$s?</string>
<string name="sync_string_files">Berkas</string>
+ <string name="setup_btn_connect">Sambungkan</string>
<string name="uploader_btn_upload_text">Unggah</string>
+ <string name="uploader_top_message">Pilih folder unggah</string>
+ <string name="uploader_wrn_no_account_title">Tidak ada akun yang ditemukan</string>
+ <string name="uploader_wrn_no_account_text">Belum ada akun %1$s pada perangkat Anda. Silahkan buat akun terlebih dahulu.</string>
+ <string name="uploader_wrn_no_account_setup_btn_text">Pengaturan</string>
<string name="uploader_wrn_no_account_quit_btn_text">Keluar</string>
- <string name="filedetails_download">unduh</string>
+ <string name="uploader_wrn_no_content_title">Tidak ada konten untuk diunggah</string>
+ <string name="uploader_wrn_no_content_text">Tidak ada konten yang diterima. Tidak ada yang diunggah</string>
+ <string name="uploader_error_forbidden_content">%1$s tidak diizinkan mengakses konten berbagi</string>
+ <string name="uploader_info_uploading">Mengunggah</string>
+ <string name="file_list_empty">Tidak ada berkas dalam folder ini.\nBerkas baru dapat ditambahkan dengan opsi menu \"Unggah\".</string>
+ <string name="filedetails_select_file">Sentuh pada berkas untuk menampilkan informasi tambahan</string>
+ <string name="filedetails_size">Ukuran:</string>
+ <string name="filedetails_type">Tipe:</string>
+ <string name="filedetails_created">Dibuat:</string>
+ <string name="filedetails_modified">Diubah:</string>
+ <string name="filedetails_download">Unduh</string>
+ <string name="filedetails_sync_file">Segarkan berkas</string>
+ <string name="filedetails_renamed_in_upload_msg">Berkas diubah namanya menjadi %1$s saat pengunggahan</string>
+ <string name="action_share_file">Bagikan tautan</string>
<string name="common_yes">Ya</string>
<string name="common_no">Tidak</string>
<string name="common_ok">Oke</string>
+ <string name="common_cancel_download">Batal mengunduh</string>
<string name="common_cancel_upload">Batal mengunggah</string>
- <string name="common_cancel">batal</string>
- <string name="common_error">kesalahan</string>
+ <string name="common_cancel">Batal</string>
+ <string name="common_save_exit">Simpan & Keluar</string>
+ <string name="common_error">Kesalahan</string>
+ <string name="common_loading">Memuat ...</string>
+ <string name="common_error_unknown">Galat tidak diketahui</string>
+ <string name="about_title">Tentang</string>
<string name="change_password">Ubah sandi</string>
+ <string name="delete_account">Hapus akun</string>
+ <string name="create_account">Buat akun</string>
+ <string name="upload_chooser_title">Unggah dari...</string>
+ <string name="uploader_info_dirname">Nama folder</string>
+ <string name="uploader_upload_in_progress_ticker">Mengungggah...</string>
+ <string name="uploader_upload_in_progress_content">%1$d%% Mengunggah %2$s</string>
+ <string name="uploader_upload_succeeded_ticker">Berhasil mengunggah</string>
+ <string name="uploader_upload_succeeded_content_single">%1$s berhasil diunggah</string>
+ <string name="uploader_upload_failed_ticker">Gagal mengunggah</string>
+ <string name="uploader_upload_failed_content_single">Unggah %1$s tidak selesai</string>
+ <string name="downloader_download_in_progress_ticker">Mengunduh...</string>
+ <string name="downloader_download_in_progress_content">%1$d%% Mengunduh %2$s</string>
+ <string name="downloader_download_succeeded_ticker">Berhasil mengunduh</string>
+ <string name="downloader_download_succeeded_content">%1$s berhasil diunduh</string>
+ <string name="downloader_download_failed_ticker">Gagal Mengunduh</string>
+ <string name="downloader_download_failed_content">Mengunduh %1$s tidak selesai</string>
+ <string name="downloader_not_downloaded_yet">Belum diunduh</string>
+ <string name="common_choose_account">Pilih akun</string>
+ <string name="sync_fail_ticker">Sinkronisasi gagal</string>
+ <string name="sync_fail_content">Sinkronisasi %1$s tidak selesai</string>
+ <string name="sync_fail_content_unauthorized">Sandi salah untuk %1$s</string>
+ <string name="sync_conflicts_in_favourites_ticker">Konflik ditemukan</string>
+ <string name="sync_fail_in_favourites_content">Konten berkas %1$d tidak dapat disinkronasikan (%2$d konflik)</string>
+ <string name="sync_foreign_files_forgotten_ticker">Beberapa berkas lokal terlupakan</string>
+ <string name="sync_foreign_files_forgotten_content">%1$d berkas dari direktori %2$s tidak dapat disalin ke</string>
+ <string name="sync_current_folder_was_removed">Folder %1$s tidak ada lagi</string>
+ <string name="foreign_files_move">Pindahkan semua</string>
+ <string name="foreign_files_success">Semua berkas sudah dipindahkan</string>
+ <string name="foreign_files_fail">Beberapa berkas tidak dapat dipindahkan</string>
+ <string name="foreign_files_local_text">Lokal: %1$s</string>
+ <string name="foreign_files_remote_text">Jauh: %1$s</string>
+ <string name="pincode_enter_pin_code">Silakan masukkan PIN Apl</string>
+ <string name="pincode_configure_your_pin">Masukkan PIN Apl</string>
+ <string name="pincode_configure_your_pin_explanation">PIN akan selalu diminta setiap kali apl dijalankan</string>
+ <string name="pincode_reenter_your_pincode">Silakan masukkan ulang PIN Apl</string>
+ <string name="pincode_remove_your_pincode">Hapus PIN Apl</string>
+ <string name="pincode_mismatch">PIN Apl tidak sama</string>
+ <string name="pincode_wrong">PIN Apl salah</string>
+ <string name="pincode_removed">PIN Apl dihapus</string>
+ <string name="pincode_stored">PIN Apl disimpan</string>
+ <string name="media_notif_ticker">Pemutar musik %1$s</string>
+ <string name="media_state_playing">%1$s (dimainkan)</string>
+ <string name="media_state_loading">%1$s (sedang dimuat)</string>
+ <string name="media_err_nothing_to_play">Tidak ditemukan berkas media</string>
+ <string name="media_err_no_account">Tidak ada akun yang diberikan</string>
+ <string name="media_err_not_in_owncloud">Brkas tidak didalam akun yang sah</string>
+ <string name="media_err_unsupported">Kodek media tidak didukung</string>
+ <string name="media_err_io">Berkas media tidak dapat dibaca</string>
+ <string name="media_err_malformed">Berkas media tidak di enkode dengan benar</string>
+ <string name="media_err_timeout">Waktu habis saat mencoba untuk main</string>
+ <string name="media_err_invalid_progressive_playback">Berkas media tidak bisa dialirkan</string>
+ <string name="media_err_unknown">Berkas media tidak dapat dimainkan dengan pemutar media</string>
+ <string name="media_err_security_ex">Kesalahan keamanan saat mencoba memutar %1$s</string>
+ <string name="media_err_io_ex">Kesalahan masukkan saat mencoba memutar %1$s</string>
+ <string name="media_err_unexpected">Kesalahan tak terduga saat mencoba memutar %1$s</string>
+ <string name="media_rewind_description">Tombol mundur</string>
+ <string name="media_play_pause_description">Tombol main dan jeda</string>
+ <string name="media_forward_description">Tombol maju</string>
+ <string name="auth_trying_to_login">Mencoba untuk masuk...</string>
+ <string name="auth_no_net_conn_title">Tidak ada koneksi internet</string>
+ <string name="auth_nossl_plain_ok_title">Sambungan aman tidak tersedia</string>
+ <string name="auth_connection_established">Sambungan dibuat</string>
+ <string name="auth_testing_connection">Pengetesan koneksi ...</string>
+ <string name="auth_not_configured_title">Konfigurasi server cacat</string>
+ <string name="auth_account_not_new">Akun untuk pengguna dan server yang sama sudah ada dalam perangkat</string>
+ <string name="auth_account_not_the_same">Pengguna yang dimasukkan tidak cocok dengan pengguna akun ini</string>
+ <string name="auth_unknown_error_title">Terjadi kesalahan yang tidak diketahui!</string>
+ <string name="auth_unknown_host_title">Tidak menemukan host</string>
+ <string name="auth_incorrect_path_title">Instansi server tidak ditemukan</string>
+ <string name="auth_timeout_title">Server terlalu lama merespon</string>
+ <string name="auth_incorrect_address_title">Format URL salah</string>
+ <string name="auth_ssl_general_error_title">Menginisiasi SSL gagal</string>
+ <string name="auth_ssl_unverified_server_title">Tidak dapat memverifikasi identitas server SSL</string>
+ <string name="auth_bad_oc_version_title">Versi server tidak dikenal</string>
+ <string name="auth_wrong_connection_title">Tidak dapat membuat koneksi</string>
+ <string name="auth_secure_connection">Sambungan aman dibuat</string>
+ <string name="auth_unauthorized">Nama pengguna dan sandi salah</string>
+ <string name="auth_oauth_error">Otorisasi tidak berhasil</string>
+ <string name="auth_oauth_error_access_denied">Akses ditolak oleh server otorisasi</string>
+ <string name="auth_wtf_reenter_URL">Keadaan tak terduga, silahkan masukkan URL server yang lagi</string>
+ <string name="auth_expired_oauth_token_toast">Otorisasi Anda telah berakhir. Silakan mengotorisasi lagi</string>
+ <string name="auth_expired_basic_auth_toast">Silakan mesukkan sandi saat ini</string>
+ <string name="auth_expired_saml_sso_token_toast">Sesi Anda telah berakhir. Silakan masuk kembali</string>
+ <string name="auth_connecting_auth_server">Menyambungkan ke server otentikasi...</string>
+ <string name="auth_unsupported_auth_method">Server tidak mendukung medote otentikasi ini</string>
+ <string name="auth_unsupported_multiaccount">%1$s tidak mendukung banyak akun </string>
+ <string name="fd_keep_in_sync">Biarkan berkas tetap terbaru</string>
<string name="common_rename">Ubah nama</string>
- <string name="common_remove">hilangkan</string>
- <string name="ssl_validator_btn_details_hide">sembunyikan</string>
+ <string name="common_remove">Hapus</string>
+ <string name="confirmation_remove_alert">Apakah Anda yakin ingin menghapus %1$s ?</string>
+ <string name="confirmation_remove_folder_alert">Apakah Anda benar-benar ingin menghapus %1$s beserta isinya?</string>
+ <string name="confirmation_remove_local">Lokal saja</string>
+ <string name="confirmation_remove_folder_local">Konten lokal saja</string>
+ <string name="confirmation_remove_remote">Hapus dari server</string>
+ <string name="confirmation_remove_remote_and_local">Jarak jauh dan lokal</string>
+ <string name="remove_success_msg">Penghapusan berhasil</string>
+ <string name="remove_fail_msg">Penghapusan gagal</string>
+ <string name="rename_dialog_title">Masukkan nama baru</string>
+ <string name="rename_local_fail_msg">Salinan lokal tidak dapat diubah nama, coba dengan nama yang berbeda</string>
+ <string name="rename_server_fail_msg">Mengubah nama tidak selesai</string>
+ <string name="sync_file_fail_msg">Berkas jauh tidak dapat diperiksa</string>
+ <string name="sync_file_nothing_to_do_msg">Isi berkas sudah diselaraskan</string>
+ <string name="create_dir_fail_msg">Folder tidak dapat dibuat</string>
+ <string name="filename_forbidden_characters">Karakter yang dilarang: / \\ < > : \" | ? *</string>
+ <string name="wait_a_moment">Tunggu sejenak</string>
+ <string name="filedisplay_unexpected_bad_get_content">Masalah tidak terduga, silahkan pilih berkas dari apl yang berbeda</string>
+ <string name="filedisplay_no_file_selected">Tidak ada berkas yang terpilih</string>
+ <string name="oauth_check_onoff">Masuk dengan oAuth2</string>
+ <string name="oauth_login_connection">Menyambungkan ke server oAuth2...</string>
+ <string name="ssl_validator_header">Identitas situs tidak dapat diverfikasi</string>
+ <string name="ssl_validator_reason_cert_not_trusted">- Sertifikat server tidak terpercaya</string>
+ <string name="ssl_validator_reason_cert_expired">- Sertifikat server kadaluarsa</string>
+ <string name="ssl_validator_reason_cert_not_yet_valid">- Tanggal sertifikat server yang valid adalah di masa depan</string>
+ <string name="ssl_validator_reason_hostname_not_verified">- URL tidak cocok dengan nama host dalam sertifikat</string>
+ <string name="ssl_validator_question">Apakah Anda percaya dengan sertifikat ini?</string>
+ <string name="ssl_validator_not_saved">Sertifikat tidak dapat disimpan</string>
+ <string name="ssl_validator_btn_details_see">Rincian</string>
+ <string name="ssl_validator_btn_details_hide">Sembunyikan</string>
+ <string name="ssl_validator_label_subject">Diterbitkan untuk:</string>
+ <string name="ssl_validator_label_issuer">Diterbitkan oleh:</string>
+ <string name="ssl_validator_label_CN">Nama panggilan:</string>
+ <string name="ssl_validator_label_O">Organisasi:</string>
+ <string name="ssl_validator_label_OU">Unit Organisasi:</string>
+ <string name="ssl_validator_label_C">Negara:</string>
+ <string name="ssl_validator_label_ST">Negara bagian:</string>
+ <string name="ssl_validator_label_L">Lokasi:</string>
+ <string name="ssl_validator_label_validity">Masa berlaku:</string>
+ <string name="ssl_validator_label_validity_from">Dari:</string>
+ <string name="ssl_validator_label_validity_to">Untuk:</string>
+ <string name="ssl_validator_label_signature">Tanda tangan:</string>
+ <string name="ssl_validator_label_signature_algorithm">Algoritma:</string>
+ <string name="placeholder_sentence">Ini adalah placeholder</string>
+ <string name="placeholder_filename">placeholder.txt</string>
+ <string name="placeholder_filetype">Gambar PNG</string>
+ <string name="placeholder_filesize">389 KB</string>
+ <string name="placeholder_timestamp">18/05/2012 12:23 PM</string>
+ <string name="placeholder_media_time">12:23:45</string>
+ <string name="instant_upload_on_wifi">Hanya unggah gambar via WiFi</string>
+ <string name="instant_upload_path">/UnggahInstan</string>
+ <string name="conflict_title">Perbarui benturan</string>
+ <string name="conflict_message">Berkas jauh %s tidak sinkron dengan berkas lokal. Melanjutkan akan menggantikan konten berkas di server.</string>
+ <string name="conflict_keep_both">Biarkan keduannya</string>
+ <string name="conflict_overwrite">Timpa</string>
+ <string name="conflict_dont_upload">Jangan mengunggah</string>
+ <string name="preview_image_description">Pratilik gambar</string>
+ <string name="preview_image_error_unknown_format">Gambar ini tidak dapat ditampilkan</string>
+ <string name="error__upload__local_file_not_copied">%1$s tidak dapat disalin ke direktori lokal %2$s</string>
+ <string name="actionbar_failed_instant_upload">UnggahInsatan Gagal</string>
+ <string name="failed_upload_headline_text">Unggahan instan gagal</string>
+ <string name="failed_upload_headline_hint">Ringkasan dari semua unggahan instan yang gagal</string>
+ <string name="failed_upload_all_cb">pilih semua</string>
+ <string name="failed_upload_headline_retryall_btn">Ulangi semua yang terpilih</string>
+ <string name="failed_upload_headline_delete_all_btn">hapus semua yang terpilih dari antrian unggahan</string>
+ <string name="failed_upload_retry_text">ulangi unggah gambar:</string>
+ <string name="failed_upload_load_more_images">Muat Gambar selengkapnya</string>
+ <string name="failed_upload_retry_do_nothing_text">Tidak melakukan apapun, Anda tidak sedang online</string>
+ <string name="failed_upload_failure_text">Pesan Kegagalan:</string>
+ <string name="failed_upload_quota_exceeded_text">Silakan periksa konfigurasi server Anda, kemungkinan kuota terlampaui.</string>
</resources>
<string name="actionbar_settings">Stillingar</string>
<string name="prefs_category_more">Meira</string>
<string name="prefs_help">Hjálp</string>
- <string name="auth_host_url">Host nafn netþjóns</string>
<string name="auth_username">Notendanafn</string>
<string name="auth_password">Lykilorð</string>
<string name="sync_string_files">Skrár</string>
<string name="recommend_subject">Prova %1$s sul tuo smartphone!</string>
<string name="recommend_text">Vorrei invitarti ad usare %1$s sul tuo smartphone!⏎\nScarica qui: %2$s</string>
<string name="auth_check_server">Verifica server</string>
- <string name="auth_host_url">Indirizzo del server</string>
+ <string name="auth_host_url">Indirizzo server https://...</string>
<string name="auth_username">Nome utente</string>
<string name="auth_password">Password</string>
<string name="auth_register">Prima volta su %1$s?</string>
<string name="filedetails_download">Scarica</string>
<string name="filedetails_sync_file">Aggiorna file</string>
<string name="filedetails_renamed_in_upload_msg">Il file è stato rinominato in %1$s durante il caricamento</string>
+ <string name="action_share_file">Condividi collegamento</string>
+ <string name="action_unshare_file">Rimuovi condivisione collegamento</string>
<string name="common_yes">Sì</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Il file remoto non può essere controllato</string>
<string name="sync_file_nothing_to_do_msg">Contenuti del file già sincronizzati</string>
<string name="create_dir_fail_msg">La cartella non può essere creata</string>
+ <string name="filename_forbidden_characters">Caratteri proibiti: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Attendi</string>
<string name="filedisplay_unexpected_bad_get_content">Problema inatteso; prova un\'altra applicazione per selezionare il file</string>
<string name="filedisplay_no_file_selected">Non è stato selezionato alcun file</string>
+ <string name="activity_chooser_title">Invia collegamento a...</string>
<string name="oauth_check_onoff">Accesso con oAuth2.</string>
<string name="oauth_login_connection">Connessione al server oAuth2 in corso...</string>
<string name="ssl_validator_header">L\'identità del sito non può essere verificata</string>
<string name="preview_image_description">Anteprima dell\'immagine</string>
<string name="preview_image_error_unknown_format">Questa immagine non può essere mostrata</string>
<string name="error__upload__local_file_not_copied">%1$s non può essere copiato nella cartella locale %2$s</string>
+ <string name="actionbar_failed_instant_upload">Caricamento istantaneo non riuscito</string>
<string name="failed_upload_headline_text">Caricamenti istantanei non riusciti</string>
<string name="failed_upload_headline_hint">Riepilogo dei caricamenti istantanei non riusciti</string>
<string name="failed_upload_all_cb">seleziona tutto</string>
<string name="failed_upload_retry_do_nothing_text">non fare niente, non sei collegato per i caricamenti istantanei</string>
<string name="failed_upload_failure_text">Messaggio d\'errore:</string>
<string name="failed_upload_quota_exceeded_text">Controlla la configurazione del server, forse hai superato la tua quota.</string>
+ <string name="share_link_no_support_share_api">Spiacenti, la condivisione non è abilitata sul server. Contatta il tuo amministratore.</string>
+ <string name="share_link_file_no_exist">Impossibile condividere il file o la cartella. Assicurati che esista.</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_error">Si è verificato un errore durante il tentativo di rimuovere la condivisione del file o della cartella</string>
</resources>
<string name="prefs_pincode_summary">クライアントを保護する</string>
<string name="prefs_instant_upload">自動アップロードを有効</string>
<string name="prefs_instant_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_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="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">ã\82µã\83¼ã\83\90ã\82¢ã\83\89ã\83¬ã\82¹</string>
+ <string name="auth_host_url">ã\82µã\83¼ã\83\90ã\83¼ã\82¢ã\83\89ã\83¬ã\82¹ https://â\80¦</string>
<string name="auth_username">ユーザー名</string>
<string name="auth_password">パスワード</string>
<string name="auth_register">%1$sは初めてですか?</string>
<string name="uploader_wrn_no_content_text">コンテンツを受信しませんでした。アップロードするものはありません。</string>
<string name="uploader_error_forbidden_content">%1$sで共有コンテンツへのアクセスが許可されていません。</string>
<string name="uploader_info_uploading">アップロード中</string>
- <string name="file_list_empty">このフォルダにはファイルがありません。\n\"アップロード\" メニューで新しいファイルを追加することができます。</string>
+ <string name="file_list_empty">ã\81\93ã\81®ã\83\95ã\82©ã\83«ã\83\80ã\83¼ã\81«ã\81¯ã\83\95ã\82¡ã\82¤ã\83«ã\81\8cã\81\82ã\82\8aã\81¾ã\81\9bã\82\93ã\80\82\n\"ã\82¢ã\83\83ã\83\97ã\83ã\83¼ã\83\89\" ã\83¡ã\83\8bã\83¥ã\83¼ã\81§æ\96°ã\81\97ã\81\84ã\83\95ã\82¡ã\82¤ã\83«ã\82\92追å\8a ã\81\99ã\82\8bã\81\93ã\81¨ã\81\8cã\81§ã\81\8dã\81¾ã\81\99ã\80\82</string>
<string name="filedetails_select_file">ファイルをタップすると追加情報が表示されます。</string>
<string name="filedetails_size">サイズ:</string>
<string name="filedetails_type">タイプ:</string>
<string name="filedetails_download">ダウンロード</string>
<string name="filedetails_sync_file">ファイルを同期</string>
<string name="filedetails_renamed_in_upload_msg">アップロード中にファイル名を %1$s に変更しました</string>
+ <string name="action_share_file">URLで共有</string>
<string name="common_yes">はい</string>
<string name="common_no">いいえ</string>
<string name="common_ok">OK</string>
<string name="sync_conflicts_in_favourites_content">%1$d 同期ファイルを同期できませんでした</string>
<string name="sync_fail_in_favourites_ticker">ファイルの同期に失敗しました</string>
<string name="sync_fail_in_favourites_content">%1$d ファイルのコンテンツを同期できませんでした(%2$d の競合)</string>
- <string name="sync_foreign_files_forgotten_ticker">いくつかのローカルファイルが忘れられています</string>
+ <string name="sync_foreign_files_forgotten_ticker">一部のローカルファイルが忘れられています</string>
<string name="sync_foreign_files_forgotten_content">%2$s ディレクトリ内の %1$d ファイルはコピーすることができませんでした</string>
- <string name="sync_foreign_files_forgotten_explanation">\"ã\83\90ã\83¼ã\82¸ã\83§ã\83³ 1.3.16ã\81\8bã\82\89ã\80\81ã\81\93ã\81®ã\83\87ã\83\90ã\82¤ã\82¹ã\81\8bã\82\89ã\82¢ã\83\83ã\83\97ã\83ã\83¼ã\83\89ã\81\95ã\82\8cã\81\9fã\83\95ã\82¡ã\82¤ã\83«ã\81¯ã\80\81å\8d\98ç\8b¬ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81\8cè¤\87æ\95°ã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81¨å\90\8cæ\9c\9fã\81\95ã\82\8cã\82\8bæ\99\82ã\81«ã\83\87ã\83¼ã\82¿ã\81®æ\90\8d失ã\82\92é\98²ã\81\90ã\81\9fã\82\81ã\80\81ã\83ã\83¼ã\82«ã\83«ã\81®%1$sã\81®ã\83\95ã\82©ã\83«ã\83\80ã\81«ã\82³ã\83\94ã\83¼ã\81\95ã\82\8cã\81¾ã\81\99ã\80\82\n\nã\81\93ã\81®å¤\89æ\9b´ã\81«ã\82\88ã\82\8aã\80\81ã\81\93ã\81®ã\82¢ã\83\97ã\83ªã\81®ä»¥å\89\8dã\81®ã\83\90ã\83¼ã\82¸ã\83§ã\83³ã\81§ã\82¢ã\83\83ã\83\97ã\83ã\83¼ã\83\89ã\81\95ã\82\8cã\81\9fã\81\99ã\81¹ã\81¦ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81\8c%2$s ã\83\95ã\82©ã\83«ã\83\80にコピーされます。ただし、アカウント同期の際に、エラーがこの操作の完了を阻止しました。このままファイルを残し、%3$sへのリンクを削除するか、あるいは%1$s ディレクトリのファイルを移動し、%4$sへのリンクを維持することができます。\n\n以下にローカルのファイルと、それにリンクしていた%5$sのリモートファイルがリストされています</string>
- <string name="sync_current_folder_was_removed">フォルダ %1$s はもう存在しません</string>
+ <string name="sync_foreign_files_forgotten_explanation">\"ã\83\90ã\83¼ã\82¸ã\83§ã\83³ 1.3.16ã\81\8bã\82\89ã\80\81ã\81\93ã\81®ã\83\87ã\83\90ã\82¤ã\82¹ã\81\8bã\82\89ã\82¢ã\83\83ã\83\97ã\83ã\83¼ã\83\89ã\81\95ã\82\8cã\81\9fã\83\95ã\82¡ã\82¤ã\83«ã\81¯ã\80\81å\8d\98ç\8b¬ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81\8cè¤\87æ\95°ã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81¨å\90\8cæ\9c\9fã\81\95ã\82\8cã\82\8bæ\99\82ã\81«ã\83\87ã\83¼ã\82¿ã\81®æ\90\8d失ã\82\92é\98²ã\81\90ã\81\9fã\82\81ã\80\81ã\83ã\83¼ã\82«ã\83«ã\81®%1$sã\81®ã\83\95ã\82©ã\83«ã\83\80ã\83¼ã\81«ã\82³ã\83\94ã\83¼ã\81\95ã\82\8cã\81¾ã\81\99ã\80\82\n\nã\81\93ã\81®å¤\89æ\9b´ã\81«ã\82\88ã\82\8aã\80\81ã\81\93ã\81®ã\82¢ã\83\97ã\83ªã\81®ä»¥å\89\8dã\81®ã\83\90ã\83¼ã\82¸ã\83§ã\83³ã\81§ã\82¢ã\83\83ã\83\97ã\83ã\83¼ã\83\89ã\81\95ã\82\8cã\81\9fã\81\99ã\81¹ã\81¦ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81\8c%2$s ã\83\95ã\82©ã\83«ã\83\80ã\83¼にコピーされます。ただし、アカウント同期の際に、エラーがこの操作の完了を阻止しました。このままファイルを残し、%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_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 ã\83\95ã\82©ã\83«ã\83\80ã\81«é\81¸æ\8a\9eã\81\95ã\82\8cã\81\9fã\83\95ã\82¡ã\82¤ã\83«ã\82\92ã\82³ã\83\94ã\83¼ã\81\99ã\82\8bã\81®ã\81«å\8d\81å\88\86ã\81ªã\82¹ã\83\9aã\83¼ã\82¹がありません。コピーする代わりに、それらを移動させますか?</string>
+ <string name="upload_query_move_foreign_files">%1$s ã\83\95ã\82©ã\83«ã\83\80ã\83¼ã\81«é\81¸æ\8a\9eã\81\95ã\82\8cã\81\9fã\83\95ã\82¡ã\82¤ã\83«ã\82\92ã\82³ã\83\94ã\83¼ã\81\99ã\82\8bã\81®ã\81«å\8d\81å\88\86ã\81ªç©ºã\81\8dé \98å\9f\9fがありません。コピーする代わりに、それらを移動させますか?</string>
<string name="pincode_enter_pin_code">アプリのパスワードを入力してください</string>
<string name="pincode_configure_your_pin">アプリのパスワードを入力してください</string>
<string name="pincode_configure_your_pin_explanation">アプリ開始時に毎回PINが要求されます。</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">メディアファイルをStock Media Playerでプレイ出来ません</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_err_unknown">メディアファイルをStock Media Playerで再生できません</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_play_pause_description">再生/一時停止ボタン</string>
<string name="media_forward_description">早送りボタン</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_testing_connection">接続をテスト中...</string>
- <string name="auth_not_configured_title">サーバーの間違った設定</string>
- <string name="auth_account_not_new">å\90\8cã\81\98ã\83¦ã\83¼ã\82¶ã\81¨ã\82µã\83¼ã\83\90のアカウントがデバイス上にすでに存在します</string>
- <string name="auth_account_not_the_same">å\85¥å\8a\9bã\81\95ã\82\8cã\81\9fã\83¦ã\83¼ã\82¶ã\81¯ã\81\93ã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\83¦ã\83¼ã\82¶と一致しません</string>
+ <string name="auth_not_configured_title">サーバー設定が間違っています</string>
+ <string name="auth_account_not_new">å\90\8cã\81\98ã\83¦ã\83¼ã\82¶ã\83¼ã\81¨ã\82µã\83¼ã\83\90ã\83¼のアカウントがデバイス上にすでに存在します</string>
+ <string name="auth_account_not_the_same">å\85¥å\8a\9bã\81\95ã\82\8cã\81\9fã\83¦ã\83¼ã\82¶ã\83¼ã\81¯ã\81\93ã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\83¦ã\83¼ã\82¶ã\83¼と一致しません</string>
<string name="auth_unknown_error_title">不明なエラーに発生しました</string>
<string name="auth_unknown_host_title">ホストが見つかりませんでした</string>
- <string name="auth_incorrect_path_title">ã\81®ã\82¤ã\83³ã\82¹ã\82¿ã\83³ã\82¹ã\81\8cè¦\8bã\81¤ã\81\8bã\82\8aã\81¾ã\81\9bã\82\93ã\81§ã\81\97ã\81\9f</string>
- <string name="auth_timeout_title">サーバーからの反応がありません。</string>
+ <string name="auth_incorrect_path_title">ã\82µã\83¼ã\83\90ã\83¼ã\81®ã\82¤ã\83³ã\82¹ã\82¿ã\83³ã\82¹ã\81\8cè¦\8bã\81¤ã\81\8bã\82\8aã\81¾ã\81\9bã\82\93</string>
+ <string name="auth_timeout_title">サーバーからの反応がありません</string>
<string name="auth_incorrect_address_title">不明なURL形式</string>
<string name="auth_ssl_general_error_title">SSLの初期化に失敗しました</string>
- <string name="auth_ssl_unverified_server_title">SSL サーバ識別子を確認できませんでした</string>
- <string name="auth_bad_oc_version_title">認識出来ないサーバのバージョンです</string>
+ <string name="auth_ssl_unverified_server_title">SSLサーバー識別子を確認できませんでした</string>
+ <string name="auth_bad_oc_version_title">認識できないサーバーのバージョンです</string>
<string name="auth_wrong_connection_title">接続を確立できませんでした</string>
<string name="auth_secure_connection">暗号化通信を確立しました</string>
<string name="auth_unauthorized">間違ったユーザー名もしくはパスワード</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_connecting_auth_server">èª\8d証ã\82µã\83¼ã\83\90ã\83¼ã\81«æ\8e¥ç¶\9aä¸ ...</string>
+ <string name="auth_unsupported_auth_method">ã\82µã\83¼ã\83\90ã\83¼ã\81¯ã\81\93ã\81®èª\8d証æ\96¹å¼\8fã\82\92ã\82µã\83\9dã\83¼ã\83\88ã\81\97ã\81¦ã\81\84ã\81¾ã\81\9bã\82\93</string>
<string name="auth_unsupported_multiaccount">%1$s は複数アカウントをサポートしていません</string>
<string name="fd_keep_in_sync">ファイルを最新に保つ</string>
<string name="common_rename">名前を変更</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">ã\82µã\83¼ã\83\90ã\83¼ã\81\8bã\82\89å\89\8aé\99¤</string>
<string name="confirmation_remove_remote_and_local">リモートとローカルの両方</string>
<string name="remove_success_msg">削除に成功しました</string>
<string name="remove_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="wait_a_moment">しばらくお待ちください</string>
<string name="filedisplay_unexpected_bad_get_content">予期せぬ問題;他のアプリでファイルを選択してみてください。</string>
<string name="filedisplay_no_file_selected">ファイルは選択されていません</string>
- <string name="oauth_check_onoff">oAuth2 でログイン</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">- サーバ証明書は信頼されていません</string>
- <string name="ssl_validator_reason_cert_expired">- サーバ証明書は有効期限切れです</string>
- <string name="ssl_validator_reason_cert_not_yet_valid">- サーバ証明書は若すぎます</string>
+ <string name="ssl_validator_reason_cert_not_trusted">- サーバー証明書は信頼されていません</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">- URLは証明書内のホスト名と一致しません</string>
<string name="ssl_validator_question">この証明書を信頼してもよろしいですか?</string>
<string name="ssl_validator_not_saved">証明書は保存できませんでした</string>
<string name="placeholder_media_time">12:23:45</string>
<string name="instant_upload_on_wifi">WiFi経由でのみ写真をアップロード</string>
<string name="instant_upload_path">/InstantUpload</string>
- <string name="conflict_title">æ\9b´æ\96°ã\81®競合</string>
- <string name="conflict_message">リモートファイル %s はローカルファイルと同期していません。続行すると、サーバ上のファイルを置き換えます。</string>
+ <string name="conflict_title">æ\9b´æ\96°ã\81\8c競合</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="error__upload__local_file_not_copied">%1$s は %2$s ローカルディレクトリにコピーできませんでした</string>
+ <string name="actionbar_failed_instant_upload">インスタントアップロードに失敗</string>
<string name="failed_upload_headline_text">インスタントアップロードに失敗</string>
<string name="failed_upload_headline_hint">全ての失敗したインスタントアップロードの要約</string>
<string name="failed_upload_all_cb">すべて選択</string>
<string name="failed_upload_load_more_images">更に画像を読み込む</string>
<string name="failed_upload_retry_do_nothing_text">オンラインでなく、インスタントアップロードのために何もしません</string>
<string name="failed_upload_failure_text">失敗メッセージ:</string>
- <string name="failed_upload_quota_exceeded_text">サーバーの設定を確認してください。許容を超過している可能性があります。</string>
+ <string name="failed_upload_quota_exceeded_text">サーバー設定を確認してください。クォータサイズを超えている可能性があります。</string>
</resources>
<string name="prefs_help">დახმარება</string>
<string name="prefs_feedback">უკუკავშირი</string>
<string name="prefs_imprint">ბეჭედი</string>
- <string name="auth_host_url">სერვერის მისამართი</string>
<string name="auth_username">მომხმარებლის სახელი</string>
<string name="auth_password">პაროლი</string>
<string name="sync_string_files">ფაილები</string>
<string name="prefs_feedback">피드백</string>
<string name="prefs_imprint">임프린트</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">서버 주소</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="filedetails_download">다운로드</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="common_yes">예</string>
<string name="common_no">아니요</string>
<string name="common_ok">확인</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="auth_connection_established">연결됨</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="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="wait_a_moment">잠시 기다려 주십시오</string>
<string name="filedisplay_unexpected_bad_get_content">예상하지 못한 오류입니다. 다른 앱에서 파일을 선택하십시오</string>
<string name="filedisplay_no_file_selected">선택한 파일 없음</string>
<string name="ssl_validator_label_signature_algorithm">알고리즘:</string>
<string name="placeholder_sentence">이것은 플레이스홀더입니다</string>
<string name="placeholder_filename">placeholder.txt</string>
- <string name="placeholder_filetype">PNG í\8c\8cì\9d¼</string>
+ <string name="placeholder_filetype">PNG 그림</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="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="actionbar_failed_instant_upload">자동 업로드가 실패했습니다</string>
<string name="failed_upload_headline_text">자동 업로드 실패함</string>
<string name="failed_upload_headline_hint">실패된 자동 업로드 전체 요약</string>
<string name="failed_upload_all_cb">전체 선택</string>
<string name="failed_upload_headline_delete_all_btn">선택된 업로드 큐 전체 삭제</string>
<string name="failed_upload_retry_text">이미지 업로드 재시도:</string>
<string name="failed_upload_load_more_images">더 많은 사진 불러오기</string>
+ <string name="failed_upload_retry_do_nothing_text">현재 온라인이 아니셔서 자동 업로드를 할수 없습니다</string>
<string name="failed_upload_failure_text">실패 메시지:</string>
<string name="failed_upload_quota_exceeded_text">서버 설정을 확인해주세요, 아마 업로드 제한을 초과하셨을겁니다.</string>
</resources>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="actionbar_upload">بارکردن</string>
+ <string name="actionbar_upload_files">پهڕگەکان</string>
+ <string name="actionbar_mkdir">درووستکردنى بوخچە</string>
<string name="actionbar_settings">دهستكاری</string>
+ <string name="prefs_category_general">گشتی</string>
+ <string name="prefs_accounts">هەژمارەکان</string>
<string name="prefs_help">یارمەتی</string>
- <string name="auth_host_url">ناونیشانی ڕاژه</string>
<string name="auth_username">ناوی بهکارهێنهر</string>
<string name="auth_password">وشەی تێپەربو</string>
+ <string name="sync_string_files">پهڕگەکان</string>
+ <string name="setup_btn_connect">بەستنەوە</string>
<string name="uploader_btn_upload_text">بارکردن</string>
+ <string name="uploader_wrn_no_account_title">هیچ هەژمارێک نهدۆزرایهوه</string>
+ <string name="uploader_wrn_no_account_setup_btn_text">جێگیرکردن</string>
+ <string name="uploader_wrn_no_account_quit_btn_text">دەرچوون</string>
+ <string name="uploader_info_uploading">بارکردن</string>
+ <string name="filedetails_size">قەبارە:</string>
+ <string name="filedetails_type">جۆر:</string>
+ <string name="filedetails_created">درووستبووە:</string>
+ <string name="filedetails_modified">گۆردراو:</string>
<string name="filedetails_download">داگرتن</string>
+ <string name="common_yes">بەڵێ</string>
+ <string name="common_no">نەخێر</string>
+ <string name="common_ok">باشە</string>
+ <string name="common_cancel">لابردن</string>
+ <string name="common_save_exit">پاشکەوتکردن & دەرچوون</string>
<string name="common_error">ههڵه</string>
</resources>
<string name="prefs_pincode">App PIN</string>
<string name="prefs_help">Hëllef</string>
<string name="prefs_feedback">Feedback</string>
- <string name="auth_host_url">Server Adress</string>
<string name="auth_username">Benotzernumm</string>
<string name="auth_password">Passwuert</string>
<string name="sync_string_files">Dateien</string>
<string name="filedetails_created">Erstallt:</string>
<string name="filedetails_modified">Geännert:</string>
<string name="filedetails_download">Download</string>
+ <string name="action_share_file">Link deelen</string>
<string name="common_yes">Jo</string>
<string name="common_no">Nee</string>
<string name="common_ok">OK</string>
<string name="recommend_subject">Išbandykite %1$s savo išmaniajame telefone!</string>
<string name="recommend_text">Siūlau pabandyti %1$s savo išmaniajame telefone!\nParsisiųskite štai čia: %2$s</string>
<string name="auth_check_server">Patikrinti Serverį</string>
- <string name="auth_host_url">Serverio adresas</string>
<string name="auth_username">Prisijungimo vardas</string>
<string name="auth_password">Slaptažodis</string>
<string name="sync_string_files">Failai</string>
<string name="filedetails_download">Atsisiųsti</string>
<string name="filedetails_sync_file">Atnaujinti failą</string>
<string name="filedetails_renamed_in_upload_msg">Įkėlimo metu failas buvo pervadintas į %1$s</string>
+ <string name="action_share_file">Dalintis nuoroda</string>
<string name="common_yes">Taip</string>
<string name="common_no">Ne</string>
<string name="common_ok">Gerai</string>
<string name="prefs_instant_upload">Aktivēt tūlītējo augšupielādēšanu</string>
<string name="prefs_instant_upload_summary">Nekavējoties augšupielādēt kameras uzņemtos attēlus</string>
<string name="prefs_help">Palīdzība</string>
- <string name="auth_host_url">Servera adrese</string>
<string name="auth_username">Lietotājvārds</string>
<string name="auth_password">Parole</string>
<string name="sync_string_files">Datnes</string>
<string name="prefs_help">Помош</string>
<string name="prefs_recommend">Препорачај на пријател</string>
<string name="prefs_feedback">Повратен одговор</string>
- <string name="auth_host_url">Адреса на сервер</string>
<string name="auth_username">Корисничко име</string>
<string name="auth_password">Лозинка</string>
<string name="sync_string_files">Датотеки</string>
<string name="filedetails_created">Создадено:</string>
<string name="filedetails_modified">Изменето:</string>
<string name="filedetails_download">Преземање</string>
+ <string name="action_share_file">Сподели ја врската</string>
<string name="common_yes">Да</string>
<string name="common_no">Не</string>
<string name="common_ok">Во ред</string>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<string name="prefs_accounts">Akaun</string>
<string name="prefs_pincode">PIN App</string>
<string name="prefs_help">Bantuan</string>
- <string name="auth_host_url">Alamat pelayan</string>
<string name="auth_username">Nama pengguna</string>
<string name="auth_password">Kata laluan</string>
<string name="sync_string_files">Fail-fail</string>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
+ <string name="about_android">%1$s Andriod app</string>
+ <string name="about_version">versjon %1$s</string>
+ <string name="actionbar_sync">Oppdater konto</string>
<string name="actionbar_upload">Last opp</string>
<string name="actionbar_upload_from_apps">Innhold fra andre applikasjoner</string>
<string name="actionbar_upload_files">Filer</string>
+ <string name="actionbar_open_with">Åpne med</string>
<string name="actionbar_mkdir">Opprett katalog</string>
<string name="actionbar_settings">Innstillinger</string>
+ <string name="actionbar_see_details">Detaljer</string>
<string name="prefs_category_general">Generelt</string>
<string name="prefs_category_more">Mer</string>
<string name="prefs_accounts">Kontoer</string>
+ <string name="prefs_manage_accounts">Håndter kontoer</string>
+ <string name="prefs_pincode">PIN kode</string>
+ <string name="prefs_pincode_summary">Beskytt klienten din</string>
+ <string name="prefs_instant_upload">Aktiver direkte opplastinger</string>
<string name="prefs_instant_upload_summary">Last opp bilder tatt med kamera øyeblikkelig</string>
+ <string name="prefs_log_title">Aktiver loggføring</string>
+ <string name="prefs_log_summary">Denne er brukt til å loggføre problemer</string>
+ <string name="prefs_log_title_history">Loggføringshistorikk</string>
+ <string name="prefs_log_summary_history">Denne viser de lagrede loggene</string>
+ <string name="prefs_log_delete_history_button">Slett historikk</string>
<string name="prefs_help">Hjelp</string>
- <string name="auth_host_url">Server-adresse</string>
+ <string name="prefs_recommend">Anbefal til en venn</string>
+ <string name="prefs_feedback">Tilbakemelding</string>
+ <string name="prefs_imprint">Avtrykk</string>
+ <string name="recommend_subject">Prøv %1$s på smarttelefonen din!</string>
+ <string name="recommend_text">Jeg vil gjerne invitere deg til å bruke %1$s på smarttelefonen din!\nLast ned her: %2$s</string>
+ <string name="auth_check_server">Sjekk server</string>
+ <string name="auth_host_url">Serveradresse https://...</string>
<string name="auth_username">Brukernavn</string>
<string name="auth_password">Passord</string>
+ <string name="auth_register">Ny med %1$s?</string>
<string name="sync_string_files">Filer</string>
<string name="setup_btn_connect">Koble til</string>
<string name="uploader_btn_upload_text">Last opp</string>
+ <string name="uploader_top_message">Velg opplastingsmappe:</string>
<string name="uploader_wrn_no_account_title">Ingen konto funnet</string>
+ <string name="uploader_wrn_no_account_text">Det finnes ingen %1$s kontoer for din enhent. For å bruker denne appen må du først opprette en.</string>
<string name="uploader_wrn_no_account_setup_btn_text">Oppsett</string>
<string name="uploader_wrn_no_account_quit_btn_text">Avslutt</string>
<string name="uploader_wrn_no_content_title">Intet innhold å laste opp</string>
<string name="uploader_wrn_no_content_text">Intet innhold ble mottatt. Intet å laste opp.</string>
+ <string name="uploader_error_forbidden_content">%1$s har ikke tilgang til det delte innholdet</string>
<string name="uploader_info_uploading">Laster opp</string>
+ <string name="file_list_empty">Det finnes ikke filer i mappen.\nNye filer kan legges til med \"last opp\" i menyen.</string>
<string name="filedetails_select_file">Trykk på en fil for å vise ekstra informasjon.</string>
<string name="filedetails_size">Størrelse:</string>
<string name="filedetails_type">Type:</string>
<string name="filedetails_created">Opprettet:</string>
<string name="filedetails_modified">Endret:</string>
<string name="filedetails_download">Last ned</string>
+ <string name="filedetails_sync_file">Oppdater fil</string>
+ <string name="filedetails_renamed_in_upload_msg">Filnavnet ble endret til %1$s under opplasting</string>
+ <string name="action_share_file">Del lenke</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nei</string>
<string name="common_ok">OK</string>
<string name="common_cancel">Avbryt</string>
<string name="common_save_exit">Lagre og avslutt</string>
<string name="common_error">Feil</string>
+ <string name="common_loading">Laster...</string>
+ <string name="common_error_unknown">Ukjent feil</string>
<string name="about_title">Om</string>
<string name="change_password">Endre passord</string>
<string name="delete_account">Slett konto</string>
<string name="uploader_upload_in_progress_ticker">Laster opp...</string>
<string name="uploader_upload_in_progress_content">%1$d%% Laster opp %2$s</string>
<string name="uploader_upload_succeeded_ticker">Opplasting fullført</string>
+ <string name="uploader_upload_succeeded_content_single">%1$s ble lastet opp</string>
<string name="uploader_upload_failed_ticker">Opplasting feilet</string>
<string name="uploader_upload_failed_content_single">Opplasting av %1$s kunne ikke fullføres</string>
<string name="downloader_download_in_progress_ticker">Laster ned...</string>
<string name="downloader_download_in_progress_content">%1$d%% Laster ned %2$s</string>
<string name="downloader_download_succeeded_ticker">Nedlasting fullført</string>
+ <string name="downloader_download_succeeded_content">%1$s ble lastet ned</string>
<string name="downloader_download_failed_ticker">Nedlasting feilet</string>
<string name="downloader_download_failed_content">Nedlasting av %1$s kunne ikke fullføres</string>
+ <string name="downloader_not_downloaded_yet">Ikke lastet ned enda</string>
<string name="common_choose_account">Velg konto</string>
<string name="sync_fail_ticker">Synkronisering feilet</string>
<string name="sync_fail_content">Synkronisering av %1$s kunne ikke fullføres</string>
+ <string name="sync_fail_content_unauthorized">Ugyldig passord for %1$s</string>
<string name="sync_conflicts_in_favourites_ticker">Konflikter funnet</string>
+ <string name="sync_conflicts_in_favourites_content">%1$d hold-i-synk filer kunne ikke synkroniseres</string>
+ <string name="sync_fail_in_favourites_ticker">Hold i synk filer mislyktes</string>
+ <string name="sync_fail_in_favourites_content">Contents of %1$d files could not be sync\'ed (%2$d conflicts)\nInnhold av %1$d filer kunne ikke synkroniseres (%2$d konflikter)</string>
+ <string name="sync_foreign_files_forgotten_ticker">Noen lokale filer ble glemt</string>
+ <string name="sync_foreign_files_forgotten_content">%1$d filer av %2$s mappen kunne ikke kopieres til</string>
+ <string name="sync_foreign_files_forgotten_explanation">Fra versjon 1.3.16 blir filer lastet opp fra denne enheten kopiert til den lokale %1$s mappen for å forhindre tap av data når én fil blir synkronisert over flere kontoer.\n\nPå grunn av denne forandringern ble alle filer lastet opp i tidligere versjoner av denne appen kopiert til %2$s mappen. Imidlertid ble fullføring av denne operasjonen forhindet av en feil, under kontosynkronisering. Du kan enten la filen(e) være som de er, og fjerne koblinken til %3$s, eller flytte filen(e) til %1$s mappe, og opprettholde koblingen til %4$s.\n\nListet under er de lokale filen(e), og de eksterne filen(e) i %5$s de var kolet til.</string>
+ <string name="sync_current_folder_was_removed">Mappen %1$s finnes ikke lengere</string>
+ <string name="foreign_files_move">Flytt alle</string>
+ <string name="foreign_files_success">Alle filer ble flyttet</string>
+ <string name="foreign_files_fail">Noen filer kunne ikke fjernes</string>
+ <string name="foreign_files_local_text">Lokal: %1$s</string>
+ <string name="foreign_files_remote_text">Ekstern: %1$s</string>
+ <string name="upload_query_move_foreign_files">Det er ikke nok plass til å kopiere de valgte filene til %1$s mappe. Vil du flytte dem i stedet?</string>
<string name="pincode_enter_pin_code">Vennligst tast inn din App-PIN</string>
+ <string name="pincode_configure_your_pin">Skriv inn din PIN kode</string>
+ <string name="pincode_configure_your_pin_explanation">PIN koden vil bli ettersourt hver gang appen starter</string>
+ <string name="pincode_reenter_your_pincode">Vennligst tast inn din PIN kode på nytt</string>
+ <string name="pincode_remove_your_pincode">Fjern din PIN kode</string>
+ <string name="pincode_mismatch">PIN kodene du tastet er ulike</string>
+ <string name="pincode_wrong">Feil PIN kode</string>
+ <string name="pincode_removed">PIN kode fjernet</string>
+ <string name="pincode_stored">PIN kode lagret</string>
+ <string name="media_notif_ticker">%1$s musikkspiller</string>
+ <string name="media_state_playing">%1$s (spiller)</string>
+ <string name="media_state_loading">%1$s (laster)</string>
+ <string name="media_event_done">%1$s avspilling avsluttet</string>
+ <string name="media_err_nothing_to_play">Ingen mediafil funnet</string>
+ <string name="media_err_no_account">Ingen konto angitt</string>
+ <string name="media_err_not_in_owncloud">Filen er ikke i en gyldig konto</string>
+ <string name="media_err_unsupported">Mediakodek er ikke støttet</string>
+ <string name="media_err_io">Mediafilen kunne ikke leses</string>
+ <string name="media_err_malformed">Mediafilen er ikke riktig kodet</string>
+ <string name="media_err_timeout">Tidsavbrudd under avspillingsforsøk</string>
+ <string name="media_err_invalid_progressive_playback">Mediafilen kan ikke strømmes</string>
+ <string name="media_err_unknown">Mediafilen kan ikke spilles med standard mediaspiller</string>
+ <string name="media_err_security_ex">Sikkerhetsfeil under avspilling av %1$s</string>
+ <string name="media_err_io_ex">Inputfeil under avspilling av %1$s</string>
+ <string name="media_err_unexpected">Uforventet feil under avspilling av %1$s</string>
+ <string name="media_rewind_description">Spol tilbake</string>
+ <string name="media_play_pause_description">Spill eller pause</string>
+ <string name="media_forward_description">Spol fremover</string>
<string name="auth_trying_to_login">Prøver å logge inn...</string>
<string name="auth_no_net_conn_title">Ingen nettverkstilkobling</string>
<string name="auth_nossl_plain_ok_title">Sikker tilkobling ikke tilgjengelig.</string>
<string name="auth_connection_established">Tilkobling opprettet</string>
<string name="auth_testing_connection">Tester tilgang...</string>
+ <string name="auth_not_configured_title">Feil i server konfigurasjon</string>
+ <string name="auth_account_not_new">En konto for samme bruker og server finnes allerede på enheten</string>
+ <string name="auth_account_not_the_same">Den innskrevne brukeren matcher ikke brukeren av denne kontoen</string>
<string name="auth_unknown_error_title">Ukjent feil oppstod!</string>
<string name="auth_unknown_host_title">Fant ikke tjener</string>
+ <string name="auth_incorrect_path_title">Finner ikke server instans</string>
<string name="auth_timeout_title">Serveren brukte for lang tid på å svare</string>
<string name="auth_incorrect_address_title">Feil formatert URL</string>
<string name="auth_ssl_general_error_title">Oppstart av SSL feilet</string>
+ <string name="auth_ssl_unverified_server_title">Kunne ikke verifisere SSL-serverens identitet</string>
+ <string name="auth_bad_oc_version_title">Ukjent server versjon</string>
<string name="auth_wrong_connection_title">Klarte ikke å opprette tilkobling</string>
<string name="auth_secure_connection">Sikker tilkobling opprettet</string>
+ <string name="auth_unauthorized">Feil brukernavn eller passord</string>
+ <string name="auth_oauth_error">Mislykket autorisasjon</string>
+ <string name="auth_oauth_error_access_denied">Tilgang nektet av autorisasjonsserver</string>
+ <string name="auth_wtf_reenter_URL">Uforventet tilstand; vennligst skriv inn serveradressen en gang til</string>
+ <string name="auth_expired_oauth_token_toast">Autorisasjonen din har gått ut. Vennligt autoriser igjen</string>
+ <string name="auth_expired_basic_auth_toast">Vennligst skriv inn gjeldende passord</string>
+ <string name="auth_expired_saml_sso_token_toast">Sesjonen din har gått ut. Vennligst koble til igjen</string>
+ <string name="auth_connecting_auth_server">Kobler til autorisasjonsserver...</string>
+ <string name="auth_unsupported_auth_method">Serveren støtter ikke denne autorisasjonsmetoden</string>
+ <string name="auth_unsupported_multiaccount">%1$s støtter ikke flere kontoer</string>
<string name="fd_keep_in_sync">Hold filen oppdatert</string>
<string name="common_rename">Endre navn</string>
<string name="common_remove">Fjern</string>
<string name="confirmation_remove_alert">Er du sikker på at du vil fjerne %1$s ?</string>
+ <string name="confirmation_remove_folder_alert">Vil du virkelig fjerne %1$s og dens innhold?</string>
<string name="confirmation_remove_local">Kun lokalt</string>
+ <string name="confirmation_remove_folder_local">Kun lokalt innhold</string>
<string name="confirmation_remove_remote">Fjern fra server</string>
+ <string name="confirmation_remove_remote_and_local">Ekstern og lokal</string>
+ <string name="remove_success_msg">Fjerning var vellykket</string>
+ <string name="remove_fail_msg">Fjerning mislyktes</string>
+ <string name="rename_dialog_title">Skriv inn et nytt navn</string>
+ <string name="rename_local_fail_msg">Lokal kopi kunne ikke endre navn; prøv et annet navn</string>
<string name="rename_server_fail_msg">Klarte ikke å endre navn</string>
+ <string name="sync_file_fail_msg">Eksterne filer kunne ikke sjekkes</string>
+ <string name="sync_file_nothing_to_do_msg">filinnhold er allerede synkronisert</string>
<string name="create_dir_fail_msg">Mappe kunne ikke opprettes</string>
+ <string name="filename_forbidden_characters">Forbudte tegn: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Vent et øyeblikk</string>
+ <string name="filedisplay_unexpected_bad_get_content">Uforventet problem; vennligst velg filen fra en annen applikasjon</string>
<string name="filedisplay_no_file_selected">Ingen fil ble valgt</string>
+ <string name="oauth_check_onoff">Logg inn med oAuth2</string>
+ <string name="oauth_login_connection">Kobler til oAuth2 server...</string>
<string name="ssl_validator_header">Identiteten til siden kunne ikke verifiseres</string>
<string name="ssl_validator_reason_cert_not_trusted">- Serverens sertifikat er ikke til å stole på</string>
<string name="ssl_validator_reason_cert_expired">- Serverens sertifikat er utløpt</string>
+ <string name="ssl_validator_reason_cert_not_yet_valid">- Server-sertifikatets gyldige datoer er i fremtiden</string>
+ <string name="ssl_validator_reason_hostname_not_verified">- Nettadressen samsvarer ikke med vertsnavnet i sertifikatet</string>
+ <string name="ssl_validator_question">Vil du stole på dette sertifikatet likevel?</string>
+ <string name="ssl_validator_not_saved">Sertifikatet kunne ikke lagres</string>
<string name="ssl_validator_btn_details_see">Detaljer</string>
<string name="ssl_validator_btn_details_hide">Skjul</string>
+ <string name="ssl_validator_label_subject">Utstedt til:</string>
+ <string name="ssl_validator_label_issuer">Utstedt av:</string>
+ <string name="ssl_validator_label_CN">Vanlig navn:</string>
+ <string name="ssl_validator_label_O">Organisasjon:</string>
+ <string name="ssl_validator_label_OU">Organisasjonsenhet:</string>
<string name="ssl_validator_label_C">Land:</string>
+ <string name="ssl_validator_label_ST">Stat:</string>
+ <string name="ssl_validator_label_L">Sted:</string>
+ <string name="ssl_validator_label_validity">Gyldighet:</string>
<string name="ssl_validator_label_validity_from">Fra:</string>
<string name="ssl_validator_label_validity_to">Til:</string>
<string name="ssl_validator_label_signature">Signatur:</string>
+ <string name="ssl_validator_label_signature_algorithm">Algoritme:</string>
+ <string name="placeholder_sentence">Dette er en plassholder</string>
+ <string name="placeholder_filename">placeholder.txt</string>
+ <string name="placeholder_filetype">PNG bilde</string>
+ <string name="placeholder_filesize">389 KB</string>
+ <string name="placeholder_timestamp">18.05.2012 12:23</string>
+ <string name="placeholder_media_time">12:23:45</string>
+ <string name="instant_upload_on_wifi">Kun last opp bilder via WiFi</string>
+ <string name="instant_upload_path">/Direkteopplasting</string>
+ <string name="conflict_title">Oppdateringskonflikt</string>
+ <string name="conflict_message">Ekstern fil %s er ikke synkronisert med lokal fil. Hvis du fortsetter vil det erstatte innhold på serveren.</string>
<string name="conflict_keep_both">Behold begge</string>
+ <string name="conflict_overwrite">Overskriv</string>
+ <string name="conflict_dont_upload">Ikke last opp</string>
+ <string name="preview_image_description">Bildeforhåndsvisning</string>
+ <string name="preview_image_error_unknown_format">Dette bildet kan ikke vises</string>
+ <string name="error__upload__local_file_not_copied">%1$s kunne ikke kopieres til %2$s lokale mapper</string>
+ <string name="actionbar_failed_instant_upload">Misslyktes direkteopplasting</string>
+ <string name="failed_upload_headline_text">Mislykket direkteopplastinger</string>
+ <string name="failed_upload_headline_hint">Oppsumering av alle mislykkede direkteopplastinger</string>
+ <string name="failed_upload_all_cb">velg alle</string>
+ <string name="failed_upload_headline_retryall_btn">forsøk alle valgte på nytt</string>
+ <string name="failed_upload_headline_delete_all_btn">slett alle valgte fra opplastingskø</string>
+ <string name="failed_upload_retry_text">forsøk å laste opp bildet på nytt:</string>
+ <string name="failed_upload_load_more_images">Last flere bilder</string>
+ <string name="failed_upload_retry_do_nothing_text">ikke gjør noe når du ikke er online for direkteopplasting</string>
+ <string name="failed_upload_failure_text">Feilmelding:</string>
+ <string name="failed_upload_quota_exceeded_text">Vennligst sjekk serverkonfigurasjon, kanskje kvoten din er brukt opp.</string>
</resources>
<string name="recommend_subject">Probeer %1$s op uw smartphone!</string>
<string name="recommend_text">Uitnodiging om %1$s op uw smartphone uit te proberen!\nDownload hier: %2$s</string>
<string name="auth_check_server">Controleer server</string>
- <string name="auth_host_url">Server adres</string>
+ <string name="auth_host_url">Serveradres https://…</string>
<string name="auth_username">Gebruikersnaam</string>
<string name="auth_password">Wachtwoord</string>
<string name="auth_register">Nieuw bij %1$s?</string>
<string name="filedetails_download">Download</string>
<string name="filedetails_sync_file">Bestand verversen</string>
<string name="filedetails_renamed_in_upload_msg">Bestand was hernoemt naar %1$s tijdens het uploaden</string>
+ <string name="action_share_file">Deel link</string>
+ <string name="action_unshare_file">Link niet meer delen</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nee</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Extern bestand kon niet worden gecontroleerd</string>
<string name="sync_file_nothing_to_do_msg">Bestandsinhoud is al gesynchroniseerd</string>
<string name="create_dir_fail_msg">Map kon niet worden aangemaakt</string>
+ <string name="filename_forbidden_characters">Verboden tekens: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Even geduld</string>
<string name="filedisplay_unexpected_bad_get_content">Onverwacht probleem; probeer een andere app om het bestand te selecteren</string>
<string name="filedisplay_no_file_selected">Er werd geen bestand geselecteerd</string>
+ <string name="activity_chooser_title">Verstuur link naar ...</string>
<string name="oauth_check_onoff">Inloggen met oAuth2</string>
<string name="oauth_login_connection">Verbinden met oAuth2-server.</string>
<string name="ssl_validator_header">De identiteit van de site kan niet worden gecontroleerd</string>
<string name="preview_image_description">Afbeelding voorbeeld</string>
<string name="preview_image_error_unknown_format">Deze afbeelding kan niet weergegeven worden</string>
<string name="error__upload__local_file_not_copied">%1$s kon niet worden gekopieerd naar de %2$s lokale map</string>
+ <string name="actionbar_failed_instant_upload">Mislukt InstantUpload</string>
<string name="failed_upload_headline_text">Mislukte directe uploads</string>
<string name="failed_upload_headline_hint">Samenvatting van alle mislukte directe uploads</string>
<string name="failed_upload_all_cb">alles selecteren</string>
<string name="failed_upload_retry_do_nothing_text">doe niks, u bent niet online voor directe upload</string>
<string name="failed_upload_failure_text">Mislukkings Bericht:</string>
<string name="failed_upload_quota_exceeded_text">Controleer uw server instellingen, misschien is uw quota overschreden.</string>
+ <string name="share_link_no_support_share_api">Sorry, delen is niet mogelijk op uw server. Neem contact op met uw beheerder.</string>
+ <string name="share_link_file_no_exist">Kan dit bestand of deze map niet delen. Controleer of dit object wel bestaat.</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_error">Er trad een fout op bij uw poging het delen van dit bestand of deze map te beëindigen</string>
</resources>
<string name="prefs_instant_upload_summary">Last opp kamerabilete med ein gong du tek dei</string>
<string name="prefs_help">Hjelp</string>
<string name="prefs_imprint">Impressum</string>
- <string name="auth_host_url">Tenaradresse</string>
<string name="auth_username">Brukarnamn</string>
<string name="auth_password">Passord</string>
<string name="sync_string_files">Filer</string>
<string name="prefs_log_title">ਲਾਗ ਰੱਖਣਾ ਚਾਲੂ</string>
<string name="prefs_log_title_history">ਲਾਗ ਰੱਖਣ ਅਤੀਤ</string>
<string name="prefs_log_delete_history_button">ਅਤੀਤ ਹਟਆਓ</string>
- <string name="auth_host_url">ਸਰਵਰ ਐਡਰੈਸ</string>
<string name="auth_username">ਯੂਜ਼ਰ-ਨਾਂ</string>
<string name="auth_password">ਪਾਸਵਰ</string>
<string name="auth_register">%1$s ਲਈ ਨਵੇਂ ਹੋ?</string>
<string name="recommend_subject">Wypróbuj %1$s na swoim smartphonie!</string>
<string name="recommend_text">Chcę was zaprosić do korzystania z %1$ s na twoim smartfonie!\nPobierz tutaj: %2$s</string>
<string name="auth_check_server">Sprawdź serwer</string>
- <string name="auth_host_url">Adres Serwera</string>
+ <string name="auth_host_url">Adres serwera https://...</string>
<string name="auth_username">Nazwa użytkownika</string>
<string name="auth_password">Hasło</string>
<string name="auth_register">Nowe %1$s?</string>
<string name="filedetails_download">Pobierz</string>
<string name="filedetails_sync_file">Odśwież plik</string>
<string name="filedetails_renamed_in_upload_msg">Podczas wysyłania nazwa pliku została zmieniona na %1$s</string>
+ <string name="action_share_file">Udostępnij link</string>
+ <string name="action_unshare_file">Anuluj udostępnianie</string>
<string name="common_yes">Tak</string>
<string name="common_no">Nie</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Nie można sprawdzić zdalnego pliku</string>
<string name="sync_file_nothing_to_do_msg">Zawartość pliku została już synchronizowana</string>
<string name="create_dir_fail_msg">Nie można utworzyć katalogu</string>
+ <string name="filename_forbidden_characters">Znaki zabronione: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Poczekaj chwilę</string>
<string name="filedisplay_unexpected_bad_get_content">Nieoczekiwany problem; spróbuj wybrać plik z innej aplikacji</string>
<string name="filedisplay_no_file_selected">Nie wybrano żadnych plików</string>
+ <string name="activity_chooser_title">Wyślij link do ...</string>
<string name="oauth_check_onoff">Loguj przez oAuth2</string>
<string name="oauth_login_connection">Łączenie z serwerem oAuth2...</string>
<string name="ssl_validator_header">Nie można zweryfikować tożsamości strony</string>
<string name="preview_image_description">Podgląd</string>
<string name="preview_image_error_unknown_format">Obraz nie może zostać wyświetlony</string>
<string name="error__upload__local_file_not_copied">%1$s nie może zostać skopiowany do lokalnego %2$s katalogu</string>
+ <string name="actionbar_failed_instant_upload">InstantUpload nie powiódł się</string>
<string name="failed_upload_headline_text">Błąd automatycznego przesyłania</string>
<string name="failed_upload_headline_hint">Podsumowanie wszystkich nieudanych transferów</string>
<string name="failed_upload_all_cb">zaznacz wszystkie</string>
<string name="failed_upload_retry_do_nothing_text">nic nie rób, ponieważ nie jesteś online, nie możesz przesyłać plików</string>
<string name="failed_upload_failure_text">Komunikat błędu:</string>
<string name="failed_upload_quota_exceeded_text">Proszę sprawdź ustawienia serwera, możliwe że przekroczyłes limit wielkości pliku</string>
+ <string name="share_link_no_support_share_api">Przepraszamy, udostępnianie jest wyłączone na twoim serwerze. Skontaktuj się z twoim administratorem.</string>
+ <string name="share_link_file_no_exist">Brak możliwości udostępnienia tego pliku lub folderu. Upewnij się, że istnieje.</string>
+ <string name="share_link_file_error">Wystąpił błąd podczas udostępniania tego pliku lub folderu.</string>
+ <string name="unshare_link_file_error">Wystąpił błąd podczas anulowania udostępniania tego pliku lub folderu.</string>
</resources>
<string name="recommend_subject">Tentar %1$s em seu smartfone!</string>
<string name="recommend_text">Gostaria de lhe convidar para usar %1$s em seu smartfone!\nBaixe aqui: %2$s</string>
<string name="auth_check_server">Verificar Servidor</string>
- <string name="auth_host_url">Endereço do servidor</string>
+ <string name="auth_host_url">Endereço do servidor https://...</string>
<string name="auth_username">Nome de usuário</string>
<string name="auth_password">Senha</string>
<string name="auth_register">Novo para %1$s?</string>
<string name="filedetails_download">Download</string>
<string name="filedetails_sync_file">Atualizar arquivo</string>
<string name="filedetails_renamed_in_upload_msg">Arquivo foi renomeado para %1$s durante o upload</string>
+ <string name="action_share_file">Compartilher link</string>
+ <string name="action_unshare_file">Descompartilhar o link</string>
<string name="common_yes">Sim</string>
<string name="common_no">Não</string>
<string name="common_ok">OK</string>
<string name="wait_a_moment">Aguarde um momento</string>
<string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, tente selecionar o arquivo com outro app</string>
<string name="filedisplay_no_file_selected">Nenhum arquivo foi selecionado</string>
+ <string name="activity_chooser_title">Enviar o link para</string>
<string name="oauth_check_onoff">Login com oAuth2</string>
<string name="oauth_login_connection">Conectando-se a oAuth2 servidor ...</string>
<string name="ssl_validator_header">A identidade do site não pode ser verificada</string>
<string name="failed_upload_retry_do_nothing_text">não fazer nada voce não está conectado para envio instantâneo </string>
<string name="failed_upload_failure_text">Mensagem de Falha:</string>
<string name="failed_upload_quota_exceeded_text">Por favor verifique a configuração do servidor, talvez a sua cota esteja vencida.</string>
+ <string name="share_link_no_support_share_api">Desculpe, compartilhamento não está habilitado para seu servidor. Por favor faça contato com o seu administrador.</string>
+ <string name="share_link_file_no_exist">Incapaz de compartilhar esse arquivo ou pasta. Por favor, certifique-se que existe</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_error">Ocorreu um erro ao tentar descompartilhar este arquivo ou pasta</string>
</resources>
<string name="prefs_log_summary_history">Isto mostra os registos guardados</string>
<string name="prefs_log_delete_history_button">Eliminar Histórico</string>
<string name="prefs_help">Ajuda</string>
+ <string name="prefs_recommend">Recomendar a um amigo</string>
<string name="prefs_feedback">Resposta</string>
<string name="prefs_imprint">Imprint</string>
+ <string name="recommend_subject">Experimente %1$s no seu smartphone!</string>
+ <string name="recommend_text">Quero convidá-lo para experimentar %1$s no seu smartphone!\nDescarregue aqui: %2$s</string>
<string name="auth_check_server">Verificar Servidor</string>
- <string name="auth_host_url">Endereço do servidor</string>
+ <string name="auth_host_url">Endereço do servidor https://..</string>
<string name="auth_username">Nome de Utilizador</string>
<string name="auth_password">Palavra-passe</string>
<string name="auth_register">Novo em %1$s?</string>
<string name="filedetails_download">Descarregar</string>
<string name="filedetails_sync_file">Atualizar ficheiro</string>
<string name="filedetails_renamed_in_upload_msg">O nome do ficheiro foi alterado para %1$s durante o envio.</string>
+ <string name="action_share_file">Partilhar o link</string>
<string name="common_yes">Sim</string>
<string name="common_no">Não</string>
<string name="common_ok">OK</string>
<string name="sync_foreign_files_forgotten_ticker">Alguns ficheiros locais ficaram esquecidos</string>
<string name="sync_foreign_files_forgotten_content">%1$d ficheiros da directoria %2$s não foram copiados</string>
<string name="sync_foreign_files_forgotten_explanation">Com a versão 1.3.16, os ficheiros que foram enviados deste dispositivo foram copiados para a pasta local %1$s para prevenir perda de dados quando um ficheiro está partilhado com várias contas.\nDevido a esta alteração, todos os ficheiros e as suas versões foram copiados para a pasta %2$s. Contudo, um erro não deixou concluír este processo durante a sincronização da conta. Pode deixar o ficheiro(s) como está(ão) e remover o link para %3$s, ou mover o(s) ficheiro(s) para a pasta %1$s e guardar o link para %4$s.\n\nEm baixo está(ão) listados o(s) ficheiro(s) locais e remotos em %5$s que foram ligados.</string>
+ <string name="sync_current_folder_was_removed">A pasta %1$s já não existe</string>
<string name="foreign_files_move">Mover Todos</string>
<string name="foreign_files_success">Todos os ficheiros foram movidos</string>
<string name="foreign_files_fail">Não foi possível mover alguns ficheiros</string>
<string name="media_err_unsupported">Codec de média não suportado</string>
<string name="media_err_io">Não foi possível reproduzir o ficheiro</string>
<string name="media_err_malformed">Ficheiro erradamente codificado (codec)</string>
+ <string name="media_err_timeout">O tempo de espera para jogar expirou</string>
<string name="media_err_invalid_progressive_playback">O ficheiro não pode ser reproduzido (streaming)</string>
<string name="media_err_unknown">O ficheiro não pode ser reproduzido com o leitor de média de origem</string>
<string name="media_err_security_ex">Erro de segurança a tentar reproduzir o ficheiro %1$s</string>
<string name="auth_connection_established">Ligação estabelecida</string>
<string name="auth_testing_connection">A testar a ligação...</string>
<string name="auth_not_configured_title">Configuração do servidor incorrecta.</string>
+ <string name="auth_account_not_new">Uma conta para este utilizador e servidor já existe no dispositivo</string>
+ <string name="auth_account_not_the_same">O utilizador que escreveu não coincide com o nome de utilizador desta conta</string>
<string name="auth_unknown_error_title">Ocorreu um erro desconhecido!</string>
<string name="auth_unknown_host_title">Não é possível encontrar o servidor</string>
<string name="auth_incorrect_path_title">Instância servidor não encontrada</string>
<string name="auth_timeout_title">O servidor levou demasiado tempo a responder</string>
<string name="auth_incorrect_address_title">URL errado</string>
<string name="auth_ssl_general_error_title">Inicialização de SSL falhou</string>
+ <string name="auth_ssl_unverified_server_title">Não foi possível verificar a identidade SSL do servidor</string>
<string name="auth_bad_oc_version_title">Versão do servidor não reconhecida</string>
<string name="auth_wrong_connection_title">Não consegue estabelecer ligação</string>
<string name="auth_secure_connection">Ligação segura estabelecida</string>
<string name="auth_oauth_error">autorização mal sucedida</string>
<string name="auth_oauth_error_access_denied">Acesso negado pelo servidor</string>
<string name="auth_wtf_reenter_URL">Estado inesperado, por favor, digite a URL do servidor novamente</string>
+ <string name="auth_expired_oauth_token_toast">O prazo da sua autorização expirou. Por favor renove-a</string>
<string name="auth_expired_basic_auth_toast">Por favor, introduza a password actual</string>
+ <string name="auth_expired_saml_sso_token_toast">A sua sessão expirou. Por favor autentique-se de novo</string>
+ <string name="auth_connecting_auth_server">A verificar a sua autenticação no servidor...</string>
+ <string name="auth_unsupported_auth_method">O servidor não suporta este método de autenticação</string>
+ <string name="auth_unsupported_multiaccount">%1$s não suporta contas múltiplas</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="sync_file_fail_msg">Não foi possível verificar o ficheiro remoto</string>
<string name="sync_file_nothing_to_do_msg">O conteúdo do ficheiro já foi sincronizado</string>
<string name="create_dir_fail_msg">Não foi possível criar a pasta</string>
+ <string name="filename_forbidden_characters">Caracteres não permitidos: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Aguarde um momento</string>
<string name="filedisplay_unexpected_bad_get_content">Erro inesperado. Por favor tente outra aplicação para seleccionar o ficheiro.</string>
<string name="filedisplay_no_file_selected">Não selecionou nenhum ficheiro</string>
+ <string name="oauth_check_onoff">Autenticar-se com oAuth2</string>
<string name="oauth_login_connection">A ligar ao servidor oAuth2</string>
<string name="ssl_validator_header">Não foi possível verificar a identidade do site.</string>
<string name="ssl_validator_reason_cert_not_trusted">- O certificado do servidor não é de confiança</string>
<string name="preview_image_description">Pré-Visualização da imagem</string>
<string name="preview_image_error_unknown_format">Esta imagem não pode ser mostrada</string>
<string name="error__upload__local_file_not_copied">Não foi possível copiar %1$s para a pasta local %2$s</string>
+ <string name="actionbar_failed_instant_upload">O envio rápido falhou</string>
<string name="failed_upload_headline_text">Falharam os Uploads-Instantâneos</string>
<string name="failed_upload_headline_hint">Sumário dos Uploads-Instantâneos falhados</string>
<string name="failed_upload_all_cb">Seleccionar Todos</string>
<string name="prefs_manage_accounts">Administrare conturi</string>
<string name="prefs_instant_upload">Activează încărcarea instant</string>
<string name="prefs_help">Ajutor</string>
- <string name="auth_host_url">Adresa server-ului</string>
<string name="auth_username">Nume utilizator</string>
<string name="auth_password">Parolă</string>
<string name="sync_string_files">Fișiere</string>
<string name="prefs_instant_upload">Включить немедленную загрузку</string>
<string name="prefs_instant_upload_summary">Мгновенно загрузить фотографии, сделанные камерой</string>
<string name="prefs_help">Помощь</string>
- <string name="auth_host_url">URL</string>
<string name="auth_username">Имя пользователя</string>
<string name="auth_password">Пароль</string>
<string name="auth_register">Я новичок в %1$s</string>
<?xml version='1.0' encoding='UTF-8'?>
<resources>
- <string name="about_android">%1$s Ð\9fÑ\80иложение Ð\90ндÑ\80оид</string>
+ <string name="about_android">%1$s Ð\9fÑ\80иложение длÑ\8f Ð\90ндÑ\80оида</string>
<string name="about_version">Версия %1$s</string>
<string name="actionbar_sync">Обновить учетную запись</string>
- <string name="actionbar_upload">Ð\97агÑ\80Ñ\83зка</string>
- <string name="actionbar_upload_from_apps">СодеÑ\80жимое оÑ\82 других приложений</string>
+ <string name="actionbar_upload">Ð\97агÑ\80Ñ\83зиÑ\82Ñ\8c</string>
+ <string name="actionbar_upload_from_apps">СодеÑ\80жимое из других приложений</string>
<string name="actionbar_upload_files">Файлы</string>
<string name="actionbar_open_with">Открыть с помощью</string>
- <string name="actionbar_mkdir">СоздаÑ\82Ñ\8c диÑ\80екÑ\82оÑ\80иÑ\8e</string>
+ <string name="actionbar_mkdir">СоздаÑ\82Ñ\8c каÑ\82алог</string>
<string name="actionbar_settings">Настройки</string>
- <string name="actionbar_see_details">Ð\94еÑ\82али</string>
- <string name="prefs_category_general">Ð\93лавные</string>
+ <string name="actionbar_see_details">Ð\9fодÑ\80обно</string>
+ <string name="prefs_category_general">Ð\9eÑ\81новные</string>
<string name="prefs_category_more">Больше</string>
- <string name="prefs_accounts">Ð\90ккаÑ\83нÑ\82Ñ\8b</string>
- <string name="prefs_manage_accounts">Управление аккаунтами</string>
+ <string name="prefs_accounts">УÑ\87Ñ\91Ñ\82нÑ\8bе запиÑ\81и</string>
+ <string name="prefs_manage_accounts">Управление учётными записями</string>
<string name="prefs_pincode">App PIN</string>
- <string name="prefs_pincode_summary">Защитить ваш клиент применение</string>
- <string name="prefs_instant_upload">Включить моментальную загрузку</string>
- <string name="prefs_instant_upload_summary">Ð\9cоменÑ\82алÑ\8cно загружать фотографии, полученные с камеры</string>
- <string name="prefs_log_title">Ð\92клÑ\8eÑ\87иÑ\82Ñ\8c запиÑ\81Ñ\8c жÑ\83Ñ\80нала</string>
- <string name="prefs_log_summary">ÐÑ\82о иÑ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f длÑ\8f Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86ии пÑ\80облем</string>
- <string name="prefs_log_title_history">Ð\97апиÑ\81Ñ\8bваÑ\82Ñ\8c иÑ\81Ñ\82оÑ\80иÑ\8e</string>
- <string name="prefs_log_summary_history">Ð\97деÑ\81Ñ\8c показанÑ\8b жÑ\83Ñ\80налÑ\8b</string>
- <string name="prefs_log_delete_history_button">Удалить историю</string>
+ <string name="prefs_pincode_summary">Защитить ваш клиент</string>
+ <string name="prefs_instant_upload">Включить режим немедленной загрузки</string>
+ <string name="prefs_instant_upload_summary">Ð\9dемедленно загружать фотографии, полученные с камеры</string>
+ <string name="prefs_log_title">Ð\92клÑ\8eÑ\87иÑ\82Ñ\8c жÑ\83Ñ\80налиÑ\80ование</string>
+ <string name="prefs_log_summary">Ð\98Ñ\81полÑ\8cзÑ\83еÑ\82Ñ\81Ñ\8f длÑ\8f Ñ\80егиÑ\81Ñ\82Ñ\80аÑ\86ии оÑ\88ибок</string>
+ <string name="prefs_log_title_history">Ð\96Ñ\83Ñ\80нал</string>
+ <string name="prefs_log_summary_history">Ð\97деÑ\81Ñ\8c показанÑ\8b запиÑ\81и в жÑ\83Ñ\80нал</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="recommend_subject">Попробуйте %1$s на вашем смартфоне!</string>
- <string name="recommend_text">ХоÑ\87Ñ\83 пÑ\80едложиÑ\82Ñ\8c вам полÑ\8cзоваÑ\82Ñ\8cÑ\81Ñ\8f %1$s на ваÑ\88ем Ñ\81маÑ\80Ñ\82Ñ\84оне!\nÐ\94лÑ\8f загÑ\80Ñ\83зки: %2$s</string>
+ <string name="recommend_text">ХоÑ\87Ñ\83 пÑ\80едложиÑ\82Ñ\8c вам полÑ\8cзоваÑ\82Ñ\8cÑ\81Ñ\8f %1$s на ваÑ\88ем Ñ\81маÑ\80Ñ\82Ñ\84оне!\nСкаÑ\87аÑ\82Ñ\8c: %2$s</string>
<string name="auth_check_server">Проверить сервер</string>
- <string name="auth_host_url">Адрес сервера</string>
- <string name="auth_username">Ð\9fолÑ\8cзоваÑ\82елÑ\8c</string>
+ <string name="auth_host_url">Адрес сервера https://...</string>
+ <string name="auth_username">Ð\98мÑ\8f полÑ\8cзоваÑ\82елÑ\8f</string>
<string name="auth_password">Пароль</string>
- <string name="auth_register">Ð\92пеÑ\80вÑ\8bе с %1$s?</string>
+ <string name="auth_register">Ð\9dезнакомÑ\8b с %1$s?</string>
<string name="sync_string_files">Файлы</string>
<string name="setup_btn_connect">Соединить</string>
- <string name="uploader_btn_upload_text">Ð\97агÑ\80Ñ\83зка</string>
- <string name="uploader_top_message">Ð\92Ñ\8bбÑ\80аÑ\82Ñ\8c меÑ\81Ñ\82о длÑ\8f загÑ\80Ñ\83зки:</string>
- <string name="uploader_wrn_no_account_title">Ð\90ккаÑ\83нÑ\82Ñ\8b не найденÑ\8b</string>
- <string name="uploader_wrn_no_account_text">На вашем устройстве нет аккаунтов %1$s. Настройте сначала аккаунт.</string>
+ <string name="uploader_btn_upload_text">Ð\97агÑ\80Ñ\83зиÑ\82Ñ\8c</string>
+ <string name="uploader_top_message">Ð\92Ñ\8bбÑ\80аÑ\82Ñ\8c каÑ\82алог длÑ\8f загÑ\80Ñ\83зок:</string>
+ <string name="uploader_wrn_no_account_title">УÑ\87Ñ\91Ñ\82наÑ\8f запиÑ\81Ñ\8c не найдена</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="uploader_wrn_no_content_text">Ð\9aонÑ\82енÑ\82 не полÑ\83Ñ\87ен. Нечего загружать.</string>
+ <string name="uploader_wrn_no_content_text">СодеÑ\80жимое не полÑ\83Ñ\87ено. Нечего загружать.</string>
<string name="uploader_error_forbidden_content">%1$s не имеет доступа к опубликованным данным</string>
<string name="uploader_info_uploading">Загрузка</string>
- <string name="file_list_empty">Ð\92 Ñ\8dÑ\82ой папке нет файлов.\nНовые файлы могут быть добавлены с помощью пункта меню \"Загрузить\".</string>
+ <string name="file_list_empty">Ð\92 Ñ\8dÑ\82ом каÑ\82алоге нет файлов.\nНовые файлы могут быть добавлены с помощью пункта меню \"Загрузить\".</string>
<string name="filedetails_select_file">Нажмите на файл для отображения дополнительной информации.</string>
<string name="filedetails_size">Размер:</string>
<string name="filedetails_type">Тип:</string>
<string name="filedetails_download">Скачать</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">Ð\9eÑ\82мена загÑ\80Ñ\83зки</string>
+ <string name="common_cancel_upload">Ð\9eÑ\82мениÑ\82Ñ\8c загÑ\80Ñ\83зкÑ\83</string>
<string name="common_cancel">Отмена</string>
<string name="common_save_exit">Сохранить & Выйти</string>
<string name="common_error">Ошибка</string>
- <string name="common_loading">Ð\97агÑ\80Ñ\83жаеÑ\82Ñ\81Ñ\8f...</string>
+ <string name="common_loading">Ð\98дÑ\91Ñ\82 загÑ\80Ñ\83зка...</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="delete_account">Удалить учётную запись</string>
+ <string name="create_account">Создать учётную запись</string>
<string name="upload_chooser_title">Загрузить из...</string>
- <string name="uploader_info_dirname">Ð\98мÑ\8f диÑ\80екÑ\82оÑ\80ии</string>
+ <string name="uploader_info_dirname">Ð\98мÑ\8f каÑ\82алога</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">Ð\97агÑ\80Ñ\83зка пÑ\80оÑ\88ла Ñ\83Ñ\81пеÑ\88но</string>
+ <string name="uploader_upload_succeeded_ticker">Ð\97агÑ\80Ñ\83зка завеÑ\80Ñ\88ена</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="downloader_download_in_progress_ticker">Скачивание...</string>
<string name="downloader_download_in_progress_content">%1$d%% скачивания %2$s</string>
- <string name="downloader_download_succeeded_ticker">СкаÑ\87ивание пÑ\80оÑ\88ло Ñ\83Ñ\81пеÑ\88но</string>
+ <string name="downloader_download_succeeded_ticker">СкаÑ\87ивание завеÑ\80Ñ\88ено</string>
<string name="downloader_download_succeeded_content">%1$s успешно скачан</string>
- <string name="downloader_download_failed_ticker">СкаÑ\87ивание не Ñ\83далась</string>
+ <string name="downloader_download_failed_ticker">СкаÑ\87ивание не Ñ\83далось</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_not_downloaded_yet">Ещё не скачано</string>
+ <string name="common_choose_account">Выберите учётную запись</string>
<string name="sync_fail_ticker">Синхронизация прошла неудачно</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_conflicts_in_favourites_content">%1$d файлы, которые должны быть синхронизированными не могут синхронизироваться</string>
+ <string name="sync_conflicts_in_favourites_content">%1$d файлы не могут быть синхронизированы</string>
<string name="sync_fail_in_favourites_ticker">Не удалось синхронизировать файлы</string>
- <string name="sync_fail_in_favourites_content">СодеÑ\80жание %1$d Ñ\84айла(ов) не можеÑ\82 бÑ\8bÑ\82Ñ\8c Ñ\81инÑ\85Ñ\80онизиÑ\80овано (%2$d конÑ\84ликÑ\82а(ов))</string>
+ <string name="sync_fail_in_favourites_content">СодеÑ\80жимое %1$d Ñ\84айлов не можеÑ\82 бÑ\8bÑ\82Ñ\8c Ñ\81инÑ\85Ñ\80онизиÑ\80овано (конÑ\84ликÑ\82ов: %2$d)</string>
<string name="sync_foreign_files_forgotten_ticker">Несколько локальных файлов были забыты</string>
- <string name="sync_foreign_files_forgotten_content">%1$d файлы из %2$s папки не могут быть скопированы в</string>
- <string name="sync_foreign_files_forgotten_explanation">файлы, загруженные с этого устройства, скопированы в локальную папку %1$s для предотвращения потери данных, когда отдельный файл синхронизируется с нескольких учётных записей. По причине этого изменения, все файлы, загруженные в предыдущих версиях этого приложения, были скопированы в папку %2$s. Однако, ошибка помешала завершению этой операции при синхронизации учётной записи. Вы можете либо оставить файлы как есть, или переместить их в папку %1$s и сохранить ссылку в %4$s. \nВ списке указаны локальные файлы, привязанные к файлам на сервере в папке %5$s.</string>
+ <string name="sync_foreign_files_forgotten_content">%1$d файлов из %2$s папок не могут быть скопированы в</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">Ð\9eÑ\82Ñ\81Ñ\83Ñ\82Ñ\81Ñ\82вÑ\83еÑ\82 доÑ\81Ñ\82аÑ\82оÑ\87ное колиÑ\87еÑ\81Ñ\82во меÑ\81Ñ\82а длÑ\8f копиÑ\80ованиÑ\8f вÑ\8bделеннÑ\8bÑ\85 Ñ\84айлов в папкÑ\83 %1$s. ХоÑ\82иÑ\82е ли Ð\92Ñ\8b переместить их в другое место?</string>
+ <string name="upload_query_move_foreign_files">Ð\9dедоÑ\81Ñ\82аÑ\82оÑ\87но меÑ\81Ñ\82а длÑ\8f копиÑ\80ованиÑ\8f вÑ\8bделеннÑ\8bÑ\85 Ñ\84айлов в каÑ\82алог %1$s. Ð\9fереместить их в другое место?</string>
<string name="pincode_enter_pin_code">Вставьте PIN вашего приложения</string>
- <string name="pincode_configure_your_pin">Введите App PIN</string>
- <string name="pincode_configure_your_pin_explanation">ПИН-код будет запрашиваться каждый раз, когда вы запускаете приложение.</string>
- <string name="pincode_reenter_your_pincode">Повторите App PIN</string>
+ <string name="pincode_configure_your_pin">Введите App PIN</string>
+ <string name="pincode_configure_your_pin_explanation">PIN-код будет запрашиваться при каждом запуске приложения.</string>
+ <string name="pincode_reenter_your_pincode">Повторите ввод App PIN</string>
<string name="pincode_remove_your_pincode">Удалить App PIN</string>
- <string name="pincode_mismatch">Ð\94ва App PIN не совпадают</string>
+ <string name="pincode_mismatch">Ð\92ведÑ\91ннÑ\8bе App PIN не совпадают</string>
<string name="pincode_wrong">Неверный App PIN</string>
<string name="pincode_removed">App PIN удалён</string>
<string name="pincode_stored">App PIN сохранён</string>
- <string name="media_notif_ticker">%1$s мÑ\83зÑ\8bкалÑ\8cнÑ\8bй пÑ\80оигÑ\80Ñ\8bваÑ\82елÑ\8c</string>
+ <string name="media_notif_ticker">%1$s аÑ\83диоплееÑ\80</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">Ð\9dе найден медиа-Ñ\84айл</string>
- <string name="media_err_no_account">Ð\9dе наÑ\81Ñ\82Ñ\80оена Ñ\83Ñ\87Ñ\91Ñ\82наÑ\8f запиÑ\81Ñ\8c</string>
+ <string name="media_err_nothing_to_play">Ð\9cедиаÑ\84айлов не найдено</string>
+ <string name="media_err_no_account">УÑ\87Ñ\91Ñ\82наÑ\8f запиÑ\81Ñ\8c не наÑ\81Ñ\82Ñ\80оена</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">Тайм аÑ\83Ñ\82 пÑ\80и воÑ\81пÑ\80оизведении</string>
- <string name="media_err_invalid_progressive_playback">Ð\9dевозможно воÑ\81пÑ\80оизвеÑ\81Ñ\82и Ñ\84айл как поÑ\82ок</string>
- <string name="media_err_unknown">Файл не можеÑ\82 бÑ\8bÑ\82Ñ\8c пÑ\80оигÑ\80ан стандартным плеером</string>
+ <string name="media_err_io">Ð\9cедиаÑ\84айл не может быть прочитан</string>
+ <string name="media_err_malformed">Ð\9cедиаÑ\84айл некоÑ\80Ñ\80екÑ\82но закодиÑ\80ован</string>
+ <string name="media_err_timeout">Ð\92Ñ\80емÑ\8f попÑ\8bÑ\82ок воÑ\81пÑ\80оизведениÑ\8f вÑ\8bÑ\88ло</string>
+ <string name="media_err_invalid_progressive_playback">Ð\9dевозможно оÑ\80ганизоваÑ\82Ñ\8c поÑ\82оковÑ\83Ñ\8e пеÑ\80едаÑ\87Ñ\83 медиаÑ\84айла</string>
+ <string name="media_err_unknown">Ð\9cедиаÑ\84айл не можеÑ\82 бÑ\8bÑ\82Ñ\8c пÑ\80оигÑ\80ан стандартным плеером</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="auth_nossl_plain_ok_title">Защищённое соединение недоступно.</string>
<string name="auth_connection_established">Соединение установлено</string>
<string name="auth_testing_connection">Тестирование соединения...</string>
- <string name="auth_not_configured_title">Ð\9dевеÑ\80наÑ\8f конÑ\84игÑ\83Ñ\80аÑ\86иÑ\8f Ñ\81еÑ\80веÑ\80</string>
- <string name="auth_account_not_new">Учётная запись для такого пользователя и сервера уже существует на устройстве</string>
- <string name="auth_account_not_the_same">Введённый пользователь не соответсвует пользователю учётной записи</string>
+ <string name="auth_not_configured_title">Ð\9aонÑ\84игÑ\83Ñ\80аÑ\86иÑ\8f Ñ\81еÑ\80веÑ\80а задана невеÑ\80но</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">ÐкземплÑ\8fÑ\80 Ñ\81ервер не найден</string>
+ <string name="auth_incorrect_path_title">Сервер не найден</string>
<string name="auth_timeout_title">Сервер слишком долго не отвечает</string>
<string name="auth_incorrect_address_title">Неверный URL</string>
<string name="auth_ssl_general_error_title">Ошибка инициализации SSL</string>
- <string name="auth_ssl_unverified_server_title">Невозможно проверить сертификат SSL сервера</string>
+ <string name="auth_ssl_unverified_server_title">Невозможно проверить SSL-сертификат сервера</string>
<string name="auth_bad_oc_version_title">Неизвестная версия сервера</string>
<string name="auth_wrong_connection_title">Невозможно установить соединение</string>
<string name="auth_secure_connection">Защищённое соединение установлено</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">Ð\9dеожиданнÑ\8bй оÑ\82веÑ\82; пожалÑ\83йÑ\81Ñ\82а, введиÑ\82е адÑ\80еÑ\81 Ñ\81еÑ\80веÑ\80а еÑ\89Ñ\91 Ñ\80аз</string>
- <string name="auth_expired_oauth_token_toast">Ваша авторизация истекла. Пожалуйста, авторизуйтесь снова</string>
+ <string name="auth_oauth_error_access_denied">СеÑ\80веÑ\80 авÑ\82оÑ\80изаÑ\86ии оÑ\82казал в доÑ\81Ñ\82Ñ\83пе</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_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_unsupported_multiaccount">%1$s не поддерживает сразу несколько учётных записей</string>
<string name="fd_keep_in_sync">Обновлять файл</string>
<string name="common_rename">Переименовать</string>
<string name="common_remove">Удалить</string>
- <string name="confirmation_remove_alert">Ð\92Ñ\8b в Ñ\81амом деле хотите удалить %1$s ?</string>
- <string name="confirmation_remove_folder_alert">Вы действительно хотите удалить %1$s и его содержимое ?</string>
+ <string name="confirmation_remove_alert">Ð\92Ñ\8b дейÑ\81Ñ\82виÑ\82елÑ\8cно хотите удалить %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">Ð\98 Ñ\83далённо и локально</string>
- <string name="remove_success_msg">Успешное удаление</string>
- <string name="remove_fail_msg">Удаление не можеÑ\82 бÑ\8bÑ\82Ñ\8c завеÑ\80Ñ\88ено</string>
+ <string name="confirmation_remove_remote_and_local">Удалённо и локально</string>
+ <string name="remove_success_msg">Удаление завершено</string>
+ <string name="remove_fail_msg">Ð\9eÑ\88ибка Ñ\83далениÑ\8f</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_nothing_to_do_msg">Содержимое файла уже синхронизировано</string>
- <string name="create_dir_fail_msg">Ð\94иÑ\80екÑ\82оÑ\80иÑ\8f не можеÑ\82 бÑ\8bÑ\82Ñ\8c Ñ\81оздана</string>
- <string name="filename_forbidden_characters">Ð\97апÑ\80еÑ\89Ñ\91нные символы: / \\ < > : \" | ? *</string>
+ <string name="create_dir_fail_msg">Ð\9aаÑ\82алог не можеÑ\82 бÑ\8bÑ\82Ñ\8c Ñ\81оздан</string>
+ <string name="filename_forbidden_characters">Ð\9dедопÑ\83Ñ\81Ñ\82имые символы: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Подождите немного</string>
- <string name="filedisplay_unexpected_bad_get_content">Ð\9dеизвеÑ\81Ñ\82наÑ\8f оÑ\88ибка; попÑ\80обÑ\83йÑ\82е дÑ\80Ñ\83гое пÑ\80иложение длÑ\8f вÑ\8bбоÑ\80а Ñ\84айла</string>
+ <string name="filedisplay_unexpected_bad_get_content">Ð\9dеизвеÑ\81Ñ\82наÑ\8f оÑ\88ибка; вÑ\8bбеÑ\80иÑ\82е Ñ\8dÑ\82оÑ\82 Ñ\84айл из дÑ\80Ñ\83гого пÑ\80иложениÑ\8f</string>
<string name="filedisplay_no_file_selected">Файлы не выбраны</string>
- <string name="oauth_check_onoff">Подключать через oAuth2</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">- Сертификат сервера не является доверенным</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">Ð\92Ñ\81е-Ñ\80авно довеÑ\80Ñ\8fÑ\82Ñ\8c данномÑ\83 Ñ\81еÑ\80Ñ\82иÑ\84икаÑ\82Ñ\83?</string>
+ <string name="ssl_validator_reason_cert_not_yet_valid">- Срок действия сертификата сервера ещё не начался</string>
+ <string name="ssl_validator_reason_hostname_not_verified">- URL не совпадает с именем сервера в сертификате</string>
+ <string name="ssl_validator_question">Ð\92Ñ\8b Ñ\85оÑ\82иÑ\82е довеÑ\80Ñ\8fÑ\82Ñ\8c данномÑ\83 Ñ\81еÑ\80Ñ\82иÑ\84икаÑ\82Ñ\83 в лÑ\8eбом Ñ\81лÑ\83Ñ\87ае?</string>
<string name="ssl_validator_not_saved">Сертификат не может быть сохранён</string>
- <string name="ssl_validator_btn_details_see">Ð\94еÑ\82али</string>
- <string name="ssl_validator_btn_details_hide">СпÑ\80Ñ\8fÑ\82ать</string>
- <string name="ssl_validator_label_subject">Ð\92Ñ\8bдано длÑ\8f:</string>
- <string name="ssl_validator_label_issuer">Ð\92Ñ\8bдан:</string>
+ <string name="ssl_validator_btn_details_see">Ð\9fодÑ\80обно</string>
+ <string name="ssl_validator_btn_details_hide">СкÑ\80Ñ\8bть</string>
+ <string name="ssl_validator_label_subject">Ð\9aомÑ\83 вÑ\8bдано:</string>
+ <string name="ssl_validator_label_issuer">Ð\9aем вÑ\8bдано:</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">Страна:</string>
- <string name="ssl_validator_label_ST">СÑ\82аÑ\82Ñ\83Ñ\81:</string>
+ <string name="ssl_validator_label_ST">ШÑ\82аÑ\82:</string>
<string name="ssl_validator_label_L">Местонахождение:</string>
<string name="ssl_validator_label_validity">Срок действия:</string>
- <string name="ssl_validator_label_validity_from">Ð\9eÑ\82:</string>
- <string name="ssl_validator_label_validity_to">Ð\94о:</string>
+ <string name="ssl_validator_label_validity_from">Ð\98з:</string>
+ <string name="ssl_validator_label_validity_to">Ð\92:</string>
<string name="ssl_validator_label_signature">Подпись:</string>
<string name="ssl_validator_label_signature_algorithm">Алгоритм:</string>
<string name="placeholder_sentence">Это заполнитель</string>
<string name="placeholder_filename">placeholder.txt</string>
<string name="placeholder_filetype">Изображение PNG</string>
- <string name="placeholder_filesize">389 KB</string>
+ <string name="placeholder_filesize">389 КБ</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">Загружать изображения только через Wi-Fi</string>
<string name="instant_upload_path">/InstantUpload</string>
<string name="conflict_title">Конфликт обновления</string>
- <string name="conflict_message">УдаленнÑ\8bй Ñ\84айл %s не Ñ\81инÑ\85Ñ\80онизиÑ\80ован Ñ\81 локалÑ\8cнÑ\8bм. Ð\97авеÑ\80Ñ\88ение пÑ\80иведеÑ\82 к замене Ñ\81одеÑ\80жаниÑ\8f файла на сервере.</string>
- <string name="conflict_keep_both">Ð\9eÑ\81Ñ\82авить оба</string>
+ <string name="conflict_message">УдаленнÑ\8bй Ñ\84айл %s не Ñ\81инÑ\85Ñ\80онизиÑ\80ован Ñ\81 локалÑ\8cнÑ\8bм. Ð\9fÑ\80одолжение пÑ\80иведеÑ\82 к замене Ñ\81одеÑ\80жимого файла на сервере.</string>
+ <string name="conflict_keep_both">СоÑ\85Ñ\80анить оба</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">ÐÑ\82о изобÑ\80ажение не можеÑ\82 бÑ\8bÑ\82Ñ\8c оÑ\82обÑ\80ажено</string>
- <string name="error__upload__local_file_not_copied">%1$s не может быть скопирован в %2$s локальною папку</string>
- <string name="actionbar_failed_instant_upload">Ð\91Ñ\8bÑ\81Ñ\82Ñ\80аÑ\8f загÑ\80Ñ\83зка не Ñ\83далаÑ\81Ñ\8c</string>
- <string name="failed_upload_headline_text">Ð\91Ñ\8bÑ\81Ñ\82Ñ\80Ñ\8bе загÑ\80Ñ\83зки не Ñ\83далиÑ\81Ñ\8c</string>
- <string name="failed_upload_headline_hint">СпиÑ\81ок вÑ\81еÑ\85 неÑ\83даÑ\87нÑ\8bÑ\85 загÑ\80Ñ\83зок</string>
+ <string name="preview_image_error_unknown_format">ÐÑ\82о изобÑ\80ажение не можеÑ\82 бÑ\8bÑ\82Ñ\8c показано</string>
+ <string name="error__upload__local_file_not_copied">%1$s не может быть скопирован в локальный каталог %2$s</string>
+ <string name="actionbar_failed_instant_upload">Сбой немедленной загÑ\80Ñ\83зки</string>
+ <string name="failed_upload_headline_text">Сбой немедленной загÑ\80Ñ\83зки</string>
+ <string name="failed_upload_headline_hint">Сводка по вÑ\81ем Ñ\81бойнÑ\8bм немедленнÑ\8bм загÑ\80Ñ\83зкам</string>
<string name="failed_upload_all_cb">Выбрать всё</string>
<string name="failed_upload_headline_retryall_btn">Ещё раз попробовать всё выделенное</string>
- <string name="failed_upload_headline_delete_all_btn">УдалиÑ\82Ñ\8c вÑ\81Ñ\91| вÑ\8bбÑ\80анное из оÑ\87еÑ\80еди загÑ\80Ñ\83зки</string>
- <string name="failed_upload_retry_text">Ð\9fопÑ\80обоваÑ\82Ñ\8c еÑ\89Ñ\91 Ñ\80аз загÑ\80Ñ\83зиÑ\82Ñ\8c изобÑ\80ажение</string>
+ <string name="failed_upload_headline_delete_all_btn">Удалить выбранное из очереди загрузки</string>
+ <string name="failed_upload_retry_text">попÑ\80обоваÑ\82Ñ\8c еÑ\89Ñ\91 Ñ\80аз загÑ\80Ñ\83зиÑ\82Ñ\8c изобÑ\80ажение:</string>
<string name="failed_upload_load_more_images">Загрузить больше картинок</string>
- <string name="failed_upload_retry_do_nothing_text">Ничего не делать, если не в сети</string>
- <string name="failed_upload_failure_text">Сообщение об ошибке</string>
- <string name="failed_upload_quota_exceeded_text">Проверьте настройки сервера, возможно ваш лимит превышен</string>
+ <string name="failed_upload_retry_do_nothing_text">Ничего не делать, если нет подключения к сети</string>
+ <string name="failed_upload_failure_text">Сообщение об ошибке:</string>
+ <string name="failed_upload_quota_exceeded_text">Проверьте настройки сервера, возможно ваш лимит исчерпан</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_error">Ошибка удаления общего доступа к этому файлу или каталогу</string>
</resources>
<string name="prefs_instant_upload">ක්ෂණික උඩුගත කිරීම් සක්රිය කරන්න</string>
<string name="prefs_instant_upload_summary">කැමරාවෙන් ගත් රූප ක්ෂණිකව උඩුගත කරන්න</string>
<string name="prefs_help">උදව්</string>
- <string name="auth_host_url">සේවාදායකයේ ලිපිනය</string>
<string name="auth_username">පරිශීලක නම</string>
<string name="auth_password">මුර පදය</string>
<string name="sync_string_files">ගොනු</string>
<string name="prefs_feedback">Spätná väzba</string>
<string name="prefs_imprint">Podmienky používania</string>
<string name="recommend_subject">Skúste %1$s na vašom telefóne!</string>
+ <string name="recommend_text">Chcem vás pozvať na používanie %1$s na vašom smartphone!\nNa stiahnutie tu: %2$s</string>
<string name="auth_check_server">Skontrolovať Server</string>
- <string name="auth_host_url">Adresa servera</string>
+ <string name="auth_host_url">Adresa servera https://...</string>
<string name="auth_username">Používateľské meno</string>
<string name="auth_password">Heslo</string>
<string name="auth_register">Ste nový v %1$s?</string>
<string name="filedetails_download">Stiahnuť</string>
<string name="filedetails_sync_file">Obnoviť súbor</string>
<string name="filedetails_renamed_in_upload_msg">Súbor bol premenovaný na %1$s počas nahrávania</string>
+ <string name="action_share_file">Zdieľať linku</string>
<string name="common_yes">Áno</string>
<string name="common_no">Nie</string>
<string name="common_ok">OK</string>
<string name="sync_foreign_files_forgotten_ticker">Niektoré lokálne súbory boli zabudnuté</string>
<string name="sync_foreign_files_forgotten_content">%1$d súborov z priečinka %2$s nemožno skopírovať do</string>
<string name="sync_foreign_files_forgotten_explanation">Od verzie 1.3.16 sú súbory nahrané z tohoto zariadenia, skopírovné do lokálneho priečinka %1$s, aby sa zabránilo strate dát, keď je jeden súbor synchronizovný s viacerými účtami.\n\nPre túto zmenu, všetky súbory nahraté v predchádzajúcich verziách tejto aplikácie boli skopírované do priečinka %2$s. Je nám to ľúto, chyba zabránila dokončeniu tejto operácie počas synchronizácie účtu. Súbor(y) môžete ponechať v súčasnom stave a zmazať odkaz na %3$s, alebo presunúť súbor(y) do priečinka %1$s a zachovať odkaz na %4$s.\n\nĎalej lokálny súbor(y) a vzdialený súbor(y) sú spojené v priečinku %5$s.</string>
+ <string name="sync_current_folder_was_removed">Priečinok %1$s už existuje</string>
<string name="foreign_files_move">Premiestniť všetko</string>
<string name="foreign_files_success">Všetky súbory boli premiestnené</string>
<string name="foreign_files_fail">Niektoré súbory nebolo možné premiestniť</string>
<string name="sync_file_fail_msg">Vzdialený súbor nemohol byť prekontrolovaný</string>
<string name="sync_file_nothing_to_do_msg">Obsah súboru je zosynchronizovaný</string>
<string name="create_dir_fail_msg">Priečinok nie je možné vytvoriť</string>
+ <string name="filename_forbidden_characters">Zakázané znaky: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Počkať chvíľu</string>
<string name="filedisplay_unexpected_bad_get_content">Neočakávaný problém; skúste vybrať súbor inou aplikáciou</string>
<string name="filedisplay_no_file_selected">Nebol vybraný súbor</string>
+ <string name="oauth_check_onoff">Prihlásiť sa z oAuth2</string>
<string name="oauth_login_connection">Pripájam sa na oAuth2 server…</string>
<string name="ssl_validator_header">Identitu stránky nemožno overiť</string>
<string name="ssl_validator_reason_cert_not_trusted">- Certifikát servera nie je overený</string>
<string name="preview_image_description">Ukážka obrazu</string>
<string name="preview_image_error_unknown_format">Obraz nemôže byť zobrazený</string>
<string name="error__upload__local_file_not_copied">%1$s nemožno skopírovať do lokálneho priečinka %2$s</string>
+ <string name="actionbar_failed_instant_upload">Okamžité odoslanie zlyhalo</string>
<string name="failed_upload_headline_text">Zlyhané instantné nahratia</string>
<string name="failed_upload_headline_hint">Zhrnutie všetkých zlyhaných nahratí</string>
<string name="failed_upload_all_cb">vybrať všetko</string>
<?xml version='1.0' encoding='UTF-8'?>
-<resources/>
+<resources>
+ <string name="actionbar_settings">Nastavenia</string>
+ <string name="prefs_category_general">Všeobecné</string>
+ <string name="filedetails_download">Stiahnuť</string>
+ <string name="common_cancel">Zrušiť</string>
+</resources>
<string name="recommend_subject">Preizkusi %1$s na pametnem telefonu!</string>
<string name="recommend_text">Želim ti predstaviti program %1$s za pametni telefon!\nPrejmeš ga lahko na: %2$s</string>
<string name="auth_check_server">Preveri strežnik</string>
- <string name="auth_host_url">Naslov strežnika</string>
+ <string name="auth_host_url">Naslov strežnika https://…</string>
<string name="auth_username">Uporabniško ime</string>
<string name="auth_password">Geslo</string>
<string name="auth_register">Ali ste novi uporabnik sistema %1$s?</string>
<string name="filedetails_download">Prejmi</string>
<string name="filedetails_sync_file">Osveži datoteko</string>
<string name="filedetails_renamed_in_upload_msg">Datoteka je bila med nalaganjem preimenovana v %1$s</string>
+ <string name="action_share_file">Povezava za souporabo</string>
+ <string name="action_unshare_file">Odstrani možnost souporabe</string>
<string name="common_yes">Da</string>
<string name="common_no">Ne</string>
<string name="common_ok">V redu</string>
<string name="sync_fail_in_favourites_content">Vsebine %1$d datotek ni bilo mogoče uskladiti (zaznanih je %2$d sporov)</string>
<string name="sync_foreign_files_forgotten_ticker">Nekatere krajevne datoteke so spregledane</string>
<string name="sync_foreign_files_forgotten_content">Skupno %1$d datotek iz mape %2$s ni mogoče kopirati v</string>
+ <string name="sync_foreign_files_forgotten_explanation">Od različice 1.3.16 dalje so datoteke, poslane iz te naprave, kopirane v krajevno mapo %1$s. S tem je preprečena izguba podatkov, kadar poteka usklajevanje ene datoteke z več računi.\n\nZaradi te spremembe so vse datoteke, poslane pred namestitvijo te različice programa, kopirane v mapo %2$s. Med usklajevanjem računov je prišlo do napake, ki je preprečila končanje tega opravila. Datoteke lahko pustite nespremenjene in odstranite povezavo do %3$s ali jih premaknete v mapo %1$s in ohranite povezavo do %4$s.\n\nNavedene so krajevne in oddaljene datoteke in mesto %5$s, kje so bile povezane.</string>
<string name="sync_current_folder_was_removed">Mapa %1$s ne obstaja več</string>
<string name="foreign_files_move">Premakni vse</string>
<string name="foreign_files_success">Vse datoteke so uspešno premaknjene na novo mesto</string>
<string name="media_err_security_ex">Prišlo je do varnostne napake med predvajanjem %1$s</string>
<string name="media_err_io_ex">Prišlo je do napake vhoda med predvajanjem %1$s</string>
<string name="media_err_unexpected">Prišlo je do nepričakovane napake med predvajanjem %1$s</string>
+ <string name="media_rewind_description">Vrni nazaj</string>
<string name="media_play_pause_description">Gumb za predvajanje in premor</string>
<string name="media_forward_description">Gumb za hitro predvajanje naprej</string>
<string name="auth_trying_to_login">Poskus prijave …</string>
<string name="auth_testing_connection">Preizkušanje povezave ...</string>
<string name="auth_not_configured_title">Napačno oblikovane nastavitve strežnika</string>
<string name="auth_account_not_new">Na napravi račun za istega uporabnika in strežnik že obstaja</string>
+ <string name="auth_account_not_the_same">Vpisan uporabnik ni lastnik tega računa</string>
<string name="auth_unknown_error_title">Prišlo je do neznane napake!</string>
<string name="auth_unknown_host_title">Gostitelja ni mogoče najti</string>
<string name="auth_incorrect_path_title">Primerka strežnika ni mogoče najti</string>
<string name="auth_expired_basic_auth_toast">Vnesite trenutno geslo</string>
<string name="auth_expired_saml_sso_token_toast">Seja je potekla. Ponovno je treba vzpostaviti povezavo.</string>
<string name="auth_connecting_auth_server">Poteka povezovanje z overitvenim strežnikom ...</string>
+ <string name="auth_unsupported_auth_method">Strežnik ne podpira tega načina overitve</string>
<string name="auth_unsupported_multiaccount">%1$s ne omogoča podpore več računom</string>
<string name="fd_keep_in_sync">Datoteka naj bo posodobljena</string>
<string name="common_rename">Preimenuj</string>
<string name="wait_a_moment">Počakajte trenutek ...</string>
<string name="filedisplay_unexpected_bad_get_content">Prišlo je do nepričakovane napake. Poskusite datoteko izbrati z drugim programom.</string>
<string name="filedisplay_no_file_selected">Ni izbranih datotek</string>
+ <string name="activity_chooser_title">Pošlji povezavo ...</string>
<string name="oauth_check_onoff">Prijava z oAuth2</string>
<string name="oauth_login_connection">Poteka povezovanje s strežnikom oAuth2 ...</string>
<string name="ssl_validator_header">Istovetnosti strani ni mogoče preveriti</string>
<string name="preview_image_description">Predogled slike</string>
<string name="preview_image_error_unknown_format">Te slike ni mogoče prikazati</string>
<string name="error__upload__local_file_not_copied">Datoteke %1$s ni mogoče kopirati v krajevno mapo %2$s.</string>
+ <string name="actionbar_failed_instant_upload">Spodletelo takojšnje pošiljanje</string>
+ <string name="failed_upload_headline_text">Spodletela takojšnja pošiljanja</string>
+ <string name="failed_upload_headline_hint">Povzetek vseh spodletelih takojšnjih pošiljanj</string>
<string name="failed_upload_all_cb">izberi vse</string>
<string name="failed_upload_headline_retryall_btn">vse izbrane poskusi znova</string>
+ <string name="failed_upload_headline_delete_all_btn">izbriši izbrane iz vrste za pošiljanje</string>
+ <string name="failed_upload_retry_text">poskusi poslati sliko:</string>
<string name="failed_upload_load_more_images">Naloži več slik</string>
+ <string name="failed_upload_retry_do_nothing_text">ne pošlji takoj, saj je povezava v omrežje ni dejavna</string>
<string name="failed_upload_failure_text">Sporočilo o napaki:</string>
<string name="failed_upload_quota_exceeded_text">Preverite nastavitve strežnika. Morda je presežena vrednost količinske omejitve.</string>
+ <string name="share_link_file_no_exist">Ni mogoče omogočiti souporabe te datoteke ali mape. Prepričajte se, da obstaja ...</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_error">Prišlo je do napake med poskusom odstranjevanja souporabe te datoteke ali mape</string>
</resources>
<resources>
<string name="actionbar_upload">Ngarko</string>
<string name="actionbar_upload_files">Skedarët</string>
+ <string name="actionbar_mkdir">Krijo kartelë</string>
<string name="actionbar_settings">Parametrat</string>
<string name="prefs_category_general">Përgjithshme</string>
<string name="prefs_category_more">Më tepër</string>
+ <string name="prefs_accounts">Llogarit</string>
<string name="prefs_help">Ndihmë</string>
+ <string name="prefs_imprint">Stampoj</string>
<string name="auth_username">Përdoruesi</string>
<string name="auth_password">Kodi</string>
<string name="sync_string_files">Skedarët</string>
+ <string name="setup_btn_connect">Lidhu</string>
<string name="uploader_btn_upload_text">Ngarko</string>
+ <string name="uploader_wrn_no_account_title">Nuk u gjend asnjë llogari</string>
+ <string name="uploader_wrn_no_account_text">Nuk ka %1$s llogari në pajisjen tuaj. Ju lutemi të krijojnë një llogari të parë.</string>
+ <string name="uploader_wrn_no_account_setup_btn_text">Ndërto</string>
+ <string name="uploader_wrn_no_account_quit_btn_text">Dil</string>
+ <string name="uploader_info_uploading">Ngarko</string>
+ <string name="filedetails_select_file">Trokitje e lehtë në një dokument për të shfaqur informacion shtesë.</string>
+ <string name="filedetails_size">Dimensioni:</string>
+ <string name="filedetails_type">Tipi:</string>
+ <string name="filedetails_created">Krijuar:</string>
+ <string name="filedetails_modified">Modifikuar:</string>
<string name="filedetails_download">Shkarko</string>
<string name="common_yes">Po</string>
<string name="common_no">Jo</string>
+ <string name="common_ok">Ok</string>
<string name="common_cancel_upload">Anulo ngarkimin</string>
<string name="common_cancel">Anulo</string>
<string name="common_error">Veprim i gabuar</string>
+ <string name="common_error_unknown">Gabim panjohur</string>
+ <string name="about_title">Rreth</string>
<string name="change_password">Ndrysho fjalëkalimin</string>
+ <string name="delete_account">Fshi llogarin</string>
+ <string name="create_account">Krijo llogari</string>
+ <string name="upload_chooser_title">Ngarko nga...</string>
+ <string name="uploader_upload_in_progress_ticker">Ngarkim...</string>
+ <string name="uploader_upload_in_progress_content">%1$d%% Ngarkim %2$s</string>
+ <string name="uploader_upload_succeeded_ticker">Ngarkimi me sukses.</string>
+ <string name="uploader_upload_succeeded_content_single">%1$s u ngarkua me sukses</string>
<string name="uploader_upload_failed_ticker">Ngarkimi dështoi</string>
+ <string name="uploader_upload_failed_content_single">Ngarkimi i %1$s nuk mund te behej</string>
+ <string name="downloader_download_in_progress_ticker">Shkarkimi...</string>
+ <string name="downloader_download_in_progress_content">%1$d%% Shkarkimi %2$s</string>
+ <string name="downloader_download_succeeded_ticker">Shkarkimi me sukses</string>
+ <string name="downloader_download_succeeded_content">%1$s u shkarkua me sukses</string>
+ <string name="downloader_download_failed_ticker">Shkarkimi dështoj</string>
+ <string name="auth_secure_connection">Lidhja e Sigurt vendos</string>
<string name="common_rename">Riemërto</string>
+ <string name="common_remove">Hiq</string>
</resources>
<resources>
<string name="actionbar_upload">Pošalji</string>
<string name="actionbar_upload_files">Fajlovi</string>
+ <string name="actionbar_mkdir">Novi folder</string>
<string name="actionbar_settings">Podešavanja</string>
+ <string name="actionbar_see_details">Detaljnije</string>
+ <string name="prefs_category_general">Opšte</string>
+ <string name="prefs_accounts">Nalozi</string>
+ <string name="prefs_manage_accounts">Upravljaj nalozima</string>
<string name="prefs_help">Pomoć</string>
<string name="auth_username">Korisničko ime</string>
<string name="auth_password">Lozinka</string>
<string name="sync_string_files">Fajlovi</string>
<string name="uploader_btn_upload_text">Pošalji</string>
+ <string name="uploader_wrn_no_account_title">Nalog nije nađen</string>
+ <string name="uploader_info_uploading">Šalje se</string>
+ <string name="filedetails_size">Veličina:</string>
+ <string name="filedetails_type">Tip:</string>
<string name="filedetails_download">Preuzmi</string>
<string name="common_yes">Da</string>
<string name="common_no">Ne</string>
<string name="common_cancel">Otkaži</string>
<string name="common_error">Greška</string>
<string name="change_password">Izmeni lozinku</string>
+ <string name="delete_account">Ukloni nalog</string>
+ <string name="create_account">Novi nalog</string>
+ <string name="uploader_upload_in_progress_ticker">Otpremanje...</string>
+ <string name="uploader_upload_succeeded_ticker">Uspešno otpremljeno</string>
+ <string name="uploader_upload_failed_ticker">Otpremanje nije uspelo</string>
+ <string name="downloader_download_in_progress_ticker">Preuzimanje...</string>
+ <string name="downloader_download_succeeded_ticker">Uspešno preuzeto</string>
+ <string name="downloader_download_failed_ticker">Preuzimanje nije uspelo</string>
+ <string name="common_choose_account">Odaberite nalog</string>
+ <string name="auth_no_net_conn_title">Nema konekcije</string>
+ <string name="auth_nossl_plain_ok_title">Sigurna konekcija nije dostupna.</string>
+ <string name="auth_connection_established">Konekcija uspostavljena</string>
+ <string name="common_rename">Preimenij</string>
+ <string name="common_remove">Ukloni</string>
+ <string name="confirmation_remove_alert">Da li želite da uklonite %1$s ?</string>
+ <string name="remove_success_msg">Uklanjanje je uspelo</string>
+ <string name="remove_fail_msg">Uklanjanje nije uspelo</string>
+ <string name="wait_a_moment">Molim pričekajte</string>
+ <string name="ssl_validator_btn_details_see">Detaljnije</string>
+ <string name="ssl_validator_btn_details_hide">Sakrij</string>
+ <string name="ssl_validator_label_O">Organizacija:</string>
+ <string name="ssl_validator_label_C">Država:</string>
+ <string name="ssl_validator_label_L">Lokacija:</string>
+ <string name="ssl_validator_label_validity_from">Od:</string>
+ <string name="ssl_validator_label_validity_to">Za:</string>
+ <string name="ssl_validator_label_signature">Potpis:</string>
+ <string name="conflict_keep_both">Zadrži oboje</string>
</resources>
<string name="prefs_accounts">Налози</string>
<string name="prefs_instant_upload_summary">Тренутно отпремај фотографије сликане камером</string>
<string name="prefs_help">Помоћ</string>
- <string name="auth_host_url">Адреса сервера</string>
<string name="auth_username">Корисничко име</string>
<string name="auth_password">Лозинка</string>
<string name="sync_string_files">Фајлови</string>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<string name="recommend_subject">Försök %1$s på din smarttelefon!</string>
<string name="recommend_text">Jag vill bjuda in dig till att anända %1$s på din smarttelefon!\nLadda ner här: %2$s</string>
<string name="auth_check_server">Kontrollera Server</string>
- <string name="auth_host_url">Serveradress</string>
<string name="auth_username">Användarnamn</string>
<string name="auth_password">Lösenord</string>
<string name="auth_register">Ny på %1$s?</string>
<string name="filedetails_download">Ladda ner</string>
<string name="filedetails_sync_file">Ladda om fil</string>
<string name="filedetails_renamed_in_upload_msg">Filen bytte namn till %1$s under uppladdningen</string>
+ <string name="action_share_file">Dela länk</string>
<string name="common_yes">Ja</string>
<string name="common_no">Nej</string>
<string name="common_ok">OK</string>
<string name="sync_file_fail_msg">Fjärrfilen kunde inte kontrolleras</string>
<string name="sync_file_nothing_to_do_msg">Filinnehåll redan synkroniserat</string>
<string name="create_dir_fail_msg">Mapp kunde inte skapas</string>
+ <string name="filename_forbidden_characters">Förbjudna tecken är: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Var god vänta</string>
<string name="filedisplay_unexpected_bad_get_content">Oväntat problem; prova annat program för aktuell fil</string>
<string name="filedisplay_no_file_selected">Ingen fil vald</string>
<string name="preview_image_description">Förhandsvisa bild</string>
<string name="preview_image_error_unknown_format">Denna bild kan inte visas</string>
<string name="error__upload__local_file_not_copied">%1$s kunde inte kopieras till %2$s lokal mapp</string>
+ <string name="actionbar_failed_instant_upload">Fel vid direktuppladdning\"</string>
<string name="failed_upload_headline_text">Misslyckades vid direktuppladdning</string>
<string name="failed_upload_headline_hint">Sammanfattning av alla misslyckade uppladdningar</string>
<string name="failed_upload_all_cb">välj alla</string>
<string name="prefs_instant_upload">உடனடி பதிவேற்றலை இயலுமைப்படுத்துக</string>
<string name="prefs_instant_upload_summary">கமராவினால் எடுக்கப்பட்ட படங்கள் உடனடியாக பதிவேற்றப்பட்டன</string>
<string name="prefs_help">உதவி</string>
- <string name="auth_host_url">சேவையக முகவரி</string>
<string name="auth_username">பயனாளர் பெயர்</string>
<string name="auth_password">கடவுச்சொல்</string>
<string name="sync_string_files">கோப்புகள்</string>
<string name="actionbar_settings">అమరికలు</string>
<string name="prefs_category_more">మరిన్ని</string>
<string name="prefs_help">సహాయం</string>
- <string name="auth_host_url">సేవకి చిరునామా</string>
<string name="auth_username">వాడుకరి పేరు</string>
<string name="auth_password">సంకేతపదం</string>
<string name="common_yes">అవును</string>
<string name="prefs_instant_upload">เปิดใช้งานระบบอัพโหลดได้ทันที</string>
<string name="prefs_instant_upload_summary">อัพโหลดรูปภาพจากกล้องขึ้นไปทันที</string>
<string name="prefs_help">ช่วยเหลือ</string>
- <string name="auth_host_url">ที่อยู่เซิร์ฟเวอร์</string>
<string name="auth_username">ชื่อผู้ใช้</string>
<string name="auth_password">รหัสผ่าน</string>
<string name="sync_string_files">ไฟล์</string>
<string name="about_android">%1$s Android Uygulaması</string>
<string name="about_version">sürüm %1$s</string>
<string name="actionbar_sync">Hesabı yenile</string>
- <string name="actionbar_upload">Dosya yükle</string>
+ <string name="actionbar_upload">Yükle</string>
<string name="actionbar_upload_from_apps">Diğer uygulamalardan içerik</string>
<string name="actionbar_upload_files">Dosyalar</string>
<string name="actionbar_open_with">ile aç</string>
- <string name="actionbar_mkdir">Klasör oluştur</string>
+ <string name="actionbar_mkdir">Dizin oluştur</string>
<string name="actionbar_settings">Ayarlar</string>
<string name="actionbar_see_details">Detaylar</string>
<string name="prefs_category_general">Genel</string>
<string name="recommend_subject">%1$s uygulamasını akıllı telefonunda dene!</string>
<string name="recommend_text">Sana, akıllı telefonunda kullanmak üzere %1$s daveti yapıyorum!\nBuradan indirebilirsin: %2$s</string>
<string name="auth_check_server">Sunucuyu kontrol et</string>
- <string name="auth_host_url">Sunucu Adresi</string>
+ <string name="auth_host_url">Sunucu Adresi https://…</string>
<string name="auth_username">Kullanıcı Adi:</string>
<string name="auth_password">Şifre:</string>
<string name="auth_register">%1$s senin için yeni mi?</string>
<string name="setup_btn_connect">Bağlan</string>
<string name="uploader_btn_upload_text">Yükle</string>
<string name="uploader_top_message">Yükleme dizinini seçiniz:</string>
- <string name="uploader_wrn_no_account_title">Hesap bulunamadi</string>
- <string name="uploader_wrn_no_account_text">Cihazınızda %1$s hesabı bulunmamaktadır. Lütfen öncelikle bir hesap ayarı giriniz.</string>
+ <string name="uploader_wrn_no_account_title">Hesap bulunamadı</string>
+ <string name="uploader_wrn_no_account_text">Cihazınızda %1$s hesabı bulunmamaktadır. Lütfen öncelikle bir hesap ayarlayın.</string>
<string name="uploader_wrn_no_account_setup_btn_text">Kurulum</string>
<string name="uploader_wrn_no_account_quit_btn_text">Çıkış</string>
<string name="uploader_wrn_no_content_title">Yüklenecek içerik yok</string>
<string name="uploader_error_forbidden_content">%1$s, paylaşılan içeriğe erişim izni vermiyor</string>
<string name="uploader_info_uploading">Yükleniyor</string>
<string name="file_list_empty">Klasörde dosya yok. Yeni dosyalar yükle\'ye tıklayarak eklenebilir.</string>
- <string name="filedetails_select_file">Ek bilgileri görmek için dosyaya tıklayınız.</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="filedetails_created">Oluşturulma:</string>
<string name="filedetails_download">İndir</string>
<string name="filedetails_sync_file">Dosyayı yenile</string>
<string name="filedetails_renamed_in_upload_msg">Dosya adı, yükleme sırasında %1$s olarak değiştirildi</string>
+ <string name="action_share_file">Paylaşma bağlantısı</string>
+ <string name="action_unshare_file">Bağlantı paylaşımını kaldır</string>
<string name="common_yes">Evet</string>
<string name="common_no">Hayır</string>
<string name="common_ok">OK</string>
<string name="downloader_not_downloaded_yet">Henüz indirilemedi</string>
<string name="common_choose_account">Hesap seçiniz</string>
<string name="sync_fail_ticker">Eşitleme başarısız</string>
- <string name="sync_fail_content">%1$s Senkronizasyonu tamamlanamadı</string>
+ <string name="sync_fail_content">%1$s eşitlemesi tamamlanamadı</string>
<string name="sync_fail_content_unauthorized">%1$s için geçersiz parola</string>
<string name="sync_conflicts_in_favourites_ticker">Çakışma bulundu</string>
- <string name="sync_conflicts_in_favourites_content">%1$d korumalı senkronizasyon dosyası, senkronize edilemedi</string>
- <string name="sync_fail_in_favourites_ticker">Korunan dosya senkronizasyonu başarısız</string>
- <string name="sync_fail_in_favourites_content">%1$d dosya senkronize edilemedi (%2$d hata)</string>
+ <string name="sync_conflicts_in_favourites_content">%1$d korumalı eşitleme dosyası, eşitlenemedi</string>
+ <string name="sync_fail_in_favourites_ticker">Korunan dosya eşitlemesi başarısız</string>
+ <string name="sync_fail_in_favourites_content">%1$d dosya eşitlenemedi (%2$d çakışma)</string>
<string name="sync_foreign_files_forgotten_ticker">Bazı yerel dosyalar unutuldu</string>
- <string name="sync_foreign_files_forgotten_content">%1$d dosyalar %2$s dizinine kopyalanamadı</string>
+ <string name="sync_foreign_files_forgotten_content">%1$d dosya %2$s dizinine kopyalanamadı</string>
<string name="sync_foreign_files_forgotten_explanation">1.3.16 sürümünden sonra, bu aygıttan yüklenen dosyalar bir dosya birden fazla hesapla eşitlendiğinde veri kaybının önlenebilmesi için %1$s yerel klasörüne kopyalanır.\n\nBu değişiklikten dolayı, bu uygulamanın yüklenmiş tüm önceki sürümündeki dosyalar %2$s klasörüne kopyalandı. Ancak hesap eşitlenmesi sırasında bu işlemin tamamlanmasını engelleyen bir hata oluştu. Dosyayı/dosyaları olduğu gibi bırakabilir ve %3$s bağlantısını kaldırabilirsiniz veya dosyayı/dosyaları %1$s dizinine taşıyıp %4$s bağlantılarını koruyabilirsiniz.\n\nAşağıda listelenenler yerel dosyalar ve bağlı oldukları %5$s içerisindeki uzak dosyalardır.</string>
<string name="sync_current_folder_was_removed">%1$s klasörü artık mevcut değil.</string>
<string name="foreign_files_move">Tümünü taşı</string>
<string name="media_err_security_ex">%1$s oynatılmaya çalışılırken güvenlik hatası oluştu</string>
<string name="media_err_io_ex">%1$s oynatılmaya çalışılırken girdi hatası oluştu</string>
<string name="media_err_unexpected">%1$s oynatılmaya çalışılırken beklenmeyen bir hata oluştu</string>
- <string name="media_rewind_description">Başa sar butonu</string>
- <string name="media_play_pause_description">Oynat veya duraklat butonu</string>
- <string name="media_forward_description">Hızlı ileri butonu</string>
+ <string name="media_rewind_description">Başa sar düğmesi</string>
+ <string name="media_play_pause_description">Oynat veya duraklat düğmesi</string>
+ <string name="media_forward_description">Hızlı ileri düğmesi</string>
<string name="auth_trying_to_login">Giriş için deneniyor</string>
<string name="auth_no_net_conn_title">Ağ bağlantısı yok</string>
<string name="auth_nossl_plain_ok_title">Günvenli bağlantı mevcut değil.</string>
<string name="auth_account_not_the_same">Girilen kullanıcı bu hesabın kullanıcısı ile eşleşmiyor</string>
<string name="auth_unknown_error_title">Bilinmeyen hata oluştu.</string>
<string name="auth_unknown_host_title">Anabilgisayar bulunamadı</string>
- <string name="auth_incorrect_path_title">sunucu servisi bulunamadı.</string>
+ <string name="auth_incorrect_path_title">Sunucu örneği bulunamadı.</string>
<string name="auth_timeout_title">Sunucu çok geç cevap veriyor</string>
<string name="auth_incorrect_address_title">Hatalı biçimlendirilmiş URL</string>
<string name="auth_ssl_general_error_title">SSL başlatılmasında hata</string>
<string name="rename_local_fail_msg">Yerel kopya adlandırılamaz; farklı bir ad deneyin</string>
<string name="rename_server_fail_msg">Yeniden adlandırılma tamamlanmadı</string>
<string name="sync_file_fail_msg">Dosya teslim edilemedi</string>
- <string name="sync_file_nothing_to_do_msg">Dosyalar başarıyla senkronize edildi</string>
+ <string name="sync_file_nothing_to_do_msg">Dosya içerikleri zaten eşitlenmiş</string>
<string name="create_dir_fail_msg">Dizin oluşturulamadı</string>
<string name="filename_forbidden_characters">Yasaklı karakterler: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Bir süre bekleyin</string>
<string name="filedisplay_unexpected_bad_get_content">Beklenmeyen problem ; lütfen, dosya seçmek için diğer uygulamayı deneyin</string>
<string name="filedisplay_no_file_selected">Hiçbir dosya seçilmedi</string>
+ <string name="activity_chooser_title">Bağlantıyı gönder ...</string>
<string name="oauth_check_onoff">oAuth2 ile oturum aç</string>
<string name="oauth_login_connection">oAuth2 sunucusuna bağlanılıyor…</string>
<string name="ssl_validator_header">Bu sitenin sertifikası doğrulanamadı</string>
<string name="instant_upload_on_wifi">Resimleri sadece WiFi bağlantısında yükle</string>
<string name="instant_upload_path">/AnındaYükle</string>
<string name="conflict_title">Çakışmayı güncelle</string>
- <string name="conflict_message">Uzaktaki %s dosyası, yerel dosya ile senkronize edilemedi. işleme devam etmek sunucudaki dosyanın içeriğini değiştirecektir.</string>
+ <string name="conflict_message">Uzaktaki %s dosyası, yerel dosya ile eşitlenemedi. İşleme devam etmek sunucudaki dosyanın içeriğini değiştirecektir.</string>
<string name="conflict_keep_both">İkisini de koru</string>
<string name="conflict_overwrite">Üzerine yaz</string>
<string name="conflict_dont_upload">Yükleme</string>
<string name="failed_upload_retry_do_nothing_text">anında yükleme için çevrimiçi değilsiniz, bir şey yapma</string>
<string name="failed_upload_failure_text">Hata Mesajı:</string>
<string name="failed_upload_quota_exceeded_text">Sunucu yapılandırmanızı kontrol edin. Kotanızı aşmış olabilirsiniz.</string>
+ <string name="share_link_no_support_share_api">Üzgünüz, paylaşım sunucunuzda etkin değil. Lütfen yöneticinizle iletişime geçin.</string>
+ <string name="share_link_file_no_exist">Bu dosya veya klasör paylaşılamıyor. Lütfen mevcut olup olmadığını denetleyin</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_error">Bu dosya veya klasör paylaşımı kaldırılmaya çalışılırken bir hata oluştu</string>
</resources>
<string name="prefs_accounts">ھېساباتلار</string>
<string name="prefs_help">ياردەم</string>
<string name="prefs_feedback">قايتۇرما ئىنكاس</string>
- <string name="auth_host_url">مۇلازىمېتىر ئادرىسى</string>
<string name="auth_username">ئىشلەتكۈچى ئاتى</string>
<string name="auth_password">ئىم</string>
<string name="sync_string_files">ھۆججەتلەر</string>
<string name="prefs_help">Допомога</string>
<string name="prefs_feedback">Зворотній зв\'язок</string>
<string name="prefs_imprint">Відбиток</string>
- <string name="auth_host_url">Адреса сервера</string>
<string name="auth_username">Ім\'я користувача</string>
<string name="auth_password">Пароль</string>
<string name="sync_string_files">Файли</string>
<string name="filedetails_modified">Змінено:</string>
<string name="filedetails_download">Завантажити</string>
<string name="filedetails_renamed_in_upload_msg">Файл був переіменований в %1$s протягом вивантаження</string>
+ <string name="action_share_file">Опублікувати посилання</string>
<string name="common_yes">Так</string>
<string name="common_no">Ні</string>
<string name="common_ok">OK</string>
--- /dev/null
+<?xml version='1.0' encoding='UTF-8'?>
+<resources/>
<string name="prefs_log_summary_history">Hiển thị các nhật trình đã được ghi nhận lại</string>
<string name="prefs_log_delete_history_button">Xóa lịch sử</string>
<string name="prefs_help">Giúp đỡ</string>
+ <string name="prefs_recommend">Giới thiệu đến bạn bè</string>
+ <string name="prefs_feedback">Phản hồi</string>
+ <string name="prefs_imprint">Đánh dấu</string>
+ <string name="recommend_subject">Thử %1$s trên smartphone của bạn!</string>
+ <string name="recommend_text">Tôi muốn mời bạn sử dụng %1$s trên smartphone của bạn!\nTải về tại đây: %2$s</string>
<string name="auth_check_server">Kiểm tra máy chủ</string>
- <string name="auth_host_url">Địa chỉ máy chủ</string>
+ <string name="auth_host_url">Địa chỉ máy chủ https://…</string>
<string name="auth_username">Tên người dùng</string>
<string name="auth_password">Mật khẩu</string>
<string name="auth_register">Lần đầu mới đến %1$s?</string>
<string name="uploader_wrn_no_content_text">Không có nội dung được nhận. Không có gì để tải lên.</string>
<string name="uploader_error_forbidden_content">%1$s không cho phép truy cập vào các nội dung chia sẻ</string>
<string name="uploader_info_uploading">Đang tải lên</string>
- <string name="file_list_empty">Không có các tập tin trong thư mục này ⏎ tập tin mới có thể được thêm vào với tùy chọn trình đơn \"Tải lên\".</string>
+ <string name="file_list_empty">Không có tập tin trong thư mục này. tập tin mới có thể được thêm vào với tùy chọn \"Tải lên\".</string>
<string name="filedetails_select_file">Tap vào một tập tin để hiển thị thêm thông tin</string>
<string name="filedetails_size">Kích thước:</string>
<string name="filedetails_type">Loại:</string>
<string name="filedetails_download">Tải về</string>
<string name="filedetails_sync_file">Cập nhật lại tập tin</string>
<string name="filedetails_renamed_in_upload_msg">Tập tin đã bị đổi tên thành %1$s trong quá trình tải lên</string>
+ <string name="action_share_file">Chia sẻ liên kết</string>
<string name="common_yes">Yes</string>
<string name="common_no">Không</string>
<string name="common_ok">Chấp nhận</string>
<string name="sync_fail_in_favourites_content">Nội dung tập tin %1$d không thể đồng bộ (%2$d xung đột)</string>
<string name="sync_foreign_files_forgotten_ticker">Một số tập tin cục bộ bị quên</string>
<string name="sync_foreign_files_forgotten_content">Các tập tin %1$d ở ngoài thư mục %2$s không thể được chép vào</string>
+ <string name="sync_foreign_files_forgotten_explanation">\"Từ phiên bản 1.3.16, các tập tin tải lên từ thiết bị này được chép vào thư mục cục bộ %1$s để đề phòng mất dữ liệu khi một tập tin được đồng bộ với nhiều tài khoản.\n\nDo thay đổi này, tất cả các tập tin được tải lên ở phiên bản trước của ứng dụng này được chép vào thư mục %2$s. Tuy nhiên, đã có lỗi ngăn cản việc này trong lúc đồng bộ tài khoản. Bạn có thể để (các) tập tin như vậy và xóa liên kết đến %3$s, hoặc di chuyển (các) tập tin vào thư mục %1$s và giữ lại liên kết tới %4$s.\n\nDanh sách bên dưới là (các) tập tin cục bộ và (các) tập tin ở xa trong %5$s mà chúng được liên kết tới.</string>
+ <string name="sync_current_folder_was_removed">Thư mục %1$s không còn tồn tại</string>
<string name="foreign_files_move">Di chuyển tất cả</string>
<string name="foreign_files_success">Tất cả tập tin đã được chuyển đi</string>
<string name="foreign_files_fail">Một vài tập tin không thể chuyển đi</string>
<string name="media_err_unsupported">Codec của media không được hỗ trợ</string>
<string name="media_err_io">Không thể đọc dữ liệu từ tập tin media</string>
<string name="media_err_malformed">Tập tin media không được mã hóa đúng quy định</string>
+ <string name="media_err_timeout">Tạm ngừng trong khi đang cố gắng chạy</string>
<string name="media_err_invalid_progressive_playback">Không thể phân luồng dữ liệu tập tin media</string>
<string name="media_err_unknown">Tập tin media không thể được phát với trình phát media của stock</string>
<string name="media_err_security_ex">Lỗi bảo mật khi cố phát %1$s</string>
<string name="auth_connection_established">Kết nối đã thiết lập</string>
<string name="auth_testing_connection">Đang kiểm tra kết nối...</string>
<string name="auth_not_configured_title">Thay đổi cấu hình máy chủ </string>
+ <string name="auth_account_not_new">Một tài khoản với cùng tên người dùng và máy chủ đã tồn tại trong thiết bị</string>
+ <string name="auth_account_not_the_same">Tên người dùng đã nhập không đúng với tên người dùng của tài khoản này</string>
<string name="auth_unknown_error_title">Không xác định được lỗi!</string>
<string name="auth_unknown_host_title">Không thể tìm thấy máy chủ</string>
<string name="auth_incorrect_path_title">Phiên bản máy chủ không tìm thấy</string>
<string name="auth_timeout_title">Máy chủ đã quá dài để đáp ứng</string>
<string name="auth_incorrect_address_title">Thay đổi URL</string>
<string name="auth_ssl_general_error_title">SSL khởi tạo thất bại</string>
+ <string name="auth_ssl_unverified_server_title">không thể xác minh danh tính của máy chủ SSL</string>
<string name="auth_bad_oc_version_title">Không chấp nhận phiên bản máy chủ</string>
<string name="auth_wrong_connection_title">Không thể thiết lập kết nối</string>
<string name="auth_secure_connection">Kết nối an toàn đã được thiết lập</string>
<string name="auth_oauth_error">Xác nhận không thành công</string>
<string name="auth_oauth_error_access_denied">Từ chối truy cập vì xác nhận từ phía máy chủ</string>
<string name="auth_wtf_reenter_URL">Trạng thái không rõ, vui lòng điền lại đường dẫn liên kết một lần nữa</string>
+ <string name="auth_expired_oauth_token_toast">Hạn xác nhận của bạn đã hết. Vui lòng, xác nhận lại</string>
<string name="auth_expired_basic_auth_toast">Vui lòng điền vào mật khẩu hiện tại của bạn</string>
+ <string name="auth_expired_saml_sso_token_toast">Phiên làm việc của bạn đã kết thúc. Vui lòng kết nối lại</string>
+ <string name="auth_connecting_auth_server">Đang kết nối đến máy chủ xác thực...</string>
+ <string name="auth_unsupported_auth_method">Máy chủ không hổ trợ phương thức xác thực này</string>
+ <string name="auth_unsupported_multiaccount">%1$s không hỗ trợ nhiều tài khoản</string>
<string name="fd_keep_in_sync">Giữ tập tin cập nhật</string>
<string name="common_rename">Sửa tên</string>
<string name="common_remove">Xóa</string>
<string name="sync_file_fail_msg">Tập tin từ xa không được kiểm tra</string>
<string name="sync_file_nothing_to_do_msg">Nội dung tập tin đã đồng bộ</string>
<string name="create_dir_fail_msg">Thư mục không thể được tạo</string>
+ <string name="filename_forbidden_characters">Những kí tự không được dùng: / \\ < > : \" | ? *</string>
<string name="wait_a_moment">Chờ một lúc</string>
<string name="filedisplay_unexpected_bad_get_content">Vấn đề bất ngờ ; hãy thử ứng dụng khác để chọn tập tin</string>
<string name="filedisplay_no_file_selected">Không có tập tin nào được chọn</string>
+ <string name="oauth_check_onoff">Đăng nhập bằng oAuth2.</string>
<string name="oauth_login_connection">Đang kết nối đến máy chủ oAuth2...</string>
<string name="ssl_validator_header">Không thể xác minh danh tính của site</string>
<string name="ssl_validator_reason_cert_not_trusted">Chứng chỉ này không đáng tin cậy</string>
<string name="preview_image_description">Xem trước hình ảnh</string>
<string name="preview_image_error_unknown_format">Không thể hiển thị hình ảnh</string>
<string name="error__upload__local_file_not_copied">%1$s không thể sao chép vào %2$s thư mục cục bộ</string>
+ <string name="actionbar_failed_instant_upload">Tải lên nhanh bị lỗi</string>
<string name="failed_upload_headline_text">Tải lên nhanh bị lỗi</string>
<string name="failed_upload_headline_hint">Tóm tắt tất cả các tải lên nhanh bị lỗi</string>
<string name="failed_upload_all_cb">chọn tất cả</string>
<?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="prefs_instant_upload">开启即时上传</string>
<string name="prefs_instant_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_feedback">反馈</string>
<string name="prefs_imprint">版本说明</string>
<string name="auth_check_server">检查服务器</string>
- <string name="auth_host_url">服务器地址</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="uploader_btn_upload_text">上传</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_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">OK</string>
<string name="auth_wrong_connection_title">无法建立连接</string>
<string name="auth_secure_connection">加密连接已建立</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_basic_auth_toast">请输入当前密码:</string>
<string name="fd_keep_in_sync">保证文件更新</string>
<string name="common_rename">重命名</string>
<string name="wait_a_moment">请稍候</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_login_connection">连接oAuth2 服务器...</string>
<string name="ssl_validator_header">站点身份无法验证</string>
<string name="ssl_validator_reason_cert_not_trusted">不受信任的服务器证书</string>
<string name="ssl_validator_reason_cert_expired">服务器证书过期</string>
<string name="failed_upload_retry_do_nothing_text">不在线时不开启即时上传</string>
<string name="failed_upload_failure_text">失败消息</string>
<string name="failed_upload_quota_exceeded_text">请检查服务器设置。可能走出配额。</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_error">解除文件或目录共享时出错</string>
</resources>
<string name="prefs_feedback">反饋</string>
<string name="prefs_imprint">法律聲明</string>
<string name="auth_check_server">檢查伺服器</string>
- <string name="auth_host_url">伺服器位址</string>
<string name="auth_username">使用者名稱</string>
<string name="auth_password">密碼</string>
<string name="auth_register">新增到 %1$s?</string>
<string name="filedetails_download">下載</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="common_yes">是</string>
<string name="common_no">否</string>
<string name="common_ok">好</string>
<string name ="db_name">ownCloud</string>
<string name ="data_folder">owncloud</string>
<string name ="log_name">Owncloud_</string>
+ <string name ="default_display_name_for_root_folder">/</string>
<!-- URLs and flags related -->
<string name="server_url"></string>
<bool name="show_server_url_input">true</bool>
<bool name="show_welcome_link">true</bool>
<string name="welcome_link_url">"https://owncloud.com/mobile/new"</string>
+ <string name="share_api_link"></string>
<!-- Flags to setup the authentication methods available in the app -->
<string name="auth_method_oauth2">off</string>
<string name="recommend_text">"I want to invite you to use %1$s on your smartphone!\nDownload here: %2$s"</string>
<string name="auth_check_server">Check Server</string>
- <string name="auth_host_url">Server address</string>
+ <string name="auth_host_url">Server address https://…</string>
<string name="auth_username">Username</string>
<string name="auth_password">Password</string>
<string name="auth_register">New to %1$s?</string>
<string name="filedetails_download">Download</string>
<string name="filedetails_sync_file">Refresh file</string>
<string name="filedetails_renamed_in_upload_msg">File was renamed to %1$s during upload</string>
+ <string name="action_share_file">Share link</string>
+ <string name="action_unshare_file">Unshare link</string>
<string name="common_yes">Yes</string>
<string name="common_no">No</string>
<string name="common_ok">OK</string>
<string name="wait_a_moment">Wait a moment</string>
<string name="filedisplay_unexpected_bad_get_content">"Unexpected problem ; please select the file from a different app"</string>
<string name="filedisplay_no_file_selected">No file was selected</string>
+ <string name="activity_chooser_title">Send link to …</string>
<string name="oauth_check_onoff">Login with oAuth2</string>
<string name="oauth_login_connection">Connecting to oAuth2 server…</string>
<string name="failed_upload_retry_do_nothing_text">do nothing you are not online for instant upload</string>
<string name="failed_upload_failure_text">Failure Message: </string>
<string name="failed_upload_quota_exceeded_text">Please check your server configuration,maybe your quota is exceeded.</string>
+
+ <string name="share_link_no_support_share_api">Sorry, sharing is not enabled on your server. Please contact your administrator.</string>
+ <string name="share_link_file_no_exist">Unable to share this file or folder. Please, make sure it exists</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 this file or folder. It does not exist.</string>
+ <string name="unshare_link_file_error">An error occurred while trying to unshare this file or folder</string>
</resources>
call git submodule init
call git submodule update
call android.bat update project -p actionbarsherlock\library -n ActionBarSherlock
-call android.bat update project -p oc_framework -n ownCloudFramework
+call android.bat update lib-project -p owncloud-android-library
call android.bat update project -p .
call android.bat update project -p oc_jb_workaround
copy /Y third_party\android-support-library\android-support-v4.jar actionbarsherlock\library\libs\android-support-v4.jar
git submodule init
git submodule update
android update project -p actionbarsherlock/library -n ActionBarSherlock
-android update project -p oc_framework -n ownCloudFramework
+android update lib-project -p owncloud-android-library
android update project -p .
android update project -p oc_jb_workaround
cp third_party/android-support-library/android-support-v4.jar actionbarsherlock/library/libs/android-support-v4.jar
import android.os.Handler;
import android.widget.Toast;
-import com.owncloud.android.oc_framework.accounts.AccountTypeUtils;
+import com.owncloud.android.lib.accounts.AccountTypeUtils;
import com.owncloud.android.utils.Log_OC;
package com.owncloud.android.authentication;\r
\r
import com.owncloud.android.MainApp;\r
-import com.owncloud.android.oc_framework.accounts.AccountTypeUtils;\r
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;\r
+import com.owncloud.android.lib.accounts.AccountTypeUtils;\r
+import com.owncloud.android.lib.utils.OwnCloudVersion;\r
\r
import android.accounts.Account;\r
import android.accounts.AccountManager;\r
import android.text.Editable;\r
import android.text.InputType;\r
import android.text.TextWatcher;\r
+import android.util.Log;\r
import android.view.KeyEvent;\r
import android.view.MotionEvent;\r
import android.view.View;\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.oc_framework.accounts.AccountTypeUtils;\r
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;\r
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;\r
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;\r
-import com.owncloud.android.operations.ExistenceCheckOperation;\r
+import com.owncloud.android.lib.accounts.AccountTypeUtils;\r
+import com.owncloud.android.lib.accounts.OwnCloudAccount;\r
+import com.owncloud.android.lib.network.OwnCloudClientFactory;\r
+import com.owncloud.android.lib.network.OwnCloudClient;\r
import com.owncloud.android.operations.OAuth2GetAccessToken;\r
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;\r
-import com.owncloud.android.operations.OwnCloudServerCheckOperation;\r
-import com.owncloud.android.oc_framework.operations.RemoteOperation;\r
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;\r
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;\r
+
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;\r
+import com.owncloud.android.lib.operations.remote.OwnCloudServerCheckOperation;\r
+import com.owncloud.android.lib.operations.common.RemoteOperation;\r
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;\r
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;\r
+import com.owncloud.android.lib.operations.remote.ExistenceCheckRemoteOperation;\r
+import com.owncloud.android.lib.operations.remote.GetUserNameRemoteOperation;\r
+
import com.owncloud.android.ui.dialog.SamlWebViewDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog;\r
import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;\r
import com.owncloud.android.utils.Log_OC;\r
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;\r
+import com.owncloud.android.lib.utils.OwnCloudVersion;\r
\r
/**\r
* This Activity is used to add an ownCloud account to the App\r
private static final String KEY_AUTH_STATUS_TEXT = "AUTH_STATUS_TEXT";\r
private static final String KEY_AUTH_STATUS_ICON = "AUTH_STATUS_ICON";\r
private static final String KEY_REFRESH_BUTTON_ENABLED = "KEY_REFRESH_BUTTON_ENABLED";\r
- \r
- private static final String KEY_OC_USERNAME_EQUALS = "oc_username=";\r
+ private static final String KEY_IS_SHARED_SUPPORTED = "KEY_IS_SHARE_SUPPORTED";\r
\r
private static final String AUTH_ON = "on";\r
private static final String AUTH_OFF = "off";\r
\r
private String mHostBaseUrl;\r
private OwnCloudVersion mDiscoveredVersion;\r
+ private boolean mIsSharedSupported;\r
\r
private String mAuthMessageText;\r
private int mAuthMessageVisibility, mServerStatusText, mServerStatusIcon;\r
private final Handler mHandler = new Handler();\r
private Thread mOperationThread;\r
private OwnCloudServerCheckOperation mOcServerChkOperation;\r
- private ExistenceCheckOperation mAuthCheckOperation;\r
+ private ExistenceCheckRemoteOperation mAuthCheckOperation;\r
private RemoteOperationResult mLastSslUntrustedServerResult;\r
\r
private Uri mNewCapturedUriFromOAuth2Redirection;\r
mServerIsChecked = false;\r
mIsSslConn = false;\r
mAuthStatusText = mAuthStatusIcon = 0;\r
+ mIsSharedSupported = false;\r
\r
/// retrieve extras from intent\r
mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);\r
mHostUrlInput.setText(mHostBaseUrl);\r
String userName = mAccount.name.substring(0, mAccount.name.lastIndexOf('@'));\r
mUsernameInput.setText(userName);\r
+ mIsSharedSupported = Boolean.getBoolean(mAccountMgr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_SHARE_API));\r
+ \r
}\r
initAuthorizationMethod(); // checks intent and setup.xml to determine mCurrentAuthorizationMethod\r
mJustCreated = true;\r
\r
/// server data\r
String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);\r
+ mIsSharedSupported = savedInstanceState.getBoolean(KEY_IS_SHARED_SUPPORTED, false);\r
if (ocVersion != null) {\r
mDiscoveredVersion = new OwnCloudVersion(ocVersion);\r
}\r
outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());\r
}\r
outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);\r
+ outState.putBoolean(KEY_IS_SHARED_SUPPORTED, mIsSharedSupported);\r
\r
/// account data, if updating\r
if (mAccount != null) {\r
getString(R.string.oauth2_redirect_uri), \r
getString(R.string.oauth2_grant_type),\r
queryParameters);\r
- //WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
- WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
+ //OwnCloudClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth2_url_endpoint_access)), getApplicationContext());\r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mOAuthTokenEndpointText.getText().toString().trim()), getApplicationContext(), true);\r
operation.execute(client, this, mHandler);\r
}\r
\r
\r
mServerIsValid = false;\r
mServerIsChecked = false;\r
+ mIsSharedSupported = false;\r
mOkButton.setEnabled(false);\r
mDiscoveredVersion = null;\r
hideRefreshButton();\r
mServerStatusIcon = R.drawable.progress_small;\r
showServerStatus();\r
mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this);\r
- WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(uri), this, true);\r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(uri), this, true);\r
mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);\r
} else {\r
mServerStatusText = 0;\r
showDialog(DIALOG_LOGIN_PROGRESS);\r
\r
/// test credentials accessing the root folder\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+ mAuthCheckOperation = new ExistenceCheckRemoteOperation("", this, false);\r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
client.setBasicCredentials(username, password);\r
mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
}\r
String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, mAuthTokenType);\r
\r
/// test credentials accessing the root folder\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
+ mAuthCheckOperation = new ExistenceCheckRemoteOperation("", this, false);\r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, false);\r
mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);\r
\r
}\r
} else if (operation instanceof OAuth2GetAccessToken) {\r
onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);\r
\r
- } else if (operation instanceof ExistenceCheckOperation) {\r
+ } else if (operation instanceof ExistenceCheckRemoteOperation) {\r
if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {\r
onSamlBasedFederatedSingleSignOnAuthorizationStart(operation, result);\r
\r
} else {\r
- onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);\r
+ onAuthorizationCheckFinish((ExistenceCheckRemoteOperation)operation, result);\r
}\r
+ } else if (operation instanceof GetUserNameRemoteOperation) {\r
+ onGetUserNameFinish((GetUserNameRemoteOperation) operation, result);\r
+ \r
}\r
+ \r
}\r
- \r
- \r
+\r
+ private void onGetUserNameFinish(GetUserNameRemoteOperation operation, RemoteOperationResult result) {\r
+ if (result.isSuccess()) {\r
+ boolean success = false;\r
+ String username = operation.getUserName();\r
+ \r
+ if ( mAction == ACTION_CREATE) {\r
+ mUsernameInput.setText(username);\r
+ success = createAccount();\r
+ } else {\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
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.d(TAG, result.getLogMessage());\r
+ } else {\r
+ updateToken();\r
+ success = true;\r
+ }\r
+ }\r
+ \r
+ if (success)\r
+ finish();\r
+ } else {\r
+ updateAuthStatusIconAndText(result);\r
+ showAuthStatus();\r
+ Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());\r
+ }\r
+ \r
+ }\r
+\r
private void onSamlBasedFederatedSingleSignOnAuthorizationStart(RemoteOperation operation, RemoteOperationResult result) {\r
try {\r
dismissDialog(DIALOG_LOGIN_PROGRESS);\r
\r
/// allow or not the user try to access the server\r
mOkButton.setEnabled(mServerIsValid);\r
+ \r
+ /// retrieve if is supported the Share API\r
+ mIsSharedSupported = operation.isSharedSupported();\r
\r
} // else nothing ; only the last check operation is considered; \r
// multiple can be triggered if the user amends a URL before a previous check can be triggered\r
/// time to test the retrieved access token on the ownCloud server\r
mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);\r
Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);\r
- mAuthCheckOperation = new ExistenceCheckOperation("", this, false);\r
- WebdavClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
+ mAuthCheckOperation = new ExistenceCheckRemoteOperation("", this, false);\r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);\r
client.setBearerCredentials(mAuthToken);\r
mAuthCheckOperation.execute(client, this, mHandler);\r
\r
* @param operation Access check performed.\r
* @param result Result of the operation.\r
*/\r
- private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {\r
+ private void onAuthorizationCheckFinish(ExistenceCheckRemoteOperation operation, RemoteOperationResult result) {\r
try {\r
dismissDialog(DIALOG_LOGIN_PROGRESS);\r
} catch (IllegalArgumentException e) {\r
success = createAccount();\r
\r
} else {\r
- success = updateToken();\r
+ updateToken();\r
+ success = true;\r
}\r
\r
if (success) {\r
* Sets the proper response to get that the Account Authenticator that started this activity saves \r
* a new authorization token for mAccount.\r
*/\r
- private boolean updateToken() {\r
+ private void updateToken() {\r
Bundle response = new Bundle();\r
response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);\r
response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);\r
mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);\r
\r
} else if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(mAuthTokenType)) {\r
- String username = getUserNameForSamlSso();\r
- if (!mUsernameInput.getText().toString().equals(username)) {\r
- // fail - not a new account, but an existing one; disallow\r
- RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME); \r
- updateAuthStatusIconAndText(result);\r
- showAuthStatus();\r
- Log_OC.d(TAG, result.getLogMessage());\r
- \r
- return false;\r
- }\r
\r
response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);\r
// the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention\r
}\r
setAccountAuthenticatorResult(response);\r
\r
- return true;\r
}\r
\r
\r
\r
Uri uri = Uri.parse(mHostBaseUrl);\r
String username = mUsernameInput.getText().toString().trim();\r
- if (isSaml) {\r
- username = getUserNameForSamlSso();\r
- \r
- } else if (isOAuth) {\r
+ if (isOAuth) {\r
username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();\r
} \r
String accountName = username + "@" + uri.getHost();\r
/// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA\r
mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_VERSION, mDiscoveredVersion.toString());\r
mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_BASE_URL, mHostBaseUrl);\r
+ mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_SHARE_API, Boolean.toString(mIsSharedSupported));\r
if (isSaml) {\r
mAccountMgr.setUserData(mAccount, OwnCloudAccount.Constants.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); \r
} else if (isOAuth) {\r
}\r
}\r
\r
- \r
- private String getUserNameForSamlSso() {\r
- if (mAuthToken != null) {\r
- String [] cookies = mAuthToken.split(";");\r
- for (int i=0; i<cookies.length; i++) {\r
- if (cookies[i].startsWith(KEY_OC_USERNAME_EQUALS )) {\r
- String value = Uri.decode(cookies[i].substring(KEY_OC_USERNAME_EQUALS.length()));\r
- return value;\r
- }\r
- }\r
- }\r
- return "";\r
- }\r
-\r
\r
/**\r
* {@inheritDoc}\r
}\r
\r
\r
- public void onSamlDialogSuccess(String sessionCookie){\r
+ public void onSamlDialogSuccess(String sessionCookie) {\r
mAuthToken = sessionCookie;\r
\r
if (sessionCookie != null && sessionCookie.length() > 0) {\r
mAuthToken = sessionCookie;\r
- boolean success = false;\r
- if (mAction == ACTION_CREATE) {\r
- success = createAccount();\r
- \r
- } else {\r
- success = updateToken();\r
- }\r
- if (success) {\r
- finish();\r
- }\r
+\r
+ GetUserNameRemoteOperation getUserOperation = new GetUserNameRemoteOperation(); \r
+ OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient(Uri.parse(mHostBaseUrl), getApplicationContext(), true);\r
+ client.setSsoSessionCookie(mAuthToken);\r
+ getUserOperation.execute(client, this, mHandler);\r
}\r
\r
\r
}\r
return super.onTouchEvent(event);\r
}\r
+ \r
}\r
import com.owncloud.android.utils.Log_OC;
-
import android.graphics.Bitmap;
+import android.net.http.SslError;
import android.os.Handler;
import android.os.Message;
+import android.view.KeyEvent;
import android.view.View;
import android.webkit.CookieManager;
+import android.webkit.HttpAuthHandler;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.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() {
public void run() {
SsoWebViewClientListener listener = mListenerRef.get();
if (listener != null) {
+ // Send Cookies to the listener
listener.onSsoFinished(cookies);
}
}
});
}
- }
-
+ }
}
- /*
+
@Override
public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
@Override
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
Log_OC.d(TAG, "onReceivedSslError : " + error);
+ handler.proceed();
}
@Override
Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
return false;
}
- */
+
}
import com.owncloud.android.MainApp;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.common.ShareType;
+import com.owncloud.android.lib.utils.FileUtils;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
-
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
+
boolean sameRemotePath = fileExists(file.getRemotePath());
if (sameRemotePath ||
fileExists(file.getFileId()) ) { // for renamed files; no more delete and create
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
boolean existsByPath = fileExists(file.getRemotePath());
if (existsByPath || fileExists(file.getFileId())) {
cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.keepInSync() ? 1 : 0);
cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, folder.isShareByLink() ? 1 : 0);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
+
operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
withValues(cv).
withSelection( ProviderTableMeta._ID + "=?",
}
updateFolderSize(file.getParentId());
}
- if (removeLocalCopy && file.isDown()) {
+ if (removeLocalCopy && file.isDown() && file.getStoragePath() != null) {
boolean success = new File(file.getStoragePath()).delete();
if (!removeDBData && success) {
// maybe unnecessary, but should be checked TODO remove if unnecessary
}
return c;
}
+
+ private Cursor getShareCursorForValue(String key, String value) {
+ Cursor c = null;
+ if (getContentResolver() != null) {
+ c = getContentResolver()
+ .query(ProviderTableMeta.CONTENT_URI_SHARE,
+ null,
+ key + "=? AND "
+ + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
+ + "=?",
+ new String[] { value, mAccount.name }, null);
+ } else {
+ try {
+ c = getContentProviderClient().query(
+ ProviderTableMeta.CONTENT_URI_SHARE,
+ null,
+ key + "=? AND " + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
+ + "=?", new String[] { value, mAccount.name },
+ null);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Could not get file details: " + e.getMessage());
+ c = null;
+ }
+ }
+ return c;
+ }
private OCFile createFileInstance(Cursor c) {
OCFile file = null;
file.setKeepInSync(c.getInt(
c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
+ file.setShareByLink(c.getInt(
+ c.getColumnIndex(ProviderTableMeta.FILE_SHARE_BY_LINK)) == 1 ? true : false);
+ file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
}
return file;
}
+
+ /**
+ * Returns if the file/folder is shared by link or not
+ * @param path Path of the file/folder
+ * @return
+ */
+ public boolean isShareByLink(String path) {
+ Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
+ OCFile file = null;
+ if (c.moveToFirst()) {
+ file = createFileInstance(c);
+ }
+ c.close();
+ return file.isShareByLink();
+ }
+
+ /**
+ * Returns the public link of the file/folder
+ * @param path Path of the file/folder
+ * @return
+ */
+ public String getPublicLink(String path) {
+ Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path);
+ OCFile file = null;
+ if (c.moveToFirst()) {
+ file = createFileInstance(c);
+ }
+ c.close();
+ return file.getPublicLink();
+ }
+
+
+ // Methods for Shares
+ public boolean saveShare(OCShare share) {
+ boolean overriden = false;
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
+ cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
+ cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
+ cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
+ cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
+ cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
+ cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, share.getSharedWithDisplayName());
+ cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
+ cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
+ cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
+ cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
+
+ if (shareExists(share.getIdRemoteShared())) { // for renamed files; no more delete and create
+
+ overriden = true;
+ if (getContentResolver() != null) {
+ getContentResolver().update(ProviderTableMeta.CONTENT_URI_SHARE, cv,
+ ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+ new String[] { String.valueOf(share.getIdRemoteShared()) });
+ } else {
+ try {
+ getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_SHARE,
+ cv, ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+ new String[] { String.valueOf(share.getIdRemoteShared()) });
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Fail to insert insert file to database "
+ + e.getMessage());
+ }
+ }
+ } else {
+ Uri result_uri = null;
+ if (getContentResolver() != null) {
+ result_uri = getContentResolver().insert(
+ ProviderTableMeta.CONTENT_URI_SHARE, cv);
+ } else {
+ try {
+ result_uri = getContentProviderClient().insert(
+ ProviderTableMeta.CONTENT_URI_SHARE, cv);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Fail to insert insert file to database "
+ + e.getMessage());
+ }
+ }
+ if (result_uri != null) {
+ long new_id = Long.parseLong(result_uri.getPathSegments()
+ .get(1));
+ share.setId(new_id);
+ }
+ }
+
+ return overriden;
+ }
+
+ private OCShare getShareById(long id) {
+ Cursor c = getShareCursorForValue(ProviderTableMeta._ID, String.valueOf(id));
+ OCShare share = null;
+ if (c.moveToFirst()) {
+ share = createShareInstance(c);
+ }
+ c.close();
+ return share;
+ }
+
+ private OCShare getShareByRemoteId(long remoteId) {
+ Cursor c = getShareCursorForValue(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
+ OCShare share = null;
+ if (c.moveToFirst()) {
+ share = createShareInstance(c);
+ }
+ c.close();
+ return share;
+ }
+
+ public OCShare getShareByPath(String path) {
+ Cursor c = getShareCursorForValue(ProviderTableMeta.OCSHARES_PATH, path);
+ OCShare share = null;
+ if (c.moveToFirst()) {
+ share = createShareInstance(c);
+ }
+ c.close();
+ return share;
+ }
+
+ private OCShare createShareInstance(Cursor c) {
+ OCShare share = null;
+ if (c != null) {
+ share = new OCShare(c.getString(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_PATH)));
+ share.setId(c.getLong(c.getColumnIndex(ProviderTableMeta._ID)));
+ share.setFileSource(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_ITEM_SOURCE)));
+ share.setShareType(ShareType.fromValue(c.getInt(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_TYPE))));
+ share.setPermissions(c.getInt(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_PERMISSIONS)));
+ share.setSharedDate(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_SHARED_DATE)));
+ share.setExpirationDate(c.getLong(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_EXPIRATION_DATE)));
+ share.setToken(c.getString(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_TOKEN)));
+ share.setSharedWithDisplayName(c.getString(c
+ .getColumnIndex(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)));
+ share.setIsFolder(c.getInt(
+ c.getColumnIndex(ProviderTableMeta.OCSHARES_IS_DIRECTORY)) == 1 ? true : false);
+ share.setUserId(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_USER_ID)));
+ share.setIdRemoteShared(c.getLong(c.getColumnIndex(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED)));
+
+ }
+ return share;
+ }
+ private boolean shareExists(String cmp_key, String value) {
+ Cursor c;
+ if (getContentResolver() != null) {
+ c = getContentResolver()
+ .query(ProviderTableMeta.CONTENT_URI_SHARE,
+ null,
+ cmp_key + "=? AND "
+ + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER
+ + "=?",
+ new String[] { value, mAccount.name }, null);
+ } else {
+ try {
+ c = getContentProviderClient().query(
+ ProviderTableMeta.CONTENT_URI_SHARE,
+ null,
+ cmp_key + "=? AND "
+ + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?",
+ new String[] { value, mAccount.name }, null);
+ } catch (RemoteException e) {
+ Log_OC.e(TAG,
+ "Couldn't determine file existance, assuming non existance: "
+ + e.getMessage());
+ return false;
+ }
+ }
+ boolean retval = c.moveToFirst();
+ c.close();
+ return retval;
+ }
+
+ private boolean shareExists(long remoteId) {
+ return shareExists(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, String.valueOf(remoteId));
+ }
+
+ private void cleanSharedFiles() {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
+ String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
+ String [] whereArgs = new String[]{mAccount.name};
+
+ if (getContentResolver() != null) {
+ getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+
+ } else {
+ try {
+ getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in cleanSharedFiles" + e.getMessage());
+ }
+ }
+ }
+
+ private void cleanSharedFilesInFolder(OCFile folder) {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, false);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, "");
+ String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + ProviderTableMeta.FILE_PARENT + "=?";
+ String [] whereArgs = new String[] { mAccount.name , String.valueOf(folder.getFileId()) };
+
+ if (getContentResolver() != null) {
+ getContentResolver().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+
+ } else {
+ try {
+ getContentProviderClient().update(ProviderTableMeta.CONTENT_URI, cv, where, whereArgs);
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in cleanSharedFilesInFolder " + e.getMessage());
+ }
+ }
+ }
+
+ private void cleanShares() {
+ String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
+ String [] whereArgs = new String[]{mAccount.name};
+
+ if (getContentResolver() != null) {
+ getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
+
+ } else {
+ try {
+ getContentProviderClient().delete(ProviderTableMeta.CONTENT_URI_SHARE, where, whereArgs);
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in cleanShares" + e.getMessage());
+ }
+ }
+ }
+
+ public void saveShares(Collection<OCShare> shares) {
+ cleanShares();
+ if (shares != null) {
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(shares.size());
+
+ // prepare operations to insert or update files to save in the given folder
+ for (OCShare share : shares) {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
+ cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
+ cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
+ cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
+ cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
+ cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
+ cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, share.getSharedWithDisplayName());
+ cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
+ cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
+ cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
+ cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
+
+ if (shareExists(share.getIdRemoteShared())) {
+ // updating an existing file
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
+ withValues(cv).
+ withSelection( ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+ new String[] { String.valueOf(share.getIdRemoteShared()) })
+ .build());
+
+ } else {
+ // adding a new file
+ operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).withValues(cv).build());
+ }
+ }
+
+ // apply operations in batch
+ if (operations.size() > 0) {
+ @SuppressWarnings("unused")
+ ContentProviderResult[] results = null;
+ Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
+ try {
+ if (getContentResolver() != null) {
+ results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ results = getContentProviderClient().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+ }
+ }
+ }
+
+ }
+
+ public void updateSharedFiles(Collection<OCFile> sharedFiles) {
+ cleanSharedFiles();
+
+ if (sharedFiles != null) {
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(sharedFiles.size());
+
+ // prepare operations to insert or update files to save in the given folder
+ for (OCFile file : sharedFiles) {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.FILE_MODIFIED, file.getModificationTimestamp());
+ cv.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA, file.getModificationTimestampAtLastSyncForData());
+ cv.put(ProviderTableMeta.FILE_CREATION, file.getCreationTimestamp());
+ cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength());
+ cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype());
+ cv.put(ProviderTableMeta.FILE_NAME, file.getFileName());
+ cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId());
+ cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath());
+ if (!file.isFolder()) {
+ cv.put(ProviderTableMeta.FILE_STORAGE_PATH, file.getStoragePath());
+ }
+ cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
+ cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
+ cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.keepInSync() ? 1 : 0);
+ cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
+ cv.put(ProviderTableMeta.FILE_SHARE_BY_LINK, file.isShareByLink() ? 1 : 0);
+ cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
+
+ boolean existsByPath = fileExists(file.getRemotePath());
+ if (existsByPath || fileExists(file.getFileId())) {
+ // updating an existing file
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
+ withValues(cv).
+ withSelection( ProviderTableMeta._ID + "=?",
+ new String[] { String.valueOf(file.getFileId()) })
+ .build());
+
+ } else {
+ // adding a new file
+ operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI).withValues(cv).build());
+ }
+ }
+
+ // apply operations in batch
+ if (operations.size() > 0) {
+ @SuppressWarnings("unused")
+ ContentProviderResult[] results = null;
+ Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
+ try {
+ if (getContentResolver() != null) {
+ results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ results = getContentProviderClient().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+ }
+ }
+ }
+
+ }
+
+ public void removeShare(OCShare share){
+ Uri share_uri = ProviderTableMeta.CONTENT_URI_SHARE;
+ String where = ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?" + " AND " + ProviderTableMeta.FILE_PATH + "=?";
+ String [] whereArgs = new String[]{mAccount.name, share.getPath()};
+ if (getContentProviderClient() != null) {
+ try {
+ getContentProviderClient().delete(share_uri, where, whereArgs);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ } else {
+ getContentResolver().delete(share_uri, where, whereArgs);
+ }
+ }
+
+ public void saveSharesDB(ArrayList<OCShare> shares) {
+ saveShares(shares);
+
+ ArrayList<OCFile> sharedFiles = new ArrayList<OCFile>();
+
+ for (OCShare share : shares) {
+ // Get the path
+ String path = share.getPath();
+ if (share.isFolder()) {
+ path = path + FileUtils.PATH_SEPARATOR;
+ }
+
+ // Update OCFile with data from share: ShareByLink ¿and publicLink?
+ OCFile file = getFileByPath(path);
+ if (file != null) {
+ if (share.getShareType().equals(ShareType.PUBLIC_LINK)) {
+ file.setShareByLink(true);
+ sharedFiles.add(file);
+ }
+ }
+ }
+
+ updateSharedFiles(sharedFiles);
+ }
+
+
+ public void saveSharesInFolder(ArrayList<OCShare> shares, OCFile folder) {
+ cleanSharedFilesInFolder(folder);
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ operations = prepareRemoveSharesInFolder(folder, operations);
+
+ if (shares != null) {
+ // prepare operations to insert or update files to save in the given folder
+ for (OCShare share : shares) {
+ ContentValues cv = new ContentValues();
+ cv.put(ProviderTableMeta.OCSHARES_FILE_SOURCE, share.getFileSource());
+ cv.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE, share.getItemSource());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_TYPE, share.getShareType().getValue());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH, share.getShareWith());
+ cv.put(ProviderTableMeta.OCSHARES_PATH, share.getPath());
+ cv.put(ProviderTableMeta.OCSHARES_PERMISSIONS, share.getPermissions());
+ cv.put(ProviderTableMeta.OCSHARES_SHARED_DATE, share.getSharedDate());
+ cv.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE, share.getExpirationDate());
+ cv.put(ProviderTableMeta.OCSHARES_TOKEN, share.getToken());
+ cv.put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME, share.getSharedWithDisplayName());
+ cv.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY, share.isFolder() ? 1 : 0);
+ cv.put(ProviderTableMeta.OCSHARES_USER_ID, share.getUserId());
+ cv.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED, share.getIdRemoteShared());
+ cv.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER, mAccount.name);
+
+ /*
+ if (shareExists(share.getIdRemoteShared())) {
+ // updating an existing share resource
+ operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI_SHARE).
+ withValues(cv).
+ withSelection( ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + "=?",
+ new String[] { String.valueOf(share.getIdRemoteShared()) })
+ .build());
+
+ } else {
+ */
+ // adding a new share resource
+ operations.add(ContentProviderOperation.newInsert(ProviderTableMeta.CONTENT_URI_SHARE).withValues(cv).build());
+ //}
+ }
+ }
+
+ // apply operations in batch
+ if (operations.size() > 0) {
+ @SuppressWarnings("unused")
+ ContentProviderResult[] results = null;
+ Log_OC.d(TAG, "Sending " + operations.size() + " operations to FileContentProvider");
+ try {
+ if (getContentResolver() != null) {
+ results = getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ results = getContentProviderClient().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+ }
+ }
+ //}
+
+ }
+
+ private ArrayList<ContentProviderOperation> prepareRemoveSharesInFolder(OCFile folder, ArrayList<ContentProviderOperation> preparedOperations) {
+ if (folder != null) {
+ String where = ProviderTableMeta.OCSHARES_PATH + "=?" + " AND " + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
+ String [] whereArgs = new String[]{ "", mAccount.name };
+
+ Vector<OCFile> files = getFolderContent(folder);
+
+ for (OCFile file : files) {
+ whereArgs[0] = file.getRemotePath();
+ preparedOperations.add(ContentProviderOperation.newDelete(ProviderTableMeta.CONTENT_URI_SHARE)
+ .withSelection(where, whereArgs)
+ .build());
+ }
+ }
+ return preparedOperations;
+
+ /*
+ if (operations.size() > 0) {
+ try {
+ if (getContentResolver() != null) {
+ getContentResolver().applyBatch(MainApp.getAuthority(), operations);
+
+ } else {
+ getContentProviderClient().applyBatch(operations);
+ }
+
+ } catch (OperationApplicationException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage());
+ }
+ }
+ */
+
+ /*
+ if (getContentResolver() != null) {
+
+ getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE,
+ where,
+ whereArgs);
+ } else {
+ try {
+ getContentProviderClient().delete( ProviderTableMeta.CONTENT_URI_SHARE,
+ where,
+ whereArgs);
+
+ } catch (RemoteException e) {
+ Log_OC.e(TAG, "Exception deleting shares in a folder " + e.getMessage());
+ }
+ }
+ */
+ //}
+ }
}
private boolean mKeepInSync;
private String mEtag;
+
+ private boolean mShareByLink;
+ private String mPublicLink;
/**
mLastSyncDateForProperties = source.readLong();
mLastSyncDateForData = source.readLong();
mEtag = source.readString();
+ mShareByLink = source.readInt() == 1;
+ mPublicLink = source.readString();
}
@Override
dest.writeLong(mLastSyncDateForProperties);
dest.writeLong(mLastSyncDateForData);
dest.writeString(mEtag);
+ dest.writeInt(mShareByLink ? 1 : 0);
+ dest.writeString(mPublicLink);
}
/**
mKeepInSync = false;
mNeedsUpdating = false;
mEtag = null;
+ mShareByLink = false;
+ mPublicLink = null;
}
/**
@Override
public String toString() {
- String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSinc=%s etag=%s]";
+ String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, parentId=%s, keepInSync=%s etag=%s]";
asString = String.format(asString, Long.valueOf(mId), getFileName(), mMimeType, isDown(), mLocalPath, mRemotePath, Long.valueOf(mParentId), Boolean.valueOf(mKeepInSync), mEtag);
return asString;
}
this.mEtag = etag;
}
+
+ public boolean isShareByLink() {
+ return mShareByLink;
+ }
+
+ public void setShareByLink(boolean shareByLink) {
+ this.mShareByLink = shareByLink;
+ }
+
+ public String getPublicLink() {
+ return mPublicLink;
+ }
+
+ public void setPublicLink(String publicLink) {
+ this.mPublicLink = publicLink;
+ }
+
public long getLocalModificationTimestamp() {
if (mLocalPath != null && mLocalPath.length() > 0) {
File f = new File(mLocalPath);
*/\r
public class ProviderMeta {\r
\r
- /* These constants are now in MainApp\r
- public static final String AUTHORITY_FILES = "org.owncloud";\r
- public static final String DB_FILE = "owncloud.db";\r
- */\r
public static final String DB_NAME = "filelist";\r
- public static final int DB_VERSION = 5;\r
+ public static final int DB_VERSION = 6;\r
\r
private ProviderMeta() {\r
}\r
\r
static public class ProviderTableMeta implements BaseColumns {\r
- public static final String DB_NAME = "filelist";\r
+ public static final String FILE_TABLE_NAME = "filelist";\r
+ public static final String OCSHARES_TABLE_NAME = "ocshares";\r
public static final Uri CONTENT_URI = Uri.parse("content://"\r
+ MainApp.getAuthority() + "/");\r
public static final Uri CONTENT_URI_FILE = Uri.parse("content://"\r
+ MainApp.getAuthority() + "/file");\r
public static final Uri CONTENT_URI_DIR = Uri.parse("content://"\r
+ MainApp.getAuthority() + "/dir");\r
+ public static final Uri CONTENT_URI_SHARE = Uri.parse("content://"\r
+ + MainApp.getAuthority() + "/shares");\r
\r
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.owncloud.file";\r
public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.owncloud.file";\r
\r
+ // Columns of filelist table\r
public static final String FILE_PARENT = "parent";\r
public static final String FILE_NAME = "filename";\r
public static final String FILE_CREATION = "created";\r
public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data";\r
public static final String FILE_KEEP_IN_SYNC = "keep_in_sync";\r
public static final String FILE_ETAG = "etag";\r
+ public static final String FILE_SHARE_BY_LINK = "share_by_link";\r
+ public static final String FILE_PUBLIC_LINK = "public_link";\r
\r
- public static final String DEFAULT_SORT_ORDER = FILE_NAME\r
+ public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME\r
+ " collate nocase asc";\r
+ \r
+ // Columns of ocshares table\r
+ public static final String OCSHARES_FILE_SOURCE = "file_source";\r
+ public static final String OCSHARES_ITEM_SOURCE = "item_source";\r
+ public static final String OCSHARES_SHARE_TYPE = "share_type";\r
+ public static final String OCSHARES_SHARE_WITH = "shate_with";\r
+ public static final String OCSHARES_PATH = "path";\r
+ public static final String OCSHARES_PERMISSIONS = "permissions";\r
+ public static final String OCSHARES_SHARED_DATE = "shared_date";\r
+ public static final String OCSHARES_EXPIRATION_DATE = "expiration_date";\r
+ public static final String OCSHARES_TOKEN = "token";\r
+ public static final String OCSHARES_SHARE_WITH_DISPLAY_NAME = "shared_with_display_name";\r
+ public static final String OCSHARES_IS_DIRECTORY = "is_directory";\r
+ public static final String OCSHARES_USER_ID = "user_id";\r
+ public static final String OCSHARES_ID_REMOTE_SHARED = "id_remote_shared";\r
+ public static final String OCSHARES_ACCOUNT_OWNER = "owner_share";\r
+ \r
+ public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE \r
+ + " collate nocase asc";\r
+ \r
\r
}\r
}\r
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.files;
-
-import com.owncloud.android.datamodel.OCFile;
-
-public interface FileHandler {
-
- /**
- * TODO
- */
- public void openFile(OCFile file);
-
-
-}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.files;
+
+import org.apache.http.protocol.HTTP;
+
+import android.accounts.AccountManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.DialogFragment;
+import android.webkit.MimeTypeMap;
+import android.widget.Toast;
+
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.network.webdav.WebdavUtils;
+import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.ui.dialog.ActivityChooserDialog;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ *
+ * @author masensio
+ * @author David A. Velasco
+ */
+public class FileOperationsHelper {
+
+ private static final String TAG = FileOperationsHelper.class.getName();
+
+ private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
+
+
+ public void openFile(OCFile file, FileActivity callerActivity) {
+ if (file != null) {
+ String storagePath = file.getStoragePath();
+ String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+
+ Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
+ intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
+ intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ Intent intentForGuessedMimeType = null;
+ if (storagePath.lastIndexOf('.') >= 0) {
+ String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
+ if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
+ intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
+ intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
+ intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+ }
+
+ Intent chooserIntent = null;
+ if (intentForGuessedMimeType != null) {
+ chooserIntent = Intent.createChooser(intentForGuessedMimeType, callerActivity.getString(R.string.actionbar_open_with));
+ } else {
+ chooserIntent = Intent.createChooser(intentForSavedMimeType, callerActivity.getString(R.string.actionbar_open_with));
+ }
+
+ callerActivity.startActivity(chooserIntent);
+
+ } else {
+ Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+ }
+ }
+
+
+ public void shareFileWithLink(OCFile file, FileActivity callerActivity) {
+
+ if (isSharedSupported(callerActivity)) {
+ if (file != null) {
+ String link = "https://fake.url";
+ Intent intent = createShareWithLinkIntent(link);
+ String[] packagesToExclude = new String[] { callerActivity.getPackageName() };
+ DialogFragment chooserDialog = ActivityChooserDialog.newInstance(intent, packagesToExclude, file);
+ chooserDialog.show(callerActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
+
+ } else {
+ Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
+ }
+
+ } else {
+ // Show a Message
+ Toast t = Toast.makeText(callerActivity, callerActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
+ t.show();
+ }
+ }
+
+
+ public void shareFileWithLinkToApp(OCFile file, Intent sendIntent, FileActivity callerActivity) {
+
+ if (file != null) {
+ callerActivity.showLoadingDialog();
+
+ Intent service = new Intent(callerActivity, OperationsService.class);
+ service.setAction(OperationsService.ACTION_CREATE_SHARE);
+ service.putExtra(OperationsService.EXTRA_ACCOUNT, callerActivity.getAccount());
+ service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+ service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
+ callerActivity.startService(service);
+
+ } else {
+ Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
+ }
+ }
+
+
+ private Intent createShareWithLinkIntent(String link) {
+ Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
+ intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
+ intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
+ return intentToShareLink;
+ }
+
+
+ /**
+ * @return 'True' if the server supports the Share API
+ */
+ public boolean isSharedSupported(FileActivity callerActivity) {
+ if (callerActivity.getAccount() != null) {
+ AccountManager accountManager = AccountManager.get(callerActivity);
+ return Boolean.parseBoolean(accountManager.getUserData(callerActivity.getAccount(), OwnCloudAccount.Constants.KEY_SUPPORTS_SHARE_API));
+ }
+ return false;
+ }
+
+
+ public void unshareFileWithLink(OCFile file, FileActivity callerActivity) {
+
+ if (isSharedSupported(callerActivity)) {
+ // Unshare the file
+ Intent service = new Intent(callerActivity, OperationsService.class);
+ service.setAction(OperationsService.ACTION_UNSHARE);
+ service.putExtra(OperationsService.EXTRA_ACCOUNT, callerActivity.getAccount());
+ service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
+ callerActivity.startService(service);
+
+ callerActivity.showLoadingDialog();
+
+ } else {
+ // Show a Message
+ Toast t = Toast.makeText(callerActivity, callerActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
+ t.show();
+
+ }
+ }
+}
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.operations.SynchronizeFileOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.utils.Log_OC;
import java.util.Map;
import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
import android.app.Notification;
import android.app.NotificationManager;
switch (type) {
case NOTIFICATION_SIMPLE:
- notification = new Notification(R.drawable.icon, data.getText(), System.currentTimeMillis());
+ notification = new Notification(DisplayUtils.getSeasonalIconId(), data.getText(), System.currentTimeMillis());
break;
case NOTIFICATION_PROGRESS:
notification = new Notification();
false);
return true;
case NOTIFICATION_SIMPLE:
- pair.mNotificaiton = new Notification(R.drawable.icon,
+ pair.mNotificaiton = new Notification(DisplayUtils.getSeasonalIconId(),
data.getText(), System.currentTimeMillis());
mNM.notify(notification_id, pair.mNotificaiton);
return true;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.network.OwnCloudClientFactory;
+import com.owncloud.android.lib.network.OwnCloudClient;
import com.owncloud.android.operations.DownloadFileOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.utils.FileUtils;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.preview.PreviewImageActivity;
import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private IBinder mBinder;
- private WebdavClient mDownloadClient = null;
+ private OwnCloudClient mDownloadClient = null;
private Account mLastAccount = null;
private FileDataStorageManager mStorageManager;
}
}
-
- @Override
- public void onTransferProgress(long progressRate) {
- // old way, should not be in use any more
- }
-
-
@Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
String fileName) {
* Updates the OC File after a successful download.
*/
private void saveDownloadedFile() {
- OCFile file = mCurrentDownload.getFile();
+ OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId());
long syncDate = System.currentTimeMillis();
file.setLastSyncDateForProperties(syncDate);
file.setLastSyncDateForData(syncDate);
private void notifyDownloadStart(DownloadFileOperation download) {
/// create status notification with a progress bar
mLastPercent = 0;
- mNotification = new Notification(R.drawable.icon, getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
+ mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.downloader_download_in_progress_ticker), System.currentTimeMillis());
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
mNotification.contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.progressbar_layout);
mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, download.getSize() < 0);
mNotification.contentView.setTextViewText(R.id.status_text, String.format(getString(R.string.downloader_download_in_progress_content), 0, new File(download.getSavePath()).getName()));
- mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+ mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId());
/// includes a pending intent in the notification showing the details view of the file
Intent showDetailsIntent = null;
* Callback method to update the progress bar in the status notification.
*/
@Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) {
int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
if (percent != mLastPercent) {
- mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);
- String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
- mNotification.contentView.setTextViewText(R.id.status_text, text);
- mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
+ mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, totalToTransfer < 0);
+ String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
+ String text = String.format(getString(R.string.downloader_download_in_progress_content), percent, fileName);
+ mNotification.contentView.setTextViewText(R.id.status_text, text);
+ mNotificationManager.notify(R.string.downloader_download_in_progress_ticker, mNotification);
}
mLastPercent = percent;
}
/**
- * Callback method to update the progress bar in the status notification (old version)
- */
- @Override
- public void onTransferProgress(long progressRate) {
- // NOTHING TO DO HERE ANYMORE
- }
-
-
- /**
* Updates the status notification with the result of a download operation.
*
* @param downloadResult Result of the download operation.
if (!downloadResult.isCancelled()) {
int tickerId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_ticker : R.string.downloader_download_failed_ticker;
int contentId = (downloadResult.isSuccess()) ? R.string.downloader_download_succeeded_content : R.string.downloader_download_failed_content;
- Notification finalNotification = new Notification(R.drawable.icon, getString(tickerId), System.currentTimeMillis());
+ Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(tickerId), System.currentTimeMillis());
finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
boolean needsToUpdateCredentials = (downloadResult.getCode() == ResultCode.UNAUTHORIZED ||
// (downloadResult.isTemporalRedirection() && downloadResult.isIdPRedirection()
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
import com.owncloud.android.R;
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.operations.ChunkedUploadFileOperation;
import com.owncloud.android.operations.CreateFolderOperation;
-import com.owncloud.android.operations.ExistenceCheckOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteFile;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.operations.UploadFileOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavEntry;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.ExistenceCheckRemoteOperation;
+import com.owncloud.android.lib.operations.remote.ReadRemoteFileOperation;
+import com.owncloud.android.lib.utils.FileUtils;
+import com.owncloud.android.lib.utils.OwnCloudVersion;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.network.OwnCloudClientFactory;
+import com.owncloud.android.lib.network.OwnCloudClient;
import com.owncloud.android.ui.activity.FailedUploadActivity;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.InstantUploadActivity;
import com.owncloud.android.ui.preview.PreviewImageActivity;
import com.owncloud.android.ui.preview.PreviewImageFragment;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private IBinder mBinder;
- private WebdavClient mUploadClient = null;
+ private OwnCloudClient mUploadClient = null;
private Account mLastAccount = null;
private FileDataStorageManager mStorageManager;
files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], ((mimeTypes != null) ? mimeTypes[i]
: (String) null), storageManager);
if (files[i] == null) {
- // TODO @andomaex add failure Notiification
+ // TODO @andomaex add failure Notification
return Service.START_NOT_STICKY;
}
}
try {
for (int i = 0; i < files.length; i++) {
uploadKey = buildRemoteName(account, files[i].getRemotePath());
- if (chunked) {
- newUpload = new ChunkedUploadFileOperation(account, files[i], isInstant, forceOverwrite,
- localAction);
- } else {
- newUpload = new UploadFileOperation(account, files[i], isInstant, forceOverwrite, localAction);
- }
+ newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, forceOverwrite, localAction,
+ getApplicationContext());
if (isInstant) {
newUpload.setRemoteFolderToBeCreated();
}
@Override
- public void onTransferProgress(long progressRate) {
- // old way, should not be in use any more
- }
-
-
- @Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer,
String fileName) {
String key = buildRemoteName(mCurrentUpload.getAccount(), mCurrentUpload.getFile());
* @return An {@link OCFile} instance corresponding to the folder where the file will be uploaded.
*/
private RemoteOperationResult grantFolderExistence(String pathToGrant) {
- RemoteOperation operation = new ExistenceCheckOperation(pathToGrant, this, false);
+ RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, this, false);
RemoteOperationResult result = operation.execute(mUploadClient);
if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mCurrentUpload.isRemoteFolderToBeCreated()) {
operation = new CreateFolderOperation( pathToGrant,
*/
private void saveUploadedFile() {
OCFile file = mCurrentUpload.getFile();
+ if (file.fileExists()) {
+ file = mStorageManager.getFileById(file.getFileId());
+ }
long syncDate = System.currentTimeMillis();
file.setLastSyncDateForData(syncDate);
- // / new PROPFIND to keep data consistent with server in theory, should
- // return the same we already have
- PropFindMethod propfind = null;
- RemoteOperationResult result = null;
- try {
- propfind = new PropFindMethod(mUploadClient.getBaseUri() + WebdavUtils.encodePath(mCurrentUpload.getRemotePath()),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_0);
- int status = mUploadClient.executeMethod(propfind);
- boolean isMultiStatus = (status == HttpStatus.SC_MULTI_STATUS);
- if (isMultiStatus) {
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0], mUploadClient.getBaseUri().getPath());
- updateOCFile(file, we);
- file.setLastSyncDateForProperties(syncDate);
-
- } else {
- mUploadClient.exhaustResponse(propfind.getResponseBodyAsStream());
- }
-
- result = new RemoteOperationResult(isMultiStatus, status, propfind.getResponseHeaders());
- Log_OC.i(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
- + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Update: synchronizing properties for uploaded " + mCurrentUpload.getRemotePath() + ": "
- + result.getLogMessage(), e);
-
- } finally {
- if (propfind != null)
- propfind.releaseConnection();
+ // new PROPFIND to keep data consistent with server
+ // in theory, should return the same we already have
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(mCurrentUpload.getRemotePath());
+ RemoteOperationResult result = operation.execute(mUploadClient);
+ if (result.isSuccess()) {
+ 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(file);
}
- private void updateOCFile(OCFile file, WebdavEntry we) {
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- file.setModificationTimestampAtLastSyncForData(we.modifiedTimestamp());
- // file.setEtag(mCurrentUpload.getEtag()); // TODO Etag, where available
+ private void updateOCFile(OCFile file, RemoteFile remoteFile) {
+ file.setCreationTimestamp(remoteFile.getCreationTimestamp());
+ file.setFileLength(remoteFile.getLength());
+ file.setMimetype(remoteFile.getMimeType());
+ file.setModificationTimestamp(remoteFile.getModifiedTimestamp());
+ file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp());
+ // file.setEtag(remoteFile.getEtag()); // TODO Etag, where available
}
private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType,
private void notifyUploadStart(UploadFileOperation upload) {
// / create status notification with a progress bar
mLastPercent = 0;
- mNotification = new Notification(R.drawable.icon, getString(R.string.uploader_upload_in_progress_ticker),
+ mNotification = new Notification(DisplayUtils.getSeasonalIconId(), getString(R.string.uploader_upload_in_progress_ticker),
System.currentTimeMillis());
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
mDefaultNotificationContentView = mNotification.contentView;
mNotification.contentView.setProgressBar(R.id.status_progress, 100, 0, false);
mNotification.contentView.setTextViewText(R.id.status_text,
String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()));
- mNotification.contentView.setImageViewResource(R.id.status_icon, R.drawable.icon);
+ mNotification.contentView.setImageViewResource(R.id.status_icon, DisplayUtils.getSeasonalIconId());
/// includes a pending intent in the notification showing the details view of the file
Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class);
* Callback method to update the progress bar in the status notification
*/
@Override
- public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) {
+ public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filePath) {
int percent = (int) (100.0 * ((double) totalTransferredSoFar) / ((double) totalToTransfer));
if (percent != mLastPercent) {
mNotification.contentView.setProgressBar(R.id.status_progress, 100, percent, false);
+ String fileName = filePath.substring(filePath.lastIndexOf(FileUtils.PATH_SEPARATOR) + 1);
String text = String.format(getString(R.string.uploader_upload_in_progress_content), percent, fileName);
mNotification.contentView.setTextViewText(R.id.status_text, text);
mNotificationManager.notify(R.string.uploader_upload_in_progress_ticker, mNotification);
}
/**
- * Callback method to update the progress bar in the status notification
- * (old version)
- */
- @Override
- public void onTransferProgress(long progressRate) {
- // NOTHING TO DO HERE ANYMORE
- }
-
- /**
* Updates the status notification with the result of an upload operation.
*
* @param uploadResult Result of the upload operation.
}
showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, upload.getFile());
showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, upload.getAccount());
+ showDetailsIntent.putExtra(FileActivity.EXTRA_FROM_NOTIFICATION, true);
showDetailsIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
mNotification.contentIntent = PendingIntent.getActivity(getApplicationContext(),
(int) System.currentTimeMillis(), showDetailsIntent, 0);
// / fail -> explicit failure notification
mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker);
- Notification finalNotification = new Notification(R.drawable.icon,
+ Notification finalNotification = new Notification(DisplayUtils.getSeasonalIconId(),
getString(R.string.uploader_upload_failed_ticker), System.currentTimeMillis());
finalNotification.flags |= Notification.FLAG_AUTO_CANCEL;
String content = null;
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.operations;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
-import java.util.Random;
-
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.PutMethod;
-
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer;
-import com.owncloud.android.oc_framework.network.webdav.ChunkFromFileChannelRequestEntity;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.utils.Log_OC;
-
-
-import android.accounts.Account;
-
-
-public class ChunkedUploadFileOperation extends UploadFileOperation {
-
- private static final long CHUNK_SIZE = 1024000;
- private static final String OC_CHUNKED_HEADER = "OC-Chunked";
- private static final String TAG = ChunkedUploadFileOperation.class.getSimpleName();
-
- public ChunkedUploadFileOperation( Account account,
- OCFile file,
- boolean isInstant,
- boolean forceOverwrite,
- int localBehaviour) {
-
- super(account, file, isInstant, forceOverwrite, localBehaviour);
- }
-
- @Override
- protected int uploadFile(WebdavClient client) throws HttpException, IOException {
- int status = -1;
-
- FileChannel channel = null;
- RandomAccessFile raf = null;
- try {
- File file = new File(getStoragePath());
- raf = new RandomAccessFile(file, "r");
- channel = raf.getChannel();
- mEntity = new ChunkFromFileChannelRequestEntity(channel, getMimeType(), CHUNK_SIZE, file);
- ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(getDataTransferListeners());
- long offset = 0;
- String uriPrefix = client.getBaseUri() + WebdavUtils.encodePath(getRemotePath()) + "-chunking-" + Math.abs((new Random()).nextInt(9000)+1000) + "-" ;
- long chunkCount = (long) Math.ceil((double)file.length() / CHUNK_SIZE);
- for (int chunkIndex = 0; chunkIndex < chunkCount ; chunkIndex++, offset += CHUNK_SIZE) {
- if (mPutMethod != null) {
- mPutMethod.releaseConnection(); // let the connection available for other methods
- }
- mPutMethod = new PutMethod(uriPrefix + chunkCount + "-" + chunkIndex);
- mPutMethod.addRequestHeader(OC_CHUNKED_HEADER, OC_CHUNKED_HEADER);
- ((ChunkFromFileChannelRequestEntity)mEntity).setOffset(offset);
- mPutMethod.setRequestEntity(mEntity);
- status = client.executeMethod(mPutMethod);
- client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
- Log_OC.d(TAG, "Upload of " + getStoragePath() + " to " + getRemotePath() + ", chunk index " + chunkIndex + ", count " + chunkCount + ", HTTP result status " + status);
- if (!isSuccess(status))
- break;
- }
-
- } finally {
- if (channel != null)
- channel.close();
- if (raf != null)
- raf.close();
- if (mPutMethod != null)
- mPutMethod.releaseConnection(); // let the connection available for other methods
- }
- return status;
- }
-
-}
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.remote.CreateRemoteFolderOperation;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.remote.CreateRemoteFolderOperation;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
CreateRemoteFolderOperation operation = new CreateRemoteFolderOperation(mRemotePath, mCreateFullPath);
RemoteOperationResult result = operation.execute(client);
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2014 ownCloud Inc.
+ *
+ * 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;
+
+/**
+ * Creates a new share from a given file
+ *
+ * @author masensio
+ *
+ */
+
+import android.content.Intent;
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.ShareType;
+import com.owncloud.android.lib.operations.remote.CreateShareRemoteOperation;
+import com.owncloud.android.lib.utils.FileUtils;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.utils.Log_OC;
+
+public class CreateShareOperation extends SyncOperation {
+
+ private static final String TAG = CreateShareOperation.class.getSimpleName();
+
+ protected FileDataStorageManager mStorageManager;
+
+ private String mPath;
+ private ShareType mShareType;
+ private String mShareWith;
+ private boolean mPublicUpload;
+ private String mPassword;
+ private int mPermissions;
+ private Intent mSendIntent;
+
+ /**
+ * Constructor
+ * @param path Full path of the file/folder being shared. Mandatory argument
+ * @param shareType \910\92 = user, \911\92 = group, \913\92 = Public link. Mandatory argument
+ * @param shareWith User/group ID with who the file should be shared. This is mandatory for shareType of 0 or 1
+ * @param publicUpload If \91false\92 (default) public cannot upload to a public shared folder.
+ * If \91true\92 public can upload to a shared folder. Only available for public link shares
+ * @param password Password to protect a public link share. Only available for public link shares
+ * @param permissions 1 - Read only \96 Default for \93public\94 shares
+ * 2 - Update
+ * 4 - Create
+ * 8 - Delete
+ * 16- Re-share
+ * 31- All above \96 Default for \93private\94 shares
+ * For user or group shares.
+ * To obtain combinations, add the desired values together.
+ * For instance, for \93Re-Share\94, \93delete\94, \93read\94, \93update\94, add 16+8+2+1 = 27.
+ */
+ public CreateShareOperation(String path, ShareType shareType, String shareWith, boolean publicUpload,
+ String password, int permissions, Intent sendIntent) {
+
+ mPath = path;
+ mShareType = shareType;
+ mShareWith = shareWith;
+ mPublicUpload = publicUpload;
+ mPassword = password;
+ mPermissions = permissions;
+ mSendIntent = sendIntent;
+ }
+
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ CreateShareRemoteOperation operation = new CreateShareRemoteOperation(mPath, mShareType, mShareWith, mPublicUpload, mPassword, mPermissions);
+ RemoteOperationResult result = operation.execute(client);
+
+ if (result.isSuccess()) {
+
+ if (result.getData().size() > 0) {
+ OCShare share = (OCShare) result.getData().get(0);
+
+ // Update DB with the response
+ share.setPath(mPath);
+ if (mPath.endsWith(FileUtils.PATH_SEPARATOR)) {
+ share.setIsFolder(true);
+ } else {
+ share.setIsFolder(false);
+ }
+ share.setPermissions(mPermissions);
+
+ getStorageManager().saveShare(share);
+
+ // Update OCFile with data from share: ShareByLink and publicLink
+ OCFile file = getStorageManager().getFileByPath(mPath);
+ if (file!=null) {
+ mSendIntent.putExtra(Intent.EXTRA_TEXT, share.getShareLink());
+ file.setPublicLink(share.getShareLink());
+ file.setShareByLink(true);
+ getStorageManager().saveFile(file);
+ Log_OC.d(TAG, "Public Link = " + file.getPublicLink());
+
+ }
+ }
+ }
+
+
+ return result;
+ }
+
+
+ public Intent getSendIntent() {
+ return mSendIntent;
+ }
+
+}
package com.owncloud.android.operations;
-import java.io.BufferedInputStream;
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.commons.httpclient.Header;
-import org.apache.commons.httpclient.HttpException;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.apache.http.HttpStatus;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.OperationCancelledException;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.remote.DownloadRemoteFileOperation;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
import android.webkit.MimeTypeMap;
/**
- * Remote operation performing the download of a file to an ownCloud server
+ * Remote mDownloadOperation performing the download of a file to an ownCloud server
*
* @author David A. Velasco
+ * @author masensio
*/
public class DownloadFileOperation extends RemoteOperation {
private Account mAccount;
private OCFile mFile;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
- private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
private long mModificationTimestamp = 0;
- private GetMethod mGet;
+
+ private DownloadRemoteFileOperation mDownloadOperation;
public DownloadFileOperation(Account account, OCFile file) {
mAccount = account;
mFile = file;
+
}
return FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath();
}
+ public String getTmpFolder() {
+ return FileStorageUtils.getTemporalPath(mAccount.name);
+ }
+
public String getRemotePath() {
return mFile.getRemotePath();
}
public long getModificationTimestamp() {
return (mModificationTimestamp > 0) ? mModificationTimestamp : mFile.getModificationTimestamp();
}
-
-
- public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.add(listener);
- }
- }
-
- public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
- synchronized (mDataTransferListeners) {
- mDataTransferListeners.remove(listener);
- }
- }
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
File newFile = null;
boolean moved = true;
/// download will be performed to a temporal file, then moved to the final location
File tmpFile = new File(getTmpPath());
+ String tmpFolder = getTmpFolder();
+
/// perform the download
- try {
- tmpFile.getParentFile().mkdirs();
- int status = downloadFile(client, tmpFile);
- if (isSuccess(status)) {
- newFile = new File(getSavePath());
- newFile.getParentFile().mkdirs();
- moved = tmpFile.renameTo(newFile);
- }
+ mDownloadOperation = new DownloadRemoteFileOperation(mFile.getRemotePath(), tmpFolder);
+ Iterator<OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
+ while (listener.hasNext()) {
+ mDownloadOperation.addDatatransferProgressListener(listener.next());
+ }
+ result = mDownloadOperation.execute(client);
+
+ if (result.isSuccess()) {
+ mModificationTimestamp = mDownloadOperation.getModificationTimestamp();
+ newFile = new File(getSavePath());
+ newFile.getParentFile().mkdirs();
+ moved = tmpFile.renameTo(newFile);
+
if (!moved)
result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
- else
- result = new RemoteOperationResult(isSuccess(status), status, (mGet != null ? mGet.getResponseHeaders() : null));
- Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
}
+ Log_OC.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
+
return result;
}
-
- public boolean isSuccess(int status) {
- return (status == HttpStatus.SC_OK);
+ public void cancel() {
+ mDownloadOperation.cancel();
}
-
-
- protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
- int status = -1;
- boolean savedFile = false;
- mGet = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
- Iterator<OnDatatransferProgressListener> it = null;
-
- FileOutputStream fos = null;
- try {
- status = client.executeMethod(mGet);
- if (isSuccess(status)) {
- targetFile.createNewFile();
- BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream());
- fos = new FileOutputStream(targetFile);
- long transferred = 0;
- byte[] bytes = new byte[4096];
- int readResult = 0;
- while ((readResult = bis.read(bytes)) != -1) {
- synchronized(mCancellationRequested) {
- if (mCancellationRequested.get()) {
- mGet.abort();
- throw new OperationCancelledException();
- }
- }
- fos.write(bytes, 0, readResult);
- transferred += readResult;
- synchronized (mDataTransferListeners) {
- it = mDataTransferListeners.iterator();
- while (it.hasNext()) {
- it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
- }
- }
- }
- savedFile = true;
- Header modificationTime = mGet.getResponseHeader("Last-Modified");
- if (modificationTime != null) {
- Date d = WebdavUtils.parseResponseDate((String) modificationTime.getValue());
- mModificationTimestamp = (d != null) ? d.getTime() : 0;
- }
-
- } else {
- client.exhaustResponse(mGet.getResponseBodyAsStream());
- }
-
- } finally {
- if (fos != null) fos.close();
- if (!savedFile && targetFile.exists()) {
- targetFile.delete();
- }
- mGet.releaseConnection(); // let the connection available for other methods
+
+ public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.add(listener);
}
- return status;
}
-
- public void cancel() {
- mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
+ public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) {
+ synchronized (mDataTransferListeners) {
+ mDataTransferListeners.remove(listener);
+ }
}
-
-
+
}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.HeadMethod;
-
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.utils.Log_OC;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-
-/**
- * Operation to check the existence or absence of a path in a remote server.
- *
- * @author David A. Velasco
- */
-public class ExistenceCheckOperation extends RemoteOperation {
-
- /** Maximum time to wait for a response from the server in MILLISECONDs. */
- public static final int TIMEOUT = 10000;
-
- private static final String TAG = ExistenceCheckOperation.class.getSimpleName();
-
- private String mPath;
- private Context mContext;
- private boolean mSuccessIfAbsent;
-
-
- /**
- * Full constructor. Success of the operation will depend upon the value of successIfAbsent.
- *
- * @param path Path to append to the URL owned by the client instance.
- * @param context Android application context.
- * @param successIfAbsent When 'true', the operation finishes in success if the path does NOT exist in the remote server (HTTP 404).
- */
- public ExistenceCheckOperation(String path, Context context, boolean successIfAbsent) {
- mPath = (path != null) ? path : "";
- mContext = context;
- mSuccessIfAbsent = successIfAbsent;
- }
-
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- if (!isOnline()) {
- return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
- }
- RemoteOperationResult result = null;
- HeadMethod head = null;
- try {
- head = new HeadMethod(client.getBaseUri() + WebdavUtils.encodePath(mPath));
- int status = client.executeMethod(head, TIMEOUT, TIMEOUT);
- client.exhaustResponse(head.getResponseBodyAsStream());
- boolean success = (status == HttpStatus.SC_OK && !mSuccessIfAbsent) || (status == HttpStatus.SC_NOT_FOUND && mSuccessIfAbsent);
- result = new RemoteOperationResult(success, status, head.getResponseHeaders());
- Log_OC.d(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + "finished with HTTP status " + status + (!success?"(FAIL)":""));
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Existence check for " + client.getBaseUri() + WebdavUtils.encodePath(mPath) + " targeting for " + (mSuccessIfAbsent ? " absence " : " existence ") + ": " + result.getLogMessage(), result.getException());
-
- } finally {
- if (head != null)
- head.releaseConnection();
- }
- return result;
- }
-
- private boolean isOnline() {
- ConnectivityManager cm = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm != null && cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().isConnectedOrConnecting();
- }
-
-
-}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+package com.owncloud.android.operations;
+
+import java.util.ArrayList;
+
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.remote.GetSharesForFileRemoteOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ * Provide a list shares for a specific file.
+ *
+ * @author masensio
+ *
+ */
+public class GetSharesForFileOperation extends SyncOperation {
+
+ private static final String TAG = GetSharesForFileOperation.class.getSimpleName();
+
+ private String mPath;
+ private boolean mReshares;
+ private boolean mSubfiles;
+
+ /**
+ * Constructor
+ *
+ * @param path Path to file or folder
+ * @param reshares If set to \91false\92 (default), only shares from the current user are returned
+ * If set to \91true\92, all shares from the given file are returned
+ * @param subfiles If set to \91false\92 (default), lists only the folder being shared
+ * If set to \91true\92, all shared files within the folder are returned.
+ */
+ public GetSharesForFileOperation(String path, boolean reshares, boolean subfiles) {
+ mPath = path;
+ mReshares = reshares;
+ mSubfiles = subfiles;
+ }
+
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(mPath, mReshares, mSubfiles);
+ RemoteOperationResult result = operation.execute(client);
+
+ if (result.isSuccess()) {
+
+ // Update DB with the response
+ Log_OC.d(TAG, "File = " + mPath + " Share list size " + result.getData().size());
+ ArrayList<OCShare> shares = new ArrayList<OCShare>();
+ for(Object obj: result.getData()) {
+ shares.add((OCShare) obj);
+ }
+
+ getStorageManager().saveSharesDB(shares);
+ }
+
+ return result;
+ }
+
+}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * 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;
+
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.remote.GetRemoteSharesOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ * 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 {
+
+ private static final String TAG = GetSharesOperation.class.getSimpleName();
+
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ GetRemoteSharesOperation operation = new GetRemoteSharesOperation();
+ RemoteOperationResult result = operation.execute(client);
+
+ if (result.isSuccess()) {
+
+ // Update DB with the response
+ Log_OC.d(TAG, "Share list size = " + result.getData().size());
+ ArrayList<OCShare> shares = new ArrayList<OCShare>();
+ for(Object obj: result.getData()) {
+ shares.add((OCShare) obj);
+ }
+
+ getStorageManager().saveSharesDB(shares);
+ }
+
+ return result;
+ }
+
+}
import org.json.JSONObject;
import com.owncloud.android.authentication.OAuth2Constants;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
import com.owncloud.android.utils.Log_OC;
}
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
PostMethod postMethod = null;
nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId);
//nameValuePairs[4] = new NameValuePair(OAuth2Constants.KEY_SCOPE, mOAuth2ParsedAuthorizationResponse.get(OAuth2Constants.KEY_SCOPE));
- postMethod = new PostMethod(client.getBaseUri().toString());
+ postMethod = new PostMethod(client.getWebdavUri().toString());
postMethod.setRequestBody(nameValuePairs);
int status = client.executeMethod(postMethod);
postMethod.releaseConnection(); // let the connection available for other methods
if (result.isSuccess()) {
- Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+ Log_OC.i(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getWebdavUri() + ": " + result.getLogMessage());
} else if (result.getException() != null) {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage(), result.getException());
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getWebdavUri() + ": " + result.getLogMessage(), result.getException());
} else if (result.getCode() == ResultCode.OAUTH2_ERROR) {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getWebdavUri() + ": " + ((mResultTokenMap != null) ? mResultTokenMap.get(OAuth2Constants.KEY_ERROR) : "NULL"));
} else {
- Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getBaseUri() + ": " + result.getLogMessage());
+ Log_OC.e(TAG, "OAuth2 TOKEN REQUEST with auth code " + mOAuth2ParsedAuthorizationResponse.get("code") + " to " + client.getWebdavUri() + ": " + result.getLogMessage());
}
}
+++ /dev/null
-/* ownCloud Android client application
- * Copyright (C) 2012-2013 ownCloud Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.operations;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
-import com.owncloud.android.utils.Log_OC;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.Uri;
-
-public class OwnCloudServerCheckOperation extends RemoteOperation {
-
- /** Maximum time to wait for a response from the server when the connection is being tested, in MILLISECONDs. */
- public static final int TRY_CONNECTION_TIMEOUT = 5000;
-
- private static final String TAG = OwnCloudServerCheckOperation.class.getSimpleName();
-
- private String mUrl;
- private RemoteOperationResult mLatestResult;
- private Context mContext;
- private OwnCloudVersion mOCVersion;
-
- public OwnCloudServerCheckOperation(String url, Context context) {
- mUrl = url;
- mContext = context;
- mOCVersion = null;
- }
-
- public OwnCloudVersion getDiscoveredVersion() {
- return mOCVersion;
- }
-
- private boolean tryConnection(WebdavClient wc, String urlSt) {
- boolean retval = false;
- GetMethod get = null;
- try {
- get = new GetMethod(urlSt);
- int status = wc.executeMethod(get, TRY_CONNECTION_TIMEOUT, TRY_CONNECTION_TIMEOUT);
- String response = get.getResponseBodyAsString();
- if (status == HttpStatus.SC_OK) {
- JSONObject json = new JSONObject(response);
- if (!json.getBoolean("installed")) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
- } else {
- mOCVersion = new OwnCloudVersion(json.getString("version"));
- if (!mOCVersion.isVersionValid()) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.BAD_OC_VERSION);
-
- } else {
- mLatestResult = new RemoteOperationResult(urlSt.startsWith("https://") ?
- RemoteOperationResult.ResultCode.OK_SSL :
- RemoteOperationResult.ResultCode.OK_NO_SSL
- );
-
- retval = true;
- }
- }
-
- } else {
- mLatestResult = new RemoteOperationResult(false, status, get.getResponseHeaders());
- }
-
- } catch (JSONException e) {
- mLatestResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
-
- } catch (Exception e) {
- mLatestResult = new RemoteOperationResult(e);
-
- } finally {
- if (get != null)
- get.releaseConnection();
- }
-
- if (mLatestResult.isSuccess()) {
- Log_OC.i(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
-
- } else if (mLatestResult.getException() != null) {
- Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage(), mLatestResult.getException());
-
- } else {
- Log_OC.e(TAG, "Connection check at " + urlSt + ": " + mLatestResult.getLogMessage());
- }
-
- return retval;
- }
-
- private boolean isOnline() {
- ConnectivityManager cm = (ConnectivityManager) mContext
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- return cm != null && cm.getActiveNetworkInfo() != null
- && cm.getActiveNetworkInfo().isConnectedOrConnecting();
- }
-
- @Override
- protected RemoteOperationResult run(WebdavClient client) {
- if (!isOnline()) {
- return new RemoteOperationResult(RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION);
- }
- if (mUrl.startsWith("http://") || mUrl.startsWith("https://")) {
- tryConnection(client, mUrl + AccountUtils.STATUS_PATH);
-
- } else {
- client.setBaseUri(Uri.parse("https://" + mUrl + AccountUtils.STATUS_PATH));
- boolean httpsSuccess = tryConnection(client, "https://" + mUrl + AccountUtils.STATUS_PATH);
- if (!httpsSuccess && !mLatestResult.isSslRecoverableException()) {
- Log_OC.d(TAG, "establishing secure connection failed, trying non secure connection");
- client.setBaseUri(Uri.parse("http://" + mUrl + AccountUtils.STATUS_PATH));
- tryConnection(client, "http://" + mUrl + AccountUtils.STATUS_PATH);
- }
- }
- return mLatestResult;
- }
-
-}
package com.owncloud.android.operations;
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.jackrabbit.webdav.client.methods.DeleteMethod;
-
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.utils.Log_OC;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.RemoveRemoteFileOperation;
/**
*/
public class RemoveFileOperation extends RemoteOperation {
- private static final String TAG = RemoveFileOperation.class.getSimpleName();
-
- private static final int REMOVE_READ_TIMEOUT = 10000;
- private static final int REMOVE_CONNECTION_TIMEOUT = 5000;
+ // private static final String TAG = RemoveFileOperation.class.getSimpleName();
OCFile mFileToRemove;
boolean mDeleteLocalCopy;
return mFileToRemove;
}
-
/**
* Performs the remove operation
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
- DeleteMethod delete = null;
- try {
- delete = new DeleteMethod(client.getBaseUri() + WebdavUtils.encodePath(mFileToRemove.getRemotePath()));
- int status = client.executeMethod(delete, REMOVE_READ_TIMEOUT, REMOVE_CONNECTION_TIMEOUT);
- if (delete.succeeded() || status == HttpStatus.SC_NOT_FOUND) {
- mDataStorageManager.removeFile(mFileToRemove, true, mDeleteLocalCopy);
- }
- delete.getResponseBodyAsString(); // exhaust the response, although not interesting
- result = new RemoteOperationResult((delete.succeeded() || status == HttpStatus.SC_NOT_FOUND), status, delete.getResponseHeaders());
- Log_OC.i(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Remove " + mFileToRemove.getRemotePath() + ": " + result.getLogMessage(), e);
-
- } finally {
- if (delete != null)
- delete.releaseConnection();
+
+ RemoveRemoteFileOperation operation = new RemoveRemoteFileOperation(mFileToRemove.getRemotePath());
+ result = operation.execute(client);
+
+ if (result.isSuccess() || result.getCode() == ResultCode.FILE_NOT_FOUND) {
+ mDataStorageManager.removeFile(mFileToRemove, true, mDeleteLocalCopy);
}
+
return result;
}
import java.io.File;
import java.io.IOException;
-import org.apache.commons.httpclient.HttpException;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework.operations.remote.RenameRemoteFileOperation;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.RenameRemoteFileOperation;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
// check if the new name is valid in the local file system
package com.owncloud.android.operations;
-import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavEntry;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteFile;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.ReadRemoteFileOperation;
+import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
+/**
+ * Remote operation performing the read of remote file in the ownCloud server.
+ *
+ * @author David A. Velasco
+ * @author masensio
+ */
public class SynchronizeFileOperation extends RemoteOperation {
private String TAG = SynchronizeFileOperation.class.getSimpleName();
- private static final int SYNC_READ_TIMEOUT = 10000;
- private static final int SYNC_CONNECTION_TIMEOUT = 5000;
private OCFile mLocalFile;
private OCFile mServerFile;
@Override
- protected RemoteOperationResult run(WebdavClient client) {
-
- PropFindMethod propfind = null;
+ protected RemoteOperationResult run(OwnCloudClient client) {
+
RemoteOperationResult result = null;
mTransferWasRequested = false;
- try {
- if (!mLocalFile.isDown()) {
- /// easy decision
- requestForDownload(mLocalFile);
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else {
- /// local copy in the device -> need to think a bit more before do anything
-
- if (mServerFile == null) {
- /// take the duty of check the server for the current state of the file there
- propfind = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(mLocalFile.getRemotePath()),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_0);
- int status = client.executeMethod(propfind, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT);
- boolean isMultiStatus = status == HttpStatus.SC_MULTI_STATUS;
- if (isMultiStatus) {
- MultiStatus resp = propfind.getResponseBodyAsMultiStatus();
- WebdavEntry we = new WebdavEntry(resp.getResponses()[0],
- client.getBaseUri().getPath());
- mServerFile = fillOCFile(we);
- mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
-
- } else {
- client.exhaustResponse(propfind.getResponseBodyAsStream());
- result = new RemoteOperationResult(false, status, propfind.getResponseHeaders());
- }
+ if (!mLocalFile.isDown()) {
+ /// easy decision
+ requestForDownload(mLocalFile);
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ /// local copy in the device -> need to think a bit more before do anything
+
+ if (mServerFile == null) {
+ String remotePath = mLocalFile.getRemotePath();
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+ result = operation.execute(client);
+ if (result.isSuccess()){
+ mServerFile = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
+ mServerFile.setLastSyncDateForProperties(System.currentTimeMillis());
}
-
- if (result == null) { // true if the server was not checked. nothing was wrong with the remote request
-
- /// check changes in server and local file
- boolean serverChanged = false;
- /* time for eTag is coming, but not yet
+ }
+
+ if (mServerFile != null) {
+
+ /// check changes in server and local file
+ boolean serverChanged = false;
+ /* time for eTag is coming, but not yet
if (mServerFile.getEtag() != null) {
serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); // TODO could this be dangerous when the user upgrades the server from non-tagged to tagged?
} else { */
- // server without etags
- serverChanged = (mServerFile.getModificationTimestamp() > mLocalFile.getModificationTimestampAtLastSyncForData());
- //}
- boolean localChanged = (mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
- // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
-
- /// decide action to perform depending upon changes
- //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
- if (localChanged && serverChanged) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
-
- } else if (localChanged) {
- if (mSyncFileContents) {
- requestForUpload(mLocalFile);
- // the local update of file properties will be done by the FileUploader service when the upload finishes
- } else {
- // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
- // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
- // that an upload is necessary (for instance, in FileObserverService).
- }
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else if (serverChanged) {
- if (mSyncFileContents) {
- requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
- // the update of local data will be done later by the FileUploader service when the upload finishes
- } else {
- // TODO CHECK: is this really useful in some point in the code?
- mServerFile.setKeepInSync(mLocalFile.keepInSync());
- mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
- mServerFile.setStoragePath(mLocalFile.getStoragePath());
- mServerFile.setParentId(mLocalFile.getParentId());
- mStorageManager.saveFile(mServerFile);
-
- }
- result = new RemoteOperationResult(ResultCode.OK);
-
+ // server without etags
+ serverChanged = (mServerFile.getModificationTimestamp() != mLocalFile.getModificationTimestampAtLastSyncForData());
+ //}
+ boolean localChanged = (mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData());
+ // TODO this will be always true after the app is upgraded to database version 2; will result in unnecessary uploads
+
+ /// decide action to perform depending upon changes
+ //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) {
+ if (localChanged && serverChanged) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT);
+
+ } else if (localChanged) {
+ if (mSyncFileContents) {
+ requestForUpload(mLocalFile);
+ // the local update of file properties will be done by the FileUploader service when the upload finishes
} else {
- // nothing changed, nothing to do
- result = new RemoteOperationResult(ResultCode.OK);
+ // NOTHING TO DO HERE: updating the properties of the file in the server without uploading the contents would be stupid;
+ // So, an instance of SynchronizeFileOperation created with syncFileContents == false is completely useless when we suspect
+ // that an upload is necessary (for instance, in FileObserverService).
}
-
- }
-
- }
-
- Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", file " + (mLocalFile != null ? mLocalFile.getRemotePath() : "NULL") + ": " + result.getLogMessage(), result.getException());
-
- } finally {
- if (propfind != null)
- propfind.releaseConnection();
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else if (serverChanged) {
+ if (mSyncFileContents) {
+ requestForDownload(mLocalFile); // local, not server; we won't to keep the value of keepInSync!
+ // the update of local data will be done later by the FileUploader service when the upload finishes
+ } else {
+ // TODO CHECK: is this really useful in some point in the code?
+ mServerFile.setKeepInSync(mLocalFile.keepInSync());
+ mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData());
+ mServerFile.setStoragePath(mLocalFile.getStoragePath());
+ mServerFile.setParentId(mLocalFile.getParentId());
+ mStorageManager.saveFile(mServerFile);
+
+ }
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ } else {
+ // nothing changed, nothing to do
+ result = new RemoteOperationResult(ResultCode.OK);
+ }
+
+ }
+
}
+
+ Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", file " + mLocalFile.getRemotePath() + ": " + result.getLogMessage());
+
return result;
}
}
- /**
- * Creates and populates a new {@link OCFile} object with the data read from the server.
- *
- * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
- * @return New OCFile instance representing the remote resource described by we.
- */
- private OCFile fillOCFile(WebdavEntry we) {
- OCFile file = new OCFile(we.decodedPath());
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- file.setEtag(we.etag());
-
- return file;
- }
-
-
public boolean transferWasRequested() {
return mTransferWasRequested;
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.apache.http.HttpStatus;
-import org.apache.jackrabbit.webdav.DavConstants;
-import org.apache.jackrabbit.webdav.MultiStatus;
-import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
-
import android.accounts.Account;
import android.content.Context;
import android.content.Intent;
+//import android.support.v4.content.LocalBroadcastManager;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavEntry;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.syncadapter.FileSyncService;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.GetSharesForFileRemoteOperation;
+import com.owncloud.android.lib.operations.remote.ReadRemoteFileOperation;
+import com.owncloud.android.lib.operations.remote.ReadRemoteFolderOperation;
+import com.owncloud.android.lib.operations.common.RemoteFile;
+import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
private static final String TAG = SynchronizeFolderOperation.class.getSimpleName();
+ public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED = SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED";
+ public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED = SynchronizeFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_SHARES_SYNCED";
/** Time stamp for the synchronization process in progress */
private long mCurrentSyncTime;
/** 'True' means that this operation is part of a full account synchronization */
private boolean mSyncFullAccount;
+ /** 'True' means that Share resources bound to the files into the folder should be refreshed also */
+ private boolean mRefreshShares;
+
/** 'True' means that the remote folder changed from last synchronization and should be fetched */
private boolean mRemoteFolderChanged;
-
+
/**
* Creates a new instance of {@link SynchronizeFolderOperation}.
public SynchronizeFolderOperation( OCFile folder,
long currentSyncTime,
boolean syncFullAccount,
+ boolean refreshShares,
FileDataStorageManager dataStorageManager,
Account account,
Context context ) {
mLocalFolder = folder;
mCurrentSyncTime = currentSyncTime;
mSyncFullAccount = syncFullAccount;
+ mRefreshShares = refreshShares;
mStorageManager = dataStorageManager;
mAccount = account;
mContext = context;
* {@inheritDoc}
*/
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
mFailsInFavouritesFound = 0;
mConflictsFound = 0;
}
if (!mSyncFullAccount) {
- sendStickyBroadcast(false, mLocalFolder.getRemotePath(), result);
+ sendLocalBroadcast(EVENT_SINGLE_FOLDER_CONTENTS_SYNCED, mLocalFolder.getRemotePath(), result);
}
-
+
+ if (result.isSuccess() && mRefreshShares) {
+ RemoteOperationResult shareResult = refreshSharesForFolder(client);
+ if (shareResult.getCode() != ResultCode.FILE_NOT_FOUND) {
+ result = shareResult;
+ } // else , keep the previous result ; being conservative for servers where Sharing API is supported, but disabled
+ }
+
+ if (!mSyncFullAccount) {
+ sendLocalBroadcast(EVENT_SINGLE_FOLDER_SHARES_SYNCED, mLocalFolder.getRemotePath(), result);
+ }
+
return result;
}
-
- private RemoteOperationResult checkForChanges(WebdavClient client) {
+ private RemoteOperationResult checkForChanges(OwnCloudClient client) {
mRemoteFolderChanged = false;
RemoteOperationResult result = null;
String remotePath = null;
- PropFindMethod query = null;
-
- try {
- remotePath = mLocalFolder.getRemotePath();
- Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
-
- // remote request
- query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_0);
- int status = client.executeMethod(query);
-
- // check and process response
- if (isMultiStatus(status)) {
- // parse data from remote folder
- WebdavEntry we = new WebdavEntry(query.getResponseBodyAsMultiStatus().getResponses()[0], client.getBaseUri().getPath());
- OCFile remoteFolder = fillOCFile(we);
-
- // check if remote and local folder are different
- mRemoteFolderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
-
- result = new RemoteOperationResult(ResultCode.OK);
-
- } else {
- // check failed
- client.exhaustResponse(query.getResponseBodyAsStream());
- if (status == HttpStatus.SC_NOT_FOUND) {
- removeLocalFolder();
- }
- result = new RemoteOperationResult(false, status, query.getResponseHeaders());
- }
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
+ remotePath = mLocalFolder.getRemotePath();
+ Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath);
+
+ // remote request
+ ReadRemoteFileOperation operation = new ReadRemoteFileOperation(remotePath);
+ result = operation.execute(client);
+ if (result.isSuccess()){
+ OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0));
-
- } finally {
- if (query != null)
- query.releaseConnection(); // let the connection available for other methods
- if (result.isSuccess()) {
- Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed"));
+ // check if remote and local folder are different
+ mRemoteFolderChanged = !(remoteFolder.getEtag().equalsIgnoreCase(mLocalFolder.getEtag()));
+
+ result = new RemoteOperationResult(ResultCode.OK);
+
+ Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed"));
+
+ } else {
+ // check failed
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND) {
+ removeLocalFolder();
+ }
+ if (result.isException()) {
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage(), result.getException());
} else {
- if (result.isException()) {
- Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage(), result.getException());
- } else {
- Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage());
- }
+ Log_OC.e(TAG, "Checked " + mAccount.name + remotePath + " : " + result.getLogMessage());
}
-
}
+
return result;
}
- private RemoteOperationResult fetchAndSyncRemoteFolder(WebdavClient client) {
- RemoteOperationResult result = null;
- String remotePath = null;
- PropFindMethod query = null;
- try {
- remotePath = mLocalFolder.getRemotePath();
- Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
-
- // remote request
- query = new PropFindMethod(client.getBaseUri() + WebdavUtils.encodePath(remotePath),
- DavConstants.PROPFIND_ALL_PROP,
- DavConstants.DEPTH_1);
- int status = client.executeMethod(query);
-
- // check and process response
- if (isMultiStatus(status)) {
- synchronizeData(query.getResponseBodyAsMultiStatus(), client);
- if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
- result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
- } else {
- result = new RemoteOperationResult(true, status, query.getResponseHeaders());
- }
-
- } else {
- // synchronization failed
- client.exhaustResponse(query.getResponseBodyAsStream());
- if (status == HttpStatus.SC_NOT_FOUND) {
- removeLocalFolder();
- }
- result = new RemoteOperationResult(false, status, query.getResponseHeaders());
- }
-
- } catch (Exception e) {
- result = new RemoteOperationResult(e);
-
-
- } finally {
- if (query != null)
- query.releaseConnection(); // let the connection available for other methods
- if (result.isSuccess()) {
- Log_OC.i(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
- } else {
- if (result.isException()) {
- Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage(), result.getException());
- } else {
- Log_OC.e(TAG, "Synchronized " + mAccount.name + remotePath + ": " + result.getLogMessage());
- }
+ private RemoteOperationResult fetchAndSyncRemoteFolder(OwnCloudClient client) {
+ String remotePath = mLocalFolder.getRemotePath();
+ ReadRemoteFolderOperation operation = new ReadRemoteFolderOperation(remotePath);
+ RemoteOperationResult result = operation.execute(client);
+ Log_OC.d(TAG, "Synchronizing " + mAccount.name + remotePath);
+
+ if (result.isSuccess()) {
+ synchronizeData(result.getData(), client);
+ if (mConflictsFound > 0 || mFailsInFavouritesFound > 0) {
+ result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); // should be different result, but will do the job
}
-
+ } else {
+ if (result.getCode() == ResultCode.FILE_NOT_FOUND)
+ removeLocalFolder();
}
+
return result;
}
-
+
private void removeLocalFolder() {
if (mStorageManager.fileExists(mLocalFolder.getFileId())) {
String currentSavePath = FileStorageUtils.getSavePath(mAccount.name);
* with the current data in the local database.
*
* Grants that mChildren is updated with fresh data after execution.
- *
- * @param dataInServer Full response got from the server with the data of the target
- * folder and its direct children.
+ *
+ * @param folderAndFiles Remote folder and children files in Folder
+ *
* @param client Client instance to the remote server where the data were
* retrieved.
* @return 'True' when any change was made in the local data, 'false' otherwise.
*/
- private void synchronizeData(MultiStatus dataInServer, WebdavClient client) {
+ private void synchronizeData(ArrayList<Object> folderAndFiles, OwnCloudClient client) {
// get 'fresh data' from the database
mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath());
// parse data from remote folder
- WebdavEntry we = new WebdavEntry(dataInServer.getResponses()[0], client.getBaseUri().getPath());
- OCFile remoteFolder = fillOCFile(we);
+ OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0));
remoteFolder.setParentId(mLocalFolder.getParentId());
remoteFolder.setFileId(mLocalFolder.getFileId());
Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data ");
- List<OCFile> updatedFiles = new Vector<OCFile>(dataInServer.getResponses().length - 1);
+ List<OCFile> updatedFiles = new Vector<OCFile>(folderAndFiles.size() - 1);
List<SynchronizeFileOperation> filesToSyncContents = new Vector<SynchronizeFileOperation>();
// get current data about local contents of the folder to synchronize
// loop to update every child
OCFile remoteFile = null, localFile = null;
- for (int i = 1; i < dataInServer.getResponses().length; ++i) {
+ for (int i=1; i<folderAndFiles.size(); i++) {
/// new OCFile instance with the data from the server
- we = new WebdavEntry(dataInServer.getResponses()[i], client.getBaseUri().getPath());
- remoteFile = fillOCFile(we);
+ remoteFile = fillOCFile((RemoteFile)folderAndFiles.get(i));
remoteFile.setParentId(mLocalFolder.getFileId());
/// retrieve local data for the read file
// request for the synchronization of file contents AFTER saving current remote properties
startContentSynchronizations(filesToSyncContents, client);
- // removal of obsolete files
- //removeObsoleteFiles();
-
- // must be done AFTER saving all the children information, so that eTag is not updated in the database in case of unexpected exceptions
- //mStorageManager.saveFile(remoteFolder);
mChildren = updatedFiles;
-
}
/**
* @param filesToSyncContents Synchronization operations to execute.
* @param client Interface to the remote ownCloud server.
*/
- private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents, WebdavClient client) {
+ private void startContentSynchronizations(List<SynchronizeFileOperation> filesToSyncContents, OwnCloudClient client) {
RemoteOperationResult contentsResult = null;
for (SynchronizeFileOperation op: filesToSyncContents) {
contentsResult = op.execute(client); // returns without waiting for upload or download finishes
return (status == HttpStatus.SC_MULTI_STATUS);
}
-
/**
* Creates and populates a new {@link OCFile} object with the data read from the server.
*
- * @param we WebDAV entry read from the server for a WebDAV resource (remote file or folder).
+ * @param remote remote file read from the server (remote file or folder).
* @return New OCFile instance representing the remote resource described by we.
*/
- private OCFile fillOCFile(WebdavEntry we) {
- OCFile file = new OCFile(we.decodedPath());
- file.setCreationTimestamp(we.createTimestamp());
- file.setFileLength(we.contentLength());
- file.setMimetype(we.contentType());
- file.setModificationTimestamp(we.modifiedTimestamp());
- file.setEtag(we.etag());
+ private OCFile fillOCFile(RemoteFile remote) {
+ OCFile file = new OCFile(remote.getRemotePath());
+ file.setCreationTimestamp(remote.getCreationTimestamp());
+ file.setFileLength(remote.getLength());
+ file.setMimetype(remote.getMimeType());
+ file.setModificationTimestamp(remote.getModifiedTimestamp());
+ file.setEtag(remote.getEtag());
return file;
}
}
}
}
+
+
+ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+
+ // remote request
+ GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(mLocalFolder.getRemotePath(), false, true);
+ result = operation.execute(client);
+
+ if (result.isSuccess()) {
+ // update local database
+ ArrayList<OCShare> shares = new ArrayList<OCShare>();
+ for(Object obj: result.getData()) {
+ shares.add((OCShare) obj);
+ }
+ mStorageManager.saveSharesInFolder(shares, mLocalFolder);
+ }
+
+ return result;
+ }
+
/**
* Scans the default location for saving local copies of files searching for
/**
* Sends a message to any application component interested in the progress of the synchronization.
*
- * @param inProgress 'True' when the synchronization progress is not finished.
+ * @param event
* @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
+ * @param result
*/
- private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
- Intent i = new Intent(FileSyncService.getSyncMessage());
- i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
- i.putExtra(FileSyncService.ACCOUNT_NAME, mAccount.name);
+ private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) {
+ Log_OC.d(TAG, "Send broadcast " + event);
+ Intent intent = new Intent(event);
+ intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, mAccount.name);
if (dirRemotePath != null) {
- i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
- }
- if (result != null) {
- i.putExtra(FileSyncService.SYNC_RESULT, result);
+ intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath);
}
- mContext.sendStickyBroadcast(i);
+ intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result);
+ mContext.sendStickyBroadcast(intent);
+ //LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2014 ownCloud Inc.
+ *
+ * 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 android.content.Context;
+
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OCShare;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.ExistenceCheckRemoteOperation;
+import com.owncloud.android.lib.operations.remote.RemoveRemoteShareOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ * Unshare file/folder
+ * Save the data in Database
+ *
+ * @author masensio
+ */
+public class UnshareLinkOperation extends SyncOperation {
+
+ private static final String TAG = UnshareLinkOperation.class.getSimpleName();
+
+ private String mRemotePath;
+ private Context mContext;
+
+
+ public UnshareLinkOperation(String remotePath, Context context) {
+ mRemotePath = remotePath;
+ mContext = context;
+ }
+
+ @Override
+ protected RemoteOperationResult run(OwnCloudClient client) {
+ RemoteOperationResult result = null;
+
+ // Get Share for a file
+ OCShare share = getStorageManager().getShareByPath(mRemotePath);
+
+ if (share != null) {
+ RemoveRemoteShareOperation operation = new RemoveRemoteShareOperation((int) share.getIdRemoteShared());
+ result = operation.execute(client);
+
+ if (result.isSuccess() || result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+ Log_OC.d(TAG, "Share id = " + share.getIdRemoteShared() + " deleted");
+
+ OCFile file = getStorageManager().getFileByPath(mRemotePath);
+ file.setShareByLink(false);
+ file.setPublicLink("");
+ getStorageManager().saveFile(file);
+ getStorageManager().removeShare(share);
+
+ if (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+ if (existsFile(client, file.getRemotePath())) {
+ result = new RemoteOperationResult(ResultCode.OK);
+ } else {
+ getStorageManager().removeFile(file, true, true);
+ }
+ }
+ }
+
+ } else {
+ result = new RemoteOperationResult(ResultCode.SHARE_NOT_FOUND);
+ }
+
+ return result;
+ }
+
+ private boolean existsFile(OwnCloudClient client, String remotePath){
+ ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, false);
+ RemoteOperationResult result = existsOperation.execute(client);
+ return result.isSuccess();
+ }
+
+}
import org.json.JSONObject;
import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.utils.OwnCloudVersion;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
AccountManager accountMngr = AccountManager.get(mContext);
String statUrl = accountMngr.getUserData(mAccount, OwnCloudAccount.Constants.KEY_OC_BASE_URL);
statUrl += AccountUtils.STATUS_PATH;
result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
}
}
- Log_OC.i(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage());
+ Log_OC.i(TAG, "Check for update of ownCloud server version at " + client.getWebdavUri() + ": " + result.getLogMessage());
} catch (JSONException e) {
result = new RemoteOperationResult(RemoteOperationResult.ResultCode.INSTANCE_NOT_CONFIGURED);
- Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+ Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getWebdavUri() + ": " + result.getLogMessage(), e);
} catch (Exception e) {
result = new RemoteOperationResult(e);
- Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getBaseUri() + ": " + result.getLogMessage(), e);
+ Log_OC.e(TAG, "Check for update of ownCloud server version at " + client.getWebdavUri() + ": " + result.getLogMessage(), e);
} finally {
if (get != null)
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
-import org.apache.http.HttpStatus;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileUploader;
-import com.owncloud.android.oc_framework.network.ProgressiveDataTransferer;
-import com.owncloud.android.oc_framework.network.webdav.FileRequestEntity;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.OperationCancelledException;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.network.ProgressiveDataTransferer;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OperationCancelledException;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.remote.ChunkedUploadRemoteFileOperation;
+import com.owncloud.android.lib.operations.remote.ExistenceCheckRemoteOperation;
+import com.owncloud.android.lib.operations.remote.UploadRemoteFileOperation;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
+import android.content.Context;
/**
private OCFile mFile;
private OCFile mOldFile;
private String mRemotePath = null;
+ private boolean mChunked = false;
private boolean mIsInstant = false;
private boolean mRemoteFolderToBeCreated = false;
private boolean mForceOverwrite = false;
PutMethod mPutMethod = null;
private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
+ private Context mContext;
+
+ private UploadRemoteFileOperation mUploadOperation;
protected RequestEntity mEntity = null;
public UploadFileOperation( Account account,
OCFile file,
+ boolean chunked,
boolean isInstant,
boolean forceOverwrite,
- int localBehaviour) {
+ int localBehaviour,
+ Context context) {
if (account == null)
throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
if (file == null)
mAccount = account;
mFile = file;
mRemotePath = file.getRemotePath();
+ mChunked = chunked;
mIsInstant = isInstant;
mForceOverwrite = forceOverwrite;
mLocalBehaviour = localBehaviour;
mOriginalStoragePath = mFile.getStoragePath();
mOriginalFileName = mFile.getFileName();
+ mContext = context;
}
public Account getAccount() {
}
@Override
- protected RemoteOperationResult run(WebdavClient client) {
+ protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result = null;
boolean localCopyPassed = false, nameCheckPassed = false;
File temporalFile = null, originalFile = new File(mOriginalStoragePath), expectedFile = null;
// !!!
expectedFile = new File(expectedPath);
- // / check location of local file; if not the expected, copy to a
+ // 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) {
}
localCopyPassed = true;
- // / perform the upload
- synchronized (mCancellationRequested) {
- if (mCancellationRequested.get()) {
- throw new OperationCancelledException();
- } else {
- mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
- }
+ /// perform the upload
+ 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());
+ }
+ Iterator <OnDatatransferProgressListener> listener = mDataTransferListeners.iterator();
+ while (listener.hasNext()) {
+ mUploadOperation.addDatatransferProgressListener(listener.next());
}
- int status = uploadFile(client);
+ result = mUploadOperation.execute(client);
- // / move local temporal file or original file to its corresponding
+ /// move local temporal file or original file to its corresponding
// location in the ownCloud local folder
- if (isSuccess(status)) {
+ if (result.isSuccess()) {
if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) {
mFile.setStoragePath(null);
}
}
- result = new RemoteOperationResult(isSuccess(status), status, (mPutMethod != null ? mPutMethod.getResponseHeaders() : null));
-
} catch (Exception e) {
// TODO something cleaner with cancellations
if (mCancellationRequested.get()) {
mFile = newFile;
}
- public boolean isSuccess(int status) {
- return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
- }
-
- protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
- int status = -1;
- try {
- File f = new File(mFile.getStoragePath());
- mEntity = new FileRequestEntity(f, getMimeType());
- synchronized (mDataTransferListeners) {
- ((ProgressiveDataTransferer)mEntity).addDatatransferProgressListeners(mDataTransferListeners);
- }
- mPutMethod.setRequestEntity(mEntity);
- status = client.executeMethod(mPutMethod);
- client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
-
- } finally {
- mPutMethod.releaseConnection(); // let the connection available for
- // other methods
- }
- return status;
- }
-
/**
* Checks if remotePath does not exist in the server and returns it, or adds
* a suffix to it in order to avoid the server file is overwritten.
* @param string
* @return
*/
- private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
- boolean check = wc.existsFile(remotePath);
+ private String getAvailableRemotePath(OwnCloudClient wc, String remotePath) throws Exception {
+ boolean check = existsFile(wc, remotePath);
if (!check) {
return remotePath;
}
int count = 2;
do {
suffix = " (" + count + ")";
- if (pos >= 0)
- check = wc.existsFile(remotePath + suffix + "." + extension);
- else
- check = wc.existsFile(remotePath + suffix);
+ if (pos >= 0) {
+ check = existsFile(wc, remotePath + suffix + "." + extension);
+ }
+ else {
+ check = existsFile(wc, remotePath + suffix);
+ }
count++;
} while (check);
}
}
+ private boolean existsFile(OwnCloudClient client, String remotePath){
+ ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, false);
+ RemoteOperationResult result = existsOperation.execute(client);
+ return result.isSuccess();
+ }
+
public void cancel() {
- synchronized (mCancellationRequested) {
- mCancellationRequested.set(true);
- if (mPutMethod != null)
- mPutMethod.abort();
- }
+ mUploadOperation.cancel();
}
}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * 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.common;
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Handler;
+
+
+/**
+ * Operation which execution involves both interactions with an ownCloud server and
+ * 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 {
+
+ //private static final String TAG = SyncOperation.class.getSimpleName();
+
+ private FileDataStorageManager mStorageManager;
+
+ public FileDataStorageManager getStorageManager() {
+ return mStorageManager;
+ }
+
+
+ /**
+ * Synchronously executes the operation on the received ownCloud account.
+ *
+ * Do not call this method from the main thread.
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @return Result of the operation.
+ */
+ public RemoteOperationResult execute(FileDataStorageManager storageManager, Context context) {
+ if (storageManager == null) {
+ throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager");
+ }
+ if (storageManager.getAccount() == null) {
+ throw new IllegalArgumentException("Trying to execute a sync operation with a storage manager for a NULL account");
+ }
+ mStorageManager = storageManager;
+ return super.execute(mStorageManager.getAccount(), context);
+ }
+
+
+ /**
+ * Synchronously executes the remote operation
+ *
+ * Do not call this method from the main thread.
+ *
+ * @param client Client object to reach an ownCloud server during the execution of the operation.
+ * @return Result of the operation.
+ */
+ public RemoteOperationResult execute(OwnCloudClient client, FileDataStorageManager storageManager) {
+ if (storageManager == null)
+ throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager");
+ mStorageManager = storageManager;
+ return super.execute(client);
+ }
+
+
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * This method should be used whenever an ownCloud account is available, instead of {@link #execute(OwnCloudClient)}.
+ *
+ * @param account ownCloud account in remote ownCloud server to reach during the execution of the operation.
+ * @param context Android context for the component calling the method.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public Thread execute(FileDataStorageManager storageManager, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) {
+ if (storageManager == null) {
+ throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager");
+ }
+ if (storageManager.getAccount() == null) {
+ throw new IllegalArgumentException("Trying to execute a sync operation with a storage manager for a NULL account");
+ }
+ mStorageManager = storageManager;
+ return super.execute(storageManager.getAccount(), context, listener, listenerHandler, callerActivity);
+ }
+
+
+ /**
+ * Asynchronously executes the remote operation
+ *
+ * @param client Client object to reach an ownCloud server during the execution of the operation.
+ * @param listener Listener to be notified about the execution of the operation.
+ * @param listenerHandler Handler associated to the thread where the methods of the listener objects must be called.
+ * @return Thread were the remote operation is executed.
+ */
+ public Thread execute(OwnCloudClient client, FileDataStorageManager storageManager, OnRemoteOperationListener listener, Handler listenerHandler) {
+ if (storageManager == null) {
+ throw new IllegalArgumentException("Trying to execute a sync operation with a NULL storage manager");
+ }
+ mStorageManager = storageManager;
+ return super.execute(client, listener, listenerHandler);
+ }
+
+
+}
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.db.ProviderMeta;
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
+import com.owncloud.android.lib.operations.common.ShareType;
import com.owncloud.android.utils.Log_OC;
private DataBaseHelper mDbHelper;
- private static HashMap<String, String> mProjectionMap;
+ // Projection for filelist table
+ private static HashMap<String, String> mFileProjectionMap;
static {
- mProjectionMap = new HashMap<String, String>();
- mProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
- mProjectionMap.put(ProviderTableMeta.FILE_PARENT,
+ mFileProjectionMap = new HashMap<String, String>();
+ mFileProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
+ mFileProjectionMap.put(ProviderTableMeta.FILE_PARENT,
ProviderTableMeta.FILE_PARENT);
- mProjectionMap.put(ProviderTableMeta.FILE_PATH,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_PATH,
ProviderTableMeta.FILE_PATH);
- mProjectionMap.put(ProviderTableMeta.FILE_NAME,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_NAME,
ProviderTableMeta.FILE_NAME);
- mProjectionMap.put(ProviderTableMeta.FILE_CREATION,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_CREATION,
ProviderTableMeta.FILE_CREATION);
- mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_MODIFIED,
ProviderTableMeta.FILE_MODIFIED);
- mProjectionMap.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA,
ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA);
- mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_CONTENT_LENGTH,
ProviderTableMeta.FILE_CONTENT_LENGTH);
- mProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_CONTENT_TYPE,
ProviderTableMeta.FILE_CONTENT_TYPE);
- mProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_STORAGE_PATH,
ProviderTableMeta.FILE_STORAGE_PATH);
- mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE,
ProviderTableMeta.FILE_LAST_SYNC_DATE);
- mProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA);
- mProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_KEEP_IN_SYNC,
ProviderTableMeta.FILE_KEEP_IN_SYNC);
- mProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_ACCOUNT_OWNER,
ProviderTableMeta.FILE_ACCOUNT_OWNER);
- mProjectionMap.put(ProviderTableMeta.FILE_ETAG,
+ mFileProjectionMap.put(ProviderTableMeta.FILE_ETAG,
ProviderTableMeta.FILE_ETAG);
+ mFileProjectionMap.put(ProviderTableMeta.FILE_SHARE_BY_LINK,
+ ProviderTableMeta.FILE_SHARE_BY_LINK);
+ mFileProjectionMap.put(ProviderTableMeta.FILE_PUBLIC_LINK,
+ ProviderTableMeta.FILE_PUBLIC_LINK);
}
private static final int SINGLE_FILE = 1;
private static final int DIRECTORY = 2;
private static final int ROOT_DIRECTORY = 3;
-
+ private static final int SHARES = 4;
+
+ // Projection for ocshares table
+ private static HashMap<String, String> mOCSharesProjectionMap;
+ static {
+ mOCSharesProjectionMap = new HashMap<String, String>();
+ mOCSharesProjectionMap.put(ProviderTableMeta._ID, ProviderTableMeta._ID);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_FILE_SOURCE,
+ ProviderTableMeta.OCSHARES_FILE_SOURCE);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ITEM_SOURCE,
+ ProviderTableMeta.OCSHARES_ITEM_SOURCE);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_TYPE,
+ ProviderTableMeta.OCSHARES_SHARE_TYPE);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_WITH,
+ ProviderTableMeta.OCSHARES_SHARE_WITH);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_PATH,
+ ProviderTableMeta.OCSHARES_PATH);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_PERMISSIONS,
+ ProviderTableMeta.OCSHARES_PERMISSIONS);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARED_DATE,
+ ProviderTableMeta.OCSHARES_SHARED_DATE);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_EXPIRATION_DATE,
+ ProviderTableMeta.OCSHARES_EXPIRATION_DATE);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_TOKEN,
+ ProviderTableMeta.OCSHARES_TOKEN);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME,
+ ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_IS_DIRECTORY,
+ ProviderTableMeta.OCSHARES_IS_DIRECTORY);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_USER_ID,
+ ProviderTableMeta.OCSHARES_USER_ID);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED,
+ ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED);
+ mOCSharesProjectionMap.put(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER,
+ ProviderTableMeta.OCSHARES_ACCOUNT_OWNER);
+ }
+
private UriMatcher mUriMatcher;
@Override
return count;
}
-
private int delete(SQLiteDatabase db, Uri uri, String where, String[] whereArgs) {
int count = 0;
switch (mUriMatcher.match(uri)) {
}
Log_OC.d(TAG, "Removing FILE " + remotePath);
*/
- count = db.delete(ProviderTableMeta.DB_NAME,
+ count = db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
}
Log_OC.d(TAG, "Removing DIRECTORY " + folderName + " (or maybe not) ");
*/
- count += db.delete(ProviderTableMeta.DB_NAME,
+ count += db.delete(ProviderTableMeta.FILE_TABLE_NAME,
ProviderTableMeta._ID
+ "="
+ uri.getPathSegments().get(1)
break;
case ROOT_DIRECTORY:
//Log_OC.d(TAG, "Removing ROOT!");
- count = db.delete(ProviderTableMeta.DB_NAME, where, whereArgs);
+ count = db.delete(ProviderTableMeta.FILE_TABLE_NAME, where, whereArgs);
+ break;
+ case SHARES:
+ count = db.delete(ProviderTableMeta.OCSHARES_TABLE_NAME, where, whereArgs);
break;
default:
//Log_OC.e(TAG, "Unknown uri " + uri);
}
+ // TODO: switch(uri)
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
}
private Uri insert(SQLiteDatabase db, Uri uri, ContentValues values) {
- if (mUriMatcher.match(uri) != SINGLE_FILE &&
- mUriMatcher.match(uri) != ROOT_DIRECTORY) {
- //Log_OC.e(TAG, "Inserting invalid URI: " + uri);
- throw new IllegalArgumentException("Unknown uri id: " + uri);
- }
+ switch (mUriMatcher.match(uri)){
+ case ROOT_DIRECTORY:
+ case SINGLE_FILE:
+ String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
+ String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
+ String[] projection = new String[] {ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, ProviderTableMeta.FILE_ACCOUNT_OWNER };
+ String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
+ String[] whereArgs = new String[] {remotePath, accountName};
+ Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
+ if (doubleCheck == null || !doubleCheck.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider
+ long rowId = db.insert(ProviderTableMeta.FILE_TABLE_NAME, null, values);
+ if (rowId > 0) {
+ Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
+ //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
+ return insertedFileUri;
+ } else {
+ //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
+ throw new SQLException("ERROR " + uri);
+ }
+ } else {
+ // file is already inserted; race condition, let's avoid a duplicated entry
+ Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID)));
+ doubleCheck.close();
- String remotePath = values.getAsString(ProviderTableMeta.FILE_PATH);
- String accountName = values.getAsString(ProviderTableMeta.FILE_ACCOUNT_OWNER);
- String[] projection = new String[] {ProviderTableMeta._ID, ProviderTableMeta.FILE_PATH, ProviderTableMeta.FILE_ACCOUNT_OWNER };
- String where = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
- String[] whereArgs = new String[] {remotePath, accountName};
- Cursor doubleCheck = query(db, uri, projection, where, whereArgs, null);
- if (doubleCheck == null || !doubleCheck.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider
- long rowId = db.insert(ProviderTableMeta.DB_NAME, null, values);
- if (rowId > 0) {
- Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, rowId);
- //Log_OC.d(TAG, "Inserted " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
return insertedFileUri;
+ }
+
+ case SHARES:
+ String path = values.getAsString(ProviderTableMeta.OCSHARES_PATH);
+ String accountNameShare= values.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER);
+ String[] projectionShare = new String[] {ProviderTableMeta._ID, ProviderTableMeta.OCSHARES_PATH, ProviderTableMeta.OCSHARES_ACCOUNT_OWNER };
+ String whereShare = ProviderTableMeta.OCSHARES_PATH + "=? AND " + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + "=?";
+ String[] whereArgsShare = new String[] {path, accountNameShare};
+ Uri insertedShareUri = null;
+ Cursor doubleCheckShare = query(db, uri, projectionShare, whereShare, whereArgsShare, null);
+ if (doubleCheckShare == null || !doubleCheckShare.moveToFirst()) { // ugly patch; serious refactorization is needed to reduce work in FileDataStorageManager and bring it to FileContentProvider
+ long rowId = db.insert(ProviderTableMeta.OCSHARES_TABLE_NAME, null, values);
+ if (rowId >0) {
+ insertedShareUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, rowId);
+ } else {
+ throw new SQLException("ERROR " + uri);
+
+ }
} else {
- //Log_OC.d(TAG, "Error while inserting " + values.getAsString(ProviderTableMeta.FILE_PATH) + " at provider " + this);
- throw new SQLException("ERROR " + uri);
+ // file is already inserted; race condition, let's avoid a duplicated entry
+ insertedShareUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_SHARE, doubleCheckShare.getLong(doubleCheckShare.getColumnIndex(ProviderTableMeta._ID)));
+ doubleCheckShare.close();
}
- } else {
- // file is already inserted; race condition, let's avoid a duplicated entry
- Uri insertedFileUri = ContentUris.withAppendedId(ProviderTableMeta.CONTENT_URI_FILE, doubleCheck.getLong(doubleCheck.getColumnIndex(ProviderTableMeta._ID)));
- doubleCheck.close();
- return insertedFileUri;
+ updateFilesTableAccordingToShareInsertion(db, uri, values);
+ return insertedShareUri;
+
+
+ default:
+ throw new IllegalArgumentException("Unknown uri id: " + uri);
}
+
}
-
+ private void updateFilesTableAccordingToShareInsertion(SQLiteDatabase db, Uri uri, ContentValues shareValues) {
+ ContentValues fileValues = new ContentValues();
+ fileValues.put(ProviderTableMeta.FILE_SHARE_BY_LINK,
+ ShareType.PUBLIC_LINK.getValue() == shareValues.getAsInteger(ProviderTableMeta.OCSHARES_SHARE_TYPE)? 1 : 0);
+ String whereShare = ProviderTableMeta.FILE_PATH + "=? AND " + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?";
+ String[] whereArgsShare = new String[] {
+ shareValues.getAsString(ProviderTableMeta.OCSHARES_PATH),
+ shareValues.getAsString(ProviderTableMeta.OCSHARES_ACCOUNT_OWNER)
+ };
+ db.update(ProviderTableMeta.FILE_TABLE_NAME, fileValues, whereShare, whereArgsShare);
+ }
+
+
@Override
public boolean onCreate() {
mDbHelper = new DataBaseHelper(getContext());
mUriMatcher.addURI(authority, "file/#", SINGLE_FILE);
mUriMatcher.addURI(authority, "dir/", DIRECTORY);
mUriMatcher.addURI(authority, "dir/#", DIRECTORY);
+ mUriMatcher.addURI(authority, "shares/", SHARES);
+ mUriMatcher.addURI(authority, "shares/#", SHARES);
return true;
}
private Cursor query(SQLiteDatabase db, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder sqlQuery = new SQLiteQueryBuilder();
- sqlQuery.setTables(ProviderTableMeta.DB_NAME);
- sqlQuery.setProjectionMap(mProjectionMap);
+ sqlQuery.setTables(ProviderTableMeta.FILE_TABLE_NAME);
+ sqlQuery.setProjectionMap(mFileProjectionMap);
switch (mUriMatcher.match(uri)) {
case ROOT_DIRECTORY:
+ uri.getPathSegments().get(1));
}
break;
+ case SHARES:
+ sqlQuery.setTables(ProviderTableMeta.OCSHARES_TABLE_NAME);
+ sqlQuery.setProjectionMap(mOCSharesProjectionMap);
+ if (uri.getPathSegments().size() > 1) {
+ sqlQuery.appendWhere(ProviderTableMeta._ID + "="
+ + uri.getPathSegments().get(1));
+ }
+ break;
default:
throw new IllegalArgumentException("Unknown uri id: " + uri);
}
String order;
if (TextUtils.isEmpty(sortOrder)) {
- order = ProviderTableMeta.DEFAULT_SORT_ORDER;
+ if (mUriMatcher.match(uri) == SHARES) {
+ order = ProviderTableMeta.OCSHARES_DEFAULT_SORT_ORDER;
+ } else {
+
+ order = ProviderTableMeta.FILE_DEFAULT_SORT_ORDER;
+ }
} else {
order = sortOrder;
}
}
+
private int update(SQLiteDatabase db, Uri uri, ContentValues values, String selection, String[] selectionArgs) {
switch (mUriMatcher.match(uri)) {
case DIRECTORY:
return updateFolderSize(db, selectionArgs[0]);
+ case SHARES:
+ return db.update(ProviderTableMeta.OCSHARES_TABLE_NAME, values, selection, selectionArgs);
default:
- return db.update(ProviderTableMeta.DB_NAME, values, selection, selectionArgs);
+ return db.update(ProviderTableMeta.FILE_TABLE_NAME, values, selection, selectionArgs);
}
}
Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize);
ContentValues cv = new ContentValues();
cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize);
- count = db.update(ProviderTableMeta.DB_NAME, cv, folderWhere, whereArgs);
+ count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs);
// propagate update until root
if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) {
public void onCreate(SQLiteDatabase db) {
// files table
Log_OC.i("SQL", "Entering in onCreate");
- db.execSQL("CREATE TABLE " + ProviderTableMeta.DB_NAME + "("
+ db.execSQL("CREATE TABLE " + ProviderTableMeta.FILE_TABLE_NAME + "("
+ ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ ProviderTableMeta.FILE_NAME + " TEXT, "
+ ProviderTableMeta.FILE_PATH + " TEXT, "
+ ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER, "
+ ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER, "
+ ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER, "
- + ProviderTableMeta.FILE_ETAG + " TEXT );"
+ + ProviderTableMeta.FILE_ETAG + " TEXT, "
+ + ProviderTableMeta.FILE_SHARE_BY_LINK + " INTEGER, "
+ + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT );"
);
+
+ // Create table ocshares
+ db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
+ + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
+ + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
+ + ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
+ + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
+ + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
+ + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
+ + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
}
@Override
boolean upgraded = false;
if (oldVersion == 1 && newVersion >= 2) {
Log_OC.i("SQL", "Entering in the #1 ADD in onUpgrade");
- db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
" ADD COLUMN " + ProviderTableMeta.FILE_KEEP_IN_SYNC + " INTEGER " +
" DEFAULT 0");
upgraded = true;
Log_OC.i("SQL", "Entering in the #2 ADD in onUpgrade");
db.beginTransaction();
try {
- db.execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ db.execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
" ADD COLUMN " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " INTEGER " +
" DEFAULT 0");
// assume there are not local changes pending to upload
- db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
+ db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
" SET " + ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA + " = " + System.currentTimeMillis() +
" WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
Log_OC.i("SQL", "Entering in the #3 ADD in onUpgrade");
db.beginTransaction();
try {
- db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
" ADD COLUMN " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " INTEGER " +
" DEFAULT 0");
- db.execSQL("UPDATE " + ProviderTableMeta.DB_NAME +
+ db.execSQL("UPDATE " + ProviderTableMeta.FILE_TABLE_NAME +
" SET " + ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA + " = " + ProviderTableMeta.FILE_MODIFIED +
" WHERE " + ProviderTableMeta.FILE_STORAGE_PATH + " IS NOT NULL");
Log_OC.i("SQL", "Entering in the #4 ADD in onUpgrade");
db.beginTransaction();
try {
- db .execSQL("ALTER TABLE " + ProviderTableMeta.DB_NAME +
+ db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
" ADD COLUMN " + ProviderTableMeta.FILE_ETAG + " TEXT " +
" DEFAULT NULL");
}
if (!upgraded)
Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
+
+ if (oldVersion < 6 && newVersion >= 6) {
+ Log_OC.i("SQL", "Entering in the #5 ADD in onUpgrade");
+ db.beginTransaction();
+ try {
+ db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
+ " ADD COLUMN " + ProviderTableMeta.FILE_SHARE_BY_LINK + " INTEGER " +
+ " DEFAULT 0");
+
+ db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME +
+ " ADD COLUMN " + ProviderTableMeta.FILE_PUBLIC_LINK + " TEXT " +
+ " DEFAULT NULL");
+
+ // Create table ocshares
+ db.execSQL("CREATE TABLE " + ProviderTableMeta.OCSHARES_TABLE_NAME + "("
+ + ProviderTableMeta._ID + " INTEGER PRIMARY KEY, "
+ + ProviderTableMeta.OCSHARES_FILE_SOURCE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_ITEM_SOURCE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARE_TYPE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARE_WITH + " TEXT, "
+ + ProviderTableMeta.OCSHARES_PATH + " TEXT, "
+ + ProviderTableMeta.OCSHARES_PERMISSIONS+ " INTEGER, "
+ + ProviderTableMeta.OCSHARES_SHARED_DATE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_EXPIRATION_DATE + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_TOKEN + " TEXT, "
+ + ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME + " TEXT, "
+ + ProviderTableMeta.OCSHARES_IS_DIRECTORY + " INTEGER, " // boolean
+ + ProviderTableMeta.OCSHARES_USER_ID + " INTEGER, "
+ + ProviderTableMeta.OCSHARES_ID_REMOTE_SHARED + " INTEGER,"
+ + ProviderTableMeta.OCSHARES_ACCOUNT_OWNER + " TEXT );" );
+
+ upgraded = true;
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+ if (!upgraded)
+ Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion);
}
}
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2013 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.services;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import com.owncloud.android.datamodel.FileDataStorageManager;
+
+import com.owncloud.android.lib.network.OwnCloudClientFactory;
+import com.owncloud.android.lib.network.OwnCloudClient;
+import com.owncloud.android.operations.CreateShareOperation;
+import com.owncloud.android.operations.UnshareLinkOperation;
+import com.owncloud.android.operations.common.SyncOperation;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.ShareType;
+import com.owncloud.android.utils.Log_OC;
+
+import android.accounts.Account;
+import android.accounts.AccountsException;
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+//import android.support.v4.content.LocalBroadcastManager;
+import android.util.Pair;
+
+public class OperationsService extends Service {
+
+ private static final String TAG = OperationsService.class.getSimpleName();
+
+ public static final String EXTRA_ACCOUNT = "ACCOUNT";
+ public static final String EXTRA_SERVER_URL = "SERVER_URL";
+ public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH";
+ public static final String EXTRA_SEND_INTENT = "SEND_INTENT";
+ public static final String EXTRA_RESULT = "RESULT";
+
+ public static final String ACTION_CREATE_SHARE = "CREATE_SHARE";
+ public static final String ACTION_UNSHARE = "UNSHARE";
+
+ public static final String ACTION_OPERATION_ADDED = OperationsService.class.getName() + ".OPERATION_ADDED";
+ public static final String ACTION_OPERATION_FINISHED = OperationsService.class.getName() + ".OPERATION_FINISHED";
+
+ private ConcurrentLinkedQueue<Pair<Target, RemoteOperation>> mPendingOperations = new ConcurrentLinkedQueue<Pair<Target, RemoteOperation>>();
+
+ private static class Target {
+ public Uri mServerUrl = null;
+ public Account mAccount = null;
+ public Target(Account account, Uri serverUrl) {
+ mAccount = account;
+ mServerUrl = serverUrl;
+ }
+ }
+
+ private Looper mServiceLooper;
+ private ServiceHandler mServiceHandler;
+ private OperationsServiceBinder mBinder;
+ private OwnCloudClient mOwnCloudClient = null;
+ private Target mLastTarget = null;
+ private FileDataStorageManager mStorageManager;
+ private RemoteOperation mCurrentOperation = null;
+
+
+ /**
+ * Service initialization
+ */
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("Operations service thread", Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+ mServiceLooper = thread.getLooper();
+ mServiceHandler = new ServiceHandler(mServiceLooper, this);
+ mBinder = new OperationsServiceBinder();
+ }
+
+ /**
+ * Entry point to add a new operation to the queue of operations.
+ *
+ * New operations 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.
+ *
+ * IMPORTANT: the only operations performed here right now is {@link GetSharedFilesOperation}. The class
+ * is taking advantage of it due to time constraints.
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (!intent.hasExtra(EXTRA_ACCOUNT) && !intent.hasExtra(EXTRA_SERVER_URL)) {
+ Log_OC.e(TAG, "Not enough information provided in intent");
+ return START_NOT_STICKY;
+ }
+ try {
+ Account account = intent.getParcelableExtra(EXTRA_ACCOUNT);
+ String serverUrl = intent.getStringExtra(EXTRA_SERVER_URL);
+
+ Target target = new Target(account, (serverUrl == null) ? null : Uri.parse(serverUrl));
+ RemoteOperation operation = null;
+
+ String action = intent.getAction();
+ if (action.equals(ACTION_CREATE_SHARE)) { // Create Share
+ String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
+ Intent sendIntent = intent.getParcelableExtra(EXTRA_SEND_INTENT);
+ if (remotePath.length() > 0) {
+ operation = new CreateShareOperation(remotePath, ShareType.PUBLIC_LINK,
+ "", false, "", 1, sendIntent);
+ }
+ } else if (action.equals(ACTION_UNSHARE)) { // Unshare file
+ String remotePath = intent.getStringExtra(EXTRA_REMOTE_PATH);
+ if (remotePath.length() > 0) {
+ operation = new UnshareLinkOperation(remotePath, this.getApplicationContext());
+ }
+ } else {
+ // nothing we are going to handle
+ return START_NOT_STICKY;
+ }
+
+ mPendingOperations.add(new Pair<Target , RemoteOperation>(target, operation));
+ //sendBroadcastNewOperation(target, operation);
+
+ Message msg = mServiceHandler.obtainMessage();
+ msg.arg1 = startId;
+ mServiceHandler.sendMessage(msg);
+
+ } catch (IllegalArgumentException e) {
+ Log_OC.e(TAG, "Bad information provided in intent: " + e.getMessage());
+ return START_NOT_STICKY;
+ }
+
+ return START_NOT_STICKY;
+ }
+
+
+ /**
+ * Provides a binder object that clients can use to perform actions on the queue of operations,
+ * except the addition of new operations.
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+
+ /**
+ * Called when ALL the bound clients were unbound.
+ */
+ @Override
+ public boolean onUnbind(Intent intent) {
+ //((OperationsServiceBinder)mBinder).clearListeners();
+ return false; // not accepting rebinding (default behaviour)
+ }
+
+
+ /**
+ * Binder to let client components to perform actions on the queue of operations.
+ *
+ * It provides by itself the available operations.
+ */
+ public class OperationsServiceBinder extends Binder /* implements OnRemoteOperationListener */ {
+
+ /**
+ * Map of listeners that will be reported about the end of operations from a {@link OperationsServiceBinder} instance
+ */
+ private Map<OnRemoteOperationListener, Handler> mBoundListeners = new HashMap<OnRemoteOperationListener, Handler>();
+
+ /**
+ * Cancels an operation
+ *
+ * TODO
+ */
+ public void cancel() {
+ // TODO
+ }
+
+
+ public void clearListeners() {
+
+ mBoundListeners.clear();
+ }
+
+
+ /**
+ * Adds a listener interested in being reported about the end of operations.
+ *
+ * @param listener Object to notify about the end of operations.
+ * @param callbackHandler {@link Handler} to access the listener without breaking Android threading protection.
+ */
+ public void addOperationListener (OnRemoteOperationListener listener, Handler callbackHandler) {
+ mBoundListeners.put(listener, callbackHandler);
+ }
+
+
+ /**
+ * Removes a listener from the list of objects interested in the being reported about the end of operations.
+ *
+ * @param listener Object to notify about progress of transfer.
+ */
+ public void removeOperationListener (OnRemoteOperationListener listener) {
+ mBoundListeners.remove(listener);
+ }
+
+
+ /**
+ * TODO - IMPORTANT: update implementation when more operations are moved into the service
+ *
+ * @return 'True' when an operation that enforces the user to wait for completion is in process.
+ */
+ public boolean isPerformingBlockingOperation() {
+ return (!mPendingOperations.isEmpty());
+ }
+
+ }
+
+
+ /**
+ * Operations worker. Performs the pending operations in the order they were requested.
+ *
+ * Created with the Looper of a new thread, started in {@link OperationsService#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
+ OperationsService mService;
+ public ServiceHandler(Looper looper, OperationsService service) {
+ super(looper);
+ if (service == null) {
+ throw new IllegalArgumentException("Received invalid NULL in parameter 'service'");
+ }
+ mService = service;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ mService.nextOperation();
+ mService.stopSelf(msg.arg1);
+ }
+ }
+
+
+ /**
+ * Performs the next operation in the queue
+ */
+ private void nextOperation() {
+
+ Pair<Target, RemoteOperation> next = null;
+ synchronized(mPendingOperations) {
+ next = mPendingOperations.peek();
+ }
+
+ if (next != null) {
+
+ mCurrentOperation = next.second;
+ RemoteOperationResult result = null;
+ try {
+ /// prepare client object to send the request to the ownCloud server
+ if (mLastTarget == null || !mLastTarget.equals(next.first)) {
+ mLastTarget = next.first;
+ if (mLastTarget.mAccount != null) {
+ mOwnCloudClient = OwnCloudClientFactory.createOwnCloudClient(mLastTarget.mAccount, getApplicationContext());
+ mStorageManager = new FileDataStorageManager(mLastTarget.mAccount, getContentResolver());
+ } else {
+ mOwnCloudClient = OwnCloudClientFactory.createOwnCloudClient(mLastTarget.mServerUrl, getApplicationContext(), true); // this is not good enough
+ mStorageManager = null;
+ }
+ }
+
+ /// perform the operation
+ if (mCurrentOperation instanceof SyncOperation) {
+ result = ((SyncOperation)mCurrentOperation).execute(mOwnCloudClient, mStorageManager);
+ } else {
+ result = mCurrentOperation.execute(mOwnCloudClient);
+ }
+
+ } catch (AccountsException e) {
+ if (mLastTarget.mAccount == null) {
+ Log_OC.e(TAG, "Error while trying to get autorization for a NULL account", e);
+ } else {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastTarget.mAccount.name, e);
+ }
+ result = new RemoteOperationResult(e);
+
+ } catch (IOException e) {
+ if (mLastTarget.mAccount == null) {
+ Log_OC.e(TAG, "Error while trying to get autorization for a NULL account", e);
+ } else {
+ Log_OC.e(TAG, "Error while trying to get autorization for " + mLastTarget.mAccount.name, e);
+ }
+ result = new RemoteOperationResult(e);
+ } catch (Exception e) {
+ if (mLastTarget.mAccount == null) {
+ Log_OC.e(TAG, "Unexpected error for a NULL account", e);
+ } else {
+ Log_OC.e(TAG, "Unexpected error for " + mLastTarget.mAccount.name, e);
+ }
+ result = new RemoteOperationResult(e);
+
+ } finally {
+ synchronized(mPendingOperations) {
+ mPendingOperations.poll();
+ }
+ }
+
+ //sendBroadcastOperationFinished(mLastTarget, mCurrentOperation, result);
+ callbackOperationListeners(mLastTarget, mCurrentOperation, result);
+ }
+ }
+
+
+ /**
+ * Sends a broadcast when a new operation is added to the queue.
+ *
+ * Local broadcasts are only delivered to activities in the same process, but can't be done sticky :\
+ *
+ * @param target Account or URL pointing to an OC server.
+ * @param operation Added operation.
+ */
+ private void sendBroadcastNewOperation(Target target, RemoteOperation operation) {
+ Intent intent = new Intent(ACTION_OPERATION_ADDED);
+ if (target.mAccount != null) {
+ intent.putExtra(EXTRA_ACCOUNT, target.mAccount);
+ } else {
+ intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);
+ }
+ //LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
+ //lbm.sendBroadcast(intent);
+ sendStickyBroadcast(intent);
+ }
+
+
+ // TODO - maybe add a notification for real start of operations
+
+ /**
+ * Sends a LOCAL broadcast when an operations finishes in order to the interested activities can update their view
+ *
+ * Local broadcasts are only delivered to activities in the same process.
+ *
+ * @param target Account or URL pointing to an OC server.
+ * @param operation Finished operation.
+ * @param result Result of the operation.
+ */
+ private void sendBroadcastOperationFinished(Target target, RemoteOperation operation, RemoteOperationResult result) {
+ Intent intent = new Intent(ACTION_OPERATION_FINISHED);
+ intent.putExtra(EXTRA_RESULT, result);
+ if (target.mAccount != null) {
+ intent.putExtra(EXTRA_ACCOUNT, target.mAccount);
+ } else {
+ intent.putExtra(EXTRA_SERVER_URL, target.mServerUrl);
+ }
+ //LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
+ //lbm.sendBroadcast(intent);
+ sendStickyBroadcast(intent);
+ }
+
+
+ /**
+ * 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.
+ */
+ private void callbackOperationListeners(Target target, final RemoteOperation operation, final RemoteOperationResult result) {
+ Iterator<OnRemoteOperationListener> listeners = mBinder.mBoundListeners.keySet().iterator();
+ while (listeners.hasNext()) {
+ final OnRemoteOperationListener listener = listeners.next();
+ final Handler handler = mBinder.mBoundListeners.get(listener);
+ if (handler != null) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ listener.onRemoteOperationFinish(operation, result);
+ }
+ });
+ }
+ }
+
+ }
+
+
+}
import org.apache.http.client.ClientProtocolException;\r
\r
import com.owncloud.android.datamodel.FileDataStorageManager;\r
-import com.owncloud.android.oc_framework.accounts.AccountUtils;\r
-import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException;\r
-import com.owncloud.android.oc_framework.network.webdav.OwnCloudClientFactory;\r
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;\r
+import com.owncloud.android.lib.accounts.AccountUtils;\r
+import com.owncloud.android.lib.accounts.AccountUtils.AccountNotFoundException;\r
+import com.owncloud.android.lib.network.OwnCloudClientFactory;\r
+import com.owncloud.android.lib.network.OwnCloudClient;\r
\r
\r
import android.accounts.Account;\r
private ContentProviderClient mContentProviderClient;\r
private FileDataStorageManager mStoreManager;\r
\r
- private WebdavClient mClient = null;\r
+ private OwnCloudClient mClient = null;\r
\r
public AbstractOwnCloudSyncAdapter(Context context, boolean autoInitialize) {\r
super(context, autoInitialize);\r
mClient = OwnCloudClientFactory.createOwnCloudClient(account, getContext());\r
}\r
\r
- protected WebdavClient getClient() {\r
+ protected OwnCloudClient getClient() {\r
return mClient;\r
}\r
\r
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ByteArrayEntity;
-import com.owncloud.android.authentication.AccountAuthenticator;
import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
import android.accounts.Account;
import org.apache.jackrabbit.webdav.DavException;
-import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.operations.SynchronizeFolderOperation;
import com.owncloud.android.operations.UpdateOCVersionOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
import com.owncloud.android.ui.activity.ErrorsWhileCopyingHandlerActivity;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
import android.accounts.Account;
+import android.accounts.AccountManager;
import android.accounts.AccountsException;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
+//import android.support.v4.content.LocalBroadcastManager;
/**
* Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing
private static final int MAX_FAILED_RESULTS = 3;
+ public static final String EVENT_FULL_SYNC_START = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_START";
+ public static final String EVENT_FULL_SYNC_END = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_END";
+ public static final String EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED";
+ public static final String EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED";
+
+ public static final String EXTRA_ACCOUNT_NAME = FileSyncAdapter.class.getName() + ".EXTRA_ACCOUNT_NAME";
+ public static final String EXTRA_FOLDER_PATH = FileSyncAdapter.class.getName() + ".EXTRA_FOLDER_PATH";
+ public static final String EXTRA_RESULT = FileSyncAdapter.class.getName() + ".EXTRA_RESULT";
+
+
/** Time stamp for the current synchronization process, used to distinguish fresh data */
private long mCurrentSyncTime;
/** {@link SyncResult} instance to return to the system when the synchronization finish */
private SyncResult mSyncResult;
+
+ /** 'True' means that the server supports the share API */
+ private boolean mIsSharedSupported;
/**
this.setAccount(account);
this.setContentProviderClient(providerClient);
this.setStorageManager(new FileDataStorageManager(account, providerClient));
+
+ AccountManager accountManager = getAccountManager();
+ mIsSharedSupported = Boolean.parseBoolean(accountManager.getUserData(account, OwnCloudAccount.Constants.KEY_SUPPORTS_SHARE_API));
+
try {
this.initClientForCurrentAccount();
} catch (IOException e) {
}
Log_OC.d(TAG, "Synchronization of ownCloud account " + account.name + " starting");
- sendStickyBroadcast(true, null, null); // message to signal the start of the synchronization to the UI
+ sendLocalBroadcast(EVENT_FULL_SYNC_START, null, null); // message to signal the start of the synchronization to the UI
try {
updateOCVersion();
if (mForgottenLocalFiles.size() > 0) {
notifyForgottenLocalFiles();
}
- sendStickyBroadcast(false, null, mLastFailedResult); // message to signal the end to the UI
+ sendLocalBroadcast(EVENT_FULL_SYNC_END, null, mLastFailedResult); // message to signal the end to the UI
}
}
DataStorageManager dataStorageManager,
Account account,
Context context ) {
-
}
*/
// folder synchronization
SynchronizeFolderOperation synchFolderOp = new SynchronizeFolderOperation( folder,
mCurrentSyncTime,
true,
+ mIsSharedSupported,
getStorageManager(),
getAccount(),
getContext()
// synchronized folder -> notice to UI - ALWAYS, although !result.isSuccess
- sendStickyBroadcast(true, folder.getRemotePath(), null);
+ sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED, folder.getRemotePath(), result);
// check the result of synchronizing the folder
if (result.isSuccess() || result.getCode() == ResultCode.SYNC_CONFLICT) {
private void fetchChildren(OCFile parent, List<OCFile> files, boolean parentEtagChanged) {
int i;
OCFile newFile = null;
- String etag = null;
- boolean syncDown = false;
+ //String etag = null;
+ //boolean syncDown = false;
for (i=0; i < files.size() && !mCancellation; i++) {
newFile = files.get(i);
if (newFile.isFolder()) {
syncDown = (parentEtagChanged || etag == null || etag.length() == 0);
if(syncDown) { */
synchronizeFolder(newFile);
- // update the size of the parent folder again after recursive synchronization
- //getStorageManager().updateFolderSize(parent.getFileId());
- sendStickyBroadcast(true, parent.getRemotePath(), null); // notify again to refresh size in UI
+ sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED, parent.getRemotePath(), null);
//}
}
}
/**
* Sends a message to any application component interested in the progress of the synchronization.
*
- * @param inProgress 'True' when the synchronization progress is not finished.
- * @param dirRemotePath Remote path of a folder that was just synchronized (with or without success)
+ * @param event Event in the process of synchronization to be notified.
+ * @param dirRemotePath Remote path of the folder target of the event occurred.
+ * @param result Result of an individual {@ SynchronizeFolderOperation}, if completed; may be null.
*/
- private void sendStickyBroadcast(boolean inProgress, String dirRemotePath, RemoteOperationResult result) {
- Intent i = new Intent(FileSyncService.getSyncMessage());
- i.putExtra(FileSyncService.IN_PROGRESS, inProgress);
- i.putExtra(FileSyncService.ACCOUNT_NAME, getAccount().name);
+ private void sendLocalBroadcast(String event, String dirRemotePath, RemoteOperationResult result) {
+ Log_OC.d(TAG, "Send broadcast " + event);
+ Intent intent = new Intent(event);
+ intent.putExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME, getAccount().name);
if (dirRemotePath != null) {
- i.putExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH, dirRemotePath);
+ intent.putExtra(FileSyncAdapter.EXTRA_FOLDER_PATH, dirRemotePath);
}
if (result != null) {
- i.putExtra(FileSyncService.SYNC_RESULT, result);
+ intent.putExtra(FileSyncAdapter.EXTRA_RESULT, result);
}
- getContext().sendStickyBroadcast(i);
+ getContext().sendStickyBroadcast(intent);
+ //LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
* Notifies the user about a failed synchronization through the status notification bar
*/
private void notifyFailedSynchronization() {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
+ Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_ticker), System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
boolean needsToUpdateCredentials = (mLastFailedResult != null &&
( mLastFailedResult.getCode() == ResultCode.UNAUTHORIZED ||
*/
private void notifyFailsInFavourites() {
if (mFailedResultsCounter > 0) {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
+ Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_fail_in_favourites_ticker), System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// TODO put something smart in the contentIntent below
notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
((NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE)).notify(R.string.sync_fail_in_favourites_ticker, notification);
} else {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
+ Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_conflicts_in_favourites_ticker), System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
// TODO put something smart in the contentIntent below
notification.contentIntent = PendingIntent.getActivity(getContext().getApplicationContext(), (int)System.currentTimeMillis(), new Intent(), 0);
* We won't consider a synchronization as failed when foreign files can not be copied to the ownCloud local directory.
*/
private void notifyForgottenLocalFiles() {
- Notification notification = new Notification(R.drawable.icon, getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
+ Notification notification = new Notification(DisplayUtils.getSeasonalIconId(), getContext().getString(R.string.sync_foreign_files_forgotten_ticker), System.currentTimeMillis());
notification.flags |= Notification.FLAG_AUTO_CANCEL;
/// includes a pending intent in the notification showing a more detailed explanation
*/\r
package com.owncloud.android.syncadapter;\r
\r
-import com.owncloud.android.utils.Log_OC;\r
-\r
import android.app.Service;\r
import android.content.Intent;\r
import android.os.IBinder;\r
*/\r
public class FileSyncService extends Service {\r
\r
- private static final String SYNC_MESSAGE = "ACCOUNT_SYNC";\r
- public static final String SYNC_FOLDER_REMOTE_PATH = "SYNC_FOLDER_REMOTE_PATH";\r
- public static final String IN_PROGRESS = "SYNC_IN_PROGRESS";\r
- public static final String ACCOUNT_NAME = "ACCOUNT_NAME";\r
- public static final String SYNC_RESULT = "SYNC_RESULT";\r
-\r
// Storage for an instance of the sync adapter\r
private static FileSyncAdapter sSyncAdapter = null;\r
// Object to use as a thread-safe lock\r
private static final Object sSyncAdapterLock = new Object();\r
\r
- public static String getSyncMessage(){\r
- return FileSyncService.class.getName().toString() + SYNC_MESSAGE;\r
- }\r
/*\r
* {@inheritDoc}\r
*/\r
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.actionbarsherlock.view.MenuItem;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.oc_framework.accounts.OwnCloudAccount;
+import com.owncloud.android.lib.accounts.OwnCloudAccount;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
mPreviousAccount = AccountUtils.getCurrentOwnCloudAccount(this);
}
- ActionBar action_bar = getSupportActionBar();
- action_bar.setDisplayShowTitleEnabled(true);
- action_bar.setDisplayHomeAsUpEnabled(false);
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
}
@Override
package com.owncloud.android.ui.activity;
-import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.actionbarsherlock.app.ActionBar;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision;
import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
import android.content.Intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
}
@Override
- public void ConflictDecisionMade(Decision decision) {
+ public void conflictDecisionMade(Decision decision) {
Intent i = new Intent(getApplicationContext(), FileUploader.class);
switch (decision) {
@Override
protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
if (getAccount() != null) {
OCFile file = getFile();
if (getFile() == null) {
finish();
} else {
/// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
- FileDataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver());
- file = storageManager.getFileByPath(file.getRemotePath()); // file = null if not in the current Account
+ file = getStorageManager().getFileByPath(file.getRemotePath()); // file = null if not in the current Account
if (file != null) {
setFile(file);
ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(file.getRemotePath(), this);
}
} else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
finish();
}
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.OperationCanceledException;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
-import android.net.Uri;
+import android.content.ServiceConnection;
import android.os.Bundle;
-import android.webkit.MimeTypeMap;
+import android.os.Handler;
+import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
+import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.CreateShareOperation;
+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.LoadingDialog;
import com.owncloud.android.utils.Log_OC;
*
* @author David A. Velasco
*/
-public abstract class FileActivity extends SherlockFragmentActivity {
+public class FileActivity extends SherlockFragmentActivity implements OnRemoteOperationListener {
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 TAG = FileActivity.class.getSimpleName();
- public static final String TAG = FileActivity.class.getSimpleName();
+ private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
/** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located. */
/** Flag to signal when the value of mAccount was restored from a saved state */
private boolean mAccountWasRestored;
+
+ /** Flag to signal if the activity is launched by a notification */
+ private boolean mFromNotification;
+
+ /** Messages handler associated to the main thread and the life cycle of the activity */
+ private Handler mHandler;
+
+ /** Access point to the cached database for the current ownCloud {@link Account} */
+ private FileDataStorageManager mStorageManager = null;
+
+ private FileOperationsHelper mFileOperationsHelper;
+
+ private ServiceConnection mOperationsServiceConnection = null;
+
+ private OperationsServiceBinder mOperationsServiceBinder = null;
/**
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
+ mHandler = new Handler();
+ mFileOperationsHelper = new FileOperationsHelper();
Account account;
if(savedInstanceState != null) {
account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT);
mFile = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE);
+ mFromNotification = savedInstanceState.getBoolean(FileActivity.EXTRA_FROM_NOTIFICATION);
} else {
account = getIntent().getParcelableExtra(FileActivity.EXTRA_ACCOUNT);
mFile = getIntent().getParcelableExtra(FileActivity.EXTRA_FILE);
+ mFromNotification = getIntent().getBooleanExtra(FileActivity.EXTRA_FROM_NOTIFICATION, false);
}
setAccount(account, savedInstanceState != null);
+
+ mOperationsServiceConnection = new OperationsServiceConnection();
+ bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection, Context.BIND_AUTO_CREATE);
}
if (!validAccount) {
swapToDefaultAccount();
}
-
}
if (mAccountWasSet) {
onAccountSet(mAccountWasRestored);
}
+ if (mOperationsServiceBinder != null) {
+ mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler);
+ }
+ }
+
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mOperationsServiceBinder != null) {
+ mOperationsServiceBinder.removeOperationListener(this);
+ }
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mOperationsServiceConnection != null) {
+ unbindService(mOperationsServiceConnection);
+ mOperationsServiceBinder = null;
+ }
}
super.onSaveInstanceState(outState);
outState.putParcelable(FileActivity.EXTRA_FILE, mFile);
outState.putParcelable(FileActivity.EXTRA_ACCOUNT, mAccount);
+ outState.putBoolean(FileActivity.EXTRA_FROM_NOTIFICATION, mFromNotification);
}
return mAccount;
}
+ /**
+ * @return Value of mFromNotification: True if the Activity is launched by a notification
+ */
+ public boolean fromNotification() {
+ return mFromNotification;
+ }
/**
* @return 'True' when the Activity is finishing to enforce the setup of a new account.
*
* Child classes must grant that state depending on the {@link Account} is updated.
*/
- protected abstract void onAccountSet(boolean stateWasRecovered);
+ protected void onAccountSet(boolean stateWasRecovered) {
+ if (getAccount() != null) {
+ mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+
+ } else {
+ Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+ }
+ }
+
+
+ public FileDataStorageManager getStorageManager() {
+ return mStorageManager;
+ }
+
+
+ public OnRemoteOperationListener getRemoteOperationListener() {
+ return this;
+ }
+
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+ public FileOperationsHelper getFileOperationsHelper() {
+ return mFileOperationsHelper;
+ }
+ /**
+ *
+ * @param operation Removal operation performed.
+ * @param result Result of the removal.
+ */
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ Log_OC.d(TAG, "Received result of operation in FileActivity - common behaviour for all the FileActivities ");
+ if (operation instanceof CreateShareOperation) {
+ onCreateShareOperationFinish((CreateShareOperation) operation, result);
+
+ } else if (operation instanceof UnshareLinkOperation) {
+ onUnshareLinkOperationFinish((UnshareLinkOperation)operation, result);
+
+ }
+ }
- public void openFile(OCFile file) {
- if (file != null) {
- String storagePath = file.getStoragePath();
- String encodedStoragePath = WebdavUtils.encodePath(storagePath);
+ private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) {
+ dismissLoadingDialog();
+ if (result.isSuccess()) {
+ updateFileFromDB();
- Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
- intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
- intentForSavedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ Intent sendIntent = operation.getSendIntent();
+ startActivity(sendIntent);
- Intent intentForGuessedMimeType = null;
- if (storagePath.lastIndexOf('.') >= 0) {
- String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1));
- if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
- intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
- intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
- intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- }
- }
+ } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) { // Error --> SHARE_NOT_FOUND
+ Toast t = Toast.makeText(this, getString(R.string.share_link_file_no_exist), Toast.LENGTH_LONG);
+ t.show();
+ } else { // Generic error
+ // Show a Message, operation finished without success
+ Toast t = Toast.makeText(this, getString(R.string.share_link_file_error), Toast.LENGTH_LONG);
+ t.show();
+ }
+ }
+
+
+ private void onUnshareLinkOperationFinish(UnshareLinkOperation operation, RemoteOperationResult result) {
+ dismissLoadingDialog();
+
+ if (result.isSuccess()){
+ updateFileFromDB();
- Intent chooserIntent = null;
- if (intentForGuessedMimeType != null) {
- chooserIntent = Intent.createChooser(intentForGuessedMimeType, getString(R.string.actionbar_open_with));
+ } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) { // Error --> SHARE_NOT_FOUND
+ Toast t = Toast.makeText(this, getString(R.string.unshare_link_file_no_exist), Toast.LENGTH_LONG);
+ t.show();
+ } else { // Generic error
+ // Show a Message, operation finished without success
+ Toast t = Toast.makeText(this, getString(R.string.unshare_link_file_error), Toast.LENGTH_LONG);
+ t.show();
+ }
+
+ }
+
+
+ private void updateFileFromDB(){
+ OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath());
+ if (file != null) {
+ setFile(file);
+ }
+ }
+
+ /**
+ * Show loading dialog
+ */
+ public void showLoadingDialog() {
+ // Construct dialog
+ LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
+ FragmentManager fm = getSupportFragmentManager();
+ FragmentTransaction ft = fm.beginTransaction();
+ loading.show(ft, DIALOG_WAIT_TAG);
+
+ }
+
+
+ /**
+ * Dismiss loading dialog
+ */
+ public void dismissLoadingDialog(){
+ Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
+ if (frag != null) {
+ LoadingDialog loading = (LoadingDialog) frag;
+ loading.dismiss();
+ }
+ }
+
+
+ /**
+ * Implements callback methods for service binding. Passed as a parameter to {
+ */
+ private class OperationsServiceConnection implements ServiceConnection {
+
+ @Override
+ public void onServiceConnected(ComponentName component, IBinder service) {
+ if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
+ Log_OC.d(TAG, "Operations service connected");
+ mOperationsServiceBinder = (OperationsServiceBinder) service;
+ mOperationsServiceBinder.addOperationListener(FileActivity.this, mHandler);
+ if (!mOperationsServiceBinder.isPerformingBlockingOperation()) {
+ dismissLoadingDialog();
+ }
+
} else {
- chooserIntent = Intent.createChooser(intentForSavedMimeType, getString(R.string.actionbar_open_with));
+ return;
}
-
- startActivity(chooserIntent);
-
- } else {
- Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
}
- }
+
+
+ @Override
+ public void onServiceDisconnected(ComponentName component) {
+ if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
+ Log_OC.d(TAG, "Operations service disconnected");
+ mOperationsServiceBinder = null;
+ // TODO whatever could be waiting for the service is unbound
+ }
+ }
+ };
}
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+//import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.view.Window;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
-import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.files.services.FileDownloader;
import com.owncloud.android.files.services.FileObserverService;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
import com.owncloud.android.operations.CreateFolderOperation;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.CreateShareOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.operations.SynchronizeFolderOperation;
-import com.owncloud.android.syncadapter.FileSyncService;
+import com.owncloud.android.operations.UnshareLinkOperation;
+import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.syncadapter.FileSyncAdapter;
import com.owncloud.android.ui.dialog.EditNameDialog;
import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener;
-import com.owncloud.android.ui.dialog.LoadingDialog;
import com.owncloud.android.ui.dialog.SslValidatorDialog;
import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
import com.owncloud.android.ui.fragment.FileDetailFragment;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.ui.fragment.OCFileListFragment;
import com.owncloud.android.ui.preview.PreviewImageActivity;
+import com.owncloud.android.ui.preview.PreviewImageFragment;
import com.owncloud.android.ui.preview.PreviewMediaFragment;
import com.owncloud.android.ui.preview.PreviewVideoActivity;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
* @author David A. Velasco
*/
-public class FileDisplayActivity extends FileActivity implements
-OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, OnRemoteOperationListener, EditNameDialogListener {
+public class FileDisplayActivity extends HookActivity implements
+OCFileListFragment.ContainerActivity, FileDetailFragment.ContainerActivity, OnNavigationListener, OnSslValidatorListener, EditNameDialogListener {
private ArrayAdapter<String> mDirectories;
- /** Access point to the cached database for the current ownCloud {@link Account} */
- private FileDataStorageManager mStorageManager = null;
-
private SyncBroadcastReceiver mSyncBroadcastReceiver;
private UploadFinishReceiver mUploadFinishReceiver;
private DownloadFinishReceiver mDownloadFinishReceiver;
+ //private OperationsServiceReceiver mOperationsServiceReceiver;
private FileDownloaderBinder mDownloaderBinder = null;
private FileUploaderBinder mUploaderBinder = null;
private ServiceConnection mDownloadConnection = null, mUploadConnection = null;
private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
+ private static final String KEY_REFRESH_SHARES_IN_PROGRESS = "SHARES_IN_PROGRESS";
public static final int DIALOG_SHORT_WAIT = 0;
private static final int DIALOG_CHOOSE_UPLOAD_SOURCE = 1;
private static final int DIALOG_SSL_VALIDATOR = 2;
private static final int DIALOG_CERT_NOT_SAVED = 3;
- private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
-
public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
private static final int ACTION_SELECT_CONTENT_FROM_APPS = 1;
private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
private OCFile mWaitingToPreview;
- private Handler mHandler;
private boolean mSyncInProgress = false;
+ //private boolean mRefreshSharesInProgress = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // this calls onAccountChanged() when ownCloud Account is valid
- mHandler = new Handler();
-
/// bindings to transference services
mUploadConnection = new ListServiceConnection();
mDownloadConnection = new ListServiceConnection();
// PIN CODE request ; best location is to decide, let's try this first
if (getIntent().getAction() != null && getIntent().getAction().equals(Intent.ACTION_MAIN) && savedInstanceState == null) {
requestPinCode();
+ } else if (getIntent().getAction() == null && savedInstanceState == null) {
+ requestPinCode();
}
/// file observer
if(savedInstanceState != null) {
mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
+ //mRefreshSharesInProgress = savedInstanceState.getBoolean(KEY_REFRESH_SHARES_IN_PROGRESS);
} else {
mWaitingToPreview = null;
mSyncInProgress = false;
+ //mRefreshSharesInProgress = false;
}
/// USER INTERFACE
mRightFragmentContainer = findViewById(R.id.right_fragment_container);
if (savedInstanceState == null) {
createMinFragments();
+ } else {
+ Log_OC.d(TAG, "Init the secondFragment again");
+ if (mDualPane) {
+ initFragmentsWithFile();
+ }
}
// Action bar setup
mDirectories = new CustomArrayAdapter<String>(this, R.layout.sherlock_spinner_dropdown_item);
getSupportActionBar().setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
- setSupportProgressBarIndeterminateVisibility(mSyncInProgress); // always AFTER setContentView(...) ; to work around bug in its implementation
-
-
+ setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/); // always AFTER setContentView(...) ; to work around bug in its implementation
Log_OC.d(TAG, "onCreate() end");
}
-
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ getSupportActionBar().setIcon(DisplayUtils.getSeasonalIconId());
+ refeshListOfFilesFragment();
+ }
@Override
protected void onDestroy() {
*/
@Override
protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
if (getAccount() != null) {
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-
/// Check whether the 'main' OCFile handled by the Activity is contained in the current Account
OCFile file = getFile();
// get parent from path
// upload in progress - right now, files are not inserted in the local cache until the upload is successful
// get parent from path
parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
- if (mStorageManager.getFileByPath(parentPath) == null)
+ if (getStorageManager().getFileByPath(parentPath) == null)
file = null; // not able to know the directory where the file is uploading
} else {
- file = mStorageManager.getFileByPath(file.getRemotePath()); // currentDir = null if not in the current Account
+ file = getStorageManager().getFileByPath(file.getRemotePath()); // currentDir = null if not in the current Account
}
}
if (file == null) {
// fall back to root folder
- file = mStorageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null
+ file = getStorageManager().getFileByPath(OCFile.ROOT_PATH); // never returns null
}
setFile(file);
setNavigationListWithFolder(file);
+
if (!stateWasRecovered) {
Log_OC.e(TAG, "Initializing Fragments in onAccountChanged..");
initFragmentsWithFile();
updateFragmentsVisibility(!file.isFolder());
updateNavigationElementsInActionBar(file.isFolder() ? null : file);
}
-
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
}
}
if (fileIt.isFolder()) {
mDirectories.add(fileIt.getFileName());
}
- //fileIt = mStorageManager.getFileById(fileIt.getParentId());
// get parent from path
parentPath = fileIt.getRemotePath().substring(0, fileIt.getRemotePath().lastIndexOf(fileIt.getFileName()));
- fileIt = mStorageManager.getFileByPath(parentPath);
+ fileIt = getStorageManager().getFileByPath(parentPath);
}
mDirectories.add(OCFile.PATH_SEPARATOR);
}
transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
transaction.commit();
}
-
+
private void initFragmentsWithFile() {
if (getAccount() != null && getFile() != null) {
/// First fragment
boolean detailsFragmentChanged = false;
if (waitedPreview) {
if (success) {
- mWaitingToPreview = mStorageManager.getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
+ mWaitingToPreview = getStorageManager().getFileById(mWaitingToPreview.getFileId()); // update the file from database, for the local storage path
if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
startMediaPreview(mWaitingToPreview, 0, true);
detailsFragmentChanged = true;
} else {
- openFile(mWaitingToPreview);
+ getFileOperationsHelper().openFile(mWaitingToPreview, this);
}
}
mWaitingToPreview = null;
}
}
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSherlock().getMenuInflater();
targetPath = mDirectories.getItem(i) + OCFile.PATH_SEPARATOR + targetPath;
}
targetPath = OCFile.PATH_SEPARATOR + targetPath;
- OCFile targetFolder = mStorageManager.getFileByPath(targetPath);
+ OCFile targetFolder = getStorageManager().getFileByPath(targetPath);
if (targetFolder != null) {
browseTo(targetFolder);
}
// the next operation triggers a new call to this method, but it's necessary to
// ensure that the name exposed in the action bar is the current directory when the
// user selected it in the navigation list
- getSupportActionBar().setSelectedNavigationItem(0);
+ if (getSupportActionBar().getNavigationMode() == ActionBar.NAVIGATION_MODE_LIST && itemPosition != 0)
+ getSupportActionBar().setSelectedNavigationItem(0);
}
return true;
}
super.onSaveInstanceState(outState);
outState.putParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW, mWaitingToPreview);
outState.putBoolean(FileDisplayActivity.KEY_SYNC_IN_PROGRESS, mSyncInProgress);
+ //outState.putBoolean(FileDisplayActivity.KEY_REFRESH_SHARES_IN_PROGRESS, mRefreshSharesInProgress);
Log_OC.d(TAG, "onSaveInstanceState() end");
}
Log_OC.e(TAG, "onResume() start");
// Listen for sync messages
- IntentFilter syncIntentFilter = new IntentFilter(FileSyncService.getSyncMessage());
+ IntentFilter syncIntentFilter = new IntentFilter(FileSyncAdapter.EVENT_FULL_SYNC_START);
+ syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_END);
+ syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED);
+ syncIntentFilter.addAction(FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_CONTENTS_SYNCED);
+ syncIntentFilter.addAction(SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED);
mSyncBroadcastReceiver = new SyncBroadcastReceiver();
registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
+ //LocalBroadcastManager.getInstance(this).registerReceiver(mSyncBroadcastReceiver, syncIntentFilter);
// Listen for upload messages
IntentFilter uploadIntentFilter = new IntentFilter(FileUploader.getUploadFinishMessage());
downloadIntentFilter.addAction(FileDownloader.getDownloadFinishMessage());
mDownloadFinishReceiver = new DownloadFinishReceiver();
registerReceiver(mDownloadFinishReceiver, downloadIntentFilter);
+
+ // Listen for messages from the OperationsService
+ /*
+ IntentFilter operationsIntentFilter = new IntentFilter(OperationsService.ACTION_OPERATION_ADDED);
+ operationsIntentFilter.addAction(OperationsService.ACTION_OPERATION_FINISHED);
+ mOperationsServiceReceiver = new OperationsServiceReceiver();
+ LocalBroadcastManager.getInstance(this).registerReceiver(mOperationsServiceReceiver, operationsIntentFilter);
+ */
Log_OC.d(TAG, "onResume() end");
}
Log_OC.e(TAG, "onPause() start");
if (mSyncBroadcastReceiver != null) {
unregisterReceiver(mSyncBroadcastReceiver);
+ //LocalBroadcastManager.getInstance(this).unregisterReceiver(mSyncBroadcastReceiver);
mSyncBroadcastReceiver = null;
}
if (mUploadFinishReceiver != null) {
unregisterReceiver(mDownloadFinishReceiver);
mDownloadFinishReceiver = null;
}
-
+ /*
+ if (mOperationsServiceReceiver != null) {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mOperationsServiceReceiver);
+ mOperationsServiceReceiver = null;
+ }
+ */
Log_OC.d(TAG, "onPause() end");
}
/**
- * Show loading dialog
- */
- public void showLoadingDialog() {
- // Construct dialog
- LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment));
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction ft = fm.beginTransaction();
- loading.show(ft, DIALOG_WAIT_TAG);
-
- }
-
- /**
- * Dismiss loading dialog
- */
- public void dismissLoadingDialog(){
- Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG);
- if (frag != null) {
- LoadingDialog loading = (LoadingDialog) frag;
- loading.dismiss();
- }
- }
-
-
- /**
* Translates a content URI of an image to a physical path
* on the disk
* @param uri The URI to resolve
((TextView) v).setTextColor(getResources().getColorStateList(
android.R.color.white));
+
+ fixRoot((TextView) v );
return v;
}
((TextView) v).setTextColor(getResources().getColorStateList(
android.R.color.white));
+ fixRoot((TextView) v );
return v;
}
+ private void fixRoot(TextView v) {
+ if (v.getText().equals(OCFile.PATH_SEPARATOR)) {
+ v.setText(R.string.default_display_name_for_root_folder);
+ }
+ }
+
}
private class SyncBroadcastReceiver extends BroadcastReceiver {
*/
@Override
public void onReceive(Context context, Intent intent) {
- boolean inProgress = intent.getBooleanExtra(FileSyncService.IN_PROGRESS, false);
- String accountName = intent.getStringExtra(FileSyncService.ACCOUNT_NAME);
- RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncService.SYNC_RESULT);
-
- if (getAccount() != null && accountName.equals(getAccount().name)
- && mStorageManager != null
- ) {
-
- String synchFolderRemotePath = intent.getStringExtra(FileSyncService.SYNC_FOLDER_REMOTE_PATH);
-
- OCFile currentFile = (getFile() == null) ? null : mStorageManager.getFileByPath(getFile().getRemotePath());
- OCFile currentDir = (getCurrentDir() == null) ? null : mStorageManager.getFileByPath(getCurrentDir().getRemotePath());
-
- if (currentDir == null) {
- // current folder was removed from the server
- Toast.makeText( FileDisplayActivity.this,
- String.format(getString(R.string.sync_current_folder_was_removed), mDirectories.getItem(0)),
- Toast.LENGTH_LONG)
- .show();
- browseToRoot();
+ String event = intent.getAction();
+ Log_OC.d(TAG, "Received broadcast " + event);
+ String accountName = intent.getStringExtra(FileSyncAdapter.EXTRA_ACCOUNT_NAME);
+ String synchFolderRemotePath = intent.getStringExtra(FileSyncAdapter.EXTRA_FOLDER_PATH);
+ RemoteOperationResult synchResult = (RemoteOperationResult)intent.getSerializableExtra(FileSyncAdapter.EXTRA_RESULT);
+ boolean sameAccount = (getAccount() != null && accountName.equals(getAccount().name) && getStorageManager() != null);
+
+ if (sameAccount) {
+
+ if (FileSyncAdapter.EVENT_FULL_SYNC_START.equals(event)) {
+ mSyncInProgress = true;
} else {
- if (currentFile == null && !getFile().isFolder()) {
- // currently selected file was removed in the server, and now we know it
- cleanSecondFragment();
- currentFile = currentDir;
- }
-
- if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
- OCFileListFragment fileListFragment = getListOfFilesFragment();
- if (fileListFragment != null) {
- fileListFragment.listDirectory(currentDir);
+ OCFile currentFile = (getFile() == null) ? null : getStorageManager().getFileByPath(getFile().getRemotePath());
+ OCFile currentDir = (getCurrentDir() == null) ? null : getStorageManager().getFileByPath(getCurrentDir().getRemotePath());
+
+ if (currentDir == null) {
+ // current folder was removed from the server
+ Toast.makeText( FileDisplayActivity.this,
+ String.format(getString(R.string.sync_current_folder_was_removed), mDirectories.getItem(0)),
+ Toast.LENGTH_LONG)
+ .show();
+ browseToRoot();
+
+ } else {
+ if (currentFile == null && !getFile().isFolder()) {
+ // currently selected file was removed in the server, and now we know it
+ cleanSecondFragment();
+ currentFile = currentDir;
}
+
+ if (synchFolderRemotePath != null && currentDir.getRemotePath().equals(synchFolderRemotePath)) {
+ OCFileListFragment fileListFragment = getListOfFilesFragment();
+ if (fileListFragment != null) {
+ fileListFragment.listDirectory(currentDir);
+ }
+ }
+ setFile(currentFile);
+ }
+
+ mSyncInProgress = (!FileSyncAdapter.EVENT_FULL_SYNC_END.equals(event) && !SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SHARES_SYNCED.equals(event));
+
+ /*
+ if (synchResult != null &&
+ synchResult.isSuccess() &&
+ (SynchronizeFolderOperation.EVENT_SINGLE_FOLDER_SYNCED.equals(event) ||
+ FileSyncAdapter.EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED.equals(event)
+ ) &&
+ !mRefreshSharesInProgress &&
+ getFileOperationsHelper().isSharedSupported(FileDisplayActivity.this)
+ ) {
+ startGetShares();
}
- setFile(currentFile);
+ */
+
}
-
- setSupportProgressBarIndeterminateVisibility(inProgress);
removeStickyBroadcast(intent);
- mSyncInProgress = inProgress;
-
+ Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
+ setSupportProgressBarIndeterminateVisibility(mSyncInProgress /*|| mRefreshSharesInProgress*/);
}
if (synchResult != null) {
return (accountName != null && getAccount() != null && accountName.equals(getAccount().name));
}
}
-
-
+
+
/**
- * {@inheritDoc}
+ * Class waiting for broadcast events from the {@link OperationsService}.
+ *
+ * Updates the list of files when a get for shares is finished; at this moment the refresh of shares is the only
+ * operation performed in {@link OperationsService}.
+ *
+ * In the future will handle the progress or finalization of all the operations performed in {@link OperationsService},
+ * probably all the operations associated to the app model.
*/
- @Override
- public FileDataStorageManager getStorageManager() {
- return mStorageManager;
- }
+ private class OperationsServiceReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (OperationsService.ACTION_OPERATION_ADDED.equals(intent.getAction())) {
+
+ } else if (OperationsService.ACTION_OPERATION_FINISHED.equals(intent.getAction())) {
+ //mRefreshSharesInProgress = false;
+
+ Account account = intent.getParcelableExtra(OperationsService.EXTRA_ACCOUNT);
+ RemoteOperationResult getSharesResult = (RemoteOperationResult)intent.getSerializableExtra(OperationsService.EXTRA_RESULT);
+ if (getAccount() != null && account.name.equals(getAccount().name)
+ && getStorageManager() != null
+ ) {
+ refeshListOfFilesFragment();
+ }
+ if ((getSharesResult != null) &&
+ RemoteOperationResult.ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED.equals(getSharesResult.getCode())) {
+ mLastSslUntrustedServerResult = getSharesResult;
+ showDialog(DIALOG_SSL_VALIDATOR);
+ }
+
+ //setSupportProgressBarIndeterminateVisibility(mRefreshSharesInProgress || mSyncInProgress);
+ }
+
+ }
+
+ }
public void browseToRoot() {
OCFileListFragment listOfFiles = getListOfFilesFragment();
while (mDirectories.getCount() > 1) {
popDirname();
}
- OCFile root = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
+ OCFile root = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
listOfFiles.listDirectory(root);
setFile(listOfFiles.getCurrentFile());
startSyncFolderOperation(root);
if (chosenFile == null || mDualPane) {
// only list of files - set for browsing through folders
OCFile currentDir = getCurrentDir();
- actionBar.setDisplayHomeAsUpEnabled(currentDir != null && currentDir.getParentId() != 0);
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ boolean noRoot = (currentDir != null && currentDir.getParentId() != 0);
+ actionBar.setDisplayHomeAsUpEnabled(noRoot);
+ actionBar.setDisplayShowTitleEnabled(!noRoot);
+ if (!noRoot) {
+ actionBar.setTitle(getString(R.string.default_display_name_for_root_folder));
+ }
+ actionBar.setNavigationMode(!noRoot ? ActionBar.NAVIGATION_MODE_STANDARD : ActionBar.NAVIGATION_MODE_LIST);
actionBar.setListNavigationCallbacks(mDirectories, this); // assuming mDirectories is updated
} else {
}
-// private void updateDisplayHomeAtSync(){
-// ActionBar actionBar = getSupportActionBar();
-// OCFile currentDir = getCurrentDir();
-// if (currentDir.getParentId() != DataStorageManager.ROOT_PARENT_ID) {
-// actionBar.setHomeButtonEnabled(!mSyncInProgress);
-// actionBar.setDisplayHomeAsUpEnabled(!mSyncInProgress);
-// }
-// else {
-// actionBar.setHomeButtonEnabled(true);
-// actionBar.setDisplayHomeAsUpEnabled(false);
-// }
-// }
-//
/**
* {@inheritDoc}
*/
*/
@Override
public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ super.onRemoteOperationFinish(operation, result);
+
if (operation instanceof RemoveFileOperation) {
onRemoveFileOperationFinish((RemoveFileOperation)operation, result);
} else if (operation instanceof CreateFolderOperation) {
onCreateFolderOperationFinish((CreateFolderOperation)operation, result);
+ } else if (operation instanceof CreateShareOperation) {
+ onCreateShareOperationFinish((CreateShareOperation) operation, result);
+
+ } else if (operation instanceof UnshareLinkOperation) {
+ onUnshareLinkOperationFinish((UnshareLinkOperation)operation, result);
+
}
+
}
+
+ private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ refreshShowDetails();
+ refeshListOfFilesFragment();
+ }
+ }
+
+ private void onUnshareLinkOperationFinish(UnshareLinkOperation operation, RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ refreshShowDetails();
+ refeshListOfFilesFragment();
+ } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+ cleanSecondFragment();
+ refeshListOfFilesFragment();
+ }
+ }
+
+ private void refreshShowDetails() {
+ FileFragment details = getSecondFragment();
+ if (details != null) {
+ OCFile file = details.getFile();
+ if (file != null) {
+ file = getStorageManager().getFileByPath(file.getRemotePath());
+ if (details instanceof PreviewMediaFragment) {
+ // Refresh OCFile of the fragment
+ ((PreviewMediaFragment) details).updateFile(file);
+ } else {
+ showDetails(file);
+ }
+ }
+ invalidateOptionsMenu();
+ }
+ }
+
/**
* Updates the view associated to the activity after the finish of an operation trying to remove a
* file.
if (second != null && removedFile.equals(second.getFile())) {
cleanSecondFragment();
}
- if (mStorageManager.getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
+ if (getStorageManager().getFileById(removedFile.getParentId()).equals(getCurrentDir())) {
refeshListOfFilesFragment();
}
((FileDetailFragment) details).updateFileDetails(renamedFile, getAccount());
}
}
- if (mStorageManager.getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
+ if (getStorageManager().getFileById(renamedFile.getParentId()).equals(getCurrentDir())) {
refeshListOfFilesFragment();
}
// Create directory
path += newDirectoryName + OCFile.PATH_SEPARATOR;
- RemoteOperation operation = new CreateFolderOperation(path, false, mStorageManager);
+ RemoteOperation operation = new CreateFolderOperation(path, false, getStorageManager());
operation.execute( getAccount(),
FileDisplayActivity.this,
FileDisplayActivity.this,
- mHandler,
+ getHandler(),
FileDisplayActivity.this);
showLoadingDialog();
if (file != null) {
if (file.isFolder()) {
return file;
- } else if (mStorageManager != null) {
+ } else if (getStorageManager() != null) {
String parentPath = file.getRemotePath().substring(0, file.getRemotePath().lastIndexOf(file.getFileName()));
- return mStorageManager.getFileByPath(parentPath);
+ return getStorageManager().getFileByPath(parentPath);
}
}
return null;
RemoteOperation synchFolderOp = new SynchronizeFolderOperation( folder,
currentSyncTime,
false,
+ getFileOperationsHelper().isSharedSupported(this),
getStorageManager(),
getAccount(),
getApplicationContext()
setSupportProgressBarIndeterminateVisibility(true);
}
-
-// public void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
-// int childCount = viewGroup.getChildCount();
-// for (int i = 0; i < childCount; i++) {
-// View view = viewGroup.getChildAt(i);
-// view.setEnabled(enabled);
-// view.setClickable(!enabled);
-// if (view instanceof ViewGroup) {
-// enableDisableViewGroup((ViewGroup) view, enabled);
-// }
-// }
-// }
+ /*
+ private void startGetShares() {
+ // Get shared files/folders
+ Intent intent = new Intent(this, OperationsService.class);
+ intent.putExtra(OperationsService.EXTRA_ACCOUNT, getAccount());
+ startService(intent);
+
+ mRefreshSharesInProgress = true;
+ }
+ */
+
}
import android.widget.ListView;
import android.widget.TextView;
+import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
/**
} else {
listView.setVisibility(View.GONE);
}
+
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
}
public class ExplanationListAdapterView extends ArrayAdapter<String> {
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * 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;
+
+public abstract class HookActivity extends FileActivity {
+
+ private static final String TAG = HookActivity.class.getName();
+
+}
import com.actionbarsherlock.view.MenuItem;
import com.owncloud.android.R;
import com.owncloud.android.ui.adapter.LogListAdapter;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
setContentView(R.layout.log_send_file);
setTitle("Log History");
ActionBar actionBar = getSherlock().getActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
actionBar.setDisplayHomeAsUpEnabled(true);
ListView listView = (ListView) findViewById(android.R.id.list);
Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton);
import java.util.Arrays;
+import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
import android.app.AlertDialog;
import android.content.DialogInterface;
public final static String EXTRA_ACTIVITY = "com.owncloud.android.ui.activity.PinCodeActivity.ACTIVITY";
public final static String EXTRA_NEW_STATE = "com.owncloud.android.ui.activity.PinCodeActivity.NEW_STATE";
- Button bCancel;
- TextView mPinHdr;
- TextView mPinHdrExplanation;
- EditText mText1;
- EditText mText2;
- EditText mText3;
- EditText mText4;
+ private Button mBCancel;
+ private TextView mPinHdr;
+ private TextView mPinHdrExplanation;
+ private EditText mText1;
+ private EditText mText2;
+ private EditText mText3;
+ private EditText mText4;
- String [] tempText ={"","","",""};
+ private String [] mTempText ={"","","",""};
- String activity;
+ private String mActivity;
- boolean confirmingPinCode = false;
- boolean pinCodeChecked = false;
- boolean newPasswordEntered = false;
- boolean bChange = true; // to control that only one blocks jump
- int tCounter ; // Count the number of attempts an user could introduce the PIN code
+ private boolean mConfirmingPinCode = false;
+ private boolean mPinCodeChecked = false;
+ private boolean mNewPasswordEntered = false;
+ private boolean mBChange = true; // to control that only one blocks jump
+ //private int mTCounter ; // Count the number of attempts an user could introduce the PIN code
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.pincodelock);
Intent intent = getIntent();
- activity = intent.getStringExtra(EXTRA_ACTIVITY);
+ mActivity = intent.getStringExtra(EXTRA_ACTIVITY);
- bCancel = (Button) findViewById(R.id.cancel);
+ mBCancel = (Button) findViewById(R.id.cancel);
mPinHdr = (TextView) findViewById(R.id.pinHdr);
mPinHdrExplanation = (TextView) findViewById(R.id.pinHdrExpl);
mText1 = (EditText) findViewById(R.id.txt1);
// In a previous version settings is allow from start
if ( (appPrefs.getString("PrefPinCode1", null) == null ) ){
setChangePincodeView(true);
- pinCodeChecked = true;
- newPasswordEntered = true;
+ mPinCodeChecked = true;
+ mNewPasswordEntered = true;
}else{
if (appPrefs.getBoolean("set_pincode", false)){
// pincode activated
- if (activity.equals("preferences")){
+ if (mActivity.equals("preferences")){
// PIN has been activated yet
mPinHdr.setText(R.string.pincode_configure_your_pin);
mPinHdrExplanation.setVisibility(View.VISIBLE);
- pinCodeChecked = true ; // No need to check it
+ mPinCodeChecked = true ; // No need to check it
setChangePincodeView(true);
}else{
// PIN active
- bCancel.setVisibility(View.INVISIBLE);
- bCancel.setVisibility(View.GONE);
+ mBCancel.setVisibility(View.INVISIBLE);
+ mBCancel.setVisibility(View.GONE);
mPinHdr.setText(R.string.pincode_enter_pin_code);
mPinHdrExplanation.setVisibility(View.INVISIBLE);
setChangePincodeView(false);
// pincode removal
mPinHdr.setText(R.string.pincode_remove_your_pincode);
mPinHdrExplanation.setVisibility(View.INVISIBLE);
- pinCodeChecked = false;
+ mPinCodeChecked = false;
setChangePincodeView(true);
}
}
setTextListeners();
-
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
}
protected void setInitVars(){
- confirmingPinCode = false;
- pinCodeChecked = false;
- newPasswordEntered = false;
+ mConfirmingPinCode = false;
+ mPinCodeChecked = false;
+ mNewPasswordEntered = false;
}
protected void setInitView(){
- bCancel.setVisibility(View.INVISIBLE);
- bCancel.setVisibility(View.GONE);
+ mBCancel.setVisibility(View.INVISIBLE);
+ mBCancel.setVisibility(View.GONE);
mPinHdr.setText(R.string.pincode_enter_pin_code);
mPinHdrExplanation.setVisibility(View.INVISIBLE);
}
protected void setChangePincodeView(boolean state){
if(state){
- bCancel.setVisibility(View.VISIBLE);
- bCancel.setOnClickListener(new OnClickListener() {
+ mBCancel.setVisibility(View.VISIBLE);
+ mBCancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@Override
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[0] = mText1.getText().toString();
+ if (!mConfirmingPinCode){
+ mTempText[0] = mText1.getText().toString();
}
mText2.requestFocus();
@Override
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[1] = mText2.getText().toString();
+ if (!mConfirmingPinCode){
+ mTempText[1] = mText2.getText().toString();
}
mText3.requestFocus();
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && mBChange) {
mText1.setText("");
mText1.requestFocus();
- if (!confirmingPinCode)
- tempText[0] = "";
- bChange= false;
+ if (!mConfirmingPinCode)
+ mTempText[0] = "";
+ mBChange= false;
- }else if(!bChange){
- bChange=true;
+ }else if(!mBChange){
+ mBChange=true;
}
return false;
@Override
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[2] = mText3.getText().toString();
+ if (!mConfirmingPinCode){
+ mTempText[2] = mText3.getText().toString();
}
mText4.requestFocus();
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && mBChange) {
mText2.requestFocus();
- if (!confirmingPinCode)
- tempText[1] = "";
+ if (!mConfirmingPinCode)
+ mTempText[1] = "";
mText2.setText("");
- bChange= false;
+ mBChange= false;
- }else if(!bChange){
- bChange=true;
+ }else if(!mBChange){
+ mBChange=true;
}
return false;
public void afterTextChanged(Editable s) {
if (s.length() > 0) {
- if (!confirmingPinCode){
- tempText[3] = mText4.getText().toString();
+ if (!mConfirmingPinCode){
+ mTempText[3] = mText4.getText().toString();
}
mText1.requestFocus();
- if (!pinCodeChecked){
- pinCodeChecked = checkPincode();
+ if (!mPinCodeChecked){
+ mPinCodeChecked = checkPincode();
}
- if (pinCodeChecked && activity.equals("FileDisplayActivity")){
+ if (mPinCodeChecked &&
+ ( mActivity.equals("FileDisplayActivity") || mActivity.equals("PreviewImageActivity") ) ){
finish();
- } else if (pinCodeChecked){
+ } else if (mPinCodeChecked){
Intent intent = getIntent();
String newState = intent.getStringExtra(EXTRA_NEW_STATE);
}else{
- if (!confirmingPinCode){
+ if (!mConfirmingPinCode){
pinCodeChangeRequest();
} else {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DEL && bChange) {
+ if (keyCode == KeyEvent.KEYCODE_DEL && mBChange) {
mText3.requestFocus();
- if (!confirmingPinCode)
- tempText[2]="";
+ if (!mConfirmingPinCode)
+ mTempText[2]="";
mText3.setText("");
- bChange= false;
+ mBChange= false;
- }else if(!bChange){
- bChange=true;
+ }else if(!mBChange){
+ mBChange=true;
}
return false;
}
clearBoxes();
mPinHdr.setText(R.string.pincode_reenter_your_pincode);
mPinHdrExplanation.setVisibility(View.INVISIBLE);
- confirmingPinCode =true;
+ mConfirmingPinCode =true;
}
String pText3 = appPrefs.getString("PrefPinCode3", null);
String pText4 = appPrefs.getString("PrefPinCode4", null);
- if ( tempText[0].equals(pText1) &&
- tempText[1].equals(pText2) &&
- tempText[2].equals(pText3) &&
- tempText[3].equals(pText4) ) {
+ if ( mTempText[0].equals(pText1) &&
+ mTempText[1].equals(pText2) &&
+ mTempText[2].equals(pText3) &&
+ mTempText[3].equals(pText4) ) {
return true;
}else {
- Arrays.fill(tempText, null);
+ Arrays.fill(mTempText, null);
AlertDialog aDialog = new AlertDialog.Builder(this).create();
CharSequence errorSeq = getString(R.string.common_error);
aDialog.setTitle(errorSeq);
clearBoxes();
mPinHdr.setText(R.string.pincode_enter_pin_code);
mPinHdrExplanation.setVisibility(View.INVISIBLE);
- newPasswordEntered = true;
- confirmingPinCode = false;
+ mNewPasswordEntered = true;
+ mConfirmingPinCode = false;
}
protected void confirmPincode(){
- confirmingPinCode = false;
+ mConfirmingPinCode = false;
String rText1 = mText1.getText().toString();
String rText2 = mText2.getText().toString();
String rText3 = mText3.getText().toString();
String rText4 = mText4.getText().toString();
- if ( tempText[0].equals(rText1) &&
- tempText[1].equals(rText2) &&
- tempText[2].equals(rText3) &&
- tempText[3].equals(rText4) ) {
+ if ( mTempText[0].equals(rText1) &&
+ mTempText[1].equals(rText2) &&
+ mTempText[2].equals(rText3) &&
+ mTempText[3].equals(rText4) ) {
savePincodeAndExit();
} else {
- Arrays.fill(tempText, null);
+ Arrays.fill(mTempText, null);
AlertDialog aDialog = new AlertDialog.Builder(this).create();
CharSequence errorSeq = getString(R.string.common_error);
aDialog.setTitle(errorSeq);
SharedPreferences.Editor appPrefs = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
- appPrefs.putString("PrefPinCode1", tempText[0]);
- appPrefs.putString("PrefPinCode2",tempText[1]);
- appPrefs.putString("PrefPinCode3", tempText[2]);
- appPrefs.putString("PrefPinCode4", tempText[3]);
+ appPrefs.putString("PrefPinCode1", mTempText[0]);
+ appPrefs.putString("PrefPinCode2",mTempText[1]);
+ appPrefs.putString("PrefPinCode3", mTempText[2]);
+ appPrefs.putString("PrefPinCode4", mTempText[3]);
appPrefs.putBoolean("set_pincode",true);
appPrefs.commit();
public boolean onKeyDown(int keyCode, KeyEvent event){
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount()== 0){
- if (activity.equals("preferences")){
+ if (mActivity.equals("preferences")){
SharedPreferences.Editor appPrefsE = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
*/
package com.owncloud.android.ui.activity;
-import java.util.Vector;
-
+import android.accounts.Account;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.db.DbHandler;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
-import com.owncloud.android.utils.OwnCloudSession;
/**
public class Preferences extends SherlockPreferenceActivity {
private static final String TAG = "OwnCloudPreferences";
- private final int mNewSession = 47;
- private final int mEditSession = 48;
private DbHandler mDbHandler;
- private Vector<OwnCloudSession> mSessions;
private CheckBoxPreference pCode;
//private CheckBoxPreference pLogging;
//private Preference pLoggingHistory;
private Preference pAboutApp;
- private int mSelectedMenuItem;
@SuppressWarnings("deprecation")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDbHandler = new DbHandler(getBaseContext());
- mSessions = new Vector<OwnCloudSession>();
addPreferencesFromResource(R.xml.preferences);
//populateAccountList();
ActionBar actionBar = getSherlock().getActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
actionBar.setDisplayHomeAsUpEnabled(true);
Preference p = findPreference("manage_account");
Intent intent = new Intent(Intent.ACTION_SENDTO);
intent.setType("text/plain");
- //Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
+ intent.setData(Uri.parse(getString(R.string.mail_recommend)));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
String appName = getString(R.string.app_name);
- //String username = currentAccount.name.substring(0, currentAccount.name.lastIndexOf('@'));
- //String recommendSubject = String.format(getString(R.string.recommend_subject), username, appName);
+ String downloadUrl = getString(R.string.url_app_download);
+ Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(Preferences.this);
+ 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);
+
intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
- //String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), username);
- String recommendText = String.format(getString(R.string.recommend_text), getString(R.string.app_name), getString(R.string.url_app_download));
intent.putExtra(Intent.EXTRA_TEXT, recommendText);
-
- intent.setData(Uri.parse(getString(R.string.mail_recommend)));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
import com.owncloud.android.ui.fragment.LocalFileListFragment;
import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.Log_OC;
// Action bar setup
ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
actionBar.setHomeButtonEnabled(true); // mandatory since Android ICS, according to the official documentation
actionBar.setDisplayHomeAsUpEnabled(mCurrentDir != null && mCurrentDir.getName() != null);
actionBar.setDisplayShowTitleEnabled(false);
@Override
protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
if (getAccount() != null) {
if (!mAccountOnCreation.equals(getAccount())) {
setResult(RESULT_CANCELED);
}
} else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
setResult(RESULT_CANCELED);
finish();
}
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
-import com.owncloud.android.R.id;
-import com.owncloud.android.R.layout;
-import com.owncloud.android.R.string;
import com.owncloud.android.authentication.AccountAuthenticator;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
public void uploadFiles() {
try {
- //WebdavClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+ //OwnCloudClient webdav = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
ArrayList<String> local = new ArrayList<String>();
ArrayList<String> remote = new ArrayList<String>();
/* TODO - mCreateDir can never be true at this moment; we will replace wdc.createDirectory by CreateFolderOperation when that is fixed
- WebdavClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
+ OwnCloudClient wdc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getApplicationContext());
// create last directory in path if necessary
if (mCreateDir) {
wdc.createDirectory(mUploadPath);
/* ownCloud Android client application\r
* Copyright (C) 2011 Bartek Przybylski\r
- * Copyright (C) 2012-2013 ownCloud Inc.\r
+ * Copyright (C) 2012-2014 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
fileSizeV.setText(DisplayUtils.bytesToHumanReadable(file.getFileLength()));\r
lastModV.setVisibility(View.VISIBLE);\r
lastModV.setText(DisplayUtils.unixTimeToHumanReadable(file.getModificationTimestamp()));\r
- checkBoxV.setVisibility(View.GONE);\r
- view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+ checkBoxV.setVisibility(View.GONE);\r
+ view.findViewById(R.id.imageView3).setVisibility(View.GONE);\r
+ }\r
+ \r
+ ImageView shareIconV = (ImageView) view.findViewById(R.id.shareIcon);\r
+ if (file.isShareByLink()) {\r
+ shareIconV.setVisibility(View.VISIBLE);\r
+ } else {\r
+ shareIconV.setVisibility(View.INVISIBLE);\r
}\r
}\r
\r
/* ownCloud Android client application
* Copyright (C) 2011 Bartek Przybylski
- * Copyright (C) 2012-2013 ownCloud Inc.
+ * Copyright (C) 2012-2014 ownCloud Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
view.findViewById(R.id.imageView2).setVisibility(View.INVISIBLE); // not GONE; the alignment changes; ugly way to keep it
view.findViewById(R.id.imageView3).setVisibility(View.GONE);
+
+ view.findViewById(R.id.shareIcon).setVisibility(View.GONE);
}
return view;
--- /dev/null
+/* ownCloud Android client application
+ * Copyright (C) 2012-2014 ownCloud Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package com.owncloud.android.ui.dialog;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockDialogFragment;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.ui.activity.FileActivity;
+import com.owncloud.android.utils.Log_OC;
+
+/**
+ * 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 ActivityChooserDialog extends SherlockDialogFragment {
+
+ private final static String TAG = ActivityChooserDialog.class.getSimpleName();
+ private final static String ARG_INTENT = ActivityChooserDialog.class.getSimpleName() + ".ARG_INTENT";
+ private final static String ARG_PACKAGES_TO_EXCLUDE = ActivityChooserDialog.class.getSimpleName() + ".ARG_PACKAGES_TO_EXCLUDE";
+ private final static String ARG_FILE_TO_SHARE = ActivityChooserDialog.class.getSimpleName() + ".FILE_TO_SHARE";
+
+ private ActivityAdapter mAdapter;
+ private OCFile mFile;
+ private Intent mIntent;
+
+ public static ActivityChooserDialog newInstance(Intent intent, String[] packagesToExclude, OCFile fileToShare) {
+ ActivityChooserDialog f = new ActivityChooserDialog();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_INTENT, intent);
+ args.putStringArray(ARG_PACKAGES_TO_EXCLUDE, packagesToExclude);
+ args.putParcelable(ARG_FILE_TO_SHARE, fileToShare);
+ f.setArguments(args);
+ return f;
+ }
+
+ public ActivityChooserDialog() {
+ super();
+ Log_OC.d(TAG, "constructor");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mIntent = getArguments().getParcelable(ARG_INTENT);
+ String[] packagesToExclude = getArguments().getStringArray(ARG_PACKAGES_TO_EXCLUDE);
+ List<String> packagesToExcludeList = Arrays.asList(packagesToExclude != null ? packagesToExclude : new String[0]);
+ mFile = getArguments().getParcelable(ARG_FILE_TO_SHARE);
+
+ PackageManager pm= getSherlockActivity().getPackageManager();
+ List<ResolveInfo> activities = pm.queryIntentActivities(mIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ Iterator<ResolveInfo> it = activities.iterator();
+ ResolveInfo resolveInfo;
+ while (it.hasNext()) {
+ resolveInfo = it.next();
+ if (packagesToExcludeList.contains(resolveInfo.activityInfo.packageName.toLowerCase())) {
+ it.remove();
+ }
+ }
+ Collections.sort(activities, new ResolveInfo.DisplayNameComparator(pm));
+ mAdapter = new ActivityAdapter(getSherlockActivity(), pm, activities);
+
+ return new AlertDialog.Builder(getSherlockActivity())
+ .setTitle(R.string.activity_chooser_title)
+ .setAdapter(mAdapter, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Add the information of the chosen activity to the intent to send
+ ResolveInfo chosen = mAdapter.getItem(which);
+ ActivityInfo actInfo = chosen.activityInfo;
+ ComponentName name=new ComponentName(actInfo.applicationInfo.packageName, actInfo.name);
+ mIntent.setComponent(name);
+
+ // Create a new share resource
+ FileOperationsHelper foh = new FileOperationsHelper();
+ foh.shareFileWithLinkToApp(mFile, mIntent, (FileActivity)getSherlockActivity());
+ }
+ })
+ .create();
+ }
+
+
+ class ActivityAdapter extends ArrayAdapter<ResolveInfo> {
+
+ private PackageManager mPackageManager;
+
+ ActivityAdapter(Context context, PackageManager pm, List<ResolveInfo> apps) {
+ super(context, R.layout.activity_row, apps);
+ this.mPackageManager = pm;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = newView(parent);
+ }
+ bindView(position, convertView);
+ return convertView;
+ }
+
+ private View newView(ViewGroup parent) {
+ return(((LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.activity_row, parent, false));
+ }
+
+ private void bindView(int position, View row) {
+ TextView label = (TextView) row.findViewById(R.id.title);
+ label.setText(getItem(position).loadLabel(mPackageManager));
+ ImageView icon = (ImageView) row.findViewById(R.id.icon);
+ icon.setImageDrawable(getItem(position).loadIcon(mPackageManager));
+ }
+ }
+
+}
import com.actionbarsherlock.app.SherlockDialogFragment;
import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
/**
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
Dialog dialog = builder.setView(webview)
- .setIcon(R.drawable.icon)
+ .setIcon(DisplayUtils.getSeasonalIconId())
//.setTitle(R.string.whats_new)
.setPositiveButton(R.string.common_ok,
new DialogInterface.OnClickListener() {
import com.actionbarsherlock.app.SherlockDialogFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.owncloud.android.R;
+import com.owncloud.android.utils.DisplayUtils;
/**
public Dialog onCreateDialog(Bundle savedInstanceState) {
String remotepath = getArguments().getString("remotepath");
return new AlertDialog.Builder(getSherlockActivity())
- .setIcon(R.drawable.icon)
+ .setIcon(DisplayUtils.getSeasonalIconId())
.setTitle(R.string.conflict_title)
.setMessage(String.format(getString(R.string.conflict_message), remotepath))
.setPositiveButton(R.string.conflict_overwrite,
@Override
public void onClick(DialogInterface dialog, int which) {
if (mListener != null)
- mListener.ConflictDecisionMade(Decision.OVERWRITE);
+ mListener.conflictDecisionMade(Decision.OVERWRITE);
}
})
.setNeutralButton(R.string.conflict_keep_both,
@Override
public void onClick(DialogInterface dialog, int which) {
if (mListener != null)
- mListener.ConflictDecisionMade(Decision.KEEP_BOTH);
+ mListener.conflictDecisionMade(Decision.KEEP_BOTH);
}
})
.setNegativeButton(R.string.conflict_dont_upload,
@Override
public void onClick(DialogInterface dialog, int which) {
if (mListener != null)
- mListener.ConflictDecisionMade(Decision.CANCEL);
+ mListener.conflictDecisionMade(Decision.CANCEL);
}
})
.create();
@Override
public void onCancel(DialogInterface dialog) {
- mListener.ConflictDecisionMade(Decision.CANCEL);
+ mListener.conflictDecisionMade(Decision.CANCEL);
}
public interface OnConflictDecisionMadeListener {
- public void ConflictDecisionMade(Decision decision);
+ public void conflictDecisionMade(Decision decision);
}
}
import com.actionbarsherlock.app.SherlockDialogFragment;
import com.owncloud.android.R;
-import com.owncloud.android.oc_framework.utils.FileUtils;
+import com.owncloud.android.lib.utils.FileUtils;
/**
import com.owncloud.android.R;
import com.owncloud.android.authentication.SsoWebViewClient;
import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
-import com.owncloud.android.oc_framework.network.webdav.WebdavClient;
+import com.owncloud.android.lib.network.OwnCloudClient;
import com.owncloud.android.utils.Log_OC;
webSettings.setBuiltInZoomControls(true);
webSettings.setLoadWithOverviewMode(false);
webSettings.setSavePassword(false);
- webSettings.setUserAgentString(WebdavClient.USER_AGENT);
+ webSettings.setUserAgentString(OwnCloudClient.USER_AGENT);
webSettings.setSaveFormData(false);
return rootView;
import android.widget.Button;
import android.widget.TextView;
-import com.owncloud.android.oc_framework.network.CertificateCombinedException;
-import com.owncloud.android.oc_framework.network.NetworkUtils;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.network.CertificateCombinedException;
+import com.owncloud.android.lib.network.NetworkUtils;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.utils.Log_OC;
/**
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult.ResultCode;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation;
super.onActivityCreated(savedInstanceState);
if (mAccount != null) {
mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+ OCFile file = mStorageManager.getFileByPath(getFile().getRemotePath());
+ if (file != null) {
+ setFile(file);
+ }
}
}
toHide.add(R.id.action_remove_file);
}
-
+
+ // Options shareLink
+ if (!file.isShareByLink()) {
+ toHide.add(R.id.action_unshare_file);
+ } else {
+ toShow.add(R.id.action_unshare_file);
+ }
+
MenuItem item = null;
for (int i : toHide) {
item = menu.findItem(i);
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case R.id.action_share_file: {
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().shareFileWithLink(getFile(), activity);
+ return true;
+ }
+ case R.id.action_unshare_file: {
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().unshareFileWithLink(getFile(), activity);
+ return true;
+ }
case R.id.action_open_file_with: {
- mContainerActivity.openFile(getFile());
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().openFile(getFile(), activity);
return true;
}
case R.id.action_remove_file: {
return false;
}
}
-
+
@Override
public void onClick(View v) {
switch (v.getId()) {
}
}
-
private void removeFile() {
OCFile file = getFile();
ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance(
@Override
public void onNeutral(String callerTag) {
- File f = null;
OCFile file = getFile();
- if (file.isDown() && (f = new File(file.getStoragePath())).exists()) {
- f.delete();
+ mStorageManager.removeFile(file, false, true); // TODO perform in background task / new thread
+ if (file.getStoragePath() != null) {
file.setStoragePath(null);
- mStorageManager.saveFile(file);
updateFileDetails(file, mAccount);
}
}
}
@Override
- public void onTransferProgress(long progressRate) {
- // old method, nothing here
- };
-
- @Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
if (percent != mLastPercent) {
import com.actionbarsherlock.app.SherlockFragment;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.FileHandler;
import com.owncloud.android.ui.activity.TransferServiceGetter;
*
* @author David A. Velasco
*/
- public interface ContainerActivity extends TransferServiceGetter, FileHandler {
+ public interface ContainerActivity extends TransferServiceGetter {
/**
* Callback method invoked when the detail fragment wants to notice its container
* @param file File to show details
*/
public void showDetails(OCFile file);
-
-
+
}
}
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.FileHandler;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.operations.RenameFileOperation;
import com.owncloud.android.operations.SynchronizeFileOperation;
// media preview
mContainerActivity.startMediaPreview(file, 0, true);
} else {
- // open with
- mContainerActivity.openFile(file);
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().openFile(file, activity);
}
} else {
toHide.add(R.id.action_cancel_upload);
}
}
+
+ // Options shareLink
+ if (!targetFile.isShareByLink()) {
+ toHide.add(R.id.action_unshare_file);
+ }
for (int i : toHide) {
item = menu.findItem(i);
public boolean onContextItemSelected (MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
mTargetFile = (OCFile) mAdapter.getItem(info.position);
- switch (item.getItemId()) {
+ switch (item.getItemId()) {
+ case R.id.action_share_file: {
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().shareFileWithLink(mTargetFile, activity);
+ return true;
+ }
+ case R.id.action_unshare_file: {
+ FileDisplayActivity activity = (FileDisplayActivity) getSherlockActivity();
+ activity.getFileOperationsHelper().unshareFileWithLink(mTargetFile, activity);
+ return true;
+ }
case R.id.action_rename_file: {
String fileName = mTargetFile.getFileName();
int extensionStart = mTargetFile.isFolder() ? -1 : fileName.lastIndexOf(".");
return super.onContextItemSelected(item);
}
}
-
+
/**
* Use this to query the {@link OCFile} that is currently
*
* @author David A. Velasco
*/
- public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler {
+ public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener {
/**
* Callback method invoked when a the user browsed into a different folder through the list of files
* @param file
*/
public void onBrowsedDownTo(OCFile folder);
-
+
public void startDownloadForPreview(OCFile file);
public void startMediaPreview(OCFile file, int i, boolean b);
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.owncloud.android.oc_framework.network.webdav.OnDatatransferProgressListener;
+import com.owncloud.android.lib.network.OnDatatransferProgressListener;
/**
}
@Override
- public void onTransferProgress(long progressRate) {
- // old method, nothing here
- };
-
- @Override
public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String filename) {
int percent = (int)(100.0*((double)totalTransferredSoFar)/((double)totalToTransfer));
if (percent != mLastPercent) {
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
+import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult.ResultCode;
+import com.owncloud.android.operations.CreateShareOperation;
+import com.owncloud.android.operations.UnshareLinkOperation;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.activity.PinCodeActivity;
import com.owncloud.android.ui.dialog.LoadingDialog;
import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.Log_OC;
*
* @author David A. Velasco
*/
-public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener {
+public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener , OnRemoteOperationListener{
public static final int DIALOG_SHORT_WAIT = 0;
private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT";
- private FileDataStorageManager mStorageManager;
-
private ViewPager mViewPager;
private PreviewImagePagerAdapter mPreviewImagePagerAdapter;
setContentView(R.layout.preview_image_activity);
ActionBar actionBar = getSupportActionBar();
+ actionBar.setIcon(DisplayUtils.getSeasonalIconId());
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.hide();
+ // PIN CODE request
+ if (getIntent().getExtras() != null && savedInstanceState == null && fromNotification()) {
+ requestPinCode();
+ }
+
mFullScreen = true;
if (savedInstanceState != null) {
mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER);
private void initViewPager() {
// get parent from path
String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName()));
- OCFile parentFolder = mStorageManager.getFileByPath(parentPath);
- //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId());
+ OCFile parentFolder = getStorageManager().getFileByPath(parentPath);
if (parentFolder == null) {
// should not be necessary
- parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH);
+ parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH);
}
- mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager);
+ mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), getStorageManager());
mViewPager = (ViewPager) findViewById(R.id.fragmentPager);
int position = mPreviewImagePagerAdapter.getFilePosition(getFile());
position = (position >= 0) ? position : 0;
outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder);
}
-
+ @Override
+ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
+ super.onRemoteOperationFinish(operation, result);
+
+ if (operation instanceof CreateShareOperation) {
+ onCreateShareOperationFinish((CreateShareOperation) operation, result);
+
+ } else if (operation instanceof UnshareLinkOperation) {
+ onUnshareLinkOperationFinish((UnshareLinkOperation) operation, result);
+
+ }
+ }
+
+
+ private void onUnshareLinkOperationFinish(UnshareLinkOperation operation, RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath());
+ if (file != null) {
+ setFile(file);
+ }
+ invalidateOptionsMenu();
+ } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) {
+ backToDisplayActivity();
+ }
+
+ }
+
+ private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) {
+ if (result.isSuccess()) {
+ OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath());
+ if (file != null) {
+ setFile(file);
+ }
+ invalidateOptionsMenu();
+ }
+ }
+
/** Defines callbacks for service binding, passed to bindService() */
private class PreviewImageServiceConnection implements ServiceConnection {
if (getAccount().name.equals(accountName) &&
downloadedRemotePath != null) {
- OCFile file = mStorageManager.getFileByPath(downloadedRemotePath);
+ OCFile file = getStorageManager().getFileByPath(downloadedRemotePath);
int position = mPreviewImagePagerAdapter.getFilePosition(file);
boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false);
//boolean isOffscreen = Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit();
@Override
protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
if (getAccount() != null) {
OCFile file = getFile();
/// Validate handled file (first image to preview)
if (!file.isImage()) {
throw new IllegalArgumentException("Non-image file passed as argument");
}
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
// Update file according to DB file, if it is possible
if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID)
- file = mStorageManager.getFileById(file.getFileId());
+ file = getStorageManager().getFileById(file.getFileId());
if (file != null) {
/// Refresh the activity according to the Account and OCFile set
- setFile(file); // reset after getting it fresh from mStorageManager
+ setFile(file); // reset after getting it fresh from storageManager
getSupportActionBar().setTitle(getFile().getFileName());
//if (!stateWasRecovered) {
initViewPager();
// handled file not in the current Account
finish();
}
-
- } else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
}
}
+ /**
+ * Launch an intent to request the PIN code to the user before letting him use the app
+ */
+ private void requestPinCode() {
+ boolean pinStart = false;
+ SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ pinStart = appPrefs.getBoolean("set_pincode", false);
+ if (pinStart) {
+ Intent i = new Intent(getApplicationContext(), PinCodeActivity.class);
+ i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "PreviewImageActivity");
+ startActivity(i);
+ }
+ }
+
}
*/
package com.owncloud.android.ui.preview;
-import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.network.webdav.WebdavUtils;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.operations.RemoveFileOperation;
+import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.fragment.ConfirmationDialogFragment;
import com.owncloud.android.ui.fragment.FileFragment;
import com.owncloud.android.utils.Log_OC;
mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
if (savedInstanceState != null) {
if (!mIgnoreFirstSavedState) {
- setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE));
+ OCFile file = (OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT);
+
+ // Update the file
+ if (mAccount!= null) {
+ mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());
+ OCFile updatedFile = mStorageManager.getFileByPath(file.getRemotePath());
+ if (updatedFile != null) {
+ setFile(updatedFile);
+ } else {
+ setFile(file);
+ }
+ } else {
+ setFile(file);
+ }
+
} else {
mIgnoreFirstSavedState = false;
}
toHide.add(R.id.action_cancel_upload);
toHide.add(R.id.action_download_file);
toHide.add(R.id.action_rename_file); // by now
+
+ // Options shareLink
+ if (!getFile().isShareByLink()) {
+ toHide.add(R.id.action_unshare_file);
+ }
for (int i : toHide) {
item = menu.findItem(i);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+
+ MenuItem item = menu.findItem(R.id.action_unshare_file);
+ // Options shareLink
+ OCFile file = ((FileActivity) getSherlockActivity()).getFile();
+ if (!file.isShareByLink()) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ } else {
+ item.setVisible(true);
+ item.setEnabled(true);
+ }
+
+ }
+
+
/**
* {@inheritDoc}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case R.id.action_share_file: {
+ FileActivity act = (FileActivity)getSherlockActivity();
+ act.getFileOperationsHelper().shareFileWithLink(getFile(), act);
+ return true;
+ }
+ case R.id.action_unshare_file: {
+ FileActivity act = (FileActivity)getSherlockActivity();
+ act.getFileOperationsHelper().unshareFileWithLink(getFile(), act);
+ return true;
+ }
case R.id.action_open_file_with: {
openFile();
return true;
}
+
private void seeDetails() {
((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
}
*/
@Override
public void onNeutral(String callerTag) {
- // TODO this code should be made in a secondary thread,
OCFile file = getFile();
- if (file.isDown()) { // checks it is still there
- File f = new File(file.getStoragePath());
- f.delete();
- file.setStoragePath(null);
- mStorageManager.saveFile(file);
- finish();
- }
+ mStorageManager.removeFile(file, false, true); // TODO perform in background task / new thread
+ finish();
}
/**
*/
package com.owncloud.android.ui.preview;
-import java.io.File;
import java.util.ArrayList;
import java.util.List;
import com.owncloud.android.media.MediaControlView;
import com.owncloud.android.media.MediaService;
import com.owncloud.android.media.MediaServiceBinder;
-import com.owncloud.android.oc_framework.network.webdav.WebdavUtils;
-import com.owncloud.android.oc_framework.operations.OnRemoteOperationListener;
-import com.owncloud.android.oc_framework.operations.RemoteOperation;
-import com.owncloud.android.oc_framework.operations.RemoteOperationResult;
+import com.owncloud.android.lib.network.webdav.WebdavUtils;
+import com.owncloud.android.lib.operations.common.OnRemoteOperationListener;
+import com.owncloud.android.lib.operations.common.RemoteOperation;
+import com.owncloud.android.lib.operations.common.RemoteOperationResult;
import com.owncloud.android.operations.RemoveFileOperation;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
toHide.add(R.id.action_download_file);
toHide.add(R.id.action_sync_file);
toHide.add(R.id.action_rename_file); // by now
-
+
+ // Options shareLink
+ if (!getFile().isShareByLink()) {
+ toHide.add(R.id.action_unshare_file);
+ }
+
for (int i : toHide) {
item = menu.findItem(i);
if (item != null) {
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+
+ MenuItem item = menu.findItem(R.id.action_unshare_file);
+ // Options shareLink
+ if (!getFile().isShareByLink()) {
+ item.setVisible(false);
+ item.setEnabled(false);
+ } else {
+ item.setVisible(true);
+ item.setEnabled(true);
+ }
+ }
+
/**
* {@inheritDoc}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case R.id.action_share_file: {
+ shareFileWithLink();
+ return true;
+ }
+ case R.id.action_unshare_file: {
+ unshareFileWithLink();
+ return true;
+ }
case R.id.action_open_file_with: {
openFile();
return true;
return false;
}
}
+
+
+ /**
+ * Update the file of the fragment with file value
+ * @param file
+ */
+ public void updateFile(OCFile file){
+ setFile(file);
+ }
+
+ private void unshareFileWithLink() {
+ stopPreview(false);
+ FileActivity activity = (FileActivity)((FileFragment.ContainerActivity)getActivity());
+ activity.getFileOperationsHelper().unshareFileWithLink(getFile(), activity);
+ }
+ private void shareFileWithLink() {
+ stopPreview(false);
+ FileActivity activity = (FileActivity)((FileFragment.ContainerActivity)getActivity());
+ activity.getFileOperationsHelper().shareFileWithLink(getFile(), activity);
+
+ }
+
+
private void seeDetails() {
stopPreview(false);
((FileFragment.ContainerActivity)getActivity()).showDetails(getFile());
@Override
public void onServiceConnected(ComponentName component, IBinder service) {
- if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
- Log_OC.d(TAG, "Media service connected");
- mMediaServiceBinder = (MediaServiceBinder) service;
- if (mMediaServiceBinder != null) {
- prepareMediaController();
- playAudio(); // do not wait for the touch of nobody to play audio
-
- Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
-
- } else {
- Log_OC.e(TAG, "Unexpected response from MediaService while binding");
+ if (getActivity() != null) {
+ if (component.equals(new ComponentName(getActivity(), MediaService.class))) {
+ Log_OC.d(TAG, "Media service connected");
+ mMediaServiceBinder = (MediaServiceBinder) service;
+ if (mMediaServiceBinder != null) {
+ prepareMediaController();
+ playAudio(); // do not wait for the touch of nobody to play audio
+
+ Log_OC.d(TAG, "Successfully bound to MediaService, MediaController ready");
+
+ } else {
+ Log_OC.e(TAG, "Unexpected response from MediaService while binding");
+ }
}
}
}
*/
@Override
public void onNeutral(String callerTag) {
- // TODO this code should be made in a secondary thread,
OCFile file = getFile();
- if (file.isDown()) { // checks it is still there
- stopPreview(true);
- File f = new File(file.getStoragePath());
- f.delete();
- file.setStoragePath(null);
- mStorageManager.saveFile(file);
- finish();
- }
+ stopPreview(true);
+ mStorageManager.removeFile(file, false, true); // TODO perform in background task / new thread
+ finish();
}
/**
package com.owncloud.android.ui.preview;
import com.owncloud.android.R;
-import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.media.MediaService;
import com.owncloud.android.ui.activity.FileActivity;
import android.widget.MediaController;
import android.widget.VideoView;
-import com.owncloud.android.oc_framework.accounts.AccountUtils;
-import com.owncloud.android.oc_framework.accounts.AccountUtils.AccountNotFoundException;
+import com.owncloud.android.lib.accounts.AccountUtils;
+import com.owncloud.android.lib.accounts.AccountUtils.AccountNotFoundException;
/**
* Activity implementing a basic video player.
private static final String TAG = PreviewVideoActivity.class.getSimpleName();
- private FileDataStorageManager mStorageManager;
-
private int mSavedPlaybackPosition; // in the unit time handled by MediaPlayer.getCurrentPosition()
private boolean mAutoplay; // when 'true', the playback starts immediately with the activity
private VideoView mVideoPlayer; // view to play the file; both performs and show the playback
@Override
protected void onAccountSet(boolean stateWasRecovered) {
+ super.onAccountSet(stateWasRecovered);
if (getAccount() != null) {
OCFile file = getFile();
/// Validate handled file (first image to preview)
if (!file.isVideo()) {
throw new IllegalArgumentException("Non-video file passed as argument");
}
- mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
- file = mStorageManager.getFileById(file.getFileId());
+ file = getStorageManager().getFileById(file.getFileId());
if (file != null) {
if (file.isDown()) {
mVideoPlayer.setVideoPath(file.getStoragePath());
finish();
}
} else {
- Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
finish();
}
}
package com.owncloud.android.utils;\r
\r
import java.util.Arrays;\r
+import java.util.Calendar;\r
import java.util.Date;\r
import java.util.HashMap;\r
import java.util.HashSet;\r
import java.util.Set;\r
\r
import com.owncloud.android.R;\r
-import com.owncloud.android.R.drawable;\r
\r
/**\r
* A helper class for some string operations.\r
Date date = new Date(milliseconds);\r
return date.toLocaleString();\r
}\r
+ \r
+ \r
+ public static int getSeasonalIconId() {\r
+ if (Calendar.getInstance().get(Calendar.DAY_OF_YEAR) >= 354) {\r
+ return R.drawable.winter_holidays_icon;\r
+ } else {\r
+ return R.drawable.icon;\r
+ }\r
+ }\r
}\r
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.operations.common.RemoteFile;
import android.annotation.SuppressLint;
import android.content.Context;
parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
return parentPath;
}
+
+ /**
+ * Creates and populates a new {@link OCFile} object with the data read from the server.
+ *
+ * @param remote remote file read from the server (remote file or folder).
+ * @return New OCFile instance representing the remote resource described by we.
+ */
+ public static OCFile fillOCFile(RemoteFile remote) {
+ OCFile file = new OCFile(remote.getRemotePath());
+ file.setCreationTimestamp(remote.getCreationTimestamp());
+ file.setFileLength(remote.getLength());
+ file.setMimetype(remote.getMimeType());
+ file.setModificationTimestamp(remote.getModifiedTimestamp());
+ file.setEtag(remote.getEtag());
+
+ return file;
+ }
+
+ /**
+ * Creates and populates a new {@link RemoteFile} object with the data read from an {@link OCFile}.
+ *
+ * @param oCFile OCFile
+ * @return New RemoteFile instance representing the resource described by ocFile.
+ */
+ public static RemoteFile fillRemoteFile(OCFile ocFile){
+ RemoteFile file = new RemoteFile(ocFile.getRemotePath());
+ file.setCreationTimestamp(ocFile.getCreationTimestamp());
+ file.setLength(ocFile.getFileLength());
+ file.setMimeType(ocFile.getMimetype());
+ file.setModifiedTimestamp(ocFile.getModificationTimestamp());
+ file.setEtag(ocFile.getEtag());
+ return file;
+ }
}
\ No newline at end of file
package com.owncloud.android.test;
+import com.owncloud.android.lib.accounts.AccountUtils;
+import com.owncloud.android.lib.utils.OwnCloudVersion;
+
import android.test.AndroidTestCase;
-import com.owncloud.android.oc_framework.accounts.AccountUtils;
-import com.owncloud.android.oc_framework.utils.OwnCloudVersion;
public class AccountUtilsTest extends AndroidTestCase {
+++ /dev/null
-.tx
-*pyc
-*pyo
-*~
-*egg-info*
+++ /dev/null
-Releasing
-=========
-
-To create a new release:
-
-1. Update local rep and update the version in ``setup.py``::
-
- $ hg pull -u
- $ vim setup.py
-
-2. Test::
-
- $ python setup.py clean sdist
- $ cd dist
- $ tar zxf ...
- $ cd transifex-client
- ...test
-
-3. Package and upload on PyPI::
-
- $ python setup.py clean sdist bdist_egg upload
+++ /dev/null
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software
- interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation. If the Program does not specify a
-version number of this License, you may choose any version ever
-published by the Free Software Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these
-terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
-
- 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, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
-
+++ /dev/null
-include tx
-
-# Docs
-include LICENSE README.rst
-recursive-include docs *
-
+++ /dev/null
-
-=============================
- Transifex Command-Line Tool
-=============================
-
-The Transifex Command-line Client is a command line tool that enables
-you to easily manage your translations within a project without the need
-of an elaborate UI system.
-
-You can use the command line client to easily create new resources, map
-locale files to translations and synchronize your Transifex project with
-your local repository and vice verca. Translators and localization
-managers can also use it to handle large volumes of translation files
-easily and without much hassle.
-
-Check the full documentation at
-http://help.transifex.com/user-guide/client/
-
-
-Installing
-==========
-
-You can install the latest version of transifex-client running ``pip
-install transifex-client`` or ``easy_install transifex-client``
-You can also install the `in-development version`_ of transifex-client
-with ``pip install transifex-client==dev`` or ``easy_install
-transifex-client==dev``.
-
-.. _in-development version: http://code.transifex.com/transifex-client/
-
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import glob
-from codecs import BOM
-
-from setuptools import setup, find_packages
-from setuptools.command.build_py import build_py as _build_py
-
-from txclib import get_version
-
-readme_file = open(u'README.rst')
-long_description = readme_file.read()
-readme_file.close()
-if long_description.startswith(BOM):
- long_description = long_description.lstrip(BOM)
-long_description = long_description.decode('utf-8')
-
-package_data = {
- '': ['LICENSE', 'README.rst'],
-}
-
-scripts = ['tx']
-
-install_requires = []
-try:
- import json
-except ImportError:
- install_requires.append('simplejson')
-
-setup(
- name="transifex-client",
- version=get_version(),
- scripts=scripts,
- description="A command line interface for Transifex",
- long_description=long_description,
- author="Transifex",
- author_email="info@transifex.com",
- url="https://www.transifex.com",
- license="GPLv2",
- dependency_links = [
- ],
- setup_requires = [
- ],
- install_requires = install_requires,
- tests_require = ["mock", ],
- data_files=[
- ],
- test_suite="tests",
- zip_safe=False,
- packages=['txclib', ],
- include_package_data=True,
- package_data = package_data,
- keywords = ('translation', 'localization', 'internationalization',),
-)
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-Unit tests for processor functions.
-"""
-
-import unittest
-from urlparse import urlparse
-from txclib.processors import hostname_tld_migration, hostname_ssl_migration
-
-
-class TestHostname(unittest.TestCase):
- """Test for hostname processors."""
-
- def test_tld_migration_needed(self):
- """
- Test the tld migration of Transifex, when needed.
- """
- hostnames = [
- 'http://transifex.net', 'http://www.transifex.net',
- 'https://fedora.transifex.net',
- ]
- for h in hostnames:
- hostname = hostname_tld_migration(h)
- self.assertTrue(hostname.endswith('com'))
- orig_hostname = 'http://www.transifex.net/path/'
- hostname = hostname_tld_migration(orig_hostname)
- self.assertEqual(hostname, orig_hostname.replace('net', 'com', 1))
-
- def test_tld_migration_needed(self):
- """
- Test that unneeded tld migrations are detected correctly.
- """
- hostnames = [
- 'https://www.transifex.com', 'http://fedora.transifex.com',
- 'http://www.example.net/path/'
- ]
- for h in hostnames:
- hostname = hostname_tld_migration(h)
- self.assertEqual(hostname, h)
-
- def test_no_scheme_specified(self):
- """
- Test that, if no scheme has been specified, the https one will be used.
- """
- hostname = '//transifex.net'
- hostname = hostname_ssl_migration(hostname)
- self.assertTrue(hostname.startswith('https://'))
-
- def test_http_replacement(self):
- """Test the replacement of http with https."""
- hostnames = [
- 'http://transifex.com', 'http://transifex.net/http/',
- 'http://www.transifex.com/path/'
- ]
- for h in hostnames:
- hostname = hostname_ssl_migration(h)
- self.assertEqual(hostname[:8], 'https://')
- self.assertEqual(hostname[7:], h[6:])
-
- def test_no_http_replacement_needed(self):
- """Test that http will not be replaces with https, when not needed."""
- for h in ['http://example.com', 'http://example.com/http/']:
- hostname = hostname_ssl_migration(h)
- self.assertEqual(hostname, hostname)
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-import unittest
-import contextlib
-import itertools
-try:
- import json
-except ImportError:
- import simplejson as json
-from mock import Mock, patch
-
-from txclib.project import Project
-from txclib.config import Flipdict
-
-
-class TestProject(unittest.TestCase):
-
- def test_extract_fields(self):
- """Test the functions that extract a field from a stats object."""
- stats = {
- 'completed': '80%',
- 'last_update': '00:00',
- 'foo': 'bar',
- }
- self.assertEqual(
- stats['completed'], '%s%%' % Project._extract_completed(stats)
- )
- self.assertEqual(stats['last_update'], Project._extract_updated(stats))
-
- def test_specifying_resources(self):
- """Test the various ways to specify resources in a project."""
- p = Project(init=False)
- resources = [
- 'proj1.res1',
- 'proj2.res2',
- 'transifex.txn',
- 'transifex.txo',
- ]
- with patch.object(p, 'get_resource_list') as mock:
- mock.return_value = resources
- cmd_args = [
- 'proj1.res1', '*1*', 'transifex*', '*r*',
- '*o', 'transifex.tx?', 'transifex.txn',
- ]
- results = [
- ['proj1.res1', ],
- ['proj1.res1', ],
- ['transifex.txn', 'transifex.txo', ],
- ['proj1.res1', 'proj2.res2', 'transifex.txn', 'transifex.txo', ],
- ['transifex.txo', ],
- ['transifex.txn', 'transifex.txo', ],
- ['transifex.txn', ],
- [],
- ]
-
- for i, arg in enumerate(cmd_args):
- resources = [arg]
- self.assertEqual(p.get_chosen_resources(resources), results[i])
-
- # wrong argument
- resources = ['*trasnifex*', ]
- self.assertRaises(Exception, p.get_chosen_resources, resources)
-
-
-class TestProjectMinimumPercent(unittest.TestCase):
- """Test the minimum-perc option."""
-
- def setUp(self):
- super(TestProjectMinimumPercent, self).setUp()
- self.p = Project(init=False)
- self.p.minimum_perc = None
- self.p.resource = "resource"
-
- def test_cmd_option(self):
- """Test command-line option."""
- self.p.minimum_perc = 20
- results = itertools.cycle([80, 90 ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertFalse(self.p._satisfies_min_translated({'completed': '12%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '20%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '30%'}))
-
- def test_global_only(self):
- """Test only global option."""
- results = itertools.cycle([80, None ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertFalse(self.p._satisfies_min_translated({'completed': '70%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
- def test_local_lower_than_global(self):
- """Test the case where the local option is lower than the global."""
- results = itertools.cycle([80, 70 ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
- def test_local_higher_than_global(self):
- """Test the case where the local option is lower than the global."""
- results = itertools.cycle([60, 70 ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
- def test_local_only(self):
- """Test the case where the local option is lower than the global."""
- results = itertools.cycle([None, 70 ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertFalse(self.p._satisfies_min_translated({'completed': '60%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '70%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '80%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
- def test_no_option(self):
- """"Test the case there is nothing defined."""
- results = itertools.cycle([None, None ])
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "get_resource_option") as mock:
- mock.side_effect = side_effect
- self.assertTrue(self.p._satisfies_min_translated({'completed': '0%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '10%'}))
- self.assertTrue(self.p._satisfies_min_translated({'completed': '90%'}))
-
-
-class TestProjectFilters(unittest.TestCase):
- """Test filters used to decide whether to push/pull a translation or not."""
-
- def setUp(self):
- super(TestProjectFilters, self).setUp()
- self.p = Project(init=False)
- self.p.minimum_perc = None
- self.p.resource = "resource"
- self.stats = {
- 'en': {
- 'completed': '100%', 'last_update': '2011-11-01 15:00:00',
- }, 'el': {
- 'completed': '60%', 'last_update': '2011-11-01 15:00:00',
- }, 'pt': {
- 'completed': '70%', 'last_update': '2011-11-01 15:00:00',
- },
- }
- self.langs = self.stats.keys()
-
- def test_add_translation(self):
- """Test filters for adding translations.
-
- We do not test here for minimum percentages.
- """
- with patch.object(self.p, "get_resource_option") as mock:
- mock.return_value = None
- should_add = self.p._should_add_translation
- for force in [True, False]:
- for lang in self.langs:
- self.assertTrue(should_add(lang, self.stats, force))
-
- # unknown language
- self.assertFalse(should_add('es', self.stats))
-
- def test_update_translation(self):
- """Test filters for updating a translation.
-
- We do not test here for minimum percentages.
- """
- with patch.object(self.p, "get_resource_option") as mock:
- mock.return_value = None
-
- should_update = self.p._should_update_translation
- force = True
- for lang in self.langs:
- self.assertTrue(should_update(lang, self.stats, 'foo', force))
-
- force = False # reminder
- local_file = 'foo'
-
- # unknown language
- self.assertFalse(should_update('es', self.stats, local_file))
-
- # no local file
- with patch.object(self.p, "_get_time_of_local_file") as time_mock:
- time_mock.return_value = None
- with patch.object(self.p, "get_full_path") as path_mock:
- path_mock.return_value = "foo"
- for lang in self.langs:
- self.assertTrue(
- should_update(lang, self.stats, local_file)
- )
-
- # older local files
- local_times = [self.p._generate_timestamp('2011-11-01 14:00:59')]
- results = itertools.cycle(local_times)
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "_get_time_of_local_file") as time_mock:
- time_mock.side_effect = side_effect
- with patch.object(self.p, "get_full_path") as path_mock:
- path_mock.return_value = "foo"
- for lang in self.langs:
- self.assertTrue(
- should_update(lang, self.stats, local_file)
- )
-
- # newer local files
- local_times = [self.p._generate_timestamp('2011-11-01 15:01:59')]
- results = itertools.cycle(local_times)
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "_get_time_of_local_file") as time_mock:
- time_mock.side_effect = side_effect
- with patch.object(self.p, "get_full_path") as path_mock:
- path_mock.return_value = "foo"
- for lang in self.langs:
- self.assertFalse(
- should_update(lang, self.stats, local_file)
- )
-
- def test_push_translation(self):
- """Test filters for pushing a translation file."""
- with patch.object(self.p, "get_resource_option") as mock:
- mock.return_value = None
-
- local_file = 'foo'
- should_push = self.p._should_push_translation
- force = True
- for lang in self.langs:
- self.assertTrue(should_push(lang, self.stats, local_file, force))
-
- force = False # reminder
-
- # unknown language
- self.assertTrue(should_push('es', self.stats, local_file))
-
- # older local files
- local_times = [self.p._generate_timestamp('2011-11-01 14:00:59')]
- results = itertools.cycle(local_times)
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "_get_time_of_local_file") as time_mock:
- time_mock.side_effect = side_effect
- with patch.object(self.p, "get_full_path") as path_mock:
- path_mock.return_value = "foo"
- for lang in self.langs:
- self.assertFalse(
- should_push(lang, self.stats, local_file)
- )
-
- # newer local files
- local_times = [self.p._generate_timestamp('2011-11-01 15:01:59')]
- results = itertools.cycle(local_times)
- def side_effect(*args):
- return results.next()
-
- with patch.object(self.p, "_get_time_of_local_file") as time_mock:
- time_mock.side_effect = side_effect
- with patch.object(self.p, "get_full_path") as path_mock:
- path_mock.return_value = "foo"
- for lang in self.langs:
- self.assertTrue(
- should_push(lang, self.stats, local_file)
- )
-
-
-class TestProjectPull(unittest.TestCase):
- """Test bits & pieces of the pull method."""
-
- def setUp(self):
- super(TestProjectPull, self).setUp()
- self.p = Project(init=False)
- self.p.minimum_perc = None
- self.p.resource = "resource"
- self.p.host = 'foo'
- self.p.project_slug = 'foo'
- self.p.resource_slug = 'foo'
- self.stats = {
- 'en': {
- 'completed': '100%', 'last_update': '2011-11-01 15:00:00',
- }, 'el': {
- 'completed': '60%', 'last_update': '2011-11-01 15:00:00',
- }, 'pt': {
- 'completed': '70%', 'last_update': '2011-11-01 15:00:00',
- },
- }
- self.langs = self.stats.keys()
- self.files = dict(zip(self.langs, itertools.repeat(None)))
- self.details = {'available_languages': []}
- for lang in self.langs:
- self.details['available_languages'].append({'code': lang})
- self.slang = 'en'
- self.lang_map = Flipdict()
-
- def test_new_translations(self):
- """Test finding new transaltions to add."""
- with patch.object(self.p, 'do_url_request') as resource_mock:
- resource_mock.return_value = json.dumps(self.details)
- files_keys = self.langs
- new_trans = self.p._new_translations_to_add
- for force in [True, False]:
- res = new_trans(
- self.files, self.slang, self.lang_map, self.stats, force
- )
- self.assertEquals(res, set([]))
-
- with patch.object(self.p, '_should_add_translation') as filter_mock:
- filter_mock.return_value = True
- for force in [True, False]:
- res = new_trans(
- {'el': None}, self.slang, self.lang_map, self.stats, force
- )
- self.assertEquals(res, set(['pt']))
- for force in [True, False]:
- res = new_trans(
- {}, self.slang, self.lang_map, self.stats, force
- )
- self.assertEquals(res, set(['el', 'pt']))
-
- files = {}
- files['pt_PT'] = None
- lang_map = {'pt': 'pt_PT'}
- for force in [True, False]:
- res = new_trans(
- files, self.slang, lang_map, self.stats, force
- )
- self.assertEquals(res, set(['el']))
-
- def test_languages_to_pull_empty_initial_list(self):
- """Test determining the languages to pull, when the initial
- list is empty.
- """
- languages = []
- force = False
-
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['el', 'en', 'pt']))
- self.assertFalse(new)
-
- del self.files['el']
- self.files['el-gr'] = None
- self.lang_map['el'] = 'el-gr'
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['el', 'en', 'pt']))
- self.assertFalse(new)
-
- def test_languages_to_pull_with_initial_list(self):
- """Test determining the languages to pull, then there is a
- language selection from the user.
- """
- languages = ['el', 'en']
- self.lang_map['el'] = 'el-gr'
- del self.files['el']
- self.files['el-gr'] = None
- force = False
-
- with patch.object(self.p, '_should_add_translation') as mock:
- mock.return_value = True
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['en', 'el-gr', ]))
- self.assertFalse(new)
-
- mock.return_value = False
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['en', 'el-gr', ]))
- self.assertFalse(new)
-
- del self.files['el-gr']
- mock.return_value = True
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['en', ]))
- self.assertEquals(new, set(['el', ]))
-
- mock.return_value = False
- res = self.p._languages_to_pull(
- languages, self.files, self.lang_map, self.stats, force
- )
- existing = res[0]
- new = res[1]
- self.assertEquals(existing, set(['en', ]))
- self.assertEquals(new, set([]))
-
- def test_in_combination_with_force_option(self):
- """Test the minumum-perc option along with -f."""
- with patch.object(self.p, 'get_resource_option') as mock:
- mock.return_value = 70
-
- res = self.p._should_download('de', self.stats, None, False)
- self.assertEquals(res, False)
- res = self.p._should_download('el', self.stats, None, False)
- self.assertEquals(res, False)
- res = self.p._should_download('el', self.stats, None, True)
- self.assertEquals(res, False)
- res = self.p._should_download('en', self.stats, None, False)
- self.assertEquals(res, True)
- res = self.p._should_download('en', self.stats, None, True)
- self.assertEquals(res, True)
-
- with patch.object(self.p, '_remote_is_newer') as local_file_mock:
- local_file_mock = False
- res = self.p._should_download('pt', self.stats, None, False)
- self.assertEquals(res, True)
- res = self.p._should_download('pt', self.stats, None, True)
- self.assertEquals(res, True)
-
-
-class TestFormats(unittest.TestCase):
- """Tests for the supported formats."""
-
- def setUp(self):
- self.p = Project(init=False)
-
- def test_extensions(self):
- """Test returning the correct extension for a format."""
- sample_formats = {
- 'PO': {'file-extensions': '.po, .pot'},
- 'QT': {'file-extensions': '.ts'},
- }
- extensions = ['.po', '.ts', '', ]
- with patch.object(self.p, "do_url_request") as mock:
- mock.return_value = json.dumps(sample_formats)
- for (type_, ext) in zip(['PO', 'QT', 'NONE', ], extensions):
- extension = self.p._extension_for(type_)
- self.assertEquals(extension, ext)
-
-
-class TestOptions(unittest.TestCase):
- """Test the methods related to parsing the configuration file."""
-
- def setUp(self):
- self.p = Project(init=False)
-
- def test_get_option(self):
- """Test _get_option method."""
- with contextlib.nested(
- patch.object(self.p, 'get_resource_option'),
- patch.object(self.p, 'config', create=True)
- ) as (rmock, cmock):
- rmock.return_value = 'resource'
- cmock.has_option.return_value = 'main'
- cmock.get.return_value = 'main'
- self.assertEqual(self.p._get_option(None, None), 'resource')
- rmock.return_value = None
- cmock.has_option.return_value = 'main'
- cmock.get.return_value = 'main'
- self.assertEqual(self.p._get_option(None, None), 'main')
- cmock.has_option.return_value = None
- self.assertIs(self.p._get_option(None, None), None)
-
-
-class TestConfigurationOptions(unittest.TestCase):
- """Test the various configuration options."""
-
- def test_i18n_type(self):
- p = Project(init=False)
- type_string = 'type'
- i18n_type = 'PO'
- with patch.object(p, 'config', create=True) as config_mock:
- p.set_i18n_type([], i18n_type)
- calls = config_mock.method_calls
- self.assertEquals('set', calls[0][0])
- self.assertEquals('main', calls[0][1][0])
- p.set_i18n_type(['transifex.txo'], 'PO')
- calls = config_mock.method_calls
- self.assertEquals('set', calls[0][0])
- p.set_i18n_type(['transifex.txo', 'transifex.txn'], 'PO')
- calls = config_mock.method_calls
- self.assertEquals('set', calls[0][0])
- self.assertEquals('set', calls[1][0])
-
-
-class TestStats(unittest.TestCase):
- """Test the access to the stats objects."""
-
- def setUp(self):
- self.stats = Mock()
- self.stats.__getitem__ = Mock()
- self.stats.__getitem__.return_value = '12%'
-
- def test_field_used_per_mode(self):
- """Test the fields used for each mode."""
- Project._extract_completed(self.stats, 'translate')
- self.stats.__getitem__.assert_called_with('completed')
- Project._extract_completed(self.stats, 'reviewed')
- self.stats.__getitem__.assert_called_with('reviewed_percentage')
-
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from optparse import OptionParser, OptionValueError
-import os
-import sys
-
-from txclib import utils
-from txclib import get_version
-from txclib.log import set_log_level, logger
-
-reload(sys) # WTF? Otherwise setdefaultencoding doesn't work
-
-# This block ensures that ^C interrupts are handled quietly.
-try:
- import signal
-
- def exithandler(signum,frame):
- signal.signal(signal.SIGINT, signal.SIG_IGN)
- signal.signal(signal.SIGTERM, signal.SIG_IGN)
- sys.exit(1)
-
- signal.signal(signal.SIGINT, exithandler)
- signal.signal(signal.SIGTERM, exithandler)
- if hasattr(signal, 'SIGPIPE'):
- signal.signal(signal.SIGPIPE, signal.SIG_DFL)
-
-except KeyboardInterrupt:
- sys.exit(1)
-
-# When we open file with f = codecs.open we specifi FROM what encoding to read
-# This sets the encoding for the strings which are created with f.read()
-sys.setdefaultencoding('utf-8')
-
-
-def main(argv):
- """
- Here we parse the flags (short, long) and we instantiate the classes.
- """
- usage = "usage: %prog [options] command [cmd_options]"
- description = "This is the Transifex command line client which"\
- " allows you to manage your translations locally and sync"\
- " them with the master Transifex server.\nIf you'd like to"\
- " check the available commands issue `%prog help` or if you"\
- " just want help with a specific command issue `%prog help"\
- " command`"
-
- parser = OptionParser(
- usage=usage, version=get_version(), description=description
- )
- parser.disable_interspersed_args()
- parser.add_option(
- "-d", "--debug", action="store_true", dest="debug",
- default=False, help=("enable debug messages")
- )
- parser.add_option(
- "-q", "--quiet", action="store_true", dest="quiet",
- default=False, help="don't print status messages to stdout"
- )
- parser.add_option(
- "-r", "--root", action="store", dest="root_dir", type="string",
- default=None, help="change root directory (default is cwd)"
- )
- parser.add_option(
- "--traceback", action="store_true", dest="trace", default=False,
- help="print full traceback on exceptions"
- )
- parser.add_option(
- "--disable-colors", action="store_true", dest="color_disable",
- default=(os.name == 'nt' or not sys.stdout.isatty()),
- help="disable colors in the output of commands"
- )
- (options, args) = parser.parse_args()
-
- if len(args) < 1:
- parser.error("No command was given")
-
- utils.DISABLE_COLORS = options.color_disable
-
- # set log level
- if options.quiet:
- set_log_level('WARNING')
- elif options.debug:
- set_log_level('DEBUG')
-
- # find .tx
- path_to_tx = options.root_dir or utils.find_dot_tx()
-
-
- cmd = args[0]
- try:
- utils.exec_command(cmd, args[1:], path_to_tx)
- except utils.UnknownCommandError:
- logger.error("tx: Command %s not found" % cmd)
- except SystemExit:
- sys.exit()
- except:
- import traceback
- if options.trace:
- traceback.print_exc()
- else:
- formatted_lines = traceback.format_exc().splitlines()
- logger.error(formatted_lines[-1])
- sys.exit(1)
-
-# Run baby :) ... run
-if __name__ == "__main__":
- # sys.argv[0] is the name of the script that we’re running.
- main(sys.argv[1:])
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-Copyright (C) 2010 by Indifex (www.indifex.com), see AUTHORS.
-License: BSD, see LICENSE for details.
-
-For further information visit http://code.indifex.com/transifex-client
-"""
-
-
-VERSION = (0, 9, 0, 'devel')
-
-def get_version():
- version = '%s.%s' % (VERSION[0], VERSION[1])
- if VERSION[2]:
- version = '%s.%s' % (version, VERSION[2])
- if VERSION[3] != 'final':
- version = '%s %s' % (version, VERSION[3])
- return version
+++ /dev/null
-# -*- coding: utf-8 -*-
-"""
-In this file we have all the top level commands for the transifex client.
-Since we're using a way to automatically list them and execute them, when
-adding code to this file you must take care of the following:
- * Added functions must begin with 'cmd_' followed by the actual name of the
- command being used in the command line (eg cmd_init)
- * The description for each function that we display to the user is read from
- the func_doc attribute which reads the doc string. So, when adding
- docstring to a new function make sure you add an oneliner which is
- descriptive and is meant to be seen by the user.
- * When including libraries, it's best if you include modules instead of
- functions because that way our function resolution will work faster and the
- chances of overlapping are minimal
- * All functions should use the OptionParser and should have a usage and
- descripition field.
-"""
-import os
-import re, shutil
-import sys
-from optparse import OptionParser, OptionGroup
-import ConfigParser
-
-
-from txclib import utils, project
-from txclib.utils import parse_json, compile_json, relpath
-from txclib.config import OrderedRawConfigParser
-from txclib.exceptions import UnInitializedError
-from txclib.parsers import delete_parser, help_parser, parse_csv_option, \
- status_parser, pull_parser, set_parser, push_parser, init_parser
-from txclib.log import logger
-
-
-def cmd_init(argv, path_to_tx):
- "Initialize a new transifex project."
- parser = init_parser()
- (options, args) = parser.parse_args(argv)
- if len(args) > 1:
- parser.error("Too many arguments were provided. Aborting...")
- if args:
- path_to_tx = args[0]
- else:
- path_to_tx = os.getcwd()
-
- if os.path.isdir(os.path.join(path_to_tx,".tx")):
- logger.info("tx: There is already a tx folder!")
- reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ")
- while (reinit != 'y' and reinit != 'Y' and reinit != 'N' and reinit != 'n' and reinit != ''):
- reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ")
- if not reinit or reinit in ['N', 'n', 'NO', 'no', 'No']:
- return
- # Clean the old settings
- # FIXME: take a backup
- else:
- rm_dir = os.path.join(path_to_tx, ".tx")
- shutil.rmtree(rm_dir)
-
- logger.info("Creating .tx folder...")
- os.mkdir(os.path.join(path_to_tx,".tx"))
-
- # Handle the credentials through transifexrc
- home = os.path.expanduser("~")
- txrc = os.path.join(home, ".transifexrc")
- config = OrderedRawConfigParser()
-
- default_transifex = "https://www.transifex.com"
- transifex_host = options.host or raw_input("Transifex instance [%s]: " % default_transifex)
-
- if not transifex_host:
- transifex_host = default_transifex
- if not transifex_host.startswith(('http://', 'https://')):
- transifex_host = 'https://' + transifex_host
-
- config_file = os.path.join(path_to_tx, ".tx", "config")
- if not os.path.exists(config_file):
- # The path to the config file (.tx/config)
- logger.info("Creating skeleton...")
- config = OrderedRawConfigParser()
- config.add_section('main')
- config.set('main', 'host', transifex_host)
- # Touch the file if it doesn't exist
- logger.info("Creating config file...")
- fh = open(config_file, 'w')
- config.write(fh)
- fh.close()
-
- prj = project.Project(path_to_tx)
- prj.getset_host_credentials(transifex_host, user=options.user,
- password=options.password)
- prj.save()
- logger.info("Done.")
-
-
-def cmd_set(argv, path_to_tx):
- "Add local or remote files under transifex"
- parser = set_parser()
- (options, args) = parser.parse_args(argv)
-
- # Implement options/args checks
- # TODO !!!!!!!
- if options.local:
- try:
- expression = args[0]
- except IndexError:
- parser.error("Please specify an expression.")
- if not options.resource:
- parser.error("Please specify a resource")
- if not options.source_language:
- parser.error("Please specify a source language.")
- if not '<lang>' in expression:
- parser.error("The expression you have provided is not valid.")
- if not utils.valid_slug(options.resource):
- parser.error("Invalid resource slug. The format is <project_slug>"\
- ".<resource_slug> and the valid characters include [_-\w].")
- _auto_local(path_to_tx, options.resource,
- source_language=options.source_language,
- expression = expression, source_file=options.source_file,
- execute=options.execute, regex=False)
- if options.execute:
- _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
- _set_mode(options.resource, options.mode, path_to_tx)
- _set_type(options.resource, options.i18n_type, path_to_tx)
- return
-
- if options.remote:
- try:
- url = args[0]
- except IndexError:
- parser.error("Please specify an remote url")
- _auto_remote(path_to_tx, url)
- _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
- _set_mode(options.resource, options.mode, path_to_tx)
- return
-
- if options.is_source:
- resource = options.resource
- if not resource:
- parser.error("You must specify a resource name with the"
- " -r|--resource flag.")
-
- lang = options.language
- if not lang:
- parser.error("Please specify a source language.")
-
- if len(args) != 1:
- parser.error("Please specify a file.")
-
- if not utils.valid_slug(resource):
- parser.error("Invalid resource slug. The format is <project_slug>"\
- ".<resource_slug> and the valid characters include [_-\w].")
-
- file = args[0]
- # Calculate relative path
- path_to_file = relpath(file, path_to_tx)
- _set_source_file(path_to_tx, resource, options.language, path_to_file)
- elif options.resource or options.language:
- resource = options.resource
- lang = options.language
-
- if len(args) != 1:
- parser.error("Please specify a file")
-
- # Calculate relative path
- path_to_file = relpath(args[0], path_to_tx)
-
- try:
- _go_to_dir(path_to_tx)
- except UnInitializedError, e:
- utils.logger.error(e)
- return
-
- if not utils.valid_slug(resource):
- parser.error("Invalid resource slug. The format is <project_slug>"\
- ".<resource_slug> and the valid characters include [_-\w].")
- _set_translation(path_to_tx, resource, lang, path_to_file)
-
- _set_mode(options.resource, options.mode, path_to_tx)
- _set_type(options.resource, options.i18n_type, path_to_tx)
- _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx)
-
- logger.info("Done.")
- return
-
-
-def _auto_local(path_to_tx, resource, source_language, expression, execute=False,
- source_file=None, regex=False):
- """Auto configure local project."""
- # The path everything will be relative to
- curpath = os.path.abspath(os.curdir)
-
- # Force expr to be a valid regex expr (escaped) but keep <lang> intact
- expr_re = utils.regex_from_filefilter(expression, curpath)
- expr_rec = re.compile(expr_re)
-
- if not execute:
- logger.info("Only printing the commands which will be run if the "
- "--execute switch is specified.")
-
- # First, let's construct a dictionary of all matching files.
- # Note: Only the last matching file of a language will be stored.
- translation_files = {}
- for root, dirs, files in os.walk(curpath):
- for f in files:
- f_path = os.path.abspath(os.path.join(root, f))
- match = expr_rec.match(f_path)
- if match:
- lang = match.group(1)
- f_path = os.path.abspath(f_path)
- if lang == source_language and not source_file:
- source_file = f_path
- else:
- translation_files[lang] = f_path
-
- if not source_file:
- raise Exception("Could not find a source language file. Please run"
- " set --source manually and then re-run this command or provide"
- " the source file with the -s flag.")
- if execute:
- logger.info("Updating source for resource %s ( %s -> %s )." % (resource,
- source_language, relpath(source_file, path_to_tx)))
- _set_source_file(path_to_tx, resource, source_language,
- relpath(source_file, path_to_tx))
- else:
- logger.info('\ntx set --source -r %(res)s -l %(lang)s %(file)s\n' % {
- 'res': resource,
- 'lang': source_language,
- 'file': relpath(source_file, curpath)})
-
- prj = project.Project(path_to_tx)
- root_dir = os.path.abspath(path_to_tx)
-
- if execute:
- try:
- prj.config.get("%s" % resource, "source_file")
- except ConfigParser.NoSectionError:
- raise Exception("No resource with slug \"%s\" was found.\nRun 'tx set --auto"
- "-local -r %s \"expression\"' to do the initial configuration." % resource)
-
- # Now let's handle the translation files.
- if execute:
- logger.info("Updating file expression for resource %s ( %s )." % (resource,
- expression))
- # Eval file_filter relative to root dir
- file_filter = relpath(os.path.join(curpath, expression),
- path_to_tx)
- prj.config.set("%s" % resource, "file_filter", file_filter)
- else:
- for (lang, f_path) in sorted(translation_files.items()):
- logger.info('tx set -r %(res)s -l %(lang)s %(file)s' % {
- 'res': resource,
- 'lang': lang,
- 'file': relpath(f_path, curpath)})
-
- if execute:
- prj.save()
-
-
-def _auto_remote(path_to_tx, url):
- """
- Initialize a remote release/project/resource to the current directory.
- """
- logger.info("Auto configuring local project from remote URL...")
-
- type, vars = utils.parse_tx_url(url)
- prj = project.Project(path_to_tx)
- username, password = prj.getset_host_credentials(vars['hostname'])
-
- if type == 'project':
- logger.info("Getting details for project %s" % vars['project'])
- proj_info = utils.get_details('project_details',
- username, password,
- hostname = vars['hostname'], project = vars['project'])
- resources = [ '.'.join([vars['project'], r['slug']]) for r in proj_info['resources'] ]
- logger.info("%s resources found. Configuring..." % len(resources))
- elif type == 'release':
- logger.info("Getting details for release %s" % vars['release'])
- rel_info = utils.get_details('release_details',
- username, password, hostname = vars['hostname'],
- project = vars['project'], release = vars['release'])
- resources = []
- for r in rel_info['resources']:
- if r.has_key('project'):
- resources.append('.'.join([r['project']['slug'], r['slug']]))
- else:
- resources.append('.'.join([vars['project'], r['slug']]))
- logger.info("%s resources found. Configuring..." % len(resources))
- elif type == 'resource':
- logger.info("Getting details for resource %s" % vars['resource'])
- resources = [ '.'.join([vars['project'], vars['resource']]) ]
- else:
- raise("Url '%s' is not recognized." % url)
-
- for resource in resources:
- logger.info("Configuring resource %s." % resource)
- proj, res = resource.split('.')
- res_info = utils.get_details('resource_details',
- username, password, hostname = vars['hostname'],
- project = proj, resource=res)
- try:
- source_lang = res_info['source_language_code']
- i18n_type = res_info['i18n_type']
- except KeyError:
- raise Exception("Remote server seems to be running an unsupported version"
- " of Transifex. Either update your server software of fallback"
- " to a previous version of transifex-client.")
- prj.set_remote_resource(
- resource=resource,
- host = vars['hostname'],
- source_lang = source_lang,
- i18n_type = i18n_type)
-
- prj.save()
-
-
-def cmd_push(argv, path_to_tx):
- "Push local files to remote server"
- parser = push_parser()
- (options, args) = parser.parse_args(argv)
- force_creation = options.force_creation
- languages = parse_csv_option(options.languages)
- resources = parse_csv_option(options.resources)
- skip = options.skip_errors
- prj = project.Project(path_to_tx)
- if not (options.push_source or options.push_translations):
- parser.error("You need to specify at least one of the -s|--source,"
- " -t|--translations flags with the push command.")
-
- prj.push(
- force=force_creation, resources=resources, languages=languages,
- skip=skip, source=options.push_source,
- translations=options.push_translations,
- no_interactive=options.no_interactive
- )
- logger.info("Done.")
-
-
-def cmd_pull(argv, path_to_tx):
- "Pull files from remote server to local repository"
- parser = pull_parser()
- (options, args) = parser.parse_args(argv)
- if options.fetchall and options.languages:
- parser.error("You can't user a language filter along with the"\
- " -a|--all option")
- languages = parse_csv_option(options.languages)
- resources = parse_csv_option(options.resources)
- skip = options.skip_errors
- minimum_perc = options.minimum_perc or None
-
- try:
- _go_to_dir(path_to_tx)
- except UnInitializedError, e:
- utils.logger.error(e)
- return
-
- # instantiate the project.Project
- prj = project.Project(path_to_tx)
- prj.pull(
- languages=languages, resources=resources, overwrite=options.overwrite,
- fetchall=options.fetchall, fetchsource=options.fetchsource,
- force=options.force, skip=skip, minimum_perc=minimum_perc,
- mode=options.mode
- )
- logger.info("Done.")
-
-
-def _set_source_file(path_to_tx, resource, lang, path_to_file):
- """Reusable method to set source file."""
- proj, res = resource.split('.')
- if not proj or not res:
- raise Exception("\"%s.%s\" is not a valid resource identifier. It should"
- " be in the following format project_slug.resource_slug." %
- (proj, res))
- if not lang:
- raise Exception("You haven't specified a source language.")
-
- try:
- _go_to_dir(path_to_tx)
- except UnInitializedError, e:
- utils.logger.error(e)
- return
-
- if not os.path.exists(path_to_file):
- raise Exception("tx: File ( %s ) does not exist." %
- os.path.join(path_to_tx, path_to_file))
-
- # instantiate the project.Project
- prj = project.Project(path_to_tx)
- root_dir = os.path.abspath(path_to_tx)
-
- if root_dir not in os.path.normpath(os.path.abspath(path_to_file)):
- raise Exception("File must be under the project root directory.")
-
- logger.info("Setting source file for resource %s.%s ( %s -> %s )." % (
- proj, res, lang, path_to_file))
-
- path_to_file = relpath(path_to_file, root_dir)
-
- prj = project.Project(path_to_tx)
-
- # FIXME: Check also if the path to source file already exists.
- try:
- try:
- prj.config.get("%s.%s" % (proj, res), "source_file")
- except ConfigParser.NoSectionError:
- prj.config.add_section("%s.%s" % (proj, res))
- except ConfigParser.NoOptionError:
- pass
- finally:
- prj.config.set("%s.%s" % (proj, res), "source_file",
- path_to_file)
- prj.config.set("%s.%s" % (proj, res), "source_lang",
- lang)
-
- prj.save()
-
-
-def _set_translation(path_to_tx, resource, lang, path_to_file):
- """Reusable method to set translation file."""
-
- proj, res = resource.split('.')
- if not project or not resource:
- raise Exception("\"%s\" is not a valid resource identifier. It should"
- " be in the following format project_slug.resource_slug." %
- resource)
-
- try:
- _go_to_dir(path_to_tx)
- except UnInitializedError, e:
- utils.logger.error(e)
- return
-
- # Warn the user if the file doesn't exist
- if not os.path.exists(path_to_file):
- logger.info("Warning: File '%s' doesn't exist." % path_to_file)
-
- # instantiate the project.Project
- prj = project.Project(path_to_tx)
- root_dir = os.path.abspath(path_to_tx)
-
- if root_dir not in os.path.normpath(os.path.abspath(path_to_file)):
- raise Exception("File must be under the project root directory.")
-
- if lang == prj.config.get("%s.%s" % (proj, res), "source_lang"):
- raise Exception("tx: You cannot set translation file for the source language."
- " Source languages contain the strings which will be translated!")
-
- logger.info("Updating translations for resource %s ( %s -> %s )." % (resource,
- lang, path_to_file))
- path_to_file = relpath(path_to_file, root_dir)
- prj.config.set("%s.%s" % (proj, res), "trans.%s" % lang,
- path_to_file)
-
- prj.save()
-
-
-def cmd_status(argv, path_to_tx):
- "Print status of current project"
- parser = status_parser()
- (options, args) = parser.parse_args(argv)
- resources = parse_csv_option(options.resources)
- prj = project.Project(path_to_tx)
- resources = prj.get_chosen_resources(resources)
- resources_num = len(resources)
- for idx, res in enumerate(resources):
- p, r = res.split('.')
- logger.info("%s -> %s (%s of %s)" % (p, r, idx + 1, resources_num))
- logger.info("Translation Files:")
- slang = prj.get_resource_option(res, 'source_lang')
- sfile = prj.get_resource_option(res, 'source_file') or "N/A"
- lang_map = prj.get_resource_lang_mapping(res)
- logger.info(" - %s: %s (%s)" % (utils.color_text(slang, "RED"),
- sfile, utils.color_text("source", "YELLOW")))
- files = prj.get_resource_files(res)
- fkeys = files.keys()
- fkeys.sort()
- for lang in fkeys:
- local_lang = lang
- if lang in lang_map.values():
- local_lang = lang_map.flip[lang]
- logger.info(" - %s: %s" % (utils.color_text(local_lang, "RED"),
- files[lang]))
- logger.info("")
-
-
-def cmd_help(argv, path_to_tx):
- """List all available commands"""
- parser = help_parser()
- (options, args) = parser.parse_args(argv)
- if len(args) > 1:
- parser.error("Multiple arguments received. Exiting...")
-
- # Get all commands
- fns = utils.discover_commands()
-
- # Print help for specific command
- if len(args) == 1:
- try:
- fns[argv[0]](['--help'], path_to_tx)
- except KeyError:
- utils.logger.error("Command %s not found" % argv[0])
- # or print summary of all commands
-
- # the code below will only be executed if the KeyError exception is thrown
- # becuase in all other cases the function called with --help will exit
- # instead of return here
- keys = fns.keys()
- keys.sort()
-
- logger.info("Transifex command line client.\n")
- logger.info("Available commands are:")
- for key in keys:
- logger.info(" %-15s\t%s" % (key, fns[key].func_doc))
- logger.info("\nFor more information run %s command --help" % sys.argv[0])
-
-
-def cmd_delete(argv, path_to_tx):
- "Delete an accessible resource or translation in a remote server."
- parser = delete_parser()
- (options, args) = parser.parse_args(argv)
- languages = parse_csv_option(options.languages)
- resources = parse_csv_option(options.resources)
- skip = options.skip_errors
- force = options.force_delete
- prj = project.Project(path_to_tx)
- prj.delete(resources, languages, skip, force)
- logger.info("Done.")
-
-
-def _go_to_dir(path):
- """Change the current working directory to the directory specified as
- argument.
-
- Args:
- path: The path to chdor to.
- Raises:
- UnInitializedError, in case the directory has not been initialized.
- """
- if path is None:
- raise UnInitializedError(
- "Directory has not been initialzied. "
- "Did you forget to run 'tx init' first?"
- )
- os.chdir(path)
-
-
-def _set_minimum_perc(resource, value, path_to_tx):
- """Set the minimum percentage in the .tx/config file."""
- args = (resource, 'minimum_perc', value, path_to_tx, 'set_min_perc')
- _set_project_option(*args)
-
-
-def _set_mode(resource, value, path_to_tx):
- """Set the mode in the .tx/config file."""
- args = (resource, 'mode', value, path_to_tx, 'set_default_mode')
- _set_project_option(*args)
-
-
-def _set_type(resource, value, path_to_tx):
- """Set the i18n type in the .tx/config file."""
- args = (resource, 'type', value, path_to_tx, 'set_i18n_type')
- _set_project_option(*args)
-
-
-def _set_project_option(resource, name, value, path_to_tx, func_name):
- """Save the option to the project config file."""
- if value is None:
- return
- if not resource:
- logger.debug("Setting the %s for all resources." % name)
- resources = []
- else:
- logger.debug("Setting the %s for resource %s." % (name, resource))
- resources = [resource, ]
- prj = project.Project(path_to_tx)
- getattr(prj, func_name)(resources, value)
- prj.save()
+++ /dev/null
-import ConfigParser
-
-
-class OrderedRawConfigParser( ConfigParser.RawConfigParser ):
- """
- Overload standard Class ConfigParser.RawConfigParser
- """
- def write(self, fp):
- """Write an .ini-format representation of the configuration state."""
- if self._defaults:
- fp.write("[%s]\n" % DEFAULTSECT)
- for key in sorted( self._defaults ):
- fp.write( "%s = %s\n" % (key, str( self._defaults[ key ]
- ).replace('\n', '\n\t')) )
- fp.write("\n")
- for section in self._sections:
- fp.write("[%s]\n" % section)
- for key in sorted( self._sections[section] ):
- if key != "__name__":
- fp.write("%s = %s\n" %
- (key, str( self._sections[section][ key ]
- ).replace('\n', '\n\t')))
- fp.write("\n")
-
- optionxform = str
-
-
-_NOTFOUND = object()
-
-
-class Flipdict(dict):
- """An injective (one-to-one) python dict. Ensures that each key maps
- to a unique value, and each value maps back to that same key.
-
- Code mostly taken from here:
- http://code.activestate.com/recipes/576968-flipdict-python-dict-that-also-maintains-a-one-to-/
- """
-
- def __init__(self, *args, **kw):
- self._flip = dict.__new__(self.__class__)
- setattr(self._flip, "_flip", self)
- for key, val in dict(*args, **kw).iteritems():
- self[key] = val
-
- @property
- def flip(self):
- """The inverse mapping."""
- return self._flip
-
- def __repr__(self):
- return "%s(%r)" % (self.__class__.__name__, dict(self))
-
- __str__ = __repr__
-
- def copy(self):
- return self.__class__(self)
-
- @classmethod
- def fromkeys(cls, keys, value=None):
- return cls(dict.fromkeys(keys, value))
-
- def __setitem__(self, key, val):
- k = self._flip.get(val, _NOTFOUND)
- if not (k is _NOTFOUND or k==key):
- raise KeyError('(key,val) would erase mapping for value %r' % val)
-
- v = self.get(key, _NOTFOUND)
- if v is not _NOTFOUND:
- dict.__delitem__(self._flip, v)
-
- dict.__setitem__(self, key, val)
- dict.__setitem__(self._flip, val, key)
-
- def setdefault(self, key, default = None):
- # Copied from python's UserDict.DictMixin code.
- try:
- return self[key]
- except KeyError:
- self[key] = default
- return default
-
- def update(self, other = None, **kwargs):
- # Copied from python's UserDict.DictMixin code.
- # Make progressively weaker assumptions about "other"
- if other is None:
- pass
- elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups
- for k, v in other.iteritems():
- self[k] = v
- elif hasattr(other, 'keys'):
- for k in other.keys():
- self[k] = other[k]
- else:
- for k, v in other:
- self[k] = v
- if kwargs:
- self.update(kwargs)
-
- def __delitem__(self, key):
- val = dict.pop(self, key)
- dict.__delitem__(self._flip, val)
-
- def pop(self, key, *args):
- val = dict.pop(self, key, *args)
- dict.__delitem__(self._flip, val)
- return val
-
- def popitem(self):
- key, val = dict.popitem(self)
- dict.__delitem__(self._flip, val)
- return key, val
-
- def clear(self):
- dict.clear(self)
- dict.clear(self._flip)
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-Exception classes for the tx client.
-"""
-
-
-class UnInitializedError(Exception):
- """The project directory has not been initialized."""
-
-
-class UnknownCommandError(Exception):
- """The provided command is not supported."""
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-HTTP-related utility functions.
-"""
-
-from __future__ import with_statement
-import gzip
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-
-
-def _gzip_decode(gzip_data):
- """
- Unzip gzipped data and return them.
-
- :param gzip_data: Gzipped data.
- :returns: The actual data.
- """
- try:
- gzip_data = StringIO.StringIO(gzip_data)
- gzip_file = gzip.GzipFile(fileobj=gzip_data)
- data = gzip_file.read()
- return data
- finally:
- gzip_data.close()
-
-
-def http_response(response):
- """
- Return the response of a HTTP request.
-
- If the response has been gzipped, gunzip it first.
-
- :param response: The raw response of a HTTP request.
- :returns: A response suitable to be used by clients.
- """
- metadata = response.info()
- data = response.read()
- response.close()
- if metadata.get('content-encoding') == 'gzip':
- return _gzip_decode(data)
- else:
- return data
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-Add logging capabilities to tx-client.
-"""
-
-import sys
-import logging
-
-_logger = logging.getLogger('txclib')
-_logger.setLevel(logging.INFO)
-
-_formatter = logging.Formatter('%(message)s')
-
-_error_handler = logging.StreamHandler(sys.stderr)
-_error_handler.setLevel(logging.ERROR)
-_error_handler.setFormatter(_formatter)
-_logger.addHandler(_error_handler)
-
-_msg_handler = logging.StreamHandler(sys.stdout)
-_msg_handler.setLevel(logging.DEBUG)
-_msg_handler.setFormatter(_formatter)
-_msg_filter = logging.Filter()
-_msg_filter.filter = lambda r: r.levelno < logging.ERROR
-_msg_handler.addFilter(_msg_filter)
-_logger.addHandler(_msg_handler)
-
-logger = _logger
-
-
-def set_log_level(level):
- """Set the level for the logger.
-
- Args:
- level: A string among DEBUG, INFO, WARNING, ERROR, CRITICAL.
- """
- logger.setLevel(getattr(logging, level))
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-from optparse import OptionParser, OptionGroup
-
-
-class EpilogParser(OptionParser):
- def format_epilog(self, formatter):
- return self.epilog
-
-
-def delete_parser():
- """Return the command-line parser for the delete command."""
- usage = "usage: %prog [tx_options] delete OPTION [OPTIONS]"
- description = (
- "This command deletes translations for a resource in the remote server."
- )
- epilog = (
- "\nExamples:\n"
- " To delete a translation:\n "
- "$ tx delete -r project.resource -l <lang_code>\n\n"
- " To delete a resource:\n $ tx delete -r project.resource\n"
- )
- parser = EpilogParser(usage=usage, description=description, epilog=epilog)
- parser.add_option(
- "-r", "--resource", action="store", dest="resources", default=None,
- help="Specify the resource you want to delete (defaults to all)"
- )
- parser.add_option(
- "-l","--language", action="store", dest="languages",
- default=None, help="Specify the translation you want to delete"
- )
- parser.add_option(
- "--skip", action="store_true", dest="skip_errors", default=False,
- help="Don't stop on errors."
- )
- parser.add_option(
- "-f","--force", action="store_true", dest="force_delete",
- default=False, help="Delete an entity forcefully."
- )
- return parser
-
-
-def help_parser():
- """Return the command-line parser for the help command."""
- usage="usage: %prog help command"
- description="Lists all available commands in the transifex command"\
- " client. If a command is specified, the help page of the specific"\
- " command is displayed instead."
-
- parser = OptionParser(usage=usage, description=description)
- return parser
-
-
-def init_parser():
- """Return the command-line parser for the init command."""
- usage="usage: %prog [tx_options] init <path>"
- description="This command initializes a new project for use with"\
- " transifex. It is recommended to execute this command in the"\
- " top level directory of your project so that you can include"\
- " all files under it in transifex. If no path is provided, the"\
- " current working dir will be used."
- parser = OptionParser(usage=usage, description=description)
- parser.add_option("--host", action="store", dest="host",
- default=None, help="Specify a default Transifex host.")
- parser.add_option("--user", action="store", dest="user",
- default=None, help="Specify username for Transifex server.")
- parser.add_option("--pass", action="store", dest="password",
- default=None, help="Specify password for Transifex server.")
- return parser
-
-
-def pull_parser():
- """Return the command-line parser for the pull command."""
- usage="usage: %prog [tx_options] pull [options]"
- description="This command pulls all outstanding changes from the remote"\
- " Transifex server to the local repository. By default, only the"\
- " files that are watched by Transifex will be updated but if you"\
- " want to fetch the translations for new languages as well, use the"\
- " -a|--all option. (Note: new translations are saved in the .tx folder"\
- " and require the user to manually rename them and add then in "\
- " transifex using the set_translation command)."
- parser = OptionParser(usage=usage,description=description)
- parser.add_option("-l","--language", action="store", dest="languages",
- default=[], help="Specify which translations you want to pull"
- " (defaults to all)")
- parser.add_option("-r","--resource", action="store", dest="resources",
- default=[], help="Specify the resource for which you want to pull"
- " the translations (defaults to all)")
- parser.add_option("-a","--all", action="store_true", dest="fetchall",
- default=False, help="Fetch all translation files from server (even new"
- " ones)")
- parser.add_option("-s","--source", action="store_true", dest="fetchsource",
- default=False, help="Force the fetching of the source file (default:"
- " False)")
- parser.add_option("-f","--force", action="store_true", dest="force",
- default=False, help="Force download of translations files.")
- parser.add_option("--skip", action="store_true", dest="skip_errors",
- default=False, help="Don't stop on errors. Useful when pushing many"
- " files concurrently.")
- parser.add_option("--disable-overwrite", action="store_false",
- dest="overwrite", default=True,
- help="By default transifex will fetch new translations files and"\
- " replace existing ones. Use this flag if you want to disable"\
- " this feature")
- parser.add_option("--minimum-perc", action="store", type="int",
- dest="minimum_perc", default=0,
- help="Specify the minimum acceptable percentage of a translation "
- "in order to download it.")
- parser.add_option(
- "--mode", action="store", dest="mode", help=(
- "Specify the mode of the translation file to pull (e.g. "
- "'reviewed'). See http://bit.ly/txcmod1 for available values."
- )
- )
- return parser
-
-
-def push_parser():
- """Return the command-line parser for the push command."""
- usage="usage: %prog [tx_options] push [options]"
- description="This command pushes all local files that have been added to"\
- " Transifex to the remote server. All new translations are merged"\
- " with existing ones and if a language doesn't exists then it gets"\
- " created. If you want to push the source file as well (either"\
- " because this is your first time running the client or because"\
- " you just have updated with new entries), use the -f|--force option."\
- " By default, this command will push all files which are watched by"\
- " Transifex but you can filter this per resource or/and language."
- parser = OptionParser(usage=usage, description=description)
- parser.add_option("-l","--language", action="store", dest="languages",
- default=None, help="Specify which translations you want to push"
- " (defaults to all)")
- parser.add_option("-r","--resource", action="store", dest="resources",
- default=None, help="Specify the resource for which you want to push"
- " the translations (defaults to all)")
- parser.add_option("-f","--force", action="store_true", dest="force_creation",
- default=False, help="Push source files without checking modification"
- " times.")
- parser.add_option("--skip", action="store_true", dest="skip_errors",
- default=False, help="Don't stop on errors. Useful when pushing many"
- " files concurrently.")
- parser.add_option("-s", "--source", action="store_true", dest="push_source",
- default=False, help="Push the source file to the server.")
-
- parser.add_option("-t", "--translations", action="store_true", dest="push_translations",
- default=False, help="Push the translation files to the server")
- parser.add_option("--no-interactive", action="store_true", dest="no_interactive",
- default=False, help="Don't require user input when forcing a push.")
- return parser
-
-
-def set_parser():
- """Return the command-line parser for the set command."""
- usage="usage: %prog [tx_options] set [options] [args]"
- description="This command can be used to create a mapping between files"\
- " and projects either using local files or using files from a remote"\
- " Transifex server."
- epilog="\nExamples:\n"\
- " To set the source file:\n $ tx set -r project.resource --source -l en <file>\n\n"\
- " To set a single translation file:\n $ tx set -r project.resource -l de <file>\n\n"\
- " To automatically detect and assign the source files and translations:\n"\
- " $ tx set --auto-local -r project.resource 'expr' --source-lang en\n\n"\
- " To set a specific file as a source and auto detect translations:\n"\
- " $ tx set --auto-local -r project.resource 'expr' --source-lang en"\
- " --source-file <file>\n\n"\
- " To set a remote release/resource/project:\n"\
- " $ tx set --auto-remote <transifex-url>\n"
- parser = EpilogParser(usage=usage, description=description, epilog=epilog)
- parser.add_option("--auto-local", action="store_true", dest="local",
- default=False, help="Used when auto configuring local project.")
- parser.add_option("--auto-remote", action="store_true", dest="remote",
- default=False, help="Used when adding remote files from Transifex"
- " server.")
- parser.add_option("-r","--resource", action="store", dest="resource",
- default=None, help="Specify the slug of the resource that you're"
- " setting up (This must be in the following format:"
- " `project_slug.resource_slug`).")
- parser.add_option(
- "--source", action="store_true", dest="is_source", default=False,
- help=(
- "Specify that the given file is a source file "
- "[doesn't work with the --auto-* commands]."
- )
- )
- parser.add_option("-l","--language", action="store", dest="language",
- default=None, help="Specify which translations you want to pull"
- " [doesn't work with the --auto-* commands].")
- parser.add_option("-t", "--type", action="store", dest="i18n_type",
- help=(
- "Specify the i18n type of the resource(s). This is only needed, if "
- "the resource(s) does not exist yet in Transifex. For a list of "
- "available i18n types, see "
- "http://help.transifex.com/features/formats.html"
- )
- )
- parser.add_option("--minimum-perc", action="store", dest="minimum_perc",
- help=(
- "Specify the minimum acceptable percentage of a translation "
- "in order to download it."
- )
- )
- parser.add_option(
- "--mode", action="store", dest="mode", help=(
- "Specify the mode of the translation file to pull (e.g. "
- "'reviewed'). See http://help.transifex.com/features/client/"
- "index.html#defining-the-mode-of-the-translated-file for the"
- "available values."
- )
- )
- group = OptionGroup(parser, "Extended options", "These options can only be"
- " used with the --auto-local command.")
- group.add_option("-s","--source-language", action="store",
- dest="source_language",
- default=None, help="Specify the source language of a resource"
- " [requires --auto-local].")
- group.add_option("-f","--source-file", action="store", dest="source_file",
- default=None, help="Specify the source file of a resource [requires"
- " --auto-local].")
- group.add_option("--execute", action="store_true", dest="execute",
- default=False, help="Execute commands [requires --auto-local].")
- parser.add_option_group(group)
- return parser
-
-
-def status_parser():
- """Return the command-line parser for the status command."""
- usage="usage: %prog [tx_options] status [options]"
- description="Prints the status of the current project by reading the"\
- " data in the configuration file."
- parser = OptionParser(usage=usage,description=description)
- parser.add_option("-r","--resource", action="store", dest="resources",
- default=[], help="Specify resources")
- return parser
-
-
-def parse_csv_option(option):
- """Return a list out of the comma-separated option or an empty list."""
- if option:
- return option.split(',')
- else:
- return []
+++ /dev/null
-# -*- coding: utf-8 -*-
-
-"""
-Module for API-related calls.
-"""
-
-import urlparse
-
-
-def hostname_tld_migration(hostname):
- """
- Migrate transifex.net to transifex.com.
-
- :param hostname: The hostname to migrate (if needed).
- :returns: A hostname with the transifex.com domain (if needed).
- """
- parts = urlparse.urlparse(hostname)
- if parts.hostname.endswith('transifex.net'):
- hostname = hostname.replace('transifex.net', 'transifex.com', 1)
- return hostname
-
-
-def hostname_ssl_migration(hostname):
- """
- Migrate Transifex hostnames to use HTTPS.
-
- :param hostname: The hostname to migrate (if needed).
- :returns: A https hostname (if needed).
- """
- parts = urlparse.urlparse(hostname)
- is_transifex = (
- parts.hostname[-14:-3] == '.transifex.' or
- parts.hostname == 'transifex.net' or
- parts.hostname == 'transifex.com'
- )
- is_https = parts.scheme == 'https'
- if is_transifex and not is_https:
- if not parts.scheme:
- hostname = 'https:' + hostname
- else:
- hostname = hostname.replace(parts.scheme, 'https', 1)
- return hostname
-
-
-def visit_hostname(hostname):
- """
- Have a chance to visit a hostname before actually using it.
-
- :param hostname: The original hostname.
- :returns: The hostname with the necessary changes.
- """
- for processor in [hostname_ssl_migration, hostname_tld_migration, ]:
- hostname = processor(hostname)
- return hostname
+++ /dev/null
-# -*- coding: utf-8 -*-
-import base64
-import copy
-import getpass
-import os
-import re
-import fnmatch
-import urllib2
-import datetime, time
-import ConfigParser
-
-from txclib.web import *
-from txclib.utils import *
-from txclib.urls import API_URLS
-from txclib.config import OrderedRawConfigParser, Flipdict
-from txclib.log import logger
-from txclib.http_utils import http_response
-from txclib.processors import visit_hostname
-
-
-class ProjectNotInit(Exception):
- pass
-
-
-class Project(object):
- """
- Represents an association between the local and remote project instances.
- """
-
- def __init__(self, path_to_tx=None, init=True):
- """
- Initialize the Project attributes.
- """
- if init:
- self._init(path_to_tx)
-
- def _init(self, path_to_tx=None):
- instructions = "Run 'tx init' to initialize your project first!"
- try:
- self.root = self._get_tx_dir_path(path_to_tx)
- self.config_file = self._get_config_file_path(self.root)
- self.config = self._read_config_file(self.config_file)
- self.txrc_file = self._get_transifex_file()
- self.txrc = self._get_transifex_config(self.txrc_file)
- except ProjectNotInit, e:
- logger.error('\n'.join([unicode(e), instructions]))
- raise
-
- def _get_config_file_path(self, root_path):
- """Check the .tx/config file exists."""
- config_file = os.path.join(root_path, ".tx", "config")
- logger.debug("Config file is %s" % config_file)
- if not os.path.exists(config_file):
- msg = "Cannot find the config file (.tx/config)!"
- raise ProjectNotInit(msg)
- return config_file
-
- def _get_tx_dir_path(self, path_to_tx):
- """Check the .tx directory exists."""
- root_path = path_to_tx or find_dot_tx()
- logger.debug("Path to tx is %s." % root_path)
- if not root_path:
- msg = "Cannot find any .tx directory!"
- raise ProjectNotInit(msg)
- return root_path
-
- def _read_config_file(self, config_file):
- """Parse the config file and return its contents."""
- config = OrderedRawConfigParser()
- try:
- config.read(config_file)
- except Exception, err:
- msg = "Cannot open/parse .tx/config file: %s" % err
- raise ProjectNotInit(msg)
- return config
-
- def _get_transifex_config(self, txrc_file):
- """Read the configuration from the .transifexrc file."""
- txrc = OrderedRawConfigParser()
- try:
- txrc.read(txrc_file)
- except Exception, e:
- msg = "Cannot read global configuration file: %s" % e
- raise ProjectNotInit(msg)
- self._migrate_txrc_file(txrc)
- return txrc
-
- def _migrate_txrc_file(self, txrc):
- """Migrate the txrc file, if needed."""
- for section in txrc.sections():
- orig_hostname = txrc.get(section, 'hostname')
- hostname = visit_hostname(orig_hostname)
- if hostname != orig_hostname:
- msg = "Hostname %s should be changed to %s."
- logger.info(msg % (orig_hostname, hostname))
- if (sys.stdin.isatty() and sys.stdout.isatty() and
- confirm('Change it now? ', default=True)):
- txrc.set(section, 'hostname', hostname)
- msg = 'Hostname changed'
- logger.info(msg)
- else:
- hostname = orig_hostname
- self._save_txrc_file(txrc)
- return txrc
-
- def _get_transifex_file(self, directory=None):
- """Fetch the path of the .transifexrc file.
-
- It is in the home directory ofthe user by default.
- """
- if directory is None:
- directory = os.path.expanduser('~')
- txrc_file = os.path.join(directory, ".transifexrc")
- logger.debug(".transifexrc file is at %s" % directory)
- if not os.path.exists(txrc_file):
- msg = "No authentication data found."
- logger.info(msg)
- mask = os.umask(077)
- open(txrc_file, 'w').close()
- os.umask(mask)
- return txrc_file
-
- def validate_config(self):
- """
- To ensure the json structure is correctly formed.
- """
- pass
-
- def getset_host_credentials(self, host, user=None, password=None):
- """
- Read .transifexrc and report user,pass for a specific host else ask the
- user for input.
- """
- try:
- username = self.txrc.get(host, 'username')
- passwd = self.txrc.get(host, 'password')
- except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
- logger.info("No entry found for host %s. Creating..." % host)
- username = user or raw_input("Please enter your transifex username: ")
- while (not username):
- username = raw_input("Please enter your transifex username: ")
- passwd = password
- while (not passwd):
- passwd = getpass.getpass()
-
- logger.info("Updating %s file..." % self.txrc_file)
- self.txrc.add_section(host)
- self.txrc.set(host, 'username', username)
- self.txrc.set(host, 'password', passwd)
- self.txrc.set(host, 'token', '')
- self.txrc.set(host, 'hostname', host)
-
- return username, passwd
-
- def set_remote_resource(self, resource, source_lang, i18n_type, host,
- file_filter="translations<sep>%(proj)s.%(res)s<sep><lang>.%(extension)s"):
- """
- Method to handle the add/conf of a remote resource.
- """
- if not self.config.has_section(resource):
- self.config.add_section(resource)
-
- p_slug, r_slug = resource.split('.')
- file_filter = file_filter.replace("<sep>", r"%s" % os.path.sep)
- self.url_info = {
- 'host': host,
- 'project': p_slug,
- 'resource': r_slug
- }
- extension = self._extension_for(i18n_type)[1:]
-
- self.config.set(resource, 'source_lang', source_lang)
- self.config.set(
- resource, 'file_filter',
- file_filter % {'proj': p_slug, 'res': r_slug, 'extension': extension}
- )
- if host != self.config.get('main', 'host'):
- self.config.set(resource, 'host', host)
-
- def get_resource_host(self, resource):
- """
- Returns the host that the resource is configured to use. If there is no
- such option we return the default one
- """
- if self.config.has_option(resource, 'host'):
- return self.config.get(resource, 'host')
- return self.config.get('main', 'host')
-
- def get_resource_lang_mapping(self, resource):
- """
- Get language mappings for a specific resource.
- """
- lang_map = Flipdict()
- try:
- args = self.config.get("main", "lang_map")
- for arg in args.replace(' ', '').split(','):
- k,v = arg.split(":")
- lang_map.update({k:v})
- except ConfigParser.NoOptionError:
- pass
- except (ValueError, KeyError):
- raise Exception("Your lang map configuration is not correct.")
-
- if self.config.has_section(resource):
- res_lang_map = Flipdict()
- try:
- args = self.config.get(resource, "lang_map")
- for arg in args.replace(' ', '').split(','):
- k,v = arg.split(":")
- res_lang_map.update({k:v})
- except ConfigParser.NoOptionError:
- pass
- except (ValueError, KeyError):
- raise Exception("Your lang map configuration is not correct.")
-
- # merge the lang maps and return result
- lang_map.update(res_lang_map)
-
- return lang_map
-
-
- def get_resource_files(self, resource):
- """
- Get a dict for all files assigned to a resource. First we calculate the
- files matching the file expression and then we apply all translation
- excpetions. The resulting dict will be in this format:
-
- { 'en': 'path/foo/en/bar.po', 'de': 'path/foo/de/bar.po', 'es': 'path/exceptions/es.po'}
-
- NOTE: All paths are relative to the root of the project
- """
- tr_files = {}
- if self.config.has_section(resource):
- try:
- file_filter = self.config.get(resource, "file_filter")
- except ConfigParser.NoOptionError:
- file_filter = "$^"
- source_lang = self.config.get(resource, "source_lang")
- source_file = self.get_resource_option(resource, 'source_file') or None
- expr_re = regex_from_filefilter(file_filter, self.root)
- expr_rec = re.compile(expr_re)
- for root, dirs, files in os.walk(self.root):
- for f in files:
- f_path = os.path.abspath(os.path.join(root, f))
- match = expr_rec.match(f_path)
- if match:
- lang = match.group(1)
- if lang != source_lang:
- f_path = relpath(f_path, self.root)
- if f_path != source_file:
- tr_files.update({lang: f_path})
-
- for (name, value) in self.config.items(resource):
- if name.startswith("trans."):
- lang = name.split('.')[1]
- # delete language which has same file
- if value in tr_files.values():
- keys = []
- for k, v in tr_files.iteritems():
- if v == value:
- keys.append(k)
- if len(keys) == 1:
- del tr_files[keys[0]]
- else:
- raise Exception("Your configuration seems wrong."\
- " You have multiple languages pointing to"\
- " the same file.")
- # Add language with correct file
- tr_files.update({lang:value})
-
- return tr_files
-
- return None
-
- def get_resource_option(self, resource, option):
- """
- Return the requested option for a specific resource
-
- If there is no such option, we return None
- """
-
- if self.config.has_section(resource):
- if self.config.has_option(resource, option):
- return self.config.get(resource, option)
- return None
-
- def get_resource_list(self, project=None):
- """
- Parse config file and return tuples with the following format
-
- [ (project_slug, resource_slug), (..., ...)]
- """
-
- resource_list= []
- for r in self.config.sections():
- if r == 'main':
- continue
- p_slug, r_slug = r.split('.', 1)
- if project and p_slug != project:
- continue
- resource_list.append(r)
-
- return resource_list
-
- def save(self):
- """
- Store the config dictionary in the .tx/config file of the project.
- """
- self._save_tx_config()
- self._save_txrc_file()
-
- def _save_tx_config(self, config=None):
- """Save the local config file."""
- if config is None:
- config = self.config
- fh = open(self.config_file,"w")
- config.write(fh)
- fh.close()
-
- def _save_txrc_file(self, txrc=None):
- """Save the .transifexrc file."""
- if txrc is None:
- txrc = self.txrc
- mask = os.umask(077)
- fh = open(self.txrc_file, 'w')
- txrc.write(fh)
- fh.close()
- os.umask(mask)
-
- def get_full_path(self, relpath):
- if relpath[0] == "/":
- return relpath
- else:
- return os.path.join(self.root, relpath)
-
- def pull(self, languages=[], resources=[], overwrite=True, fetchall=False,
- fetchsource=False, force=False, skip=False, minimum_perc=0, mode=None):
- """Pull all translations file from transifex server."""
- self.minimum_perc = minimum_perc
- resource_list = self.get_chosen_resources(resources)
-
- if mode == 'reviewed':
- url = 'pull_reviewed_file'
- elif mode == 'translator':
- url = 'pull_translator_file'
- elif mode == 'developer':
- url = 'pull_developer_file'
- else:
- url = 'pull_file'
-
- for resource in resource_list:
- logger.debug("Handling resource %s" % resource)
- self.resource = resource
- project_slug, resource_slug = resource.split('.')
- files = self.get_resource_files(resource)
- slang = self.get_resource_option(resource, 'source_lang')
- sfile = self.get_resource_option(resource, 'source_file')
- lang_map = self.get_resource_lang_mapping(resource)
- host = self.get_resource_host(resource)
- logger.debug("Language mapping is: %s" % lang_map)
- if mode is None:
- mode = self._get_option(resource, 'mode')
- self.url_info = {
- 'host': host,
- 'project': project_slug,
- 'resource': resource_slug
- }
- logger.debug("URL data are: %s" % self.url_info)
-
- stats = self._get_stats_for_resource()
-
-
- try:
- file_filter = self.config.get(resource, 'file_filter')
- except ConfigParser.NoOptionError:
- file_filter = None
-
- # Pull source file
- pull_languages = set([])
- new_translations = set([])
-
- if fetchall:
- new_translations = self._new_translations_to_add(
- files, slang, lang_map, stats, force
- )
- if new_translations:
- msg = "New translations found for the following languages: %s"
- logger.info(msg % ', '.join(new_translations))
-
- existing, new = self._languages_to_pull(
- languages, files, lang_map, stats, force
- )
- pull_languages |= existing
- new_translations |= new
- logger.debug("Adding to new translations: %s" % new)
-
- if fetchsource:
- if sfile and slang not in pull_languages:
- pull_languages.add(slang)
- elif slang not in new_translations:
- new_translations.add(slang)
-
- if pull_languages:
- logger.debug("Pulling languages for: %s" % pull_languages)
- msg = "Pulling translations for resource %s (source: %s)"
- logger.info(msg % (resource, sfile))
-
- for lang in pull_languages:
- local_lang = lang
- if lang in lang_map.values():
- remote_lang = lang_map.flip[lang]
- else:
- remote_lang = lang
- if languages and lang not in pull_languages:
- logger.debug("Skipping language %s" % lang)
- continue
- if lang != slang:
- local_file = files.get(lang, None) or files[lang_map[lang]]
- else:
- local_file = sfile
- logger.debug("Using file %s" % local_file)
-
- kwargs = {
- 'lang': remote_lang,
- 'stats': stats,
- 'local_file': local_file,
- 'force': force,
- 'mode': mode,
- }
- if not self._should_update_translation(**kwargs):
- msg = "Skipping '%s' translation (file: %s)."
- logger.info(
- msg % (color_text(remote_lang, "RED"), local_file)
- )
- continue
-
- if not overwrite:
- local_file = ("%s.new" % local_file)
- logger.warning(
- " -> %s: %s" % (color_text(remote_lang, "RED"), local_file)
- )
- try:
- r = self.do_url_request(url, language=remote_lang)
- except Exception,e:
- if not skip:
- raise e
- else:
- logger.error(e)
- continue
- base_dir = os.path.split(local_file)[0]
- mkdir_p(base_dir)
- fd = open(local_file, 'wb')
- fd.write(r)
- fd.close()
-
- if new_translations:
- msg = "Pulling new translations for resource %s (source: %s)"
- logger.info(msg % (resource, sfile))
- for lang in new_translations:
- if lang in lang_map.keys():
- local_lang = lang_map[lang]
- else:
- local_lang = lang
- remote_lang = lang
- if file_filter:
- local_file = relpath(os.path.join(self.root,
- file_filter.replace('<lang>', local_lang)), os.curdir)
- else:
- trans_dir = os.path.join(self.root, ".tx", resource)
- if not os.path.exists(trans_dir):
- os.mkdir(trans_dir)
- local_file = relpath(os.path.join(trans_dir, '%s_translation' %
- local_lang, os.curdir))
-
- if lang != slang:
- satisfies_min = self._satisfies_min_translated(
- stats[remote_lang], mode
- )
- if not satisfies_min:
- msg = "Skipping language %s due to used options."
- logger.info(msg % lang)
- continue
- logger.warning(
- " -> %s: %s" % (color_text(remote_lang, "RED"), local_file)
- )
- r = self.do_url_request(url, language=remote_lang)
-
- base_dir = os.path.split(local_file)[0]
- mkdir_p(base_dir)
- fd = open(local_file, 'wb')
- fd.write(r)
- fd.close()
-
- def push(self, source=False, translations=False, force=False, resources=[], languages=[],
- skip=False, no_interactive=False):
- """
- Push all the resources
- """
- resource_list = self.get_chosen_resources(resources)
- self.skip = skip
- self.force = force
- for resource in resource_list:
- push_languages = []
- project_slug, resource_slug = resource.split('.')
- files = self.get_resource_files(resource)
- slang = self.get_resource_option(resource, 'source_lang')
- sfile = self.get_resource_option(resource, 'source_file')
- lang_map = self.get_resource_lang_mapping(resource)
- host = self.get_resource_host(resource)
- logger.debug("Language mapping is: %s" % lang_map)
- logger.debug("Using host %s" % host)
- self.url_info = {
- 'host': host,
- 'project': project_slug,
- 'resource': resource_slug
- }
-
- logger.info("Pushing translations for resource %s:" % resource)
-
- stats = self._get_stats_for_resource()
-
- if force and not no_interactive:
- answer = raw_input("Warning: By using --force, the uploaded"
- " files will overwrite remote translations, even if they"
- " are newer than your uploaded files.\nAre you sure you"
- " want to continue? [y/N] ")
-
- if not answer in ["", 'Y', 'y', "yes", 'YES']:
- return
-
- if source:
- if sfile == None:
- logger.error("You don't seem to have a proper source file"
- " mapping for resource %s. Try without the --source"
- " option or set a source file first and then try again." %
- resource)
- continue
- # Push source file
- try:
- logger.warning("Pushing source file (%s)" % sfile)
- if not self._resource_exists(stats):
- logger.info("Resource does not exist. Creating...")
- fileinfo = "%s;%s" % (resource_slug, slang)
- filename = self.get_full_path(sfile)
- self._create_resource(resource, project_slug, fileinfo, filename)
- self.do_url_request(
- 'push_source', multipart=True, method="PUT",
- files=[(
- "%s;%s" % (resource_slug, slang)
- , self.get_full_path(sfile)
- )],
- )
- except Exception, e:
- if not skip:
- raise
- else:
- logger.error(e)
- else:
- try:
- self.do_url_request('resource_details')
- except Exception, e:
- code = getattr(e, 'code', None)
- if code == 404:
- msg = "Resource %s doesn't exist on the server."
- logger.error(msg % resource)
- continue
-
- if translations:
- # Check if given language codes exist
- if not languages:
- push_languages = files.keys()
- else:
- push_languages = []
- f_langs = files.keys()
- for l in languages:
- if l in lang_map.keys():
- l = lang_map[l]
- push_languages.append(l)
- if l not in f_langs:
- msg = "Warning: No mapping found for language code '%s'."
- logger.error(msg % color_text(l,"RED"))
- logger.debug("Languages to push are %s" % push_languages)
-
- # Push translation files one by one
- for lang in push_languages:
- local_lang = lang
- if lang in lang_map.values():
- remote_lang = lang_map.flip[lang]
- else:
- remote_lang = lang
-
- local_file = files[local_lang]
-
- kwargs = {
- 'lang': remote_lang,
- 'stats': stats,
- 'local_file': local_file,
- 'force': force,
- }
- if not self._should_push_translation(**kwargs):
- msg = "Skipping '%s' translation (file: %s)."
- logger.info(msg % (color_text(lang, "RED"), local_file))
- continue
-
- msg = "Pushing '%s' translations (file: %s)"
- logger.warning(
- msg % (color_text(remote_lang, "RED"), local_file)
- )
- try:
- self.do_url_request(
- 'push_translation', multipart=True, method='PUT',
- files=[(
- "%s;%s" % (resource_slug, remote_lang),
- self.get_full_path(local_file)
- )], language=remote_lang
- )
- logger.debug("Translation %s pushed." % remote_lang)
- except Exception, e:
- if not skip:
- raise e
- else:
- logger.error(e)
-
- def delete(self, resources=[], languages=[], skip=False, force=False):
- """Delete translations."""
- resource_list = self.get_chosen_resources(resources)
- self.skip = skip
- self.force = force
-
- if not languages:
- delete_func = self._delete_resource
- else:
- delete_func = self._delete_translations
-
- for resource in resource_list:
- project_slug, resource_slug = resource.split('.')
- host = self.get_resource_host(resource)
- self.url_info = {
- 'host': host,
- 'project': project_slug,
- 'resource': resource_slug
- }
- logger.debug("URL data are: %s" % self.url_info)
- project_details = parse_json(
- self.do_url_request('project_details', project=self)
- )
- teams = project_details['teams']
- stats = self._get_stats_for_resource()
- delete_func(project_details, resource, stats, languages)
-
- def _delete_resource(self, project_details, resource, stats, *args):
- """Delete a resource from Transifex."""
- project_slug, resource_slug = resource.split('.')
- project_resource_slugs = [
- r['slug'] for r in project_details['resources']
- ]
- logger.info("Deleting resource %s:" % resource)
- if resource_slug not in project_resource_slugs:
- if not self.skip:
- msg = "Skipping: %s : Resource does not exist."
- logger.info(msg % resource)
- return
- if not self.force:
- slang = self.get_resource_option(resource, 'source_lang')
- for language in stats:
- if language == slang:
- continue
- if int(stats[language]['translated_entities']) > 0:
- msg = (
- "Skipping: %s : Unable to delete resource because it "
- "has a not empty %s translation.\nPlease use -f or "
- "--force option to delete this resource."
- )
- logger.info(msg % (resource, language))
- return
- try:
- self.do_url_request('delete_resource', method="DELETE")
- self.config.remove_section(resource)
- self.save()
- msg = "Deleted resource %s of project %s."
- logger.info(msg % (resource_slug, project_slug))
- except Exception, e:
- msg = "Unable to delete resource %s of project %s."
- logger.error(msg % (resource_slug, project_slug))
- if not self.skip:
- raise
-
- def _delete_translations(self, project_details, resource, stats, languages):
- """Delete the specified translations for the specified resource."""
- logger.info("Deleting translations from resource %s:" % resource)
- for language in languages:
- self._delete_translation(project_details, resource, stats, language)
-
- def _delete_translation(self, project_details, resource, stats, language):
- """Delete a specific translation from the specified resource."""
- project_slug, resource_slug = resource.split('.')
- if language not in stats:
- if not self.skip:
- msg = "Skipping %s: Translation does not exist."
- logger.warning(msg % (language))
- return
- if not self.force:
- teams = project_details['teams']
- if language in teams:
- msg = (
- "Skipping %s: Unable to delete translation because it is "
- "associated with a team.\nPlease use -f or --force option "
- "to delete this translation."
- )
- logger.warning(msg % language)
- return
- if int(stats[language]['translated_entities']) > 0:
- msg = (
- "Skipping %s: Unable to delete translation because it "
- "is not empty.\nPlease use -f or --force option to delete "
- "this translation."
- )
- logger.warning(msg % language)
- return
- try:
- self.do_url_request(
- 'delete_translation', language=language, method="DELETE"
- )
- msg = "Deleted language %s from resource %s of project %s."
- logger.info(msg % (language, resource_slug, project_slug))
- except Exception, e:
- msg = "Unable to delete translation %s"
- logger.error(msg % language)
- if not self.skip:
- raise
-
- def do_url_request(self, api_call, multipart=False, data=None,
- files=[], encoding=None, method="GET", **kwargs):
- """
- Issues a url request.
- """
- # Read the credentials from the config file (.transifexrc)
- host = self.url_info['host']
- try:
- username = self.txrc.get(host, 'username')
- passwd = self.txrc.get(host, 'password')
- token = self.txrc.get(host, 'token')
- hostname = self.txrc.get(host, 'hostname')
- except ConfigParser.NoSectionError:
- raise Exception("No user credentials found for host %s. Edit"
- " ~/.transifexrc and add the appropriate info in there." %
- host)
-
- # Create the Url
- kwargs['hostname'] = hostname
- kwargs.update(self.url_info)
- url = (API_URLS[api_call] % kwargs).encode('UTF-8')
- logger.debug(url)
-
- opener = None
- headers = None
- req = None
-
- if multipart:
- opener = urllib2.build_opener(MultipartPostHandler)
- for info,filename in files:
- data = { "resource" : info.split(';')[0],
- "language" : info.split(';')[1],
- "uploaded_file" : open(filename,'rb') }
-
- urllib2.install_opener(opener)
- req = RequestWithMethod(url=url, data=data, method=method)
- else:
- req = RequestWithMethod(url=url, data=data, method=method)
- if encoding:
- req.add_header("Content-Type",encoding)
-
- base64string = base64.encodestring('%s:%s' % (username, passwd))[:-1]
- authheader = "Basic %s" % base64string
- req.add_header("Authorization", authheader)
- req.add_header("Accept-Encoding", "gzip,deflate")
- req.add_header("User-Agent", user_agent_identifier())
-
- try:
- response = urllib2.urlopen(req, timeout=300)
- return http_response(response)
- except urllib2.HTTPError, e:
- if e.code in [401, 403, 404]:
- raise e
- elif 200 <= e.code < 300:
- return None
- else:
- # For other requests, we should print the message as well
- raise Exception("Remote server replied: %s" % e.read())
- except urllib2.URLError, e:
- error = e.args[0]
- raise Exception("Remote server replied: %s" % error[1])
-
-
- def _should_update_translation(self, lang, stats, local_file, force=False,
- mode=None):
- """Whether a translation should be udpated from Transifex.
-
- We use the following criteria for that:
- - If user requested to force the download.
- - If language exists in Transifex.
- - If the local file is older than the Transifex's file.
- - If the user requested a x% completion.
-
- Args:
- lang: The language code to check.
- stats: The (global) statistics object.
- local_file: The local translation file.
- force: A boolean flag.
- mode: The mode for the translation.
- Returns:
- True or False.
- """
- return self._should_download(lang, stats, local_file, force)
-
- def _should_add_translation(self, lang, stats, force=False, mode=None):
- """Whether a translation should be added from Transifex.
-
- We use the following criteria for that:
- - If user requested to force the download.
- - If language exists in Transifex.
- - If the user requested a x% completion.
-
- Args:
- lang: The language code to check.
- stats: The (global) statistics object.
- force: A boolean flag.
- mode: The mode for the translation.
- Returns:
- True or False.
- """
- return self._should_download(lang, stats, None, force)
-
- def _should_download(self, lang, stats, local_file=None, force=False,
- mode=None):
- """Return whether a translation should be downloaded.
-
- If local_file is None, skip the timestamps check (the file does
- not exist locally).
- """
- try:
- lang_stats = stats[lang]
- except KeyError, e:
- logger.debug("No lang %s in statistics" % lang)
- return False
-
- satisfies_min = self._satisfies_min_translated(lang_stats, mode)
- if not satisfies_min:
- return False
-
- if force:
- logger.debug("Downloading translation due to -f")
- return True
-
- if local_file is not None:
- remote_update = self._extract_updated(lang_stats)
- if not self._remote_is_newer(remote_update, local_file):
- logger.debug("Local is newer than remote for lang %s" % lang)
- return False
- return True
-
- def _should_push_translation(self, lang, stats, local_file, force=False):
- """Return whether a local translation file should be
- pushed to Trasnifex.
-
- We use the following criteria for that:
- - If user requested to force the upload.
- - If language exists in Transifex.
- - If local file is younger than the remote file.
-
- Args:
- lang: The language code to check.
- stats: The (global) statistics object.
- local_file: The local translation file.
- force: A boolean flag.
- Returns:
- True or False.
- """
- if force:
- logger.debug("Push translation due to -f.")
- return True
- try:
- lang_stats = stats[lang]
- except KeyError, e:
- logger.debug("Language %s does not exist in Transifex." % lang)
- return True
- if local_file is not None:
- remote_update = self._extract_updated(lang_stats)
- if self._remote_is_newer(remote_update, local_file):
- msg = "Remote translation is newer than local file for lang %s"
- logger.debug(msg % lang)
- return False
- return True
-
- def _generate_timestamp(self, update_datetime):
- """Generate a UNIX timestamp from the argument.
-
- Args:
- update_datetime: The datetime in the format used by Transifex.
- Returns:
- A float, representing the timestamp that corresponds to the
- argument.
- """
- time_format = "%Y-%m-%d %H:%M:%S"
- return time.mktime(
- datetime.datetime(
- *time.strptime(update_datetime, time_format)[0:5]
- ).utctimetuple()
- )
-
- def _get_time_of_local_file(self, path):
- """Get the modified time of the path_.
-
- Args:
- path: The path we want the mtime for.
- Returns:
- The time as a timestamp or None, if the file does not exist
- """
- if not os.path.exists(path):
- return None
- return time.mktime(time.gmtime(os.path.getmtime(path)))
-
- def _satisfies_min_translated(self, stats, mode=None):
- """Check whether a translation fulfills the filter used for
- minimum translated percentage.
-
- Args:
- perc: The current translation percentage.
- Returns:
- True or False
- """
- cur = self._extract_completed(stats, mode)
- option_name = 'minimum_perc'
- if self.minimum_perc is not None:
- minimum_percent = self.minimum_perc
- else:
- global_minimum = int(
- self.get_resource_option('main', option_name) or 0
- )
- resource_minimum = int(
- self.get_resource_option(
- self.resource, option_name
- ) or global_minimum
- )
- minimum_percent = resource_minimum
- return cur >= minimum_percent
-
- def _remote_is_newer(self, remote_updated, local_file):
- """Check whether the remote translation is newer that the local file.
-
- Args:
- remote_updated: The date and time the translation was last
- updated remotely.
- local_file: The local file.
- Returns:
- True or False.
- """
- if remote_updated is None:
- logger.debug("No remote time")
- return False
- remote_time = self._generate_timestamp(remote_updated)
- local_time = self._get_time_of_local_file(
- self.get_full_path(local_file)
- )
- logger.debug(
- "Remote time is %s and local %s" % (remote_time, local_time)
- )
- if local_time is not None and remote_time < local_time:
- return False
- return True
-
- @classmethod
- def _extract_completed(cls, stats, mode=None):
- """Extract the information for the translated percentage from the stats.
-
- Args:
- stats: The stats object for a language as returned by Transifex.
- mode: The mode of translations requested.
- Returns:
- The percentage of translation as integer.
- """
- if mode == 'reviewed':
- key = 'reviewed_percentage'
- else:
- key = 'completed'
- try:
- return int(stats[key][:-1])
- except KeyError, e:
- return 0
-
- @classmethod
- def _extract_updated(cls, stats):
- """Extract the information for the last update of a translation.
-
- Args:
- stats: The stats object for a language as returned by Transifex.
- Returns:
- The last update field.
- """
- try:
- return stats['last_update']
- except KeyError, e:
- return None
-
- def _new_translations_to_add(self, files, slang, lang_map,
- stats, force=False):
- """Return a list of translations which are new to the
- local installation.
- """
- new_translations = []
- timestamp = time.time()
- langs = stats.keys()
- logger.debug("Available languages are: %s" % langs)
-
- for lang in langs:
- lang_exists = lang in files.keys()
- lang_is_source = lang == slang
- mapped_lang_exists = (
- lang in lang_map and lang_map[lang] in files.keys()
- )
- if lang_exists or lang_is_source or mapped_lang_exists:
- continue
- if self._should_add_translation(lang, stats, force):
- new_translations.append(lang)
- return set(new_translations)
-
- def _get_stats_for_resource(self):
- """Get the statistics information for a resource."""
- try:
- r = self.do_url_request('resource_stats')
- logger.debug("Statistics response is %s" % r)
- stats = parse_json(r)
- except urllib2.HTTPError, e:
- logger.debug("Resource not found: %s" % e)
- stats = {}
- except Exception,e:
- logger.debug("Network error: %s" % e)
- raise
- return stats
-
- def get_chosen_resources(self, resources):
- """Get the resources the user selected.
-
- Support wildcards in the resources specified by the user.
-
- Args:
- resources: A list of resources as specified in command-line or
- an empty list.
- Returns:
- A list of resources.
- """
- configured_resources = self.get_resource_list()
- if not resources:
- return configured_resources
-
- selected_resources = []
- for resource in resources:
- found = False
- for full_name in configured_resources:
- if fnmatch.fnmatch(full_name, resource):
- selected_resources.append(full_name)
- found = True
- if not found:
- msg = "Specified resource '%s' does not exist."
- raise Exception(msg % resource)
- logger.debug("Operating on resources: %s" % selected_resources)
- return selected_resources
-
- def _languages_to_pull(self, languages, files, lang_map, stats, force):
- """Get a set of langauges to pull.
-
- Args:
- languages: A list of languages the user selected in cmd.
- files: A dictionary of current local translation files.
- Returns:
- A tuple of a set of existing languages and new translations.
- """
- if not languages:
- pull_languages = set([])
- pull_languages |= set(files.keys())
- mapped_files = []
- for lang in pull_languages:
- if lang in lang_map.flip:
- mapped_files.append(lang_map.flip[lang])
- pull_languages -= set(lang_map.flip.keys())
- pull_languages |= set(mapped_files)
- return (pull_languages, set([]))
- else:
- pull_languages = []
- new_translations = []
- f_langs = files.keys()
- for l in languages:
- if l not in f_langs and not (l in lang_map and lang_map[l] in f_langs):
- if self._should_add_translation(l, stats, force):
- new_translations.append(l)
- else:
- if l in lang_map.keys():
- l = lang_map[l]
- pull_languages.append(l)
- return (set(pull_languages), set(new_translations))
-
- def _extension_for(self, i18n_type):
- """Return the extension used for the specified type."""
- try:
- res = parse_json(self.do_url_request('formats'))
- return res[i18n_type]['file-extensions'].split(',')[0]
- except Exception,e:
- logger.error(e)
- return ''
-
- def _resource_exists(self, stats):
- """Check if resource exists.
-
- Args:
- stats: The statistics dict as returned by Tx.
- Returns:
- True, if the resource exists in the server.
- """
- return bool(stats)
-
- def _create_resource(self, resource, pslug, fileinfo, filename, **kwargs):
- """Create a resource.
-
- Args:
- resource: The full resource name.
- pslug: The slug of the project.
- fileinfo: The information of the resource.
- filename: The name of the file.
- Raises:
- URLError, in case of a problem.
- """
- multipart = True
- method = "POST"
- api_call = 'create_resource'
-
- host = self.url_info['host']
- try:
- username = self.txrc.get(host, 'username')
- passwd = self.txrc.get(host, 'password')
- token = self.txrc.get(host, 'token')
- hostname = self.txrc.get(host, 'hostname')
- except ConfigParser.NoSectionError:
- raise Exception("No user credentials found for host %s. Edit"
- " ~/.transifexrc and add the appropriate info in there." %
- host)
-
- # Create the Url
- kwargs['hostname'] = hostname
- kwargs.update(self.url_info)
- kwargs['project'] = pslug
- url = (API_URLS[api_call] % kwargs).encode('UTF-8')
-
- opener = None
- headers = None
- req = None
-
- i18n_type = self._get_option(resource, 'type')
- if i18n_type is None:
- logger.error(
- "Please define the resource type in .tx/config (eg. type = PO)."
- " More info: http://bit.ly/txcl-rt"
- )
-
- opener = urllib2.build_opener(MultipartPostHandler)
- data = {
- "slug": fileinfo.split(';')[0],
- "name": fileinfo.split(';')[0],
- "uploaded_file": open(filename,'rb'),
- "i18n_type": i18n_type
- }
- urllib2.install_opener(opener)
- req = RequestWithMethod(url=url, data=data, method=method)
-
- base64string = base64.encodestring('%s:%s' % (username, passwd))[:-1]
- authheader = "Basic %s" % base64string
- req.add_header("Authorization", authheader)
-
- try:
- fh = urllib2.urlopen(req)
- except urllib2.HTTPError, e:
- if e.code in [401, 403, 404]:
- raise e
- else:
- # For other requests, we should print the message as well
- raise Exception("Remote server replied: %s" % e.read())
- except urllib2.URLError, e:
- error = e.args[0]
- raise Exception("Remote server replied: %s" % error[1])
-
- raw = fh.read()
- fh.close()
- return raw
-
- def _get_option(self, resource, option):
- """Get the value for the option in the config file.
-
- If the option is not in the resource section, look for it in
- the project.
-
- Args:
- resource: The resource name.
- option: The option the value of which we are interested in.
- Returns:
- The option value or None, if it does not exist.
- """
- value = self.get_resource_option(resource, option)
- if value is None:
- if self.config.has_option('main', option):
- return self.config.get('main', option)
- return value
-
- def set_i18n_type(self, resources, i18n_type):
- """Set the type for the specified resources."""
- self._set_resource_option(resources, key='type', value=i18n_type)
-
- def set_min_perc(self, resources, perc):
- """Set the minimum percentage for the resources."""
- self._set_resource_option(resources, key='minimum_perc', value=perc)
-
- def set_default_mode(self, resources, mode):
- """Set the default mode for the specified resources."""
- self._set_resource_option(resources, key='mode', value=mode)
-
- def _set_resource_option(self, resources, key, value):
- """Set options in the config file.
-
- If resources is empty. set the option globally.
- """
- if not resources:
- self.config.set('main', key, value)
- return
- for r in resources:
- self.config.set(r, key, value)
+++ /dev/null
-# These are the Transifex API urls
-
-API_URLS = {
- 'get_resources': '%(hostname)s/api/2/project/%(project)s/resources/',
- 'project_details': '%(hostname)s/api/2/project/%(project)s/?details',
- 'resource_details': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/',
- 'release_details': '%(hostname)s/api/2/project/%(project)s/release/%(release)s/',
- 'pull_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file',
- 'pull_reviewed_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=reviewed',
- 'pull_translator_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=translated',
- 'pull_developer_file': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/?file&mode=default',
- 'resource_stats': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/stats/',
- 'create_resource': '%(hostname)s/api/2/project/%(project)s/resources/',
- 'push_source': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/content/',
- 'push_translation': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/',
- 'delete_translation': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/translation/%(language)s/',
- 'formats': '%(hostname)s/api/2/formats/',
- 'delete_resource': '%(hostname)s/api/2/project/%(project)s/resource/%(resource)s/',
-}
-
-
+++ /dev/null
-import os, sys, re, errno
-try:
- from json import loads as parse_json, dumps as compile_json
-except ImportError:
- from simplejson import loads as parse_json, dumps as compile_json
-import urllib2 # This should go and instead use do_url_request everywhere
-
-from urls import API_URLS
-from txclib.log import logger
-from txclib.exceptions import UnknownCommandError
-
-
-def find_dot_tx(path = os.path.curdir, previous = None):
- """
- Return the path where .tx folder is found.
-
- The 'path' should be a DIRECTORY.
- This process is functioning recursively from the current directory to each
- one of the ancestors dirs.
- """
- path = os.path.abspath(path)
- if path == previous:
- return None
- joined = os.path.join(path, ".tx")
- if os.path.isdir(joined):
- return path
- else:
- return find_dot_tx(os.path.dirname(path), path)
-
-
-#################################################
-# Parse file filter expressions and create regex
-
-def regex_from_filefilter(file_filter, root_path = os.path.curdir):
- """
- Create proper regex from <lang> expression
- """
- # Force expr to be a valid regex expr (escaped) but keep <lang> intact
- expr_re = re.escape(os.path.join(root_path, file_filter))
- expr_re = expr_re.replace("\\<lang\\>", '<lang>').replace(
- '<lang>', '([^%(sep)s]+)' % { 'sep': re.escape(os.path.sep)})
-
- return "^%s$" % expr_re
-
-
-TX_URLS = {
- 'resource': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/resource/(?P<resource>(\w|-)+)/?$',
- 'release': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/r/(?P<release>(\w|-)+)/?$',
- 'project': '(?P<hostname>https?://(\w|\.|:|-)+)/projects/p/(?P<project>(\w|-)+)/?$',
-}
-
-
-def parse_tx_url(url):
- """
- Try to match given url to any of the valid url patterns specified in
- TX_URLS. If not match is found, we raise exception
- """
- for type in TX_URLS.keys():
- pattern = TX_URLS[type]
- m = re.match(pattern, url)
- if m:
- return type, m.groupdict()
-
- raise Exception("tx: Malformed url given. Please refer to our docs: http://bit.ly/txautor")
-
-
-def get_details(api_call, username, password, *args, **kwargs):
- """
- Get the tx project info through the API.
-
- This function can also be used to check the existence of a project.
- """
- import base64
- url = (API_URLS[api_call] % (kwargs)).encode('UTF-8')
-
- req = urllib2.Request(url=url)
- base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
- authheader = "Basic %s" % base64string
- req.add_header("Authorization", authheader)
-
- try:
- fh = urllib2.urlopen(req)
- raw = fh.read()
- fh.close()
- remote_project = parse_json(raw)
- except urllib2.HTTPError, e:
- if e.code in [401, 403, 404]:
- raise e
- else:
- # For other requests, we should print the message as well
- raise Exception("Remote server replied: %s" % e.read())
- except urllib2.URLError, e:
- error = e.args[0]
- raise Exception("Remote server replied: %s" % error[1])
-
- return remote_project
-
-
-def valid_slug(slug):
- """
- Check if a slug contains only valid characters.
-
- Valid chars include [-_\w]
- """
- try:
- a, b = slug.split('.')
- except ValueError:
- return False
- else:
- if re.match("^[A-Za-z0-9_-]*$", a) and re.match("^[A-Za-z0-9_-]*$", b):
- return True
- return False
-
-
-def discover_commands():
- """
- Inspect commands.py and find all available commands
- """
- import inspect
- from txclib import commands
-
- command_table = {}
- fns = inspect.getmembers(commands, inspect.isfunction)
-
- for name, fn in fns:
- if name.startswith("cmd_"):
- command_table.update({
- name.split("cmd_")[1]:fn
- })
-
- return command_table
-
-
-def exec_command(command, *args, **kwargs):
- """
- Execute given command
- """
- commands = discover_commands()
- try:
- cmd_fn = commands[command]
- except KeyError:
- raise UnknownCommandError
- cmd_fn(*args,**kwargs)
-
-
-def mkdir_p(path):
- try:
- if path:
- os.makedirs(path)
- except OSError, exc: # Python >2.5
- if exc.errno == errno.EEXIST:
- pass
- else:
- raise
-
-
-def confirm(prompt='Continue?', default=True):
- """
- Prompt the user for a Yes/No answer.
-
- Args:
- prompt: The text displayed to the user ([Y/n] will be appended)
- default: If the default value will be yes or no
- """
- valid_yes = ['Y', 'y', 'Yes', 'yes', ]
- valid_no = ['N', 'n', 'No', 'no', ]
- if default:
- prompt = prompt + '[Y/n]'
- valid_yes.append('')
- else:
- prompt = prompt + '[y/N]'
- valid_no.append('')
-
- ans = raw_input(prompt)
- while (ans not in valid_yes and ans not in valid_no):
- ans = raw_input(prompt)
-
- return ans in valid_yes
-
-
-# Stuff for command line colored output
-
-COLORS = [
- 'BLACK', 'RED', 'GREEN', 'YELLOW',
- 'BLUE', 'MAGENTA', 'CYAN', 'WHITE'
-]
-
-DISABLE_COLORS = False
-
-
-def color_text(text, color_name, bold=False):
- """
- This command can be used to colorify command line output. If the shell
- doesn't support this or the --disable-colors options has been set, it just
- returns the plain text.
-
- Usage:
- print "%s" % color_text("This text is red", "RED")
- """
- if color_name in COLORS and not DISABLE_COLORS:
- return '\033[%s;%sm%s\033[0m' % (
- int(bold), COLORS.index(color_name) + 30, text)
- else:
- return text
-
-
-##############################################
-# relpath implementation taken from Python 2.7
-
-if not hasattr(os.path, 'relpath'):
- if os.path is sys.modules.get('ntpath'):
- def relpath(path, start=os.path.curdir):
- """Return a relative version of a path"""
-
- if not path:
- raise ValueError("no path specified")
- start_list = os.path.abspath(start).split(os.path.sep)
- path_list = os.path.abspath(path).split(os.path.sep)
- if start_list[0].lower() != path_list[0].lower():
- unc_path, rest = os.path.splitunc(path)
- unc_start, rest = os.path.splitunc(start)
- if bool(unc_path) ^ bool(unc_start):
- raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)"
- % (path, start))
- else:
- raise ValueError("path is on drive %s, start on drive %s"
- % (path_list[0], start_list[0]))
- # Work out how much of the filepath is shared by start and path.
- for i in range(min(len(start_list), len(path_list))):
- if start_list[i].lower() != path_list[i].lower():
- break
- else:
- i += 1
-
- rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return os.path.curdir
- return os.path.join(*rel_list)
-
- else:
- # default to posixpath definition
- def relpath(path, start=os.path.curdir):
- """Return a relative version of a path"""
-
- if not path:
- raise ValueError("no path specified")
-
- start_list = os.path.abspath(start).split(os.path.sep)
- path_list = os.path.abspath(path).split(os.path.sep)
-
- # Work out how much of the filepath is shared by start and path.
- i = len(os.path.commonprefix([start_list, path_list]))
-
- rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return os.path.curdir
- return os.path.join(*rel_list)
-else:
- from os.path import relpath
+++ /dev/null
-# -*- coding: utf-8 -*-
-import urllib2
-import itertools, mimetools, mimetypes
-import platform
-from txclib import get_version
-
-# Helper class to enable urllib2 to handle PUT/DELETE requests as well
-class RequestWithMethod(urllib2.Request):
- """Workaround for using DELETE with urllib2"""
- def __init__(self, url, method, data=None, headers={},
- origin_req_host=None, unverifiable=False):
- self._method = method
- urllib2.Request.__init__(self, url, data=data, headers=headers,
- origin_req_host=None, unverifiable=False)
-
- def get_method(self):
- return self._method
-
-import urllib
-import os, stat
-from cStringIO import StringIO
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
-
-# Controls how sequences are uncoded. If true, elements may be given multiple
-# values by assigning a sequence.
-doseq = 1
-
-class MultipartPostHandler(urllib2.BaseHandler):
- handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
-
- def http_request(self, request):
- data = request.get_data()
- if data is not None and type(data) != str:
- v_files = []
- v_vars = []
- try:
- for(key, value) in data.items():
- if type(value) == file:
- v_files.append((key, value))
- else:
- v_vars.append((key, value))
- except TypeError:
- systype, value, traceback = sys.exc_info()
- raise TypeError, "not a valid non-string sequence or mapping object", traceback
-
- if len(v_files) == 0:
- data = urllib.urlencode(v_vars, doseq)
- else:
- boundary, data = self.multipart_encode(v_vars, v_files)
-
- contenttype = 'multipart/form-data; boundary=%s' % boundary
- if(request.has_header('Content-Type')
- and request.get_header('Content-Type').find('multipart/form-data') != 0):
- print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
- request.add_unredirected_header('Content-Type', contenttype)
-
- request.add_data(data)
-
- return request
-
- def multipart_encode(vars, files, boundary = None, buf = None):
- if boundary is None:
- boundary = mimetools.choose_boundary()
- if buf is None:
- buf = StringIO()
- for(key, value) in vars:
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"' % key)
- buf.write('\r\n\r\n' + value + '\r\n')
- for(key, fd) in files:
- file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
- filename = fd.name.split('/')[-1]
- contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
- buf.write('Content-Type: %s\r\n' % contenttype)
- # buffer += 'Content-Length: %s\r\n' % file_size
- fd.seek(0)
- buf.write('\r\n' + fd.read() + '\r\n')
- buf.write('--' + boundary + '--\r\n\r\n')
- buf = buf.getvalue()
- return boundary, buf
- multipart_encode = Callable(multipart_encode)
-
- https_request = http_request
-
-
-def user_agent_identifier():
- """Return the user agent for the client."""
- client_info = (get_version(), platform.system(), platform.machine())
- return "txclient/%s (%s %s)" % client_info