Procházet zdrojové kódy

Merge branch 'master' into externalLinks

Mario Đanić před 8 roky
rodič
revize
19705ff438
98 změnil soubory, kde provedl 5221 přidání a 668 odebrání
  1. 3 3
      .drone.yml
  2. 23 11
      THIRD_PARTY.txt
  3. 12 9
      build.gradle
  4. 234 0
      build.gradle.modified
  5. 58 0
      drawable_resources/ic_notification.svg
  6. 58 0
      drawable_resources/ic_notification_light_grey.svg
  7. 12 5
      src/main/AndroidManifest.xml
  8. 13 8
      src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java
  9. 2 1
      src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java
  10. 32 0
      src/main/java/com/owncloud/android/db/PreferenceManager.java
  11. 1 1
      src/main/java/com/owncloud/android/files/services/FileDownloader.java
  12. 3 2
      src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java
  13. 266 0
      src/main/java/com/owncloud/android/services/ContactsBackupJob.java
  14. 77 0
      src/main/java/com/owncloud/android/services/ContactsImportJob.java
  15. 7 3
      src/main/java/com/owncloud/android/services/NCJobCreator.java
  16. 2 1
      src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java
  17. 20 2
      src/main/java/com/owncloud/android/ui/TextDrawable.java
  18. 429 0
      src/main/java/com/owncloud/android/ui/activity/ContactListFragment.java
  19. 372 0
      src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java
  20. 42 12
      src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java
  21. 15 0
      src/main/java/com/owncloud/android/ui/activity/FileActivity.java
  22. 53 13
      src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java
  23. 286 0
      src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java
  24. 2 1
      src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java
  25. 68 5
      src/main/java/com/owncloud/android/ui/activity/WhatsNewActivity.java
  26. 6 1
      src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
  27. 24 10
      src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java
  28. 129 0
      src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java
  29. 11 4
      src/main/java/com/owncloud/android/ui/events/SearchEvent.java
  30. 26 0
      src/main/java/com/owncloud/android/ui/events/TokenPushEvent.java
  31. 14 4
      src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java
  32. 143 128
      src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
  33. 112 60
      src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java
  34. 3 5
      src/main/java/com/owncloud/android/utils/BitmapUtils.java
  35. 16 0
      src/main/java/com/owncloud/android/utils/MimeTypeUtil.java
  36. 36 1
      src/main/java/com/owncloud/android/utils/PermissionUtil.java
  37. 114 0
      src/main/java/third_parties/ezvcard_android/AndroidCustomField.java
  38. 567 0
      src/main/java/third_parties/ezvcard_android/ContactOperations.java
  39. 292 0
      src/main/java/third_parties/ezvcard_android/DataMappings.java
  40. binární
      src/main/res/drawable-hdpi/ic_notification.png
  41. binární
      src/main/res/drawable-hdpi/ic_notification_light_grey.png
  42. binární
      src/main/res/drawable-mdpi/ic_notification.png
  43. binární
      src/main/res/drawable-mdpi/ic_notification_light_grey.png
  44. binární
      src/main/res/drawable-xhdpi/ic_notification.png
  45. binární
      src/main/res/drawable-xhdpi/ic_notification_light_grey.png
  46. binární
      src/main/res/drawable-xxhdpi/ic_notification.png
  47. binární
      src/main/res/drawable-xxhdpi/ic_notification_light_grey.png
  48. binární
      src/main/res/drawable-xxxhdpi/ic_notification.png
  49. binární
      src/main/res/drawable-xxxhdpi/ic_notification_light_grey.png
  50. 1 1
      src/main/res/drawable/indicator_dot_not_selected.xml
  51. 2 2
      src/main/res/drawable/main_header_bg.xml
  52. 1 1
      src/main/res/drawable/round_button.xml
  53. 1 1
      src/main/res/layout/account_item.xml
  54. 43 0
      src/main/res/layout/contactlist_fragment.xml
  55. 46 0
      src/main/res/layout/contactlist_list_item.xml
  56. 120 0
      src/main/res/layout/contacts_preference.xml
  57. 1 1
      src/main/res/layout/edit_share_layout.xml
  58. 4 4
      src/main/res/layout/list_fragment.xml
  59. 82 0
      src/main/res/layout/notifications_layout.xml
  60. 1 1
      src/main/res/layout/share_file_layout.xml
  61. 8 8
      src/main/res/layout/whats_new_activity.xml
  62. 13 0
      src/main/res/layout/whats_new_webview_element.xml
  63. 10 0
      src/main/res/menu/drawer_menu.xml
  64. 4 0
      src/main/res/values-ca/strings.xml
  65. 64 2
      src/main/res/values-da/strings.xml
  66. 3 2
      src/main/res/values-de-rDE/strings.xml
  67. 3 2
      src/main/res/values-de/strings.xml
  68. 113 51
      src/main/res/values-el/strings.xml
  69. 11 8
      src/main/res/values-es-rMX/strings.xml
  70. 118 79
      src/main/res/values-es/strings.xml
  71. 2 3
      src/main/res/values-fi-rFI/strings.xml
  72. 4 3
      src/main/res/values-fr/strings.xml
  73. 16 0
      src/main/res/values-hu-rHU/strings.xml
  74. 6 5
      src/main/res/values-is/strings.xml
  75. 35 0
      src/main/res/values-it/strings.xml
  76. 3 2
      src/main/res/values-ja-rJP/strings.xml
  77. 69 22
      src/main/res/values-nb-rNO/strings.xml
  78. 17 7
      src/main/res/values-nl/strings.xml
  79. 3 2
      src/main/res/values-pl/strings.xml
  80. 113 113
      src/main/res/values-pt-rBR/strings.xml
  81. 19 2
      src/main/res/values-ro/strings.xml
  82. 21 20
      src/main/res/values-ru/strings.xml
  83. 1 1
      src/main/res/values-sq/strings.xml
  84. 3 2
      src/main/res/values-tr/strings.xml
  85. 3 2
      src/main/res/values-zh-rCN/strings.xml
  86. 2 9
      src/main/res/values/colors.xml
  87. 22 6
      src/main/res/values/setup.xml
  88. 28 0
      src/main/res/values/strings.xml
  89. 9 9
      src/main/res/values/styles.xml
  90. 3 2
      src/main/res/xml/exposed_filepaths.xml
  91. 100 0
      src/modified/AndroidManifest.xml
  92. 41 0
      src/modified/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java
  93. 45 0
      src/modified/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java
  94. 66 0
      src/modified/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java
  95. 36 0
      src/modified/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java
  96. 49 0
      src/modified/java/com/owncloud/android/utils/GooglePlayUtils.java
  97. 248 0
      src/modified/java/com/owncloud/android/utils/PushUtils.java
  98. 25 5
      src/modified/res/values/setup.xml

+ 3 - 3
.drone.yml

@@ -6,14 +6,14 @@ pipeline:
       - emulator -avd test -no-window &
       - ./wait_for_emulator.sh
       # build app and assemble APK, in debug mode
-      - ./gradlew assembleDebug
+      - ./gradlew assembleGeneric
       # run all the instrumented tests of app module - DISABLED until we get an stable setup for Espresso in Travis
       # - ./gradlew connectedDebugAndroidTest --info
       # install app, then assemble and install instrumented tests of app module
       - ./gradlew :installGenericDebug
-      - ./gradlew :installModifiedDebug
+      #- ./gradlew :installModifiedDebug
       - ./gradlew :installGenericDebugAndroidTest
-      - ./gradlew :installModifiedDebugAndroidTest
+      #- ./gradlew :installModifiedDebugAndroidTest
       # run sample instrumented unit test
       # TODO fails because test runner is not available
       #- adb shell am instrument -w -e debug false -e class com.owncloud.android.datamodel.OCFileUnitTest com.owncloud.android.test/android.support.test.runner.AndroidJUnitRunner

+ 23 - 11
THIRD_PARTY.txt

@@ -2,7 +2,7 @@
   Nextcloud Android client                                       
 
   Copyright (C) 2016      Nextcloud Project
-  Copyright (C) 2012-2016 ownCloud Inc.
+  Copyright (C) 2012-2016 ownCloud GmbH
   Copyright (C) 2012      Bartek Przybylski
 ###################################################################
 
@@ -37,24 +37,22 @@ that govern this software, for the purposes they are being used.
 
 The third party software included and used by this project is:
 
- * Apache JackRabbit, version 2.2.5.
-   Copyright (C) 2004-2010 The Apache Software Foundation.
+ * Apache JackRabbit 2.12.4.
+   Copyright (C) 2004-2016 The Apache Software Foundation.
    Licensed under Apache License, Version 2.0.
-   Placed at libs/jackrabbit-webdav-2.2.5-jar-with-dependencies.jar
-   The jar file must be included in the ownCloud client APK.
-   Original license document included at libs/LICENSE.txt
+   The jar file must be included in the Nextcloud client APK.
    See http://jackrabbit.apache.org/
  
  * Transifex client.
    Copyright (C) Transifex.
    Licensed under GNU General Public License.
    Placed at third_party/transifex-client.
-   Used as a helper tool, not included in the ownCloud client APK.
+   Used as a helper tool, not included in the Nextcloud client APK.
    Original license document included at third_party/transifex-client/LICENSE.
    See http://help.transifex.com/features/client/
    
- * TouchImageView, commit 6dbeac4f11936185ba374c73144ac431c23c9aab
-   Copyright (c) 2012 Michael Ortiz
+ * TouchImageView, 1.2.0. commit 6dbeac4f11936185ba374c73144ac431c23c9aab
+   Copyright (c) 2014 Michael Ortiz
    Licensed under MIT License
    JAR file libs/touch-image-view.jar has been generated by ownCloud Inc., including without 
    modifications com.ortiz.touch.ExtendedViewPager and com.ortiz.touch.TouchImageView classes. 
@@ -63,5 +61,19 @@ The third party software included and used by this project is:
  * floatingactionbutton 1.10.1.
    Copyright (c) 2014 Jerzy Chalupski
    Licensed under Apache License, Version 2.0.
-   placed at libs/com-getbase-floatingactionbutton-1-10-0-exploded-aar has been exploded by ownCloud Inc.
-   See https://github.com/futuresimple/android-floating-action-button 
+   The jar file must be included in the Nextcloud client APK.
+   See https://github.com/futuresimple/android-floating-action-button 
+ 
+ * AndroidSVG 1.2.1.
+   Copyright (c) 2014 Paul LeBeau
+   Licensed under Apache License, Version 2.0.
+   placed at libs/androidsvg-1.2.1.jar
+   The jar file must be included in the Nextcloud client APK.
+   See https://github.com/BigBadaboom/androidsvg
+   
+ * Disk LRU Cache 2.0.2.
+   Copyright (c) 2013 Jake Wharton 
+   Licensed under Apache License, Version 2.0.
+   placed at libs/disklrucache-2.0.2.jar
+   The jar file must be included in the Nextcloud client APK.
+   See https://github.com/JakeWharton/DiskLruCache

+ 12 - 9
build.gradle

@@ -13,7 +13,7 @@ buildscript {
         }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.0'
+        classpath 'com.android.tools.build:gradle:2.3.1'
         classpath 'com.google.gms:google-services:3.0.0'
     }
 }
@@ -37,14 +37,12 @@ ext {
     preDexEnabled = "true".equals(System.getProperty("pre-dex", "true"))
 }
 
-configurations.all {
-    // Check for updates every build
-    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
-}
-
 repositories {
     jcenter()
     maven { url "https://jitpack.io" }
+    maven {
+        url 'https://oss.sonatype.org/content/repositories/snapshots/'
+    }
 
     flatDir {
         dirs 'libs'
@@ -118,6 +116,7 @@ android {
 
     packagingOptions {
         exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/LICENSE'
     }
 
     task checkstyle(type: Checkstyle) {
@@ -178,7 +177,7 @@ dependencies {
     compile name: 'touch-image-view'
     compile 'com.android.support:multidex:1.0.1'
 
-    compile 'com.github.nextcloud:android-library:externalLinks-SNAPSHOT'
+    compile 'com.github.nextcloud:android-library:-SNAPSHOT'
     compile "com.android.support:support-v4:${supportLibraryVersion}"
     compile "com.android.support:design:${supportLibraryVersion}"
     compile 'com.jakewharton:disklrucache:2.0.2'
@@ -187,11 +186,11 @@ dependencies {
     compile 'com.getbase:floatingactionbutton:1.10.1'
     compile 'com.google.code.findbugs:annotations:2.0.1'
     compile group: 'commons-io', name: 'commons-io', version: '2.4'
-    compile 'com.google.android.gms:play-services:10.2.0'
-    compile 'com.github.evernote:android-job:v1.1.8'
+    compile 'com.github.evernote:android-job:v1.1.9'
     compile 'com.jakewharton:butterknife:8.4.0'
     annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
     compile 'org.greenrobot:eventbus:3.0.0'
+    compile 'com.googlecode.ez-vcard:ez-vcard:0.10.2'
 
     compile 'org.parceler:parceler-api:1.1.6'
     annotationProcessor 'org.parceler:parceler:1.1.6'
@@ -222,6 +221,10 @@ dependencies {
 
 }
 
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
 tasks.withType(Test) {
     /// increased logging for tests
     testLogging {

+ 234 - 0
build.gradle.modified

@@ -0,0 +1,234 @@
+// Gradle build file
+//
+// This project was started in Eclipse and later moved to Android Studio. In the transition, both IDEs were supported.
+// Due to this, the files layout is not the usual in new projects created with Android Studio / gradle. This file
+// merges declarations usually split in two separates build.gradle file, one for global settings of the project in
+// its root folder, another one for the app module in subfolder of root.
+
+buildscript {
+    repositories {
+        jcenter()
+        maven {
+            url 'https://oss.sonatype.org/content/repositories/snapshots/'
+        }
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:2.3.1'
+        classpath 'com.google.gms:google-services:3.0.0'
+    }
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'checkstyle'
+apply plugin: 'pmd'
+apply plugin: 'findbugs'
+
+configurations.all {
+    // check for updates every build
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+ext {
+    supportLibraryVersion = '25.0.0'
+
+    travisBuild = System.getenv("TRAVIS") == "true"
+
+    // allows for -Dpre-dex=false to be set :)
+    preDexEnabled = "true".equals(System.getProperty("pre-dex", "true"))
+}
+
+repositories {
+    jcenter()
+    maven { url "https://jitpack.io" }
+    maven {
+        url 'https://oss.sonatype.org/content/repositories/snapshots/'
+    }
+
+    flatDir {
+        dirs 'libs'
+    }
+}
+
+android {
+    lintOptions {
+        abortOnError true
+        lintConfig file("${project.rootDir}/lint.xml")
+        htmlReport true
+        htmlOutput file("$project.buildDir/reports/lint/lint.html")
+    }
+
+    dexOptions {
+        javaMaxHeapSize "4g"
+    }
+
+    compileSdkVersion 25
+    buildToolsVersion '25.0.0'
+
+    defaultConfig {
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+        // arguments to be passed to functional tests
+        testInstrumentationRunnerArgument "TEST_USER", "\"$System.env.OCTEST_APP_USERNAME\""
+        testInstrumentationRunnerArgument "TEST_PASSWORD", "\"$System.env.OCTEST_APP_PASSWORD\""
+        testInstrumentationRunnerArgument "TEST_SERVER_URL", "\"$System.env.OCTEST_SERVER_BASE_URL\""
+
+        multiDexEnabled true
+
+        // adapt structure from Eclipse to Gradle/Android Studio expectations;
+        // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure
+
+        productFlavors {
+            generic {
+                applicationId 'com.nextcloud.client'
+            }
+
+            modified {
+                // structure is:
+                // domain tld
+                // domain name
+                // .client
+                applicationId 'com.custom.client'
+            }
+        }
+
+        configurations {
+            modifiedCompile
+        }
+    }
+
+
+    // adapt structure from Eclipse to Gradle/Android Studio expectations;
+    // see http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Configuring-the-Structure
+
+    dexOptions {
+        // Skip pre-dexing whe`n running on Travis CI or when disabled via -Dpre-dex=false.
+        preDexLibraries = preDexEnabled && !travisBuild
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    packagingOptions {
+        exclude 'META-INF/LICENSE.txt'
+    }
+
+    task checkstyle(type: Checkstyle) {
+        configFile = file("${rootProject.projectDir}/checkstyle.xml")
+        configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath
+        source 'src'
+        include '**/*.java'
+        exclude '**/gen/**'
+        classpath = files()
+    }
+
+    task pmd(type: Pmd) {
+        ruleSetFiles = files("${project.rootDir}/pmd-ruleset.xml")
+        ignoreFailures = false
+        ruleSets = []
+
+        source 'src'
+        include '**/*.java'
+        exclude '**/gen/**'
+
+        reports {
+            xml.enabled = false
+            html.enabled = true
+            xml {
+                destination "$project.buildDir/reports/pmd/pmd.xml"
+            }
+            html {
+                destination "$project.buildDir/reports/pmd/pmd.html"
+            }
+        }
+    }
+
+    task findbugs(type: FindBugs) {
+        ignoreFailures = false
+        effort = "max"
+        reportLevel = "high"
+        classes = files("$project.buildDir/intermediates/classes")
+        excludeFilter = new File("${project.rootDir}/findbugs-filter.xml")
+        source 'src'
+        include '**/*.java'
+        exclude '**/gen/**'
+
+        reports {
+            xml.enabled = false
+            html.enabled = true
+            html {
+                destination "$project.buildDir/reports/findbugs/findbugs.html"
+            }
+        }
+        classpath = files()
+    }
+    check.dependsOn 'checkstyle', 'findbugs', 'pmd', 'lint'
+
+}
+
+dependencies {
+    /// dependencies for app building
+    compile name: 'touch-image-view'
+    compile 'com.android.support:multidex:1.0.1'
+
+    compile 'com.github.nextcloud:android-library:notifications-push-SNAPSHOT'
+    compile "com.android.support:support-v4:${supportLibraryVersion}"
+    compile "com.android.support:design:${supportLibraryVersion}"
+    compile 'com.jakewharton:disklrucache:2.0.2'
+    compile "com.android.support:appcompat-v7:${supportLibraryVersion}"
+    compile "com.android.support:cardview-v7:${supportLibraryVersion}"
+    compile 'com.getbase:floatingactionbutton:1.10.1'
+    compile 'com.google.code.findbugs:annotations:2.0.1'
+    compile group: 'commons-io', name: 'commons-io', version: '2.4'
+    compile 'com.github.evernote:android-job:v1.1.9'
+    compile 'com.jakewharton:butterknife:8.4.0'
+    annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
+    compile 'org.greenrobot:eventbus:3.0.0'
+
+    compile 'com.google.android.gms:play-services:10.2.1'
+
+    compile 'org.parceler:parceler-api:1.1.6'
+    annotationProcessor 'org.parceler:parceler:1.1.6'
+
+    compile 'com.github.bumptech.glide:glide:3.7.0'
+    compile 'com.caverock:androidsvg:1.2.1'
+    /// dependencies for local unit tests
+    testCompile 'junit:junit:4.12'
+    testCompile 'org.mockito:mockito-core:1.10.19'
+
+    /// dependencies for instrumented tests
+    // JUnit4 Rules
+    androidTestCompile 'com.android.support.test:rules:0.5'
+
+    // Android JUnit Runner
+    androidTestCompile 'com.android.support.test:runner:0.5'
+
+    // Android Annotation Support
+    androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
+
+    // Espresso core
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
+
+    // UIAutomator - for cross-app UI tests, and to grant screen is turned on in Espresso tests
+    //androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
+    // fix conflict in dependencies; see http://g.co/androidstudio/app-test-app-conflict for details
+    //androidTestCompile "com.android.support:support-annotations:${supportLibraryVersion}"
+}
+
+configurations.all {
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
+tasks.withType(Test) {
+    /// increased logging for tests
+    testLogging {
+        events "passed", "skipped", "failed"
+    }
+}
+
+apply plugin: 'com.google.gms.google-services'

+ 58 - 0
drawable_resources/ic_notification.svg

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="16"
+   width="16"
+   version="1.1"
+   viewBox="0 0 16 16"
+   id="svg4"
+   sodipodi:docname="ic_notification.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\notifications\src\main\res\drawable-mdpi\ic_notification.png"
+   inkscape:export-xdpi="144"
+   inkscape:export-ydpi="144">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1005"
+     id="namedview6"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="8"
+     inkscape:cy="8"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="m8 2c-0.5523 0-1 0.4477-1 1 0 0.0472 0.021 0.0873 0.0273 0.1328-1.7366 0.4362-3.0273 1.9953-3.0273 3.8672v2l-1 1v1h10v-1l-1-1v-2c0-1.8719-1.291-3.431-3.0273-3.8672 0.0063-0.0455 0.0273-0.0856 0.0273-0.1328 0-0.5523-0.4477-1-1-1zm-2 10c0 1.1046 0.8954 2 2 2s2-0.8954 2-2z"
+     fill="#fff"
+     id="path2"
+     style="fill:#757575;fill-opacity:1" />
+</svg>

+ 58 - 0
drawable_resources/ic_notification_light_grey.svg

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   height="16"
+   width="16"
+   version="1.1"
+   viewBox="0 0 16 16"
+   id="svg4"
+   sodipodi:docname="ic_notification_light_grey.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\notifications\src\main\res\drawable-mdpi\ic_notification_light_grey.png"
+   inkscape:export-xdpi="432"
+   inkscape:export-ydpi="432">
+  <metadata
+     id="metadata10">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs8" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1005"
+     id="namedview6"
+     showgrid="false"
+     inkscape:zoom="14.75"
+     inkscape:cx="-10.20339"
+     inkscape:cy="8"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="m8 2c-0.5523 0-1 0.4477-1 1 0 0.0472 0.021 0.0873 0.0273 0.1328-1.7366 0.4362-3.0273 1.9953-3.0273 3.8672v2l-1 1v1h10v-1l-1-1v-2c0-1.8719-1.291-3.431-3.0273-3.8672 0.0063-0.0455 0.0273-0.0856 0.0273-0.1328 0-0.5523-0.4477-1-1-1zm-2 10c0 1.1046 0.8954 2 2 2s2-0.8954 2-2z"
+     fill="#fff"
+     id="path2"
+     style="fill:#000000;fill-opacity:1;opacity:0.5" />
+</svg>

+ 12 - 5
src/main/AndroidManifest.xml

@@ -19,9 +19,10 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.owncloud.android"
-    android:versionCode="10040299"
-    android:versionName="1.4.2">
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.owncloud.android"
+          android:versionCode="10040299"
+          android:versionName="1.4.2">
 
     <uses-sdk
         android:minSdkVersion="14"
@@ -35,6 +36,9 @@
         See note in http://developer.android.com/intl/es/reference/android/Manifest.permission.html#GET_ACCOUNTS -->
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
 
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+
     <!-- USE_CREDENTIALS, MANAGE_ACCOUNTS and AUTHENTICATE_ACCOUNTS are needed for API < 23.
         In API >= 23 the do not exist anymore -->
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@ -76,13 +80,14 @@
         </activity>
         <activity android:name=".ui.activity.ManageAccountsActivity" />
         <activity android:name=".ui.activity.UserInfoActivity" />
+        <activity android:name=".ui.activity.NotificationsActivity"/>
         <activity android:name=".ui.activity.ParticipateActivity" />
         <activity android:name=".ui.activity.ActivitiesListActivity"/>
         <activity android:name=".ui.activity.FolderSyncActivity" />
         <activity android:name=".ui.activity.UploadFilesActivity" />
         <activity android:name=".ui.activity.ExternalSiteWebView"
-                  android:configChanges="orientation|screenSize|keyboardHidden"
-            />
+                  android:configChanges="orientation|screenSize|keyboardHidden" />
+        <activity android:name=".ui.activity.ContactsPreferenceActivity" />
         <activity android:name=".ui.activity.ReceiveExternalFilesActivity"
                   
                   android:taskAffinity=""
@@ -256,6 +261,8 @@
 
         <service android:name=".services.observer.FileObserverService" />
 
+        <service android:name="com.evernote.android.job.gcm.PlatformGcmService" tools:node="remove"/>
+
         <activity
             android:name=".ui.activity.CopyToClipboardActivity"
             android:icon="@drawable/copy_link"

+ 13 - 8
src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java

@@ -21,16 +21,19 @@
 
 package com.owncloud.android.authentication;
 
-import com.owncloud.android.MainApp;
-import com.owncloud.android.R;
-
-import android.accounts.*;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
 import android.widget.Toast;
 
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
 import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
@@ -90,8 +93,8 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
                         + e.getMessage(), e);
                 return e.getFailureBundle();
             }
-            
-            final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+
+            Intent intent = new Intent(mContext, AuthenticatorActivity.class);
             intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
             intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
             intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);
@@ -134,6 +137,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
             Log_OC.e(TAG, "Failed to validate account type " + account.type + ": " + e.getMessage(), e);
             return e.getFailureBundle();
         }
+
         Intent intent = new Intent(mContext, AuthenticatorActivity.class);
         intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
                 response);
@@ -185,7 +189,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
         }
         
         /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
-        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+        Intent intent = new Intent(mContext, AuthenticatorActivity.class);
         intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
         intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
         intent.putExtra(KEY_LOGIN_OPTIONS, options);
@@ -215,7 +219,8 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
     public Bundle updateCredentials(AccountAuthenticatorResponse response,
             Account account, String authTokenType, Bundle options)
             throws NetworkErrorException {
-        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
+
+        Intent intent = new Intent(mContext, AuthenticatorActivity.class);
         intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
         intent.putExtra(KEY_ACCOUNT, account);
         intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);

+ 2 - 1
src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java

@@ -38,6 +38,7 @@ import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.operations.UploadFileOperation;
+import com.owncloud.android.services.AutoUploadJob;
 
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -396,7 +397,7 @@ public class UploadsStorageManager extends Observable {
 
     @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     private List<OCUpload> getPendingJobs() {
-        Set<JobRequest> jobRequests = JobManager.create(mContext).getAllJobRequests();
+        Set<JobRequest> jobRequests = JobManager.create(mContext).getAllJobRequestsForTag(AutoUploadJob.TAG);
 
         ArrayList<OCUpload> list = new ArrayList<>();
 

+ 32 - 0
src/main/java/com/owncloud/android/db/PreferenceManager.java

@@ -46,7 +46,33 @@ public abstract class PreferenceManager {
     private static final String PREF__INSTANT_VIDEO_UPLOAD_PATH_USE_SUBFOLDERS = "instant_video_upload_path_use_subfolders";
     private static final String PREF__LEGACY_CLEAN = "legacyClean";
     private static final String PREF__AUTO_UPLOAD_UPDATE_PATH = "autoUploadPathUpdate";
+    private static final String PREF__PUSH_TOKEN = "pushToken";
+    private static final String PREF__PUSH_TOKEN_UPDATE_TIME = "pushTokenLastUpdated";
+    private static final String PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME = "pushTokenLastSent";
 
+    public static void setPushTokenLastSentTime(Context context, long time) {
+        saveLongPreference(context, PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME, time);
+    }
+
+    public static long getPushTokenLastSentTime(Context context) {
+        return getDefaultSharedPreferences(context).getLong(PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME, -1);
+    }
+
+    public static void setPushToken(Context context, String pushToken) {
+        saveStringPreference(context, PREF__PUSH_TOKEN, pushToken);
+    }
+
+    public static String getPushToken(Context context) {
+        return getDefaultSharedPreferences(context).getString(PREF__PUSH_TOKEN, "");
+    }
+
+    public static void setPushTokenUpdateTime(Context context, long time) {
+        saveLongPreference(context, PREF__PUSH_TOKEN_UPDATE_TIME, time);
+    }
+
+    public static long getPushTokenUpdateTime(Context context) {
+        return getDefaultSharedPreferences(context).getLong(PREF__PUSH_TOKEN_UPDATE_TIME, -1);
+    }
 
     public static boolean instantPictureUploadEnabled(Context context) {
         return getDefaultSharedPreferences(context).getBoolean(PREF__INSTANT_UPLOADING, false);
@@ -267,6 +293,12 @@ public abstract class PreferenceManager {
         appPreferences.apply();
     }
 
+    private static void saveLongPreference(Context context, String key, long value) {
+        SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
+        appPreferences.putLong(key, value);
+        appPreferences.apply();
+    }
+
     public static SharedPreferences getDefaultSharedPreferences(Context context) {
         return android.preference.PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
     }

+ 1 - 1
src/main/java/com/owncloud/android/files/services/FileDownloader.java

@@ -51,10 +51,10 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.FileUtils;
-import com.owncloud.android.ui.notifications.NotificationUtils;
 import com.owncloud.android.operations.DownloadFileOperation;
 import com.owncloud.android.ui.activity.FileActivity;
 import com.owncloud.android.ui.activity.FileDisplayActivity;
+import com.owncloud.android.ui.notifications.NotificationUtils;
 import com.owncloud.android.ui.preview.PreviewImageActivity;
 import com.owncloud.android.ui.preview.PreviewImageFragment;
 import com.owncloud.android.utils.ErrorMessageAdapter;

+ 3 - 2
src/main/java/com/owncloud/android/operations/GetServerInfoOperation.java

@@ -21,7 +21,7 @@
 
 package com.owncloud.android.operations;
 
-import java.util.ArrayList;
+import android.content.Context;
 
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.lib.common.OwnCloudClient;
@@ -33,7 +33,7 @@ import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation;
 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.operations.DetectAuthenticationMethodOperation.AuthenticationMethod;
 
-import android.content.Context;
+import java.util.ArrayList;
 
 /**
  * Get basic information from an ownCloud server given its URL.
@@ -78,6 +78,7 @@ public class GetServerInfoOperation extends RemoteOperation {
 	    
 	    // first: check the status of the server (including its version)
 	    GetRemoteStatusOperation getStatus = new GetRemoteStatusOperation(mContext);
+
 	    RemoteOperationResult result = getStatus.execute(client);
 	    
         if (result.isSuccess()) {

+ 266 - 0
src/main/java/com/owncloud/android/services/ContactsBackupJob.java

@@ -0,0 +1,266 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud GmbH.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.services;
+
+import android.accounts.Account;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.IBinder;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.text.format.DateFormat;
+
+import com.evernote.android.job.Job;
+import com.evernote.android.job.util.support.PersistableBundleCompat;
+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.db.PreferenceManager;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.operations.UploadFileOperation;
+import com.owncloud.android.ui.activity.ContactsPreferenceActivity;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Vector;
+
+/**
+ * Job that backup contacts to /Contacts-Backup and deletes files older than x days
+ */
+
+public class ContactsBackupJob extends Job {
+    public static final String TAG = "ContactsBackupJob";
+    public static final String ACCOUNT = "account";
+    public static final String FORCE = "force";
+    private OperationsServiceConnection operationsServiceConnection;
+    private OperationsService.OperationsServiceBinder operationsServiceBinder;
+
+
+    @NonNull
+    @Override
+    protected Result onRunJob(Params params) {
+        final Context context = MainApp.getAppContext();
+        PersistableBundleCompat bundle = params.getExtras();
+
+        boolean force = bundle.getBoolean(FORCE, false);
+
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+        Long lastExecution = sharedPreferences.getLong(ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP, -1);
+
+        if (force || (lastExecution + 24 * 60 * 60 * 1000) < Calendar.getInstance().getTimeInMillis()) {
+            Log_OC.d(TAG, "start contacts backup job");
+
+            final Account account = AccountUtils.getOwnCloudAccountByName(context, bundle.getString(ACCOUNT, ""));
+            String backupFolder = getContext().getResources().getString(R.string.contacts_backup_folder) +
+                    OCFile.PATH_SEPARATOR;
+            Integer daysToExpire = getContext().getResources().getInteger(R.integer.contacts_backup_expire);
+
+            backupContact(account, backupFolder);
+
+            // bind to Operations Service
+            operationsServiceConnection = new OperationsServiceConnection(daysToExpire, backupFolder, account);
+
+            getContext().bindService(new Intent(getContext(), OperationsService.class), operationsServiceConnection,
+                    OperationsService.BIND_AUTO_CREATE);
+
+            // store execution date
+            sharedPreferences.edit().putLong(ContactsPreferenceActivity.PREFERENCE_CONTACTS_LAST_BACKUP,
+                    Calendar.getInstance().getTimeInMillis()).apply();
+
+        } else {
+            Log_OC.d(TAG, "last execution less than 24h ago");
+        }
+
+        return Result.SUCCESS;
+    }
+
+    private void backupContact(Account account, String backupFolder) {
+        ArrayList<String> vCard = new ArrayList<>();
+        try {
+
+            Cursor cursor = getContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null,
+                    null, null, null);
+
+            if (cursor != null && cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                for (int i = 0; i < cursor.getCount(); i++) {
+
+                    vCard.add(getContactFromCursor(cursor));
+                    cursor.moveToNext();
+                }
+            }
+
+            String filename = DateFormat.format("yyyy-MM-dd_HH-mm-ss", Calendar.getInstance()).toString() + ".vcf";
+            Log_OC.d(TAG, "Storing: " + filename);
+            File file = new File(getContext().getCacheDir(), filename);
+
+            FileWriter fw = null;
+            try {
+                fw = new FileWriter(file);
+
+                for (String card : vCard) {
+                    fw.write(card);
+                }
+
+            } catch (IOException e) {
+                Log_OC.d(TAG, "Error ", e);
+            } finally {
+                if (fw != null) {
+                    try {
+                        fw.close();
+                    } catch (IOException e) {
+                        Log_OC.d(TAG, "Error closing file writer ", e);
+                    }
+                }
+            }
+
+            FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
+            requester.uploadNewFile(
+                    getContext(),
+                    account,
+                    file.getAbsolutePath(),
+                    backupFolder + filename,
+                    FileUploader.LOCAL_BEHAVIOUR_MOVE,
+                    null,
+                    true,
+                    UploadFileOperation.CREATED_BY_USER
+            );
+
+        } catch (Exception e) {
+            Log_OC.d(TAG, e.getMessage());
+        }
+    }
+
+    private void expireFiles(Integer daysToExpire, String backupFolderString, Account account) {
+        // -1 disables expiration
+        if (daysToExpire > -1) {
+            FileDataStorageManager storageManager = new FileDataStorageManager(account, getContext().getContentResolver());
+            OCFile backupFolder = storageManager.getFileByPath(backupFolderString);
+            Calendar cal = Calendar.getInstance();
+            cal.add(Calendar.DAY_OF_YEAR, -daysToExpire);
+            Long timestampToExpire = cal.getTimeInMillis();
+
+            Log_OC.d(TAG, "expire: " + daysToExpire + " " + backupFolder.getFileName());
+
+            Vector<OCFile> backups = storageManager.getFolderContent(backupFolder, false);
+
+            for (OCFile backup : backups) {
+                if (timestampToExpire > backup.getModificationTimestamp()) {
+                    Log_OC.d(TAG, "delete " + backup.getRemotePath());
+
+                    // delete backups
+                    Intent service = new Intent(getContext(), OperationsService.class);
+                    service.setAction(OperationsService.ACTION_REMOVE);
+                    service.putExtra(OperationsService.EXTRA_ACCOUNT, account);
+                    service.putExtra(OperationsService.EXTRA_REMOTE_PATH, backup.getRemotePath());
+                    service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, false);
+                    operationsServiceBinder.queueNewOperation(service);
+                }
+            }
+        }
+
+        getContext().unbindService(operationsServiceConnection);
+    }
+
+    private String getContactFromCursor(Cursor cursor) {
+        String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
+        Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_VCARD_URI, lookupKey);
+
+        String vCard = "";
+        try {
+            InputStream inputStream = getContext().getContentResolver().openInputStream(uri);
+            InputStreamReader inputStreamReader;
+            char[] buffer = new char[1024];
+            StringBuilder stringBuilder = new StringBuilder();
+
+            if (inputStream != null) {
+                inputStreamReader = new InputStreamReader(inputStream);
+
+                while (true) {
+                    int byteCount = inputStreamReader.read(buffer, 0, buffer.length);
+
+                    if (byteCount > 0) {
+                        stringBuilder.append(buffer, 0, byteCount);
+                    } else {
+                        break;
+                    }
+                }
+            }
+
+            vCard = stringBuilder.toString();
+
+            return vCard;
+
+        } catch (IOException e) {
+            Log_OC.d(TAG, e.getMessage());
+        }
+        return vCard;
+    }
+
+    /**
+     * Implements callback methods for service binding.
+     */
+    private class OperationsServiceConnection implements ServiceConnection {
+        private Integer daysToExpire;
+        private String backupFolder;
+        private Account account;
+
+        OperationsServiceConnection(Integer daysToExpire, String backupFolder, Account account) {
+            this.daysToExpire = daysToExpire;
+            this.backupFolder = backupFolder;
+            this.account = account;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+            Log_OC.d(TAG, "service connected");
+
+
+            if (component.equals(new ComponentName(getContext(), OperationsService.class))) {
+                operationsServiceBinder = (OperationsService.OperationsServiceBinder) service;
+                expireFiles(daysToExpire, backupFolder, account);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            Log_OC.d(TAG, "service disconnected");
+
+            if (component.equals(new ComponentName(getContext(), OperationsService.class))) {
+             operationsServiceBinder = null;
+            }
+        }
+    }
+}

+ 77 - 0
src/main/java/com/owncloud/android/services/ContactsImportJob.java

@@ -0,0 +1,77 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud GmbH.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.services;
+
+import android.support.annotation.NonNull;
+
+import com.evernote.android.job.Job;
+import com.evernote.android.job.util.support.PersistableBundleCompat;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import ezvcard.Ezvcard;
+import ezvcard.VCard;
+import third_parties.ezvcard_android.ContactOperations;
+
+/**
+ * Job to import contacts
+ */
+
+public class ContactsImportJob extends Job {
+    public static final String TAG = "ContactsImportJob";
+
+    public static final String ACCOUNT_TYPE = "account_type";
+    public static final String ACCOUNT_NAME = "account_name";
+    public static final String VCARD_FILE_PATH = "vcard_file_path";
+    public static final String CHECKED_ITEMS_ARRAY = "checked_items_array";
+
+    @NonNull
+    @Override
+    protected Result onRunJob(Params params) {
+        PersistableBundleCompat bundle = params.getExtras();
+
+        String vCardFilePath = bundle.getString(VCARD_FILE_PATH, "");
+        String accountName = bundle.getString(ACCOUNT_NAME, "");
+        String accountType = bundle.getString(ACCOUNT_TYPE, "");
+        int[] intArray = bundle.getIntArray(CHECKED_ITEMS_ARRAY);
+
+        File file = new File(vCardFilePath);
+        ArrayList<VCard> vCards = new ArrayList<>();
+
+        try {
+            ContactOperations operations = new ContactOperations(getContext(), accountName, accountType);
+            vCards.addAll(Ezvcard.parse(file).all());
+
+            for (int i = 0; i < intArray.length; i++ ){
+                if (intArray[i] == 1){
+                    operations.insertContact(vCards.get(i));
+                }
+            }
+        } catch (Exception e) {
+            Log_OC.e(TAG, e.getMessage());
+        }
+
+        return Result.SUCCESS;
+    }
+}

+ 7 - 3
src/main/java/com/owncloud/android/services/NCJobCreator.java

@@ -4,17 +4,17 @@
  * @author Mario Danic
  * Copyright (C) 2017 Mario Danic
  * Copyright (C) 2017 Nextcloud GmbH
- *
+ * <p>
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * at your option) any later version.
- *
+ * <p>
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Affero General Public License for more details.
- *
+ * <p>
  * You should have received a copy of the GNU Affero General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
@@ -33,6 +33,10 @@ public class NCJobCreator implements JobCreator {
         switch (tag) {
             case AutoUploadJob.TAG:
                 return new AutoUploadJob();
+            case ContactsBackupJob.TAG:
+                return new ContactsBackupJob();
+            case ContactsImportJob.TAG:
+                return new ContactsImportJob();
             default:
                 return null;
         }

+ 2 - 1
src/main/java/com/owncloud/android/services/observer/SyncedFolderObserverService.java

@@ -57,7 +57,8 @@ public class SyncedFolderObserverService extends Service {
         fileFilter = new FileFilter() {
             @Override
             public boolean accept(File pathname) {
-                return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp");
+                return !pathname.getName().startsWith(".") && !pathname.getName().endsWith(".tmp") &&
+                        !pathname.getName().endsWith(".temp") && !pathname.getName().endsWith(".thumbnail");
             }
         };
 

+ 20 - 2
src/main/java/com/owncloud/android/ui/TextDrawable.java

@@ -28,6 +28,7 @@ import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.utils.BitmapUtils;
 
 import java.io.UnsupportedEncodingException;
@@ -95,9 +96,26 @@ public class TextDrawable extends Drawable {
     @NonNull
     public static TextDrawable createAvatar(String accountName, float radiusInDp) throws
             UnsupportedEncodingException, NoSuchAlgorithmException {
-        int[] rgb = BitmapUtils.calculateRGB(accountName);
+        String username = AccountUtils.getAccountUsername(accountName);
+        return createNamedAvatar(username, radiusInDp);
+    }
+
+    /**
+     * creates an avatar in form of a TextDrawable with the first letter of a name in a circle with the
+     * given radius.
+     *
+     * @param name the name
+     * @param radiusInDp  the circle's radius
+     * @return the avatar as a TextDrawable
+     * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values
+     * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
+     */
+    @NonNull
+    public static TextDrawable createNamedAvatar(String name, float radiusInDp) throws
+            UnsupportedEncodingException, NoSuchAlgorithmException {
+        int[] rgb = BitmapUtils.calculateRGB(name);
         TextDrawable avatar = new TextDrawable(
-                accountName.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInDp);
+                name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInDp);
         return avatar;
     }
 

+ 429 - 0
src/main/java/com/owncloud/android/ui/activity/ContactListFragment.java

@@ -0,0 +1,429 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud GmbH.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.activity;
+
+import android.Manifest;
+import android.accounts.Account;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.ImageView;
+
+import com.evernote.android.job.JobRequest;
+import com.evernote.android.job.util.support.PersistableBundleCompat;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.ContactsImportJob;
+import com.owncloud.android.ui.TextDrawable;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.utils.BitmapUtils;
+import com.owncloud.android.utils.PermissionUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import ezvcard.Ezvcard;
+import ezvcard.VCard;
+import ezvcard.property.StructuredName;
+
+/**
+ * This fragment shows all contacts from a file and allows to import them.
+ */
+
+public class ContactListFragment extends FileFragment {
+    public static final String TAG = ContactListFragment.class.getSimpleName();
+
+    public static final String FILE_NAME = "FILE_NAME";
+    public static final String ACCOUNT = "ACCOUNT";
+
+    private RecyclerView recyclerView;
+    private Set<Integer> checkedVCards;
+
+    public static ContactListFragment newInstance(OCFile file, Account account) {
+        ContactListFragment frag = new ContactListFragment();
+        Bundle arguments = new Bundle();
+        arguments.putParcelable(FILE_NAME, file);
+        arguments.putParcelable(ACCOUNT, account);
+        frag.setArguments(arguments);
+
+        return frag;
+    }
+
+    @Override
+    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+        View view = inflater.inflate(R.layout.contactlist_fragment, null);
+        setHasOptionsMenu(true);
+
+        ArrayList<VCard> vCards = new ArrayList<>();
+        checkedVCards = new HashSet<>();
+
+        try {
+            OCFile ocFile = getArguments().getParcelable(FILE_NAME);
+            setFile(ocFile);
+            Account account = getArguments().getParcelable(ACCOUNT);
+
+            if (!ocFile.isDown()) {
+                Intent i = new Intent(getContext(), FileDownloader.class);
+                i.putExtra(FileDownloader.EXTRA_ACCOUNT, account);
+                i.putExtra(FileDownloader.EXTRA_FILE, ocFile);
+                getContext().startService(i);
+            } else {
+                File file = new File(ocFile.getStoragePath());
+                vCards.addAll(Ezvcard.parse(file).all());
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        final Button restoreContacts = (Button) view.findViewById(R.id.contactlist_restore_selected);
+        restoreContacts.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+
+                if (checkAndAskForContactsWritePermission()) {
+                    getAccountForImport();
+                }
+            }
+        });
+
+        recyclerView = (RecyclerView) view.findViewById(R.id.contactlist_recyclerview);
+
+
+        ContactListAdapter.OnVCardClickListener vCardClickListener = new ContactListAdapter.OnVCardClickListener() {
+            private void setRestoreButton() {
+                if (checkedVCards.size() > 0) {
+                    restoreContacts.setEnabled(true);
+                    restoreContacts.setBackgroundColor(getResources().getColor(R.color.primary_button_background_color));
+                } else {
+                    restoreContacts.setEnabled(false);
+                    restoreContacts.setBackgroundColor(getResources().getColor(R.color.standard_grey));
+                }
+            }
+
+            @Override
+            public void onVCardCheck(int position) {
+                checkedVCards.add(position);
+                Log_OC.d(TAG, position + " checked");
+
+                setRestoreButton();
+            }
+
+            @Override
+            public void onVCardUncheck(int position) {
+                checkedVCards.remove(position);
+                Log_OC.d(TAG, position + " unchecked");
+
+                setRestoreButton();
+            }
+        };
+
+        ContactListAdapter contactListAdapter = new ContactListAdapter(getContext(), vCards, vCardClickListener);
+        recyclerView.setAdapter(contactListAdapter);
+        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+        return view;
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        menu.findItem(R.id.action_search).setVisible(false);
+        menu.findItem(R.id.action_sync_account).setVisible(false);
+        menu.findItem(R.id.action_sort).setVisible(false);
+        menu.findItem(R.id.action_switch_view).setVisible(false);
+    }
+
+    static class ContactItemViewHolder extends RecyclerView.ViewHolder {
+        private ImageView badge;
+        private CheckedTextView name;
+
+        ContactItemViewHolder(View itemView) {
+            super(itemView);
+
+            badge = (ImageView) itemView.findViewById(R.id.contactlist_item_icon);
+            name = (CheckedTextView) itemView.findViewById(R.id.contactlist_item_name);
+
+            itemView.setTag(this);
+        }
+
+        public void setVCardListener(View.OnClickListener onClickListener) {
+            itemView.setOnClickListener(onClickListener);
+        }
+
+        public ImageView getBadge() {
+            return badge;
+        }
+
+        public void setBadge(ImageView badge) {
+            this.badge = badge;
+        }
+
+        public CheckedTextView getName() {
+            return name;
+        }
+
+        public void setName(CheckedTextView name) {
+            this.name = name;
+        }
+    }
+
+    private void importContacts(ContactAccount account) {
+        int[] intArray = new int[checkedVCards.size()];
+
+        int i = 0;
+        for (Integer checkedVCard : checkedVCards) {
+            intArray[i] = checkedVCard;
+            i++;
+        }
+
+        PersistableBundleCompat bundle = new PersistableBundleCompat();
+        bundle.putString(ContactsImportJob.ACCOUNT_NAME, account.name);
+        bundle.putString(ContactsImportJob.ACCOUNT_TYPE, account.type);
+        bundle.putString(ContactsImportJob.VCARD_FILE_PATH, getFile().getStoragePath());
+        bundle.putIntArray(ContactsImportJob.CHECKED_ITEMS_ARRAY, intArray);
+
+        new JobRequest.Builder(ContactsImportJob.TAG)
+                .setExtras(bundle)
+                .setExecutionWindow(3_000L, 10_000L)
+                .setRequiresCharging(false)
+                .setPersisted(false)
+                .setUpdateCurrent(false)
+                .build()
+                .schedule();
+
+
+        Snackbar.make(recyclerView, R.string.contacts_preferences_import_scheduled, Snackbar.LENGTH_LONG).show();
+    }
+
+    private void getAccountForImport() {
+        final ArrayList<ContactAccount> accounts = new ArrayList<>();
+
+        // add local one
+        accounts.add(new ContactAccount("Local contacts", null, null));
+
+        Cursor cursor = null;
+        try {
+            cursor = getContext().getContentResolver().query(ContactsContract.RawContacts.CONTENT_URI,
+                    new String[]{ContactsContract.RawContacts.ACCOUNT_NAME, ContactsContract.RawContacts.ACCOUNT_TYPE},
+                    null,
+                    null,
+                    null);
+
+            if (cursor != null && cursor.getCount() > 0) {
+                while (cursor.moveToNext()) {
+                    String name = cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_NAME));
+                    String type = cursor.getString(cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE));
+
+                    ContactAccount account = new ContactAccount(name, name, type);
+
+                    if (!accounts.contains(account)) {
+                        accounts.add(account);
+                    }
+                }
+
+                cursor.close();
+            }
+        } catch (Exception e) {
+            Log_OC.d(TAG, e.getMessage());
+        } finally {
+            cursor.close();
+        }
+
+        if (accounts.size() == 1) {
+            importContacts(accounts.get(0));
+        } else {
+
+            ArrayAdapter adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, accounts);
+            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+            builder.setTitle(R.string.contactlist_account_chooser_title)
+                    .setAdapter(adapter, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            importContacts(accounts.get(which));
+                        }
+                    }).show();
+        }
+    }
+
+    private boolean checkAndAskForContactsWritePermission() {
+        // check permissions
+        if (!PermissionUtil.checkSelfPermission(getContext(), Manifest.permission.WRITE_CONTACTS)) {
+            PermissionUtil.requestWriteContactPermission(this);
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+        if (requestCode == PermissionUtil.PERMISSIONS_WRITE_CONTACTS) {
+            for (int index = 0; index < permissions.length; index++) {
+                if (Manifest.permission.WRITE_CONTACTS.equalsIgnoreCase(permissions[index])) {
+                    if (grantResults[index] >= 0) {
+                        getAccountForImport();
+                    } else {
+                        Snackbar.make(getView(), R.string.contactlist_no_permission, Snackbar.LENGTH_LONG).show();
+                    }
+                    break;
+                }
+            }
+        }
+    }
+
+    private class ContactAccount {
+        private String displayName;
+        private String name;
+        private String type;
+
+        ContactAccount(String displayName, String name, String type) {
+            this.displayName = displayName;
+            this.name = name;
+            this.type = type;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ContactAccount) {
+                ContactAccount other = (ContactAccount) obj;
+                return this.name.equalsIgnoreCase(other.name) && this.type.equalsIgnoreCase(other.type);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return displayName;
+        }
+    }
+}
+
+class ContactListAdapter extends RecyclerView.Adapter<ContactListFragment.ContactItemViewHolder> {
+    private List<VCard> vCards;
+    private Context context;
+    private OnVCardClickListener vCardClickListener;
+
+    ContactListAdapter(Context context, List<VCard> vCards, OnVCardClickListener vCardClickListener) {
+        this.vCards = vCards;
+        this.context = context;
+        this.vCardClickListener = vCardClickListener;
+    }
+
+
+    @Override
+    public ContactListFragment.ContactItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(context).inflate(R.layout.contactlist_list_item, parent, false);
+
+        return new ContactListFragment.ContactItemViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final ContactListFragment.ContactItemViewHolder holder, final int position) {
+        final VCard vcard = vCards.get(holder.getAdapterPosition());
+
+        if (vcard != null) {
+            // name
+            StructuredName name = vcard.getStructuredName();
+            if (name != null) {
+                String first = (name.getGiven() == null) ? "" : name.getGiven() + " ";
+                String last = (name.getFamily() == null) ? "" : name.getFamily();
+                holder.getName().setText(first + last);
+            } else {
+                holder.getName().setText("");
+            }
+
+            // photo
+            if (vcard.getPhotos().size() > 0) {
+                byte[] data = vcard.getPhotos().get(0).getData();
+
+                Bitmap thumbnail = BitmapFactory.decodeByteArray(data, 0, data.length);
+                RoundedBitmapDrawable drawable = BitmapUtils.bitmapToCircularBitmapDrawable(context.getResources(),
+                        thumbnail);
+
+                holder.getBadge().setImageDrawable(drawable);
+            } else {
+                try {
+                    holder.getBadge().setImageDrawable(
+                            TextDrawable.createNamedAvatar(
+                                    holder.getName().getText().toString(),
+                                    context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius)
+                            )
+                    );
+                } catch (Exception e) {
+                    holder.getBadge().setImageResource(R.drawable.ic_user);
+                }
+            }
+
+            // Checkbox
+            holder.setVCardListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    holder.getName().setChecked(!holder.getName().isChecked());
+
+                    if (holder.getName().isChecked()) {
+                        vCardClickListener.onVCardCheck(holder.getAdapterPosition());
+                    } else {
+                        vCardClickListener.onVCardUncheck(holder.getAdapterPosition());
+                    }
+                }
+            });
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return vCards.size();
+    }
+
+    interface OnVCardClickListener {
+        void onVCardCheck(int position);
+
+        void onVCardUncheck(int position);
+    }
+}

+ 372 - 0
src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java

@@ -0,0 +1,372 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Tobias Kaminsky
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud GmbH.
+ * <p>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ * <p>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ * <p>
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.activity;
+
+import android.Manifest;
+import android.accounts.Account;
+import android.app.DatePickerDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.SwitchCompat;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.evernote.android.job.JobManager;
+import com.evernote.android.job.JobRequest;
+import com.evernote.android.job.util.support.PersistableBundleCompat;
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.db.PreferenceManager;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.ContactsBackupJob;
+import com.owncloud.android.ui.fragment.FileFragment;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.PermissionUtil;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * This activity shows all settings for contact backup/restore
+ */
+
+public class ContactsPreferenceActivity extends FileActivity implements FileFragment.ContainerActivity {
+    public static final String TAG = ContactsPreferenceActivity.class.getSimpleName();
+
+    public static final String PREFERENCE_CONTACTS_AUTOMATIC_BACKUP = "PREFERENCE_CONTACTS_AUTOMATIC_BACKUP";
+    public static final String PREFERENCE_CONTACTS_LAST_BACKUP = "PREFERENCE_CONTACTS_LAST_BACKUP";
+
+    private SwitchCompat backupSwitch;
+    private SharedPreferences sharedPreferences;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.contacts_preference);
+
+        // setup toolbar
+        setupToolbar();
+
+        // setup drawer
+        setupDrawer(R.id.nav_contacts);
+
+        getSupportActionBar().setTitle(R.string.actionbar_contacts);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+
+        backupSwitch = (SwitchCompat) findViewById(R.id.contacts_automatic_backup);
+        backupSwitch.setChecked(sharedPreferences.getBoolean(PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, false));
+
+        backupSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                if (isChecked &&
+                        checkAndAskForContactsReadPermission(PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC)) {
+                    // store value
+                    setAutomaticBackup(backupSwitch, true);
+
+                    // enable daily job
+                    startContactBackupJob(getAccount());
+                } else {
+                    setAutomaticBackup(backupSwitch, false);
+
+                    // cancel pending jobs
+                    cancelContactBackupJob(getBaseContext());
+                }
+            }
+        });
+
+        // display last backup
+        TextView lastBackup = (TextView) findViewById(R.id.contacts_last_backup_timestamp);
+        Long lastBackupTimestamp = sharedPreferences.getLong(PREFERENCE_CONTACTS_LAST_BACKUP, -1);
+
+        if (lastBackupTimestamp == -1) {
+            lastBackup.setText(R.string.contacts_preference_backup_never);
+        } else {
+            lastBackup.setText(DisplayUtils.getRelativeTimestamp(getBaseContext(), lastBackupTimestamp));
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+
+        if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_AUTOMATIC) {
+            for (int index = 0; index < permissions.length; index++) {
+                if (Manifest.permission.READ_CONTACTS.equalsIgnoreCase(permissions[index])) {
+                    if (grantResults[index] >= 0) {
+                        setAutomaticBackup(backupSwitch, true);
+                    } else {
+                        setAutomaticBackup(backupSwitch, false);
+                    }
+
+                    break;
+                }
+            }
+        }
+
+        if (requestCode == PermissionUtil.PERMISSIONS_READ_CONTACTS_MANUALLY) {
+            for (int index = 0; index < permissions.length; index++) {
+                if (Manifest.permission.READ_CONTACTS.equalsIgnoreCase(permissions[index])) {
+                    if (grantResults[index] >= 0) {
+                        startContactsBackupJob();
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    public void backupContacts(View v) {
+        if (checkAndAskForContactsReadPermission(PermissionUtil.PERMISSIONS_READ_CONTACTS_MANUALLY)) {
+            startContactsBackupJob();
+        }
+    }
+
+    private void startContactsBackupJob() {
+        PersistableBundleCompat bundle = new PersistableBundleCompat();
+        bundle.putString(ContactsBackupJob.ACCOUNT, getAccount().name);
+        bundle.putBoolean(ContactsBackupJob.FORCE, true);
+
+        new JobRequest.Builder(ContactsBackupJob.TAG)
+                .setExtras(bundle)
+                .setExecutionWindow(3_000L, 10_000L)
+                .setRequiresCharging(false)
+                .setPersisted(false)
+                .setUpdateCurrent(false)
+                .build()
+                .schedule();
+
+        Snackbar.make(findViewById(R.id.contacts_linear_layout), R.string.contacts_preferences_backup_scheduled,
+                Snackbar.LENGTH_LONG).show();
+    }
+
+    private void setAutomaticBackup(SwitchCompat backupSwitch, boolean bool) {
+        backupSwitch.setChecked(bool);
+        SharedPreferences.Editor editor = sharedPreferences.edit();
+        editor.putBoolean(PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, bool);
+        editor.apply();
+    }
+
+    private boolean checkAndAskForContactsReadPermission(final int permission) {
+        // check permissions
+        if ((PermissionUtil.checkSelfPermission(this, Manifest.permission.READ_CONTACTS))) {
+            return true;
+        } else {
+            // Check if we should show an explanation
+            if (PermissionUtil.shouldShowRequestPermissionRationale(ContactsPreferenceActivity.this,
+                    android.Manifest.permission.READ_CONTACTS)) {
+                // Show explanation to the user and then request permission
+                Snackbar snackbar = Snackbar.make(findViewById(R.id.contacts_linear_layout), R.string.contacts_read_permission,
+                        Snackbar.LENGTH_INDEFINITE)
+                        .setAction(R.string.common_ok, new View.OnClickListener() {
+                            @Override
+                            public void onClick(View v) {
+                                PermissionUtil.requestReadContactPermission(ContactsPreferenceActivity.this, permission);
+                            }
+                        });
+
+                DisplayUtils.colorSnackbar(this, snackbar);
+
+                snackbar.show();
+
+                return false;
+            } else {
+                // No explanation needed, request the permission.
+                PermissionUtil.requestReadContactPermission(ContactsPreferenceActivity.this, permission);
+
+                return false;
+            }
+        }
+    }
+
+    public void openDate(View v) {
+        String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
+        OCFile backupFolder = getStorageManager().getFileByPath(backupFolderString);
+
+        Vector<OCFile> backupFiles = getStorageManager().getFolderContent(backupFolder, false);
+
+        Collections.sort(backupFiles, new Comparator<OCFile>() {
+            @Override
+            public int compare(OCFile o1, OCFile o2) {
+                if (o1.getModificationTimestamp() == o2.getModificationTimestamp()) {
+                    return 0;
+                }
+
+                if (o1.getModificationTimestamp() > o2.getModificationTimestamp()) {
+                    return 1;
+                } else {
+                    return -1;
+                }
+            }
+        });
+
+        Calendar cal = Calendar.getInstance();
+        int year = cal.get(Calendar.YEAR);
+        int month = cal.get(Calendar.MONTH) + 1;
+        int day = cal.get(Calendar.DAY_OF_MONTH);
+
+        DatePickerDialog.OnDateSetListener dateSetListener = new DatePickerDialog.OnDateSetListener() {
+            @Override
+            public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
+                String backupFolderString = getResources().getString(R.string.contacts_backup_folder) + OCFile.PATH_SEPARATOR;
+                OCFile backupFolder = getStorageManager().getFileByPath(backupFolderString);
+                Vector<OCFile> backupFiles = getStorageManager().getFolderContent(backupFolder, false);
+
+                // find file with modification with date and time between 00:00 and 23:59
+                // if more than one file exists, take oldest
+                Calendar date = Calendar.getInstance();
+                date.set(year, month, dayOfMonth);
+
+                // start
+                date.set(Calendar.HOUR, 0);
+                date.set(Calendar.MINUTE, 0);
+                date.set(Calendar.SECOND, 1);
+                date.set(Calendar.MILLISECOND, 0);
+                date.set(Calendar.AM_PM, Calendar.AM);
+                Long start = date.getTimeInMillis();
+
+                // end
+                date.set(Calendar.HOUR, 23);
+                date.set(Calendar.MINUTE, 59);
+                date.set(Calendar.SECOND, 59);
+                Long end = date.getTimeInMillis();
+
+                OCFile backupToRestore = null;
+
+                for (OCFile file : backupFiles) {
+                    if (start < file.getModificationTimestamp() && end > file.getModificationTimestamp()) {
+                        if (backupToRestore == null) {
+                            backupToRestore = file;
+                        } else if (backupToRestore.getModificationTimestamp() < file.getModificationTimestamp()) {
+                            backupToRestore = file;
+                        }
+                    }
+                }
+
+                if (backupToRestore != null) {
+                    Fragment contactListFragment = ContactListFragment.newInstance(backupToRestore, getAccount());
+
+                    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+                    transaction.replace(R.id.contacts_linear_layout, contactListFragment);
+                    transaction.commit();
+                } else {
+                    Toast.makeText(ContactsPreferenceActivity.this, R.string.contacts_preferences_no_file_found,
+                            Toast.LENGTH_SHORT).show();
+                }
+            }
+        };
+
+        DatePickerDialog datePickerDialog = new DatePickerDialog(this, dateSetListener, year, month, day);
+        datePickerDialog.getDatePicker().setMaxDate(backupFiles.lastElement().getModificationTimestamp());
+        datePickerDialog.getDatePicker().setMinDate(backupFiles.firstElement().getModificationTimestamp());
+
+        datePickerDialog.show();
+    }
+
+    public static void startContactBackupJob(Account account) {
+        Log_OC.d(TAG, "start daily contacts backup job");
+
+        PersistableBundleCompat bundle = new PersistableBundleCompat();
+        bundle.putString(ContactsBackupJob.ACCOUNT, account.name);
+
+        new JobRequest.Builder(ContactsBackupJob.TAG)
+                .setExtras(bundle)
+                .setRequiresCharging(false)
+                .setPersisted(true)
+                .setUpdateCurrent(true)
+                .setPeriodic(24 * 60 * 60 * 1000)
+                .build()
+                .schedule();
+    }
+
+    public static void cancelContactBackupJob(Context context) {
+        Log_OC.d(TAG, "disabling contacts backup job");
+
+        JobManager jobManager = JobManager.create(context);
+        Set<JobRequest> jobs = jobManager.getAllJobRequestsForTag(ContactsBackupJob.TAG);
+
+        for (JobRequest jobRequest : jobs) {
+            jobManager.cancel(jobRequest.getJobId());
+        }
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean retval;
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                if (isDrawerOpen()) {
+                    closeDrawer();
+                } else {
+                    openDrawer();
+                }
+                retval = true;
+                break;
+
+            default:
+                retval = super.onOptionsItemSelected(item);
+                break;
+        }
+        return retval;
+    }
+
+    @Override
+    public void showFiles(boolean onDeviceOnly) {
+        super.showFiles(onDeviceOnly);
+        Intent fileDisplayActivity = new Intent(getApplicationContext(), FileDisplayActivity.class);
+        fileDisplayActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(fileDisplayActivity);
+    }
+
+    @Override
+    public void showDetails(OCFile file) {
+        // not needed
+    }
+
+    @Override
+    public void onBrowsedDownTo(OCFile folder) {
+        // not needed
+    }
+
+    @Override
+    public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) {
+        // not needed
+    }
+}

+ 42 - 12
src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -69,12 +69,14 @@ import com.owncloud.android.ui.events.ChangeMenuEvent;
 import com.owncloud.android.ui.events.DummyDrawerEvent;
 import com.owncloud.android.ui.events.MenuItemClickEvent;
 import com.owncloud.android.ui.events.SearchEvent;
+import com.owncloud.android.ui.fragment.OCFileListFragment;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.svg.MenuSimpleTarget;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
 import org.greenrobot.eventbus.ThreadMode;
+import org.parceler.Parcels;
 
 import java.util.ArrayList;
 
@@ -347,6 +349,10 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
             navigationView.getMenu().removeItem(R.id.nav_shared);
         }
 
+        if (!getResources().getBoolean(R.bool.contacts_backup)) {
+            navigationView.getMenu().removeItem(R.id.nav_contacts);
+        }
+
         if (AccountUtils.hasSearchSupport(account)) {
             if (!getResources().getBoolean(R.bool.recently_added_enabled)) {
                 navigationView.getMenu().removeItem(R.id.nav_recently_added);
@@ -401,12 +407,16 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
             case R.id.nav_favorites:
                 menuItem.setChecked(true);
                 mCheckedMenuItem = menuItem.getItemId();
-                EventBus.getDefault().post(new SearchEvent("", SearchOperation.SearchType.FAVORITE_SEARCH,
-                        SearchEvent.UnsetType.NO_UNSET));
+
+                switchToSearchFragment(new SearchEvent("", SearchOperation.SearchType.FAVORITE_SEARCH,
+                        SearchEvent.UnsetType.NO_UNSET), menuItem);
                 break;
             case R.id.nav_photos:
-                EventBus.getDefault().post(new SearchEvent("image/%",
-                        SearchOperation.SearchType.CONTENT_TYPE_SEARCH, SearchEvent.UnsetType.NO_UNSET));
+                menuItem.setChecked(true);
+                mCheckedMenuItem = menuItem.getItemId();
+
+                switchToSearchFragment(new SearchEvent("image/%", SearchOperation.SearchType.CONTENT_TYPE_SEARCH,
+                        SearchEvent.UnsetType.NO_UNSET), menuItem);
                 break;
             case R.id.nav_on_device:
                 menuItem.setChecked(true);
@@ -424,10 +434,18 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
                 activityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                 startActivity(activityIntent);
                 break;
+            case R.id.nav_notifications:
+                Intent notificationsIntent = new Intent(getApplicationContext(), NotificationsActivity.class);
+                startActivity(notificationsIntent);
+                break;
             case R.id.nav_folder_sync:
                 Intent folderSyncIntent = new Intent(getApplicationContext(), FolderSyncActivity.class);
                 startActivity(folderSyncIntent);
                 break;
+            case R.id.nav_contacts:
+                Intent contactsIntent = new Intent(getApplicationContext(), ContactsPreferenceActivity.class);
+                startActivity(contactsIntent);
+                break;
             case R.id.nav_settings:
                 Intent settingsIntent = new Intent(getApplicationContext(), Preferences.class);
                 startActivity(settingsIntent);
@@ -448,26 +466,30 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
             case R.id.nav_recently_added:
                 menuItem.setChecked(true);
                 mCheckedMenuItem = menuItem.getItemId();
-                EventBus.getDefault().post(new SearchEvent("%", SearchOperation.SearchType.CONTENT_TYPE_SEARCH,
-                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR));
+
+                switchToSearchFragment(new SearchEvent("%",SearchOperation.SearchType.CONTENT_TYPE_SEARCH,
+                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem);
                 break;
             case R.id.nav_recently_modified:
                 menuItem.setChecked(true);
                 mCheckedMenuItem = menuItem.getItemId();
-                EventBus.getDefault().post(new SearchEvent("", SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH,
-                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR));
+
+                switchToSearchFragment(new SearchEvent("", SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH,
+                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem);
                 break;
             case R.id.nav_shared:
                 menuItem.setChecked(true);
                 mCheckedMenuItem = menuItem.getItemId();
-                EventBus.getDefault().post(new SearchEvent("", SearchOperation.SearchType.SHARED_SEARCH,
-                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR));
+
+                switchToSearchFragment(new SearchEvent("", SearchOperation.SearchType.SHARED_SEARCH,
+                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem);
                 break;
             case R.id.nav_videos:
                 menuItem.setChecked(true);
                 mCheckedMenuItem = menuItem.getItemId();
-                EventBus.getDefault().post(new SearchEvent("video/%", SearchOperation.SearchType.CONTENT_TYPE_SEARCH,
-                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR));
+
+                switchToSearchFragment(new SearchEvent("video/%", SearchOperation.SearchType.CONTENT_TYPE_SEARCH,
+                        SearchEvent.UnsetType.UNSET_BOTTOM_NAV_BAR), menuItem);
                 break;
             case MENU_ITEM_EXTERNAL_LINK:
                 // external link clicked
@@ -481,6 +503,14 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
         }
     }
 
+    private void switchToSearchFragment(SearchEvent event, MenuItem menuItem) {
+        Intent recentlyAddedIntent = new Intent(getBaseContext(), FileDisplayActivity.class);
+        recentlyAddedIntent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(event));
+        recentlyAddedIntent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
+        recentlyAddedIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(recentlyAddedIntent);
+    }
+
     /**
      * show the file list to the user.
      *

+ 15 - 0
src/main/java/com/owncloud/android/ui/activity/FileActivity.java

@@ -28,6 +28,7 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -41,6 +42,8 @@ import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
 import com.owncloud.android.datamodel.OCFile;
+
+import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.files.services.FileDownloader;
 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
 import com.owncloud.android.files.services.FileUploader;
@@ -161,6 +164,8 @@ public abstract class FileActivity extends DrawerActivity
 
         setAccount(account, savedInstanceState != null);
 
+        checkContactsBackupJob();
+
         mOperationsServiceConnection = new OperationsServiceConnection();
         bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection,
                 Context.BIND_AUTO_CREATE);
@@ -240,6 +245,16 @@ public abstract class FileActivity extends DrawerActivity
         }
     }
 
+    private void checkContactsBackupJob() {
+        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+
+        if (sharedPreferences.getBoolean(ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, false)) {
+            ContactsPreferenceActivity.startContactBackupJob(getAccount());
+        } else {
+            ContactsPreferenceActivity.cancelContactBackupJob(getBaseContext());
+        }
+    }
+
 
     /**
      * Getter for the main {@link OCFile} handled by the activity.

+ 53 - 13
src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -82,6 +82,7 @@ import com.owncloud.android.operations.UploadFileOperation;
 import com.owncloud.android.services.observer.FileObserverService;
 import com.owncloud.android.syncadapter.FileSyncAdapter;
 import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
+import com.owncloud.android.ui.events.TokenPushEvent;
 import com.owncloud.android.ui.fragment.ExtendedListFragment;
 import com.owncloud.android.ui.fragment.FileDetailFragment;
 import com.owncloud.android.ui.fragment.FileFragment;
@@ -96,8 +97,11 @@ import com.owncloud.android.ui.preview.PreviewVideoActivity;
 import com.owncloud.android.utils.DataHolderUtil;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.ErrorMessageAdapter;
+import com.owncloud.android.utils.MimeTypeUtil;
 import com.owncloud.android.utils.PermissionUtil;
 
+import org.greenrobot.eventbus.EventBus;
+
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -131,6 +135,8 @@ public class FileDisplayActivity extends HookActivity
 
     public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";
 
+    public static final String DRAWER_MENU_ID = "DRAWER_MENU_ID";
+
     public static final int REQUEST_CODE__SELECT_CONTENT_FROM_APPS = REQUEST_CODE__LAST_SHARED + 1;
     public static final int REQUEST_CODE__SELECT_FILES_FROM_FILE_SYSTEM = REQUEST_CODE__LAST_SHARED + 2;
     public static final int REQUEST_CODE__MOVE_FILES = REQUEST_CODE__LAST_SHARED + 3;
@@ -141,7 +147,7 @@ public class FileDisplayActivity extends HookActivity
     private static final String TAG = FileDisplayActivity.class.getSimpleName();
 
     private static final String TAG_LIST_OF_FILES = "LIST_OF_FILES";
-    private static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
+    public static final String TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT";
 
     private OCFile mWaitingToPreview;
 
@@ -170,11 +176,9 @@ public class FileDisplayActivity extends HookActivity
 
         /// Load of saved instance state
         if (savedInstanceState != null) {
-            mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(
-                    FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
+            mWaitingToPreview = savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW);
             mSyncInProgress = savedInstanceState.getBoolean(KEY_SYNC_IN_PROGRESS);
-            mWaitingToSend = (OCFile) savedInstanceState.getParcelable(
-                    FileDisplayActivity.KEY_WAITING_TO_SEND);
+            mWaitingToSend = savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_SEND);
             searchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
         } else {
             mWaitingToPreview = null;
@@ -222,7 +226,7 @@ public class FileDisplayActivity extends HookActivity
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
 
-        if (PermissionUtil.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+        if (!PermissionUtil.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
             // Check if we should show an explanation
             if (PermissionUtil.shouldShowRequestPermissionRationale(this,
                     Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
@@ -245,12 +249,18 @@ public class FileDisplayActivity extends HookActivity
             }
         }
 
-        if (savedInstanceState == null) {
+        if (getIntent().getParcelableExtra(OCFileListFragment.SEARCH_EVENT) != null) {
+            switchToSearchFragment();
+
+            int menuId = getIntent().getIntExtra(DRAWER_MENU_ID, -1);
+            if (menuId != -1) {
+                setupDrawer(menuId);
+            }
+        } else if (savedInstanceState == null) {
             createMinFragments();
+            refreshList(true);
         }
 
-        refreshList(true);
-
         setIndeterminate(mSyncInProgress);
         // always AFTER setContentView(...) in onCreate(); to work around bug in its implementation
 
@@ -315,7 +325,8 @@ public class FileDisplayActivity extends HookActivity
                 if (grantResults.length > 0
                         && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                     // permission was granted
-                    startSynchronization();
+                    EventBus.getDefault().post(new TokenPushEvent());
+                    refreshList(true);
                     // toggle on is save since this is the only scenario this code gets accessed
                 } else {
                     // permission denied --> do nothing
@@ -323,6 +334,8 @@ public class FileDisplayActivity extends HookActivity
                 }
                 return;
             }
+            default:
+                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         }
     }
 
@@ -377,6 +390,19 @@ public class FileDisplayActivity extends HookActivity
         }
     }
 
+    private void switchToSearchFragment() {
+        OCFileListFragment listOfFiles = new OCFileListFragment();
+        Bundle args = new Bundle();
+
+        args.putParcelable(OCFileListFragment.SEARCH_EVENT,
+                getIntent().getParcelableExtra(OCFileListFragment.SEARCH_EVENT));
+
+        listOfFiles.setArguments(args);
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES);
+        transaction.commit();
+    }
+
     private void createMinFragments() {
         OCFileListFragment listOfFiles = new OCFileListFragment();
         Bundle args = new Bundle();
@@ -406,7 +432,9 @@ public class FileDisplayActivity extends HookActivity
                 updateActionBarTitleAndHomeButton(file);
             } else {
                 cleanSecondFragment();
-                if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
+                if (file.isDown() && MimeTypeUtil.isVCard(file.getMimetype())){
+                    startContactListFragment(file);
+                } else if (file.isDown() && PreviewTextFragment.canBePreviewed(file)) {
                     startTextPreview(file);
                 }
             }
@@ -563,6 +591,9 @@ public class FileDisplayActivity extends HookActivity
                         if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) {
                             startMediaPreview(mWaitingToPreview, 0, true);
                             detailsFragmentChanged = true;
+                        } else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimetype())){
+                            startContactListFragment(mWaitingToPreview);
+                            detailsFragmentChanged = true;
                         } else if (PreviewTextFragment.canBePreviewed(mWaitingToPreview)) {
                             startTextPreview(mWaitingToPreview);
                             detailsFragmentChanged = true;
@@ -892,7 +923,7 @@ public class FileDisplayActivity extends HookActivity
     /**
      * Request the operation for moving the file/folder from one path to another
      *
-     * @param data       Intent received
+     * @param data Intent received
      */
     private void requestMoveOperation(Intent data) {
         OCFile folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
@@ -903,7 +934,7 @@ public class FileDisplayActivity extends HookActivity
     /**
      * Request the operation for copying the file/folder from one path to another
      *
-     * @param data       Intent received
+     * @param data Intent received
      */
     private void requestCopyOperation(Intent data) {
         OCFile folderToMoveAt = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER);
@@ -1901,6 +1932,15 @@ public class FileDisplayActivity extends HookActivity
         setFile(file);
     }
 
+    public void startContactListFragment(OCFile file) {
+        Fragment contactListFragment = ContactListFragment.newInstance(file, getAccount());
+
+        setSecondFragment(contactListFragment);
+        updateFragmentsVisibility(true);
+        updateActionBarTitleAndHomeButton(file);
+        setFile(file);
+    }
+
     /**
      * Requests the download of the received {@link OCFile} , updates the UI
      * to monitor the download progress and prepares the activity to preview

+ 286 - 0
src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java

@@ -0,0 +1,286 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Andy Scherzinger
+ * @author Mario Danic
+ * Copyright (C) 2017 Andy Scherzinger
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.activity;
+
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.notifications.GetRemoteNotificationsOperation;
+import com.owncloud.android.lib.resources.notifications.models.Notification;
+import com.owncloud.android.ui.adapter.NotificationListAdapter;
+
+import java.io.IOException;
+import java.util.List;
+
+import butterknife.BindString;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Unbinder;
+
+/**
+ * Activity displaying all server side stored activity items.
+ */
+public class NotificationsActivity extends FileActivity {
+
+    private static final String TAG = NotificationsActivity.class.getSimpleName();
+
+    @BindView(R.id.empty_list_view)
+    public LinearLayout emptyContentContainer;
+
+    @BindView(R.id.swipe_containing_list)
+    public SwipeRefreshLayout swipeListRefreshLayout;
+
+    @BindView(R.id.swipe_containing_empty)
+    public SwipeRefreshLayout swipeEmptyListRefreshLayout;
+
+    @BindView(R.id.empty_list_view_text)
+    public TextView emptyContentMessage;
+
+    @BindView(R.id.empty_list_view_headline)
+    public TextView emptyContentHeadline;
+
+    @BindView(R.id.empty_list_icon)
+    public ImageView emptyContentIcon;
+
+    @BindView(R.id.empty_list_progress)
+    public ProgressBar emptyContentProgressBar;
+
+    @BindView(android.R.id.list)
+    public RecyclerView recyclerView;
+
+    @BindString(R.string.notifications_no_results_headline)
+    public String noResultsHeadline;
+
+    @BindString(R.string.notifications_no_results_message)
+    public String noResultsMessage;
+
+    private Unbinder unbinder;
+
+    private NotificationListAdapter adapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        Log_OC.v(TAG, "onCreate() start");
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.notifications_layout);
+        unbinder = ButterKnife.bind(this);
+
+        // setup toolbar
+        setupToolbar();
+
+        // setup drawer
+        setupDrawer(R.id.nav_notifications);
+        getSupportActionBar().setTitle(getString(R.string.drawer_item_notifications));
+
+        swipeListRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                setLoadingMessage();
+                fetchAndSetData();
+            }
+        });
+
+        swipeEmptyListRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                setLoadingMessage();
+                fetchAndSetData();
+
+            }
+        });
+
+        setupContent();
+    }
+
+    public void onDestroy() {
+        super.onDestroy();
+        unbinder.unbind();
+    }
+
+    @Override
+    public void showFiles(boolean onDeviceOnly) {
+        super.showFiles(onDeviceOnly);
+        Intent i = new Intent(getApplicationContext(), FileDisplayActivity.class);
+        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        startActivity(i);
+    }
+
+    /**
+     * sets up the UI elements and loads all activity items.
+     */
+    private void setupContent() {
+        emptyContentIcon.setImageResource(R.drawable.ic_notification_light_grey);
+        setLoadingMessage();
+
+        adapter = new NotificationListAdapter(this);
+        recyclerView.setAdapter(adapter);
+
+        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
+                recyclerView.getContext(),
+                layoutManager.getOrientation()
+        );
+
+        recyclerView.setLayoutManager(layoutManager);
+        recyclerView.addItemDecoration(dividerItemDecoration);
+
+        fetchAndSetData();
+    }
+
+    private void populateList(List<Notification> notifications) {
+        adapter.setNotificationItems(notifications);
+    }
+
+    private void fetchAndSetData() {
+        final Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext());
+        final Context context = MainApp.getAppContext();
+
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                OwnCloudAccount ocAccount;
+                try {
+                    ocAccount = new OwnCloudAccount(currentAccount, context);
+                    OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, MainApp.getAppContext());
+                    mClient.setOwnCloudVersion(AccountUtils.getServerVersion(currentAccount));
+
+                    RemoteOperation getRemoteNotificationOperation = new GetRemoteNotificationsOperation();
+                    final RemoteOperationResult result = getRemoteNotificationOperation.execute(mClient);
+
+                    if (result.isSuccess() && result.getNotificationData() != null) {
+                        final List<Notification> notifications = result.getNotificationData();
+
+                        runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                if (notifications.size() > 0) {
+                                    populateList(notifications);
+                                    swipeEmptyListRefreshLayout.setVisibility(View.GONE);
+                                    swipeListRefreshLayout.setVisibility(View.VISIBLE);
+                                } else {
+                                    setEmptyContent(noResultsHeadline, noResultsMessage);
+                                }
+                            }
+                        });
+                    } else {
+                        Log_OC.d(TAG, result.getLogMessage());
+                        // show error
+                        runOnUiThread(new Runnable() {
+                            @Override
+                            public void run() {
+                                setEmptyContent(noResultsHeadline, result.getLogMessage());
+                            }
+                        });
+                    }
+
+                    hideRefreshLayoutLoader();
+                } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
+                    Log_OC.e(TAG, "Account not found", e);
+                } catch (IOException e) {
+                    Log_OC.e(TAG, "IO error", e);
+                } catch (OperationCanceledException e) {
+                    Log_OC.e(TAG, "Operation has been canceled", e);
+                } catch (AuthenticatorException e) {
+                    Log_OC.e(TAG, "Authentication Exception", e);
+                }
+            }
+        });
+
+        t.start();
+
+    }
+
+    private void hideRefreshLayoutLoader() {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                swipeListRefreshLayout.setRefreshing(false);
+                swipeEmptyListRefreshLayout.setRefreshing(false);
+            }
+        });
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean retval = true;
+
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                if (isDrawerOpen()) {
+                    closeDrawer();
+                } else {
+                    openDrawer();
+                }
+                break;
+
+            default:
+                retval = super.onOptionsItemSelected(item);
+                break;
+        }
+
+        return retval;
+    }
+
+    private void setLoadingMessage() {
+        emptyContentHeadline.setText(R.string.file_list_loading);
+        emptyContentMessage.setText("");
+
+        emptyContentIcon.setVisibility(View.GONE);
+        emptyContentProgressBar.setVisibility(View.VISIBLE);
+    }
+
+    private void setEmptyContent(String headline, String message) {
+        if (emptyContentContainer != null && emptyContentMessage != null) {
+            emptyContentHeadline.setText(headline);
+            emptyContentMessage.setText(message);
+
+            emptyContentProgressBar.setVisibility(View.GONE);
+            emptyContentIcon.setVisibility(View.VISIBLE);
+        }
+    }
+}

+ 2 - 1
src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java

@@ -284,7 +284,8 @@ public class UserInfoActivity extends FileActivity {
     }
 
     private void changeAccountPassword(Account account) {
-        Intent updateAccountCredentials = new Intent(UserInfoActivity.this, AuthenticatorActivity.class);
+        // let the user update credentials with one click
+        Intent updateAccountCredentials = new Intent(this, AuthenticatorActivity.class);
         updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
         updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION,
                 AuthenticatorActivity.ACTION_UPDATE_TOKEN);

+ 68 - 5
src/main/java/com/owncloud/android/ui/activity/WhatsNewActivity.java

@@ -37,6 +37,8 @@ import android.support.v4.view.ViewPager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
@@ -74,11 +76,20 @@ public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPa
         mProgress = (ProgressIndicator) findViewById(R.id.progressIndicator);
         mPager = (ViewPager)findViewById(R.id.contentPanel);
         final boolean isBeta = getResources().getBoolean(R.bool.is_beta);
-        FeaturesViewAdapter adapter = new FeaturesViewAdapter(getSupportFragmentManager(),
-                FeatureList.getFiltered(getLastSeenVersionCode(), isFirstRun(), isBeta));
+        String[] urls = getResources().getStringArray(R.array.whatsnew_urls);
+
+        if (urls.length > 0) {
+            FeaturesWebViewAdapter featuresWebViewAdapter = new FeaturesWebViewAdapter(getSupportFragmentManager(),
+                    urls);
+            mProgress.setNumberOfSteps(featuresWebViewAdapter.getCount());
+            mPager.setAdapter(featuresWebViewAdapter);
+        } else {
+            FeaturesViewAdapter featuresViewAdapter = new FeaturesViewAdapter(getSupportFragmentManager(),
+                    FeatureList.getFiltered(getLastSeenVersionCode(), isFirstRun(), isBeta));
+            mProgress.setNumberOfSteps(featuresViewAdapter.getCount());
+            mPager.setAdapter(featuresViewAdapter);
+        }
 
-        mProgress.setNumberOfSteps(adapter.getCount());
-        mPager.setAdapter(adapter);
         mPager.addOnPageChangeListener(this);
 
 
@@ -191,6 +202,59 @@ public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPa
         // unused but to be implemented due to abstract parent
     }
 
+    private final class FeaturesWebViewAdapter extends FragmentPagerAdapter {
+        private String[] mWebUrls;
+
+        public FeaturesWebViewAdapter(FragmentManager fm, String[] webUrls) {
+            super(fm);
+            mWebUrls = webUrls;
+        }
+
+        @Override
+        public Fragment getItem(int position) {
+            return FeatureWebFragment.newInstance(mWebUrls[position]);
+        }
+
+        @Override
+        public int getCount() {
+            return mWebUrls.length;
+        }
+    }
+
+    public static class FeatureWebFragment extends Fragment {
+        private String mWebUrl;
+
+        static public FeatureWebFragment newInstance(String webUrl) {
+            FeatureWebFragment f = new FeatureWebFragment();
+            Bundle args = new Bundle();
+            args.putString("url", webUrl);
+            f.setArguments(args);
+            return f;
+        }
+
+        @Override
+        public void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            mWebUrl = getArguments() != null ? (String)getArguments().getString("url") : null;
+        }
+
+        @Nullable
+        @Override
+        public View onCreateView(LayoutInflater inflater,
+                                 @Nullable ViewGroup container,
+                                 @Nullable Bundle savedInstanceState) {
+            View v = inflater.inflate(R.layout.whats_new_webview_element, container, false);
+
+            WebView webView = (WebView) v.findViewById(R.id.whatsNewWebView);
+            webView.getSettings().setJavaScriptEnabled(true);
+            webView.getSettings().setAllowFileAccess(false);
+            webView.setWebViewClient(new WebViewClient());
+            webView.loadUrl(mWebUrl);
+
+            return v;
+        }
+    }
+
     private final class FeaturesViewAdapter extends FragmentPagerAdapter {
 
         private FeatureItem[] mFeatures;
@@ -253,5 +317,4 @@ public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPa
             return v;
         }
     }
-
 }

+ 6 - 1
src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java

@@ -90,6 +90,10 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
             return name;
         }
 
+        public int getGroupItemCount() {
+            return items == null ? 0 : items.length;
+        }
+
         public Comparator<OCUpload> comparator = new Comparator<OCUpload>() {
 
             @Override
@@ -702,7 +706,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
             convertView = inflaInflater.inflate(R.layout.upload_list_group, null);
         }
         TextView tv = (TextView) convertView.findViewById(R.id.uploadListGroupName);
-        tv.setText(group.getGroupName());
+        tv.setText(String.format(mParentActivity.getString(R.string.uploads_view_group_header),
+                group.getGroupName(), group.getGroupItemCount()));
         return convertView;
     }
 

+ 24 - 10
src/main/java/com/owncloud/android/ui/adapter/FileListListAdapter.java

@@ -109,6 +109,17 @@ public class FileListListAdapter extends BaseAdapter {
         new ThumbnailsCacheManager.InitDiskCacheTask().execute();
     }
 
+    public FileListListAdapter(
+            boolean justFolders,
+            Context context,
+            ComponentsGetter transferServiceGetter,
+            OCFileListFragmentInterface OCFileListFragmentInterface,
+            FileDataStorageManager fileDataStorageManager
+    ) {
+        this(justFolders, context, transferServiceGetter, OCFileListFragmentInterface);
+        mStorageManager = fileDataStorageManager;
+    }
+
     @Override
     public boolean areAllItemsEnabled() {
         return true;
@@ -467,21 +478,24 @@ public class FileListListAdapter extends BaseAdapter {
         if (searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
             ArrayList<OCShare> shares = new ArrayList<>();
             for (int i = 0; i < objects.size(); i++) {
-                shares.add((OCShare) objects.get(i));
+                // check type before cast as of long running data fetch it is possible that old result is filled
+                if (objects.get(i) instanceof OCShare) {
+                    OCShare ocShare = (OCShare) objects.get(i);
+
+                    shares.add(ocShare);
+
+                    OCFile ocFile = mStorageManager.getFileByPath(ocShare.getPath());
+                    if (!mFiles.contains(ocFile)) {
+                        mFiles.add(ocFile);
+                    }
+                }
             }
             mStorageManager.saveShares(shares);
-        }
-        for (int i = 0; i < objects.size(); i++) {
-            if (!searchType.equals(ExtendedListFragment.SearchType.SHARED_FILTER)) {
+        } else {
+            for (int i = 0; i < objects.size(); i++) {
                 OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) objects.get(i));
                 searchForLocalFileInDefaultPath(ocFile);
                 mFiles.add(ocFile);
-            } else {
-                OCShare ocShare = (OCShare) objects.get(i);
-                OCFile ocFile = mStorageManager.getFileByPath(ocShare.getPath());
-                if (!mFiles.contains(ocFile)) {
-                    mFiles.add(ocFile);
-                }
             }
         }
 

+ 129 - 0
src/main/java/com/owncloud/android/ui/adapter/NotificationListAdapter.java

@@ -0,0 +1,129 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Andy Scherzinger
+ * Copyright (C) 2016 ownCloud Inc.
+ * <p/>
+ * 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.
+ * <p/>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p/>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.content.Context;
+import android.graphics.drawable.PictureDrawable;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.GenericRequestBuilder;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.model.StreamEncoder;
+import com.bumptech.glide.load.resource.file.FileToStreamDecoder;
+import com.caverock.androidsvg.SVG;
+import com.owncloud.android.R;
+import com.owncloud.android.lib.resources.notifications.models.Notification;
+import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.svg.SvgDecoder;
+import com.owncloud.android.utils.svg.SvgDrawableTranscoder;
+import com.owncloud.android.utils.svg.SvgSoftwareLayerSetter;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This Adapter populates a ListView with all notifications for an account within the app.
+ */
+public class NotificationListAdapter extends RecyclerView.Adapter<NotificationListAdapter.NotificationViewHolder> {
+    private List<Notification> mValues;
+    private Context context;
+
+    public NotificationListAdapter(Context context) {
+        this.mValues = new ArrayList<>();
+        this.context = context;
+    }
+
+    public void setNotificationItems(List<Notification> notificationItems) {
+        mValues.clear();
+        mValues.addAll(notificationItems);
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public NotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_list_item, parent, false);
+        return new NotificationViewHolder(v);
+    }
+
+    @Override
+    public void onBindViewHolder(NotificationViewHolder holder, int position) {
+        Notification notification = mValues.get(position);
+        holder.dateTime.setText(DisplayUtils.getRelativeTimestamp(context, notification.getDatetime().getTime()));
+        holder.subject.setText(notification.getSubject());
+        holder.message.setText(notification.getMessage());
+
+        // Todo set proper action icon (to be clarified how to pick)
+        if (!TextUtils.isEmpty(notification.getIcon())) {
+            downloadIcon(notification.getIcon(), holder.activityIcon);
+        }
+
+    }
+
+    private void downloadIcon(String icon, ImageView itemViewType) {
+        GenericRequestBuilder<Uri, InputStream, SVG, PictureDrawable> requestBuilder = Glide.with(context)
+                .using(Glide.buildStreamModelLoader(Uri.class, context), InputStream.class)
+                .from(Uri.class)
+                .as(SVG.class)
+                .transcode(new SvgDrawableTranscoder(), PictureDrawable.class)
+                .sourceEncoder(new StreamEncoder())
+                .cacheDecoder(new FileToStreamDecoder<>(new SvgDecoder()))
+                .decoder(new SvgDecoder())
+                .placeholder(R.drawable.ic_notification)
+                .error(R.drawable.ic_notification)
+                .animate(android.R.anim.fade_in)
+                .listener(new SvgSoftwareLayerSetter<Uri>());
+
+
+        Uri uri = Uri.parse(icon);
+        requestBuilder
+                .diskCacheStrategy(DiskCacheStrategy.SOURCE)
+                .load(uri)
+                .into(itemViewType);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mValues.size();
+    }
+
+    static class NotificationViewHolder extends RecyclerView.ViewHolder {
+        private final ImageView activityIcon;
+        private final TextView subject;
+        private final TextView message;
+        private final TextView dateTime;
+
+        private NotificationViewHolder(View itemView) {
+            super(itemView);
+            activityIcon = (ImageView) itemView.findViewById(R.id.activity_icon);
+            subject = (TextView) itemView.findViewById(R.id.activity_subject);
+            message = (TextView) itemView.findViewById(R.id.activity_message);
+            dateTime = (TextView) itemView.findViewById(R.id.activity_datetime);
+        }
+    }
+}

+ 11 - 4
src/main/java/com/owncloud/android/ui/events/SearchEvent.java

@@ -21,20 +21,27 @@ package com.owncloud.android.ui.events;
 
 import com.owncloud.android.lib.resources.files.SearchOperation;
 
+import org.parceler.Parcel;
+
 /**
  * Search event
  */
+@Parcel
 public class SearchEvent {
-    public final String searchQuery;
+    public String searchQuery;
 
-    public final SearchOperation.SearchType searchType;
+    public SearchOperation.SearchType searchType;
 
-    public final UnsetType unsetType;
+    public UnsetType unsetType;
 
     public enum UnsetType {
         NO_UNSET,
         UNSET_DRAWER,
-        UNSET_BOTTOM_NAV_BAR;
+        UNSET_BOTTOM_NAV_BAR
+    }
+
+    public SearchEvent() {
+
     }
 
     public SearchEvent(String searchQuery, SearchOperation.SearchType searchType, UnsetType unsetType) {

+ 26 - 0
src/main/java/com/owncloud/android/ui/events/TokenPushEvent.java

@@ -0,0 +1,26 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.events;
+
+/**
+ * Event to send push token where it belongs
+ */
+public class TokenPushEvent {
+}

+ 14 - 4
src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -282,14 +282,24 @@ public class ExtendedListFragment extends Fragment
     }
 
     public boolean onQueryTextChange(final String query) {
-        performSearch(query, false);
-        return true;
+        if (getFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT)
+                instanceof ExtendedListFragment){
+            performSearch(query, false);
+            return true;
+        } else {
+            return false;
+        }
     }
 
     @Override
     public boolean onQueryTextSubmit(String query) {
-        performSearch(query, true);
-        return true;
+        if (getFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_SECOND_FRAGMENT)
+                instanceof ExtendedListFragment){
+            performSearch(query, true);
+            return true;
+        } else {
+            return false;
+        }
     }
 
     private void performSearch(final String query, boolean isSubmit) {

+ 143 - 128
src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java

@@ -28,6 +28,7 @@ import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
@@ -61,6 +62,7 @@ import com.owncloud.android.files.FileMenuFilter;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.utils.Log_OC;
 import com.owncloud.android.lib.resources.files.SearchOperation;
@@ -89,6 +91,7 @@ import com.owncloud.android.ui.preview.PreviewMediaFragment;
 import com.owncloud.android.ui.preview.PreviewTextFragment;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileStorageUtils;
+import com.owncloud.android.utils.MimeTypeUtil;
 
 import org.greenrobot.eventbus.EventBus;
 import org.greenrobot.eventbus.Subscribe;
@@ -116,6 +119,8 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL";
     public final static String ARG_HIDE_FAB = MY_PACKAGE + ".HIDE_FAB";
 
+    public static final String SEARCH_EVENT = "SEARCH_EVENT";
+
     private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE";
     private static final String KEY_FAB_EVER_CLICKED = "FAB_EVER_CLICKED";
 
@@ -146,6 +151,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     private BottomNavigationView bottomNavigationView;
 
     private SearchType currentSearchType;
+    private boolean searchFragment = false;
 
     private enum MenuItemAddRemove {
         DO_NOTHING, REMOVE_SORT, REMOVE_GRID_AND_SORT, ADD_SORT, ADD_GRID_AND_SORT, ADD_GRID_AND_SORT_WITH_SEARCH,
@@ -310,7 +316,8 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 mJustFolders,
                 getActivity(),
                 mContainerActivity,
-                this
+                this,
+                mContainerActivity.getStorageManager()
         );
         setListAdapter(mAdapter);
 
@@ -334,6 +341,11 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 removeFabLabels();
             }
         }
+
+        SearchEvent searchEvent = Parcels.unwrap(getArguments().getParcelable(OCFileListFragment.SEARCH_EVENT));
+        if (searchEvent != null){
+            onMessageEvent(searchEvent);
+        }
     }
 
     /**
@@ -799,6 +811,8 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
                 if (PreviewImageFragment.canBePreviewed(file)) {
                     // preview image - it handles the download, if needed
                     ((FileDisplayActivity) mContainerActivity).startImagePreview(file);
+                } else if (file.isDown() && MimeTypeUtil.isVCard(file)){
+                    ((FileDisplayActivity) mContainerActivity).startContactListFragment(file);
                 } else if (PreviewTextFragment.canBePreviewed(file)) {
                     ((FileDisplayActivity) mContainerActivity).startTextPreview(file);
                 } else if (file.isDown()) {
@@ -940,6 +954,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     }
 
     public void refreshDirectory() {
+        searchFragment = false;
         listDirectory(getCurrentFile(), MainApp.isOnlyOnDevice(), false);
     }
 
@@ -951,56 +966,58 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
      * @param directory File to be listed
      */
     public void listDirectory(OCFile directory, boolean onlyOnDevice, boolean fromSearch) {
-        FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
-        if (storageManager != null) {
+        if (!searchFragment) {
+            FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
+            if (storageManager != null) {
 
-            // Check input parameters for null
-            if (directory == null) {
-                if (mFile != null) {
-                    directory = mFile;
-                } else {
-                    directory = storageManager.getFileByPath("/");
-                    if (directory == null) {
-                        return; // no files, wait for sync
+                // Check input parameters for null
+                if (directory == null) {
+                    if (mFile != null) {
+                        directory = mFile;
+                    } else {
+                        directory = storageManager.getFileByPath("/");
+                        if (directory == null) {
+                            return; // no files, wait for sync
+                        }
                     }
                 }
-            }
 
 
-            // If that's not a directory -> List its parent
-            if (!directory.isFolder()) {
-                Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
-                directory = storageManager.getFileById(directory.getParentId());
-            }
+                // If that's not a directory -> List its parent
+                if (!directory.isFolder()) {
+                    Log_OC.w(TAG, "You see, that is not a directory -> " + directory.toString());
+                    directory = storageManager.getFileById(directory.getParentId());
+                }
 
 
-            if (searchView != null && !searchView.isIconified() && !fromSearch) {
+                if (searchView != null && !searchView.isIconified() && !fromSearch) {
 
-                searchView.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        searchView.setQuery("", false);
-                        searchView.onActionViewCollapsed();
-                        Activity activity;
-                        if ((activity = getActivity()) != null && activity instanceof FileDisplayActivity) {
-                            FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) activity;
-                            if (getCurrentFile() != null) {
-                                fileDisplayActivity.setDrawerIndicatorEnabled(fileDisplayActivity.isRoot(getCurrentFile()));
+                    searchView.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            searchView.setQuery("", false);
+                            searchView.onActionViewCollapsed();
+                            Activity activity;
+                            if ((activity = getActivity()) != null && activity instanceof FileDisplayActivity) {
+                                FileDisplayActivity fileDisplayActivity = (FileDisplayActivity) activity;
+                                if (getCurrentFile() != null) {
+                                    fileDisplayActivity.setDrawerIndicatorEnabled(fileDisplayActivity.isRoot(getCurrentFile()));
+                                }
                             }
-                        }
 
-                    }
-                });
-            }
+                        }
+                    });
+                }
 
-            mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
-            if (mFile == null || !mFile.equals(directory)) {
-                mCurrentListView.setSelection(0);
-            }
-            mFile = directory;
+                mAdapter.swapDirectory(directory, storageManager, onlyOnDevice);
+                if (mFile == null || !mFile.equals(directory)) {
+                    mCurrentListView.setSelection(0);
+                }
+                mFile = directory;
 
-            updateLayout();
+                updateLayout();
 
+            }
         }
     }
 
@@ -1047,42 +1064,46 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
     }
 
     private String generateFooterText(int filesCount, int foldersCount) {
-        String output;
-        if (filesCount <= 0) {
-            if (foldersCount <= 0) {
-                output = "";
+        String output = "";
 
-            } else if (foldersCount == 1) {
-                output = getResources().getString(R.string.file_list__footer__folder);
+        if (getActivity() != null) {
+            if (filesCount <= 0) {
+                if (foldersCount <= 0) {
+                    output = "";
 
-            } else { // foldersCount > 1
-                output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
-            }
+                } else if (foldersCount == 1) {
+                    output = getResources().getString(R.string.file_list__footer__folder);
 
-        } else if (filesCount == 1) {
-            if (foldersCount <= 0) {
-                output = getResources().getString(R.string.file_list__footer__file);
+                } else { // foldersCount > 1
+                    output = getResources().getString(R.string.file_list__footer__folders, foldersCount);
+                }
 
-            } else if (foldersCount == 1) {
-                output = getResources().getString(R.string.file_list__footer__file_and_folder);
+            } else if (filesCount == 1) {
+                if (foldersCount <= 0) {
+                    output = getResources().getString(R.string.file_list__footer__file);
 
-            } else { // foldersCount > 1
-                output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
-            }
-        } else {    // filesCount > 1
-            if (foldersCount <= 0) {
-                output = getResources().getString(R.string.file_list__footer__files, filesCount);
+                } else if (foldersCount == 1) {
+                    output = getResources().getString(R.string.file_list__footer__file_and_folder);
 
-            } else if (foldersCount == 1) {
-                output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
+                } else { // foldersCount > 1
+                    output = getResources().getString(R.string.file_list__footer__file_and_folders, foldersCount);
+                }
+            } else {    // filesCount > 1
+                if (foldersCount <= 0) {
+                    output = getResources().getString(R.string.file_list__footer__files, filesCount);
 
-            } else { // foldersCount > 1
-                output = getResources().getString(
-                        R.string.file_list__footer__files_and_folders, filesCount, foldersCount
-                );
+                } else if (foldersCount == 1) {
+                    output = getResources().getString(R.string.file_list__footer__files_and_folder, filesCount);
 
+                } else { // foldersCount > 1
+                    output = getResources().getString(
+                            R.string.file_list__footer__files_and_folders, filesCount, foldersCount
+                    );
+
+                }
             }
         }
+
         return output;
     }
 
@@ -1245,6 +1266,7 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
 
     @Subscribe(threadMode = ThreadMode.BACKGROUND)
     public void onMessageEvent(SearchEvent event) {
+        searchFragment = true;
         setEmptyListLoadingMessage();
         mAdapter.setData(new ArrayList<>(), SearchType.NO_SEARCH);
 
@@ -1284,84 +1306,77 @@ public class OCFileListFragment extends ExtendedListFragment implements OCFileLi
             }
         };
 
-        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext());
+        final RemoteOperation remoteOperation;
+        if (!currentSearchType.equals(SearchType.SHARED_FILTER)) {
+            remoteOperation = new SearchOperation(event.getSearchQuery(), event.getSearchType());
+        } else {
+            remoteOperation = new GetRemoteSharesOperation();
+        }
 
-        try {
-            OwnCloudAccount ocAccount = new OwnCloudAccount(
-                    currentAccount,
-                    MainApp.getAppContext()
-            );
+        final Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext());
+
+        AsyncTask task = new AsyncTask() {
+            @Override
+            protected Object doInBackground(Object[] params) {
+                RemoteOperationResult remoteOperationResult = remoteOperation.execute(currentAccount, getContext());
 
-            OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
-                    getClientFor(ocAccount, MainApp.getAppContext());
-            if (!currentSearchType.equals(SearchType.SHARED_FILTER)) {
-                SearchOperation operation = new SearchOperation(event.getSearchQuery(), event.getSearchType());
-                RemoteOperationResult remoteOperationResult = operation.execute(mClient);
-                if (remoteOperationResult.isSuccess() && remoteOperationResult.getData() != null) {
-                    mAdapter.setData(remoteOperationResult.getData(), currentSearchType);
-                }
-            } else {
-                GetRemoteSharesOperation operation = new GetRemoteSharesOperation();
-                RemoteOperationResult remoteOperationResult = operation.execute(mClient);
                 if (remoteOperationResult.isSuccess() && remoteOperationResult.getData() != null) {
                     mAdapter.setData(remoteOperationResult.getData(), currentSearchType);
                 }
-            }
-
-            if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
-                setEmptyListMessage(SearchType.FILE_SEARCH);
 
-            } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
-                if (event.getSearchQuery().equals("image/%")) {
-                    setEmptyListMessage(SearchType.PHOTO_SEARCH);
-                    menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
-                } else if (event.getSearchQuery().equals("video/%")) {
-                    setEmptyListMessage(SearchType.VIDEO_SEARCH);
-                    menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
-                }
-            } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
-                setEmptyListMessage(SearchType.FAVORITE_SEARCH);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
-                setEmptyListMessage(SearchType.RECENTLY_ADDED_SEARCH);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-            } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
-                setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
-            } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
-                setEmptyListMessage(SearchType.SHARED_FILTER);
-                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
+                return remoteOperationResult.isSuccess();
             }
 
-            if (!currentSearchType.equals(SearchType.FILE_SEARCH) && getActivity() != null) {
-                getActivity().invalidateOptionsMenu();
+            @Override
+            protected void onPostExecute(Object o) {
+                mAdapter.notifyDataSetChanged();
             }
+        };
 
-            if (currentSearchType.equals(SearchType.PHOTO_SEARCH)) {
-                new Handler(Looper.getMainLooper()).post(new Runnable() {
-                    @Override
-                    public void run() {
-                        switchToGridView();
-                    }
-                });
-            } else if (currentSearchType.equals(SearchType.NO_SEARCH) || currentSearchType.equals(
-                    SearchType.REGULAR_FILTER)) {
-                new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
-            } else {
-                new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
-            }
+        task.execute(true);
 
-        } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
-            Log_OC.e(TAG, "Account not found", e);
-        } catch (AuthenticatorException e) {
-            Log_OC.e(TAG, "Authentication failed", e);
-        } catch (IOException e) {
-            Log_OC.e(TAG, "IO error", e);
-        } catch (OperationCanceledException e) {
-            Log_OC.e(TAG, "Operation has been canceled", e);
+        if (event.getSearchType().equals(SearchOperation.SearchType.FILE_SEARCH)) {
+            setEmptyListMessage(SearchType.FILE_SEARCH);
+
+        } else if (event.getSearchType().equals(SearchOperation.SearchType.CONTENT_TYPE_SEARCH)) {
+            if (event.getSearchQuery().equals("image/%")) {
+                setEmptyListMessage(SearchType.PHOTO_SEARCH);
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
+            } else if (event.getSearchQuery().equals("video/%")) {
+                setEmptyListMessage(SearchType.VIDEO_SEARCH);
+                menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
+            }
+        } else if (event.getSearchType().equals(SearchOperation.SearchType.FAVORITE_SEARCH)) {
+            setEmptyListMessage(SearchType.FAVORITE_SEARCH);
+            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_ADDED_SEARCH)) {
+            setEmptyListMessage(SearchType.RECENTLY_ADDED_SEARCH);
+            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+        } else if (event.getSearchType().equals(SearchOperation.SearchType.RECENTLY_MODIFIED_SEARCH)) {
+            setEmptyListMessage(SearchType.RECENTLY_MODIFIED_SEARCH);
+            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SORT;
+        } else if (event.getSearchType().equals(SearchOperation.SearchType.SHARED_SEARCH)) {
+            setEmptyListMessage(SearchType.SHARED_FILTER);
+            menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_SEARCH;
         }
 
+        if (!currentSearchType.equals(SearchType.FILE_SEARCH) && getActivity() != null) {
+            getActivity().invalidateOptionsMenu();
+        }
 
+        if (currentSearchType.equals(SearchType.PHOTO_SEARCH)) {
+            new Handler(Looper.getMainLooper()).post(new Runnable() {
+                @Override
+                public void run() {
+                    switchToGridView();
+                }
+            });
+        } else if (currentSearchType.equals(SearchType.NO_SEARCH) || currentSearchType.equals(
+                SearchType.REGULAR_FILTER)) {
+            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
+        } else {
+            new Handler(Looper.getMainLooper()).post(switchViewsRunnable);
+        }
     }
 
     @Override

+ 112 - 60
src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -29,6 +29,7 @@ import android.graphics.Point;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.PictureDrawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.support.annotation.DrawableRes;
@@ -48,6 +49,8 @@ import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.caverock.androidsvg.SVG;
+import com.caverock.androidsvg.SVGParseException;
 import com.owncloud.android.R;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.FileMenuFilter;
@@ -59,6 +62,8 @@ import com.owncloud.android.utils.BitmapUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimeTypeUtil;
 
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.lang.ref.WeakReference;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -109,10 +114,10 @@ public class PreviewImageFragment extends FileFragment {
      *
      * This method hides to client objects the need of doing the construction in two steps.
      *
-     * @param imageFile                 An {@link OCFile} to preview as an image in the fragment
-     * @param ignoreFirstSavedState     Flag to work around an unexpected behaviour of
-     *                                  {@link FragmentStatePagerAdapter}
-     *                                  ; TODO better solution
+     * @param imageFile             An {@link OCFile} to preview as an image in the fragment
+     * @param ignoreFirstSavedState Flag to work around an unexpected behaviour of
+     *                              {@link FragmentStatePagerAdapter}
+     *                              ; TODO better solution
      */
     public static PreviewImageFragment newInstance(OCFile imageFile, boolean ignoreFirstSavedState) {
         PreviewImageFragment frag = new PreviewImageFragment();
@@ -125,13 +130,13 @@ public class PreviewImageFragment extends FileFragment {
 
 
     /**
-     *  Creates an empty fragment for image previews.
+     * Creates an empty fragment for image previews.
      *
-     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically
-     *  (for instance, when the device is turned a aside).
+     * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically
+     * (for instance, when the device is turned a aside).
      *
-     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful
-     *  construction
+     * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful
+     * construction
      */
     public PreviewImageFragment() {
         mIgnoreFirstSavedState = false;
@@ -180,7 +185,7 @@ public class PreviewImageFragment extends FileFragment {
                 toggleImageBackground();
             }
         });
-        
+
         mMultiView = (RelativeLayout) view.findViewById(R.id.multi_view);
 
         setupMultiView(view);
@@ -337,7 +342,7 @@ public class PreviewImageFragment extends FileFragment {
             case R.id.action_share_file:
                 mContainerActivity.getFileOperationsHelper().showShareFile(getFile());
                 return true;
-            
+
             case R.id.action_open_file_with:
                 openFile();
                 return true;
@@ -419,12 +424,14 @@ public class PreviewImageFragment extends FileFragment {
          * @param imageView Target {@link ImageView} where the bitmap will be loaded into.
          */
         public LoadBitmapTask(ImageViewCustom imageView) {
-            mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
+            mImageViewRef = new WeakReference<>(imageView);
         }
 
         @Override
         protected LoadImage doInBackground(OCFile... params) {
-            Bitmap result = null;
+            Bitmap bitmapResult = null;
+            Drawable drawableResult = null;
+
             if (params.length != 1) {
                 return null;
             }
@@ -436,51 +443,72 @@ public class PreviewImageFragment extends FileFragment {
                 Point screenSize = DisplayUtils.getScreenSize(getActivity());
                 int minWidth = screenSize.x;
                 int minHeight = screenSize.y;
-                for (int i = 0; i < maxDownScale && result == null; i++) {
-                    if (isCancelled()) {
-                        return null;
-                    }
-                    try {
-                        result = BitmapUtils.decodeSampledBitmapFromFile(storagePath, minWidth,
-                                minHeight);
+                for (int i = 0; i < maxDownScale && bitmapResult == null && drawableResult == null; i++) {
 
+                    if (ocFile.getMimetype().equalsIgnoreCase("image/svg+xml")) {
                         if (isCancelled()) {
-                            return new LoadImage(result, ocFile);
+                            return null;
                         }
 
-                        if (result == null) {
-                            mErrorMessageId = R.string.preview_image_error_unknown_format;
-                            Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
-                            break;
-                        } else {
-                            if (ocFile.getMimetype().equalsIgnoreCase("image/jpeg")) {
-                                // Rotate image, obeying exif tag.
-                                result = BitmapUtils.rotateImage(result, storagePath);
+                        try {
+                            SVG svg = SVG.getFromInputStream(new FileInputStream(storagePath));
+                            drawableResult = new PictureDrawable(svg.renderToPicture());
+
+                            if (isCancelled()) {
+                                return new LoadImage(null, drawableResult, ocFile);
                             }
+                        } catch (FileNotFoundException e) {
+                            mErrorMessageId = R.string.common_error_unknown;
+                            Log_OC.e(TAG, "File not found trying to load " + getFile().getStoragePath(), e);
+                        } catch (SVGParseException e) {
+                            mErrorMessageId = R.string.common_error_unknown;
+                            Log_OC.e(TAG, "Couldn't parse SVG " + getFile().getStoragePath(), e);
+                        }
+                    } else {
+                        if (isCancelled()) {
+                            return null;
                         }
 
-                    } catch (OutOfMemoryError e) {
-                        mErrorMessageId = R.string.common_error_out_memory;
-                        if (i < maxDownScale - 1) {
-                            Log_OC.w(TAG, "Out of memory rendering file " + storagePath +
-                                    " ; scaling down");
-                            minWidth = minWidth / 2;
-                            minHeight = minHeight / 2;
+                        try {
+                            bitmapResult = BitmapUtils.decodeSampledBitmapFromFile(storagePath, minWidth,
+                                    minHeight);
 
-                        } else {
-                            Log_OC.w(TAG, "Out of memory rendering file " + storagePath +
-                                    " ; failing");
-                        }
-                        if (result != null) {
-                            result.recycle();
+                            if (isCancelled()) {
+                                return new LoadImage(bitmapResult, null, ocFile);
+                            }
+
+                            if (bitmapResult == null) {
+                                mErrorMessageId = R.string.preview_image_error_unknown_format;
+                                Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath);
+                                break;
+                            } else {
+                                if (ocFile.getMimetype().equalsIgnoreCase("image/jpeg")) {
+                                    // Rotate image, obeying exif tag.
+                                    bitmapResult = BitmapUtils.rotateImage(bitmapResult, storagePath);
+                                }
+                            }
+
+                        } catch (OutOfMemoryError e) {
+                            mErrorMessageId = R.string.common_error_out_memory;
+                            if (i < maxDownScale - 1) {
+                                Log_OC.w(TAG, "Out of memory rendering file " + storagePath + " ; scaling down");
+                                minWidth = minWidth / 2;
+                                minHeight = minHeight / 2;
+
+                            } else {
+                                Log_OC.w(TAG, "Out of memory rendering file " + storagePath + " ; failing");
+                            }
+                            if (bitmapResult != null) {
+                                bitmapResult.recycle();
+                            }
+                            bitmapResult = null;
                         }
-                        result = null;
                     }
                 }
 
             } catch (NoSuchFieldError e) {
                 mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file "
+                Log_OC.e(TAG, "Error from access to non-existing field despite protection; file "
                         + storagePath, e);
 
             } catch (Throwable t) {
@@ -489,7 +517,7 @@ public class PreviewImageFragment extends FileFragment {
 
             }
 
-            return new LoadImage(result, ocFile);
+            return new LoadImage(bitmapResult, drawableResult, ocFile);
         }
 
         @Override
@@ -501,7 +529,7 @@ public class PreviewImageFragment extends FileFragment {
 
         @Override
         protected void onPostExecute(LoadImage result) {
-            if (result.bitmap != null) {
+            if (result.bitmap != null || result.drawable != null) {
                 showLoadedImage(result);
             } else {
                 showErrorMessage();
@@ -516,23 +544,43 @@ public class PreviewImageFragment extends FileFragment {
         private void showLoadedImage(LoadImage result) {
             final ImageViewCustom imageView = mImageViewRef.get();
             Bitmap bitmap = result.bitmap;
-            if (imageView != null) {
-                Log_OC.d(TAG, "Showing image with resolution " + bitmap.getWidth() + "x" +
-                        bitmap.getHeight());
 
 
-                if (result.ocFile.getMimetype().equalsIgnoreCase("image/png")) {
+            if (imageView != null) {
+                if (bitmap != null) {
+                    Log_OC.d(TAG, "Showing image with resolution " + bitmap.getWidth() + "x" +
+                            bitmap.getHeight());
+                }
+
+                if (result.ocFile.getMimetype().equalsIgnoreCase("image/png") ||
+                        result.ocFile.getMimetype().equals("image/svg+xml")) {
                     if (getResources() != null) {
                         Resources r = getResources();
                         Drawable[] layers = new Drawable[2];
                         layers[0] = r.getDrawable(R.color.white);
-                        Drawable bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
+                        Drawable bitmapDrawable;
+                        if (result.ocFile.getMimetype().equalsIgnoreCase("image/png") ) {
+                            bitmapDrawable = new BitmapDrawable(getResources(), bitmap);
+                        } else {
+                            bitmapDrawable = result.drawable;
+                        }
                         layers[1] = bitmapDrawable;
                         LayerDrawable layerDrawable = new LayerDrawable(layers);
-                        layerDrawable.setLayerHeight(0, convertDpToPixel(bitmap.getHeight(), getActivity()));
-                        layerDrawable.setLayerHeight(1, convertDpToPixel(bitmap.getHeight(), getActivity()));
-                        layerDrawable.setLayerWidth(0, convertDpToPixel(bitmap.getWidth(), getActivity()));
-                        layerDrawable.setLayerWidth(1, convertDpToPixel(bitmap.getWidth(), getActivity()));
+                        if (result.ocFile.getMimetype().equalsIgnoreCase("image/png") ) {
+                            layerDrawable.setLayerHeight(0, convertDpToPixel(bitmap.getHeight(), getActivity()));
+                            layerDrawable.setLayerHeight(1, convertDpToPixel(bitmap.getHeight(), getActivity()));
+                            layerDrawable.setLayerWidth(0, convertDpToPixel(bitmap.getWidth(), getActivity()));
+                            layerDrawable.setLayerWidth(1, convertDpToPixel(bitmap.getWidth(), getActivity()));
+                        } else {
+                            layerDrawable.setLayerHeight(0, convertDpToPixel(bitmapDrawable.getIntrinsicHeight(),
+                                    getActivity()));
+                            layerDrawable.setLayerHeight(1, convertDpToPixel(bitmapDrawable.getIntrinsicHeight(),
+                                    getActivity()));
+                            layerDrawable.setLayerWidth(0, convertDpToPixel(bitmapDrawable.getIntrinsicWidth(),
+                                    getActivity()));
+                            layerDrawable.setLayerWidth(1, convertDpToPixel(bitmapDrawable.getIntrinsicWidth(),
+                                    getActivity()));
+                        }
                         imageView.setImageDrawable(layerDrawable);
                     } else {
                         imageView.setImageBitmap(bitmap);
@@ -541,7 +589,8 @@ public class PreviewImageFragment extends FileFragment {
 
                 if (result.ocFile.getMimetype().equalsIgnoreCase("image/gif")) {
                     imageView.setGIFImageFromStoragePath(result.ocFile.getStoragePath());
-                } else if (!result.ocFile.getMimetype().equalsIgnoreCase("image/png")) {
+                } else if (!result.ocFile.getMimetype().equalsIgnoreCase("image/png") &&
+                        !result.ocFile.getMimetype().equals("image/svg+xml")) {
                     imageView.setImageBitmap(bitmap);
                 }
 
@@ -589,8 +638,8 @@ public class PreviewImageFragment extends FileFragment {
      * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment}
      * to be previewed.
      *
-     * @param file      File to test if can be previewed.
-     * @return          'True' if the file can be handled by the fragment.
+     * @param file File to test if can be previewed.
+     * @return 'True' if the file can be handled by the fragment.
      */
     public static boolean canBePreviewed(OCFile file) {
         return (file != null && MimeTypeUtil.isImage(file));
@@ -606,7 +655,8 @@ public class PreviewImageFragment extends FileFragment {
     }
 
     private void toggleImageBackground() {
-        if (getFile() != null && getFile().getMimetype().equalsIgnoreCase("image/png") && getActivity() != null
+        if (getFile() != null && (getFile().getMimetype().equalsIgnoreCase("image/png") ||
+                getFile().getMimetype().equalsIgnoreCase("image/svg+xml")) && getActivity() != null
                 && getActivity() instanceof PreviewImageActivity && getResources() != null) {
             PreviewImageActivity previewImageActivity = (PreviewImageActivity) getActivity();
             LayerDrawable layerDrawable = (LayerDrawable) mImageView.getDrawable();
@@ -640,10 +690,12 @@ public class PreviewImageFragment extends FileFragment {
 
     private class LoadImage {
         private Bitmap bitmap;
+        private Drawable drawable;
         private OCFile ocFile;
 
-        public LoadImage(Bitmap bitmap, OCFile ocFile) {
+        public LoadImage(Bitmap bitmap, Drawable drawable, OCFile ocFile) {
             this.bitmap = bitmap;
+            this.drawable = drawable;
             this.ocFile = ocFile;
         }
 

+ 3 - 5
src/main/java/com/owncloud/android/utils/BitmapUtils.java

@@ -28,7 +28,6 @@ import android.media.ExifInterface;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
 
-import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
 import java.io.UnsupportedEncodingException;
@@ -265,15 +264,14 @@ public class BitmapUtils {
     /**
      * calculates the RGB value based on a given account name.
      *
-     * @param accountName The account name
+     * @param name The name
      * @return corresponding RGB color
      * @throws UnsupportedEncodingException if the charset is not supported
      * @throws NoSuchAlgorithmException if the specified algorithm is not available
      */
-    public static int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+    public static int[] calculateRGB(String name) throws UnsupportedEncodingException, NoSuchAlgorithmException {
         // using adapted algorithm from /core/js/placeholder.js:50
-        String username = AccountUtils.getAccountUsername(accountName);
-        byte[] seed = username.getBytes("UTF-8");
+        byte[] seed = name.getBytes("UTF-8");
         MessageDigest md = MessageDigest.getInstance("MD5");
         Integer seedMd5Int = String.format(Locale.ROOT, "%032x",
                 new BigInteger(1, md.digest(seed))).hashCode();

+ 16 - 0
src/main/java/com/owncloud/android/utils/MimeTypeUtil.java

@@ -149,6 +149,13 @@ public class MimeTypeUtil {
         return (mimeType != null && mimeType.toLowerCase().startsWith("text/"));
     }
 
+    /**
+     * @return 'True' if mime type defines vcard
+     */
+    public static boolean isVCard(String mimeType) {
+        return "text/vcard".equalsIgnoreCase(mimeType);
+    }
+
     /**
      * Checks if file passed is a video.
      *
@@ -203,6 +210,15 @@ public class MimeTypeUtil {
                 || MimeTypeUtil.isText(getMimeTypeFromPath(file.getRemotePath())));
     }
 
+
+    /**
+     * @param file the file to be analyzed
+     * @return 'True' if the file is a vcard
+     */
+    public static boolean isVCard(OCFile file) {
+        return isVCard(file.getMimetype()) || isVCard(getMimeTypeFromPath(file.getRemotePath()));
+    }
+
     /**
      * Extracts the mime type for the given file.
      *

+ 36 - 1
src/main/java/com/owncloud/android/utils/PermissionUtil.java

@@ -11,6 +11,9 @@ import android.support.v4.content.ContextCompat;
  */
 public class PermissionUtil {
     public static final int PERMISSIONS_WRITE_EXTERNAL_STORAGE = 1;
+    public static final int PERMISSIONS_READ_CONTACTS_AUTOMATIC = 2;
+    public static final int PERMISSIONS_READ_CONTACTS_MANUALLY = 3;
+    public static final int PERMISSIONS_WRITE_CONTACTS = 4;
 
     /**
      * Wrapper method for ContextCompat.checkSelfPermission().
@@ -22,7 +25,7 @@ public class PermissionUtil {
      */
     public static boolean checkSelfPermission(Context context, String permission) {
         return ContextCompat.checkSelfPermission(context, permission)
-                != android.content.pm.PackageManager.PERMISSION_GRANTED;
+                == android.content.pm.PackageManager.PERMISSION_GRANTED;
     }
 
     /**
@@ -50,4 +53,36 @@ public class PermissionUtil {
                 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                 PERMISSIONS_WRITE_EXTERNAL_STORAGE);
     }
+
+    /**
+     * request the read permission for contacts
+     *
+     * @param activity The target activity.
+     */
+    public static void requestReadContactPermission(Activity activity, int permission) {
+        ActivityCompat.requestPermissions(activity,
+                new String[]{Manifest.permission.READ_CONTACTS},
+                permission);
+    }
+
+    /**
+     * request the write permission for contacts
+     *
+     * @param activity The target activity.
+     */
+    public static void requestWriteContactPermission(Activity activity) {
+        ActivityCompat.requestPermissions(activity,
+                new String[]{Manifest.permission.WRITE_CONTACTS},
+                PERMISSIONS_WRITE_CONTACTS);
+    }
+
+    /**
+     * request the write permission for contacts
+     *
+     * @param fragment The target fragment.
+     */
+    public static void requestWriteContactPermission(android.support.v4.app.Fragment fragment) {
+        fragment.requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS},
+                PERMISSIONS_WRITE_CONTACTS);
+    }
 }

+ 114 - 0
src/main/java/third_parties/ezvcard_android/AndroidCustomField.java

@@ -0,0 +1,114 @@
+package third_parties.ezvcard_android;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ezvcard.property.VCardProperty;
+
+/*
+ Copyright (c) 2014-2015, Michael Angstadt
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those
+ of the authors and should not be interpreted as representing official policies,
+ either expressed or implied, of the FreeBSD Project.
+ */
+
+/**
+ * Represents an "X-ANDROID-CUSTOM" property.
+ * @author Michael Angstadt
+ */
+public class AndroidCustomField extends VCardProperty {
+    private String type;
+    private boolean dir;
+    private List<String> values = new ArrayList<String>();
+
+    /**
+     * Creates an "item" field.
+     * @param type the type
+     * @param value the value
+     * @return the property
+     */
+    public static AndroidCustomField item(String type, String value) {
+        AndroidCustomField property = new AndroidCustomField();
+        property.dir = false;
+        property.type = type;
+        property.values.add(value);
+        return property;
+    }
+
+    /**
+     * Creates a "dir" field.
+     * @param type the type
+     * @param values the values
+     * @return the property
+     */
+    public static AndroidCustomField dir(String type, String... values) {
+        AndroidCustomField property = new AndroidCustomField();
+        property.dir = true;
+        property.type = type;
+        Collections.addAll(property.values, values);
+        return property;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public List<String> getValues() {
+        return values;
+    }
+
+    public boolean isDir() {
+        return dir;
+    }
+
+    public void setDir(boolean dir) {
+        this.dir = dir;
+    }
+
+    public boolean isItem() {
+        return !isDir();
+    }
+
+    public void setItem(boolean item) {
+        setDir(!item);
+    }
+
+    public boolean isNickname() {
+        return "nickname".equals(type);
+    }
+
+    public boolean isContactEvent() {
+        return "contact_event".equals(type);
+    }
+
+    public boolean isRelation() {
+        return "relation".equals(type);
+    }
+}

+ 567 - 0
src/main/java/third_parties/ezvcard_android/ContactOperations.java

@@ -0,0 +1,567 @@
+package third_parties.ezvcard_android;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ezvcard.VCard;
+import ezvcard.property.Address;
+import ezvcard.property.Birthday;
+import ezvcard.property.Email;
+import ezvcard.property.FormattedName;
+import ezvcard.property.Impp;
+import ezvcard.property.Nickname;
+import ezvcard.property.Note;
+import ezvcard.property.Organization;
+import ezvcard.property.Photo;
+import ezvcard.property.RawProperty;
+import ezvcard.property.StructuredName;
+import ezvcard.property.Telephone;
+import ezvcard.property.Title;
+import ezvcard.property.Url;
+import ezvcard.property.VCardProperty;
+import ezvcard.util.TelUri;
+
+import static android.text.TextUtils.isEmpty;
+
+/*
+ Copyright (c) 2014-2015, Michael Angstadt
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those
+ of the authors and should not be interpreted as representing official policies,
+ either expressed or implied, of the FreeBSD Project.
+ */
+
+/**
+ * Inserts a {@link VCard} into an Android database.
+ *
+ * @author Pratyush
+ * @author Michael Angstadt
+ */
+public class ContactOperations {
+    private static final int rawContactID = 0;
+
+    private final Context context;
+    private final NonEmptyContentValues account;
+
+    public ContactOperations(Context context) {
+        this(context, null, null);
+    }
+
+    public ContactOperations(Context context, String accountName, String accountType) {
+        this.context = context;
+
+        account = new NonEmptyContentValues();
+        account.put(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType);
+        account.put(ContactsContract.RawContacts.ACCOUNT_NAME, accountName);
+    }
+
+    public void insertContact(VCard vcard) throws RemoteException, OperationApplicationException {
+        // TODO handle Raw properties - Raw properties include various extension which start with "X-" like X-ASSISTANT, X-AIM, X-SPOUSE
+
+        List<NonEmptyContentValues> contentValues = new ArrayList<NonEmptyContentValues>();
+        convertName(contentValues, vcard);
+        convertNickname(contentValues, vcard);
+        convertPhones(contentValues, vcard);
+        convertEmails(contentValues, vcard);
+        convertAddresses(contentValues, vcard);
+        convertIms(contentValues, vcard);
+
+        // handle Android Custom fields..This is only valid for Android generated Vcards. As the Android would
+        // generate NickName, ContactEvents other than Birthday and RelationShip with this "X-ANDROID-CUSTOM" name
+        convertCustomFields(contentValues, vcard);
+
+        // handle Iphone kinda of group properties. which are grouped together.
+        convertGroupedProperties(contentValues, vcard);
+
+        convertBirthdays(contentValues, vcard);
+
+        convertWebsites(contentValues, vcard);
+        convertNotes(contentValues, vcard);
+        convertPhotos(contentValues, vcard);
+        convertOrganization(contentValues, vcard);
+
+        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(contentValues.size());
+        ContentValues cv = account.getContentValues();
+        //ContactsContract.RawContact.CONTENT_URI needed to add account, backReference is also not needed
+        ContentProviderOperation operation =
+                ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
+                        .withValues(cv)
+                        .build();
+        operations.add(operation);
+        for (NonEmptyContentValues values : contentValues) {
+            cv = values.getContentValues();
+            if (cv.size() == 0) {
+                continue;
+            }
+
+            //@formatter:off
+            operation =
+                    ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
+                            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactID)
+                            .withValues(cv)
+                            .build();
+            //@formatter:on
+            operations.add(operation);
+        }
+
+        // Executing all the insert operations as a single database transaction
+        context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
+    }
+
+    private void convertName(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        NonEmptyContentValues values = new NonEmptyContentValues(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+
+        String firstName = null, lastName = null, namePrefix = null, nameSuffix = null;
+        StructuredName n = vcard.getStructuredName();
+        if (n != null) {
+            firstName = n.getGiven();
+            values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, firstName);
+
+            lastName = n.getFamily();
+            values.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, lastName);
+
+            List<String> prefixes = n.getPrefixes();
+            if (!prefixes.isEmpty()) {
+                namePrefix = prefixes.get(0);
+                values.put(ContactsContract.CommonDataKinds.StructuredName.PREFIX, namePrefix);
+            }
+
+            List<String> suffixes = n.getSuffixes();
+            if (!suffixes.isEmpty()) {
+                nameSuffix = suffixes.get(0);
+                values.put(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, nameSuffix);
+            }
+        }
+
+        FormattedName fn = vcard.getFormattedName();
+        String formattedName = (fn == null) ? null : fn.getValue();
+
+        String displayName;
+        if (isEmpty(formattedName)) {
+            StringBuilder sb = new StringBuilder();
+            if (!isEmpty(namePrefix)){
+                sb.append(namePrefix).append(' ');
+            }
+            if (!isEmpty(firstName)){
+                sb.append(firstName).append(' ');
+            }
+            if (!isEmpty(lastName)){
+                sb.append(lastName).append(' ');
+            }
+            if (!isEmpty(nameSuffix)){
+                if (sb.length() > 0){
+                    sb.deleteCharAt(sb.length()-1); //delete space character
+                    sb.append(", ");
+                }
+                sb.append(nameSuffix);
+            }
+
+            displayName = sb.toString().trim();
+        } else {
+            displayName = formattedName;
+        }
+        values.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName);
+
+        RawProperty xPhoneticFirstName = vcard.getExtendedProperty("X-PHONETIC-FIRST-NAME");
+        String firstPhoneticName = (xPhoneticFirstName == null) ? null : xPhoneticFirstName.getValue();
+        values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_GIVEN_NAME, firstPhoneticName);
+
+        RawProperty xPhoneticLastName = vcard.getExtendedProperty("X-PHONETIC-LAST-NAME");
+        String lastPhoneticName = (xPhoneticLastName == null) ? null : xPhoneticLastName.getValue();
+        values.put(ContactsContract.CommonDataKinds.StructuredName.PHONETIC_FAMILY_NAME, lastPhoneticName);
+
+        contentValues.add(values);
+    }
+
+    private void convertNickname(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Nickname nickname : vcard.getNicknames()) {
+            List<String> nicknameValues = nickname.getValues();
+            if (nicknameValues.isEmpty()) {
+                continue;
+            }
+
+            for (String nicknameValue : nicknameValues) {
+                NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
+                cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, nicknameValue);
+                contentValues.add(cv);
+            }
+        }
+    }
+
+    private void convertPhones(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Telephone telephone : vcard.getTelephoneNumbers()) {
+            String value = telephone.getText();
+            TelUri uri = telephone.getUri();
+            if (isEmpty(value)) {
+                if (uri == null) {
+                    continue;
+                }
+                value = uri.toString();
+            }
+
+            int phoneKind = DataMappings.getPhoneType(telephone);
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Phone.NUMBER, value);
+            cv.put(ContactsContract.CommonDataKinds.Phone.TYPE, phoneKind);
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertEmails(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Email email : vcard.getEmails()) {
+            String value = email.getValue();
+            if (isEmpty(value)) {
+                continue;
+            }
+
+            int emailKind = DataMappings.getEmailType(email);
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Email.ADDRESS, value);
+            cv.put(ContactsContract.CommonDataKinds.Email.TYPE, emailKind);
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertAddresses(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Address address : vcard.getAddresses()) {
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
+
+            String street = address.getStreetAddress();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, street);
+
+            String poBox = address.getPoBox();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.POBOX, poBox);
+
+            String city = address.getLocality();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, city);
+
+            String state = address.getRegion();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, state);
+
+            String zipCode = address.getPostalCode();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, zipCode);
+
+            String country = address.getCountry();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, country);
+
+            String label = address.getLabel();
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.LABEL, label);
+
+            int addressKind = DataMappings.getAddressType(address);
+            cv.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, addressKind);
+
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertIms(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        //handle extended properties
+        for (Map.Entry<String, Integer> entry : DataMappings.getImPropertyNameMappings().entrySet()) {
+            String propertyName = entry.getKey();
+            Integer protocolType = entry.getValue();
+            List<RawProperty> rawProperties = vcard.getExtendedProperties(propertyName);
+            for (RawProperty rawProperty : rawProperties) {
+                NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
+
+                String value = rawProperty.getValue();
+                cv.put(ContactsContract.CommonDataKinds.Im.DATA, value);
+
+                cv.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, protocolType);
+
+                contentValues.add(cv);
+            }
+        }
+
+        //handle IMPP properties
+        for (Impp impp : vcard.getImpps()) {
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
+
+            String immpAddress = impp.getHandle();
+            cv.put(ContactsContract.CommonDataKinds.Im.DATA, immpAddress);
+
+            int immpProtocolType = DataMappings.getIMTypeFromProtocol(impp.getProtocol());
+            cv.put(ContactsContract.CommonDataKinds.Im.PROTOCOL, immpProtocolType);
+
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertCustomFields(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (AndroidCustomField customField : vcard.getProperties(AndroidCustomField.class)) {
+            List<String> values = customField.getValues();
+            if (values.isEmpty()) {
+                continue;
+            }
+
+            NonEmptyContentValues cv;
+            if (customField.isNickname()) {
+                cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
+                cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, values.get(0));
+            } else if (customField.isContactEvent()) {
+                cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
+                cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, values.get(0));
+                cv.put(ContactsContract.CommonDataKinds.Event.TYPE, values.get(1));
+            } else if (customField.isRelation()) {
+                cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE);
+                cv.put(ContactsContract.CommonDataKinds.Relation.NAME, values.get(0));
+                cv.put(ContactsContract.CommonDataKinds.Relation.TYPE, values.get(1));
+            } else {
+                continue;
+            }
+
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertGroupedProperties(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        List<RawProperty> extendedProperties = vcard.getExtendedProperties();
+        Map<String, List<RawProperty>> orderedByGroup = orderPropertiesByGroup(extendedProperties);
+        final int ABDATE = 1, ABRELATEDNAMES = 2;
+
+        for (List<RawProperty> properties : orderedByGroup.values()) {
+            if (properties.size() == 1) {
+                continue;
+            }
+
+            String label = null;
+            String val = null;
+            int mime = 0;
+            for (RawProperty property : properties) {
+                String name = property.getPropertyName();
+
+                if (name.equalsIgnoreCase("X-ABDATE")) {
+                    label = property.getValue(); //date
+                    mime = ABDATE;
+                    continue;
+                }
+
+                if (name.equalsIgnoreCase("X-ABRELATEDNAMES")) {
+                    label = property.getValue(); //name
+                    mime = ABRELATEDNAMES;
+                    continue;
+                }
+
+                if (name.equalsIgnoreCase("X-ABLABEL")) {
+                    val = property.getValue(); // type of value ..Birthday,anniversary
+                    continue;
+                }
+            }
+
+            NonEmptyContentValues cv = null;
+            switch (mime) {
+                case ABDATE:
+                    cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
+
+                    cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, label);
+
+                    int type = DataMappings.getDateType(val);
+                    cv.put(ContactsContract.CommonDataKinds.Event.TYPE, type);
+
+                    break;
+
+                case ABRELATEDNAMES:
+                    if (val != null) {
+                        cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
+                        cv.put(ContactsContract.CommonDataKinds.Nickname.NAME, label);
+
+                        if (!val.equals("Nickname")) {
+                            type = DataMappings.getNameType(val);
+                            cv.put(ContactsContract.CommonDataKinds.Relation.TYPE, type);
+                        }
+                    }
+
+                    break;
+
+                default:
+                    continue;
+            }
+
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertBirthdays(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        for (Birthday birthday : vcard.getBirthdays()) {
+            Date date = birthday.getDate();
+            if (date == null) {
+                continue;
+            }
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
+            cv.put(ContactsContract.CommonDataKinds.Event.START_DATE, df.format(date));
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertWebsites(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Url url : vcard.getUrls()) {
+            String urlValue = url.getValue();
+            if (isEmpty(urlValue)) {
+                continue;
+            }
+
+            int type = DataMappings.getWebSiteType(url.getType());
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Website.URL, urlValue);
+            cv.put(ContactsContract.CommonDataKinds.Website.TYPE, type);
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertNotes(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Note note : vcard.getNotes()) {
+            String noteValue = note.getValue();
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Note.NOTE, noteValue);
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertPhotos(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        for (Photo photo : vcard.getPhotos()) {
+            byte[] data = photo.getData();
+
+            NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
+            cv.put(ContactsContract.CommonDataKinds.Photo.PHOTO, data);
+            contentValues.add(cv);
+        }
+    }
+
+    private void convertOrganization(List<NonEmptyContentValues> contentValues, VCard vcard) {
+        NonEmptyContentValues cv = new NonEmptyContentValues(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+
+        Organization organization = vcard.getOrganization();
+        if (organization != null) {
+            List<String> values = organization.getValues();
+            String keys[] = { ContactsContract.CommonDataKinds.Organization.COMPANY, ContactsContract.CommonDataKinds.Organization.DEPARTMENT, ContactsContract.CommonDataKinds.Organization.OFFICE_LOCATION };
+            for (int i = 0; i < values.size(); i++) {
+                String key = keys[i];
+                String value = values.get(i);
+                cv.put(key, value);
+            }
+        }
+
+        List<Title> titleList = vcard.getTitles();
+        if (!titleList.isEmpty()) {
+            cv.put(ContactsContract.CommonDataKinds.Organization.TITLE, titleList.get(0).getValue());
+        }
+
+        contentValues.add(cv);
+    }
+
+    /**
+     * Groups properties by their group name.
+     * @param properties the properties to group
+     * @return a map where the key is the group name (null for no group) and the
+     * value is the list of properties that belong to that group
+     */
+    private <T extends VCardProperty> Map<String, List<T>> orderPropertiesByGroup(List<T> properties) {
+        Map<String, List<T>> groupedProperties = new HashMap<String, List<T>>();
+
+        for (T property : properties) {
+            String group = property.getGroup();
+            if (isEmpty(group)) {
+                continue;
+            }
+
+            List<T> groupPropertiesList = groupedProperties.get(group);
+            if (groupPropertiesList == null) {
+                groupPropertiesList = new ArrayList<T>();
+                groupedProperties.put(group, groupPropertiesList);
+            }
+            groupPropertiesList.add(property);
+        }
+
+        return groupedProperties;
+    }
+
+    /**
+     * A wrapper for {@link ContentValues} that only adds values which are
+     * non-null and non-empty (in the case of Strings).
+     */
+    private static class NonEmptyContentValues {
+        private final ContentValues contentValues = new ContentValues();
+        private final String contentItemType;
+
+        public NonEmptyContentValues() {
+            this(null);
+        }
+
+        /**
+         * @param contentItemType the MIME type (value of
+         * {@link ContactsContract.Contacts.Data#MIMETYPE})
+         */
+        public NonEmptyContentValues(String contentItemType) {
+            this.contentItemType = contentItemType;
+        }
+
+        public void put(String key, String value) {
+            if (isEmpty(value)) {
+                return;
+            }
+            contentValues.put(key, value);
+        }
+
+        public void put(String key, int value) {
+            contentValues.put(key, value);
+        }
+
+        public void put(String key, byte[] value) {
+            if (value == null) {
+                return;
+            }
+            contentValues.put(key, value);
+        }
+
+        /**
+         * Gets the wrapped {@link ContentValues} object, adding the MIME type
+         * entry if the values map is not empty.
+         * @return the wrapped {@link ContentValues} object
+         */
+        public ContentValues getContentValues() {
+            if (contentValues.size() > 0 && contentItemType != null) {
+                put(ContactsContract.Contacts.Data.MIMETYPE, contentItemType);
+            }
+            return contentValues;
+        }
+    }
+}

+ 292 - 0
src/main/java/third_parties/ezvcard_android/DataMappings.java

@@ -0,0 +1,292 @@
+package third_parties.ezvcard_android;
+
+import android.provider.ContactsContract;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import ezvcard.parameter.AddressType;
+import ezvcard.parameter.EmailType;
+import ezvcard.parameter.TelephoneType;
+import ezvcard.property.Address;
+import ezvcard.property.Email;
+import ezvcard.property.Impp;
+import ezvcard.property.Telephone;
+
+/*
+ Copyright (c) 2014-2015, Michael Angstadt
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ The views and conclusions contained in the software and documentation are those
+ of the authors and should not be interpreted as representing official policies,
+ either expressed or implied, of the FreeBSD Project.
+ */
+
+/**
+ * Maps between vCard contact data types and Android {@link ContactsContract}
+ * data types.
+ *
+ * @author Pratyush
+ * @author Julien Garrigou
+ * @author Michael Angstadt
+ */
+public class DataMappings {
+    private static final Map<TelephoneType, Integer> phoneTypeMappings;
+    static {
+        Map<TelephoneType, Integer> m = new HashMap<TelephoneType, Integer>();
+        m.put(TelephoneType.BBS, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM);
+        m.put(TelephoneType.CAR, ContactsContract.CommonDataKinds.Phone.TYPE_CAR);
+        m.put(TelephoneType.CELL, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
+        m.put(TelephoneType.FAX, ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME);
+        m.put(TelephoneType.HOME, ContactsContract.CommonDataKinds.Phone.TYPE_HOME);
+        m.put(TelephoneType.ISDN, ContactsContract.CommonDataKinds.Phone.TYPE_ISDN);
+        m.put(TelephoneType.MODEM, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+        m.put(TelephoneType.PAGER, ContactsContract.CommonDataKinds.Phone.TYPE_PAGER);
+        m.put(TelephoneType.MSG, ContactsContract.CommonDataKinds.Phone.TYPE_MMS);
+        m.put(TelephoneType.PCS, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+        m.put(TelephoneType.TEXT, ContactsContract.CommonDataKinds.Phone.TYPE_MMS);
+        m.put(TelephoneType.TEXTPHONE, ContactsContract.CommonDataKinds.Phone.TYPE_MMS);
+        m.put(TelephoneType.VIDEO, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+        m.put(TelephoneType.WORK, ContactsContract.CommonDataKinds.Phone.TYPE_WORK);
+        m.put(TelephoneType.VOICE, ContactsContract.CommonDataKinds.Phone.TYPE_OTHER);
+        phoneTypeMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<String, Integer> websiteTypeMappings;
+    static {
+        Map<String, Integer> m = new HashMap<String, Integer>();
+        m.put("home", ContactsContract.CommonDataKinds.Website.TYPE_HOME);
+        m.put("work", ContactsContract.CommonDataKinds.Website.TYPE_WORK);
+        m.put("homepage", ContactsContract.CommonDataKinds.Website.TYPE_HOMEPAGE);
+        m.put("profile", ContactsContract.CommonDataKinds.Website.TYPE_PROFILE);
+        websiteTypeMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<EmailType, Integer> emailTypeMappings;
+    static {
+        Map<EmailType, Integer> m = new HashMap<EmailType, Integer>();
+        m.put(EmailType.HOME, ContactsContract.CommonDataKinds.Email.TYPE_HOME);
+        m.put(EmailType.WORK, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
+        emailTypeMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<AddressType, Integer> addressTypeMappings;
+    static {
+        Map<AddressType, Integer> m = new HashMap<AddressType, Integer>();
+        m.put(AddressType.HOME, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME);
+        m.put(AddressType.get("business"), ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
+        m.put(AddressType.WORK, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK);
+        m.put(AddressType.get("other"), ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER);
+        addressTypeMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<String, Integer> abRelatedNamesMappings;
+    static {
+        Map<String, Integer> m = new HashMap<String, Integer>();
+        m.put("father", ContactsContract.CommonDataKinds.Relation.TYPE_FATHER);
+        m.put("spouse", ContactsContract.CommonDataKinds.Relation.TYPE_SPOUSE);
+        m.put("mother", ContactsContract.CommonDataKinds.Relation.TYPE_MOTHER);
+        m.put("brother", ContactsContract.CommonDataKinds.Relation.TYPE_BROTHER);
+        m.put("parent", ContactsContract.CommonDataKinds.Relation.TYPE_PARENT);
+        m.put("sister", ContactsContract.CommonDataKinds.Relation.TYPE_SISTER);
+        m.put("child", ContactsContract.CommonDataKinds.Relation.TYPE_CHILD);
+        m.put("assistant", ContactsContract.CommonDataKinds.Relation.TYPE_ASSISTANT);
+        m.put("partner", ContactsContract.CommonDataKinds.Relation.TYPE_PARTNER);
+        m.put("manager", ContactsContract.CommonDataKinds.Relation.TYPE_MANAGER);
+        abRelatedNamesMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<String, Integer> abDateMappings;
+    static {
+        Map<String, Integer> m = new HashMap<String, Integer>();
+        m.put("anniversary", ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY);
+        m.put("other", ContactsContract.CommonDataKinds.Event.TYPE_OTHER);
+        abDateMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<String, Integer> imPropertyNameMappings;
+    static{
+        Map<String, Integer> m = new HashMap<String, Integer>();
+        m.put("X-AIM", ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM);
+        m.put("X-ICQ", ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ);
+        m.put("X-QQ", ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ);
+        m.put("X-GOOGLE-TALK", ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM);
+        m.put("X-JABBER", ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER);
+        m.put("X-MSN", ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN);
+        m.put("X-MS-IMADDRESS", ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN);
+        m.put("X-YAHOO", ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO);
+        m.put("X-SKYPE", ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE);
+        m.put("X-SKYPE-USERNAME", ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE);
+        m.put("X-TWITTER", ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM);
+        imPropertyNameMappings = Collections.unmodifiableMap(m);
+    }
+
+    private static final Map<String, Integer> imProtocolMappings;
+    static{
+        Map<String, Integer> m = new HashMap<String, Integer>();
+        m.put("aim", ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM);
+        m.put("icq", ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ);
+        m.put("msn", ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN);
+        m.put("ymsgr", ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO);
+        m.put("skype", ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE);
+        imProtocolMappings = Collections.unmodifiableMap(m);
+    }
+
+    /**
+     * Maps the value of a URL property's TYPE parameter to the appropriate
+     * Android {@link ContactsContract.CommonDataKinds.Website} value.
+     * @param type the TYPE parameter value (can be null)
+     * @return the Android type
+     */
+    public static int getWebSiteType(String type) {
+        if (type == null){
+            return ContactsContract.CommonDataKinds.Website.TYPE_CUSTOM;
+        }
+
+        type = type.toLowerCase();
+        Integer value = websiteTypeMappings.get(type);
+        return (value == null) ? ContactsContract.CommonDataKinds.Website.TYPE_CUSTOM : value;
+    }
+
+    /**
+     * Maps the value of a X-ABLABEL property to the appropriate
+     * Android {@link ContactsContract.CommonDataKinds.Event} value.
+     * @param type the property value
+     * @return the Android type
+     */
+    public static int getDateType(String type) {
+        if (type == null) {
+            return ContactsContract.CommonDataKinds.Event.TYPE_OTHER;
+        }
+
+        type = type.toLowerCase();
+        for (Map.Entry<String, Integer> entry : abDateMappings.entrySet()){
+            if (type.contains(entry.getKey())){
+                return entry.getValue();
+            }
+        }
+        return ContactsContract.CommonDataKinds.Event.TYPE_OTHER;
+    }
+
+    /**
+     * Maps the value of a X-ABLABEL property to the appropriate
+     * Android {@link ContactsContract.CommonDataKinds.Relation} value.
+     * @param type the property value
+     * @return the Android type
+     */
+    public static int getNameType(String type) {
+        if (type == null) {
+            return ContactsContract.CommonDataKinds.Relation.TYPE_CUSTOM;
+        }
+
+        type = type.toLowerCase();
+        for (Map.Entry<String, Integer> entry : abRelatedNamesMappings.entrySet()){
+            if (type.contains(entry.getKey())){
+                return entry.getValue();
+            }
+        }
+        return ContactsContract.CommonDataKinds.Relation.TYPE_CUSTOM;
+    }
+
+    /**
+     * Gets the mappings that associate an extended property name (e.g. "X-AIM")
+     * with its appropriate Android {@link ContactsContract.CommonDataKinds.Im}
+     * value.
+     * @return the mappings (the key is the property name, the value is the Android value)
+     */
+    public static Map<String, Integer> getImPropertyNameMappings(){
+        return imPropertyNameMappings;
+    }
+
+    /**
+     * Converts an IM protocol from a {@link Impp} property (e.g. "aim") to the
+     * appropriate Android {@link ContactsContract.CommonDataKinds.Im} value.
+     * @param protocol the IM protocol (e.g. "aim", can be null)
+     * @return the Android value
+     */
+    public static int getIMTypeFromProtocol(String protocol) {
+        if (protocol == null){
+            return ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM;
+        }
+
+        protocol = protocol.toLowerCase();
+        Integer value = imProtocolMappings.get(protocol);
+        return (value == null) ? ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM : value;
+    }
+
+    /**
+     * Determines the appropriate Android
+     * {@link ContactsContract.CommonDataKinds.Phone} value for a
+     * {@link Telephone} property.
+     * @param property the property
+     * @return the Android type value
+     */
+    public static int getPhoneType(Telephone property) {
+        for (TelephoneType type : property.getTypes()){
+            Integer androidType = phoneTypeMappings.get(type);
+            if (androidType != null){
+                return androidType;
+            }
+        }
+        return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER;
+    }
+
+    /**
+     * Determines the appropriate Android
+     * {@link ContactsContract.CommonDataKinds.Email} value for an {@link Email}
+     * property.
+     * @param property the property
+     * @return the Android type value
+     */
+    public static int getEmailType(Email property) {
+        for (EmailType type : property.getTypes()){
+            Integer androidType = emailTypeMappings.get(type);
+            if (androidType != null){
+                return androidType;
+            }
+        }
+        return ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
+    }
+
+    /**
+     * Determines the appropriate Android
+     * {@link ContactsContract.CommonDataKinds.StructuredPostal} value for an
+     * {@link Address} property.
+     * @param property the property
+     * @return the Android type value
+     */
+    public static int getAddressType(Address property) {
+        for (AddressType type : property.getTypes()){
+            Integer androidType = addressTypeMappings.get(type);
+            if (androidType != null){
+                return androidType;
+            }
+        }
+        return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_CUSTOM;
+    }
+
+    private DataMappings(){
+        //hide constructor
+    }
+}

binární
src/main/res/drawable-hdpi/ic_notification.png


binární
src/main/res/drawable-hdpi/ic_notification_light_grey.png


binární
src/main/res/drawable-mdpi/ic_notification.png


binární
src/main/res/drawable-mdpi/ic_notification_light_grey.png


binární
src/main/res/drawable-xhdpi/ic_notification.png


binární
src/main/res/drawable-xhdpi/ic_notification_light_grey.png


binární
src/main/res/drawable-xxhdpi/ic_notification.png


binární
src/main/res/drawable-xxhdpi/ic_notification_light_grey.png


binární
src/main/res/drawable-xxxhdpi/ic_notification.png


binární
src/main/res/drawable-xxxhdpi/ic_notification_light_grey.png


+ 1 - 1
src/main/res/drawable/indicator_dot_not_selected.xml

@@ -24,7 +24,7 @@
 
         <shape
             android:shape="oval">
-            <solid android:color="@color/owncloud_blue_dark_transparent"/>
+            <solid android:color="@color/color_dark_transparent"/>
             <size android:width="8dp" android:height="8dp" />
         </shape>
     </item>

+ 2 - 2
src/main/res/drawable/main_header_bg.xml

@@ -19,8 +19,8 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
     <gradient
-        android:startColor="@color/actionbar_start_color" 
-        android:endColor="@color/actionbar_end_color"
+        android:startColor="@color/navigation_bar_start_color"
+        android:endColor="@color/navigation_bar_end_color"
         android:angle="270"
      />
     <corners android:radius="0dp" />

+ 1 - 1
src/main/res/drawable/round_button.xml

@@ -21,5 +21,5 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
-    <solid android:color="@color/owncloud_blue_accent"/>
+    <solid android:color="@color/nc_blue_accent"/>
 </shape>

+ 1 - 1
src/main/res/layout/account_item.xml

@@ -52,7 +52,7 @@
             android:layout_gravity="bottom|right"
             android:background="@drawable/round_bgnd"
             android:src="@drawable/ic_account_circle_white_18dp"
-            android:tint="@color/actionbar_start_color"/>
+            android:tint="@color/navigation_bar_start_color"/>
     </FrameLayout>
 
     <LinearLayout

+ 43 - 0
src/main/res/layout/contactlist_fragment.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2017 Tobias Kaminsky
+  Copyright (C) 2017 Nextcloud.
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/contactlist_recyclerview"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:choiceMode="multipleChoice"/>
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/contactlist_restore_selected"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/standard_margin"
+        android:enabled="false"
+        android:text="@string/contaclist_restore_selected"
+        android:background="@color/standard_grey"
+        android:theme="@style/Button.Primary"/>
+
+</LinearLayout>

+ 46 - 0
src/main/res/layout/contactlist_list_item.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2017 Tobias Kaminsky
+  Copyright (C) 2017 Nextcloud.
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="@dimen/standard_list_item_size">
+
+    <ImageView
+        android:id="@+id/contactlist_item_icon"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_margin="16dp"
+        android:scaleType="centerCrop"
+        android:src="@drawable/ic_user"/>
+
+    <CheckedTextView
+        android:id="@+id/contactlist_item_name"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/standard_list_item_size"
+        android:layout_marginRight="@dimen/standard_margin"
+        android:layout_weight="1"
+        android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+        android:ellipsize="marquee"
+        android:gravity="center_vertical"
+        android:maxLines="1"
+        android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+</LinearLayout>

+ 120 - 0
src/main/res/layout/contacts_preference.xml

@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2017 Tobias Kaminsky
+  Copyright (C) 2017 Nextcloud.
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                        android:id="@+id/drawer_layout"
+                                        android:layout_width="match_parent"
+                                        android:layout_height="match_parent"
+                                        android:clickable="true"
+                                        android:fitsSystemWindows="true">
+
+    <!-- The main content view -->
+    <LinearLayout
+        android:id="@+id/contacts_linear_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <include
+            layout="@layout/toolbar_standard"/>
+
+        <TextView
+            android:id="@+id/contacts_header_backup"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/standard_margin"
+            android:layout_marginRight="@dimen/standard_margin"
+            android:layout_marginTop="@dimen/standard_margin"
+            android:text="@string/contacts_header_backup"
+            android:textColor="@color/primary"
+            android:textStyle="bold"/>
+
+        <android.support.v7.widget.SwitchCompat
+            android:id="@+id/contacts_automatic_backup"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/standard_margin"
+            android:text="@string/contacts_automatic_backup"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <TextView
+                android:id="@+id/contacts_last_backup"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/standard_margin"
+                android:layout_weight="1"
+                android:text="@string/contacts_last_backup"
+                android:textAppearance="?android:attr/textAppearanceMedium"
+                android:textColor="@color/black"/>
+
+            <TextView
+                android:id="@+id/contacts_last_backup_timestamp"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="@dimen/standard_margin"
+                android:layout_weight="1"
+                android:gravity="right"
+                android:text="@string/contacts_preference_backup_never"
+                android:textAppearance="?android:attr/textAppearanceMedium"/>
+        </LinearLayout>
+
+        <android.support.v7.widget.AppCompatButton
+            android:id="@+id/contacts_backup_now"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/standard_margin"
+            android:onClick="backupContacts"
+            android:text="@string/contacts_backup_button"
+            android:theme="@style/Button.Primary"/>
+
+        <TextView
+            android:id="@+id/contacts_header_restore"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/standard_margin"
+            android:layout_marginRight="@dimen/standard_margin"
+            android:layout_marginTop="@dimen/standard_margin"
+            android:text="@string/contacts_header_restore"
+            android:textColor="@color/primary"
+            android:textStyle="bold"/>
+
+        <android.support.v7.widget.AppCompatButton
+            android:id="@+id/contacts_datepacker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="@dimen/standard_margin"
+            android:onClick="openDate"
+            android:text="@string/contacts_preference_choose_date"
+            android:theme="@style/Button.Primary"/>
+
+    </LinearLayout>
+
+    <include
+        layout="@layout/drawer"
+        android:layout_width="@dimen/drawer_width"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"/>
+
+</android.support.v4.widget.DrawerLayout>

+ 1 - 1
src/main/res/layout/edit_share_layout.xml

@@ -42,7 +42,7 @@
             android:id="@+id/share_header_divider"
             android:layout_width="match_parent"
             android:layout_height="@dimen/edit_share_layout_view_height"
-            android:background="@color/owncloud_blue"
+            android:background="@color/nc_blue"
             />
     <LinearLayout
         android:orientation="vertical"

+ 4 - 4
src/main/res/layout/list_fragment.xml

@@ -89,7 +89,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentRight="true"
         app:fab_addButtonColorNormal="@color/primary_button_background_color"
-        app:fab_addButtonColorPressed="@color/owncloud_blue"
+        app:fab_addButtonColorPressed="@color/nc_blue"
         app:fab_addButtonPlusIconColor="@color/white"
         app:fab_labelStyle="@style/menu_labels_style"
         android:layout_marginBottom="@dimen/standard_margin"
@@ -105,7 +105,7 @@
             app:fab_size="mini"
             app:fab_icon="@drawable/ic_action_upload"
             app:fab_colorNormal="@color/primary_button_background_color"
-            app:fab_colorPressed="@color/owncloud_blue"
+            app:fab_colorPressed="@color/nc_blue"
             app:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
@@ -115,7 +115,7 @@
             app:fab_size="mini"
             app:fab_icon="@drawable/ic_import"
             app:fab_colorNormal="@color/primary_button_background_color"
-            app:fab_colorPressed="@color/owncloud_blue"
+            app:fab_colorPressed="@color/nc_blue"
             app:fab_title=""/>
 
         <com.getbase.floatingactionbutton.FloatingActionButton
@@ -125,7 +125,7 @@
             app:fab_size="mini"
             app:fab_icon="@drawable/ic_action_create_dir"
             app:fab_colorNormal="@color/primary_button_background_color"
-            app:fab_colorPressed="@color/owncloud_blue"
+            app:fab_colorPressed="@color/nc_blue"
             app:fab_title=""/>
 
     </com.getbase.floatingactionbutton.FloatingActionsMenu>

+ 82 - 0
src/main/res/layout/notifications_layout.xml

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2017 Andy Scherzinger
+  Copyright (C) 2017 Mario Danic
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+  License as published by the Free Software Foundation; either
+  version 3 of the License, or any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+
+  You should have received a copy of the GNU Affero General Public
+  License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+-->
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                        android:id="@+id/drawer_layout"
+                                        android:layout_width="match_parent"
+                                        android:layout_height="match_parent"
+                                        android:clickable="true"
+                                        android:fitsSystemWindows="true">
+
+    <!-- The main content view -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical">
+
+        <include
+            layout="@layout/toolbar_standard"/>
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <android.support.v4.widget.SwipeRefreshLayout
+                android:id="@+id/swipe_containing_list"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:footerDividersEnabled="false"
+                android:visibility="visible">
+
+                <android.support.v7.widget.RecyclerView
+                    android:id="@android:id/list"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginBottom="-3dp"
+                    android:layout_marginLeft="-3dp"
+                    android:layout_marginRight="-3dp"
+                    android:clipToPadding="false"
+                    android:scrollbarStyle="outsideOverlay"
+                    android:scrollbars="vertical"
+                    android:visibility="visible"/>
+
+            </android.support.v4.widget.SwipeRefreshLayout>
+
+            <android.support.v4.widget.SwipeRefreshLayout
+                android:id="@+id/swipe_containing_empty"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:footerDividersEnabled="false"
+                android:visibility="visible">
+
+                <include layout="@layout/empty_list"/>
+            </android.support.v4.widget.SwipeRefreshLayout>
+
+        </FrameLayout>
+
+    </LinearLayout>
+
+    <include
+        layout="@layout/drawer"
+        android:layout_width="240dp"
+        android:layout_height="match_parent"
+        android:layout_gravity="start"/>
+
+</android.support.v4.widget.DrawerLayout>

+ 1 - 1
src/main/res/layout/share_file_layout.xml

@@ -77,7 +77,7 @@
             android:id="@+id/share_header_divider"
             android:layout_width="match_parent"
             android:layout_height="@dimen/standard_eighth_margin"
-            android:background="@color/owncloud_blue"
+            android:background="@color/nc_blue"
             />
 
         <LinearLayout

+ 8 - 8
src/main/res/layout/whats_new_activity.xml

@@ -30,7 +30,7 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_margin="5dp"
-        android:layout_weight="10"
+        android:layout_weight="6"
         android:gravity="center"
         android:text="@string/placeholder_sentence"
         android:textAppearance="?android:attr/textAppearanceLarge"
@@ -40,16 +40,15 @@
         android:id="@+id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="80">
+        android:layout_weight="86">
     </android.support.v4.view.ViewPager>
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_marginBottom="@dimen/standard_margin"
         android:layout_marginLeft="@dimen/standard_margin"
         android:layout_marginRight="@dimen/standard_margin"
-        android:layout_weight="10"
+        android:layout_weight="8"
         android:orientation="horizontal"
         android:weightSum="3">
 
@@ -57,11 +56,12 @@
             android:id="@+id/skip"
             style="@style/Button.Borderless.Login"
             android:layout_width="0dp"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="center_vertical|center_horizontal"
             android:layout_weight="1"
-            android:paddingRight="0dp"
+            android:gravity="center"
             android:paddingLeft="0dp"
+            android:paddingRight="0dp"
             android:text="@string/whats_new_skip"/>
 
         <com.owncloud.android.ui.whatsnew.ProgressIndicator
@@ -74,9 +74,10 @@
 
         <LinearLayout
             android:layout_width="0dp"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="center"
             android:layout_weight="1"
+            android:gravity="center"
             android:orientation="vertical">
 
             <ImageButton
@@ -84,7 +85,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center"
-                android:padding="@dimen/standard_padding"
                 android:src="@drawable/arrow_right"/>
         </LinearLayout>
     </LinearLayout>

+ 13 - 0
src/main/res/layout/whats_new_webview_element.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <WebView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/whatsNewWebView">
+    </WebView>
+
+</LinearLayout>

+ 10 - 0
src/main/res/menu/drawer_menu.xml

@@ -73,6 +73,11 @@
             android:orderInCategory="0"
             android:title="@string/drawer_item_recently_modified"
             android:visible="false"/>
+        <item
+            android:orderInCategory="0"
+            android:id="@+id/nav_notifications"
+            android:icon="@drawable/ic_notification"
+            android:title="@string/drawer_item_notifications"/>
         <item
             android:orderInCategory="0"
             android:id="@+id/nav_folder_sync"
@@ -117,6 +122,11 @@
     <group
         android:id="@+id/drawer_menu_bottom"
         android:checkableBehavior="single">
+        <item
+            android:id="@+id/nav_contacts"
+            android:icon="@drawable/ic_user"
+            android:orderInCategory="3"
+            android:title="@string/actionbar_contacts"/>
         <item
             android:id="@+id/nav_settings"
             android:icon="@drawable/ic_settings"

+ 4 - 0
src/main/res/values-ca/strings.xml

@@ -13,7 +13,11 @@
     <string name="actionbar_send_file">Envia</string>
     <string name="actionbar_sort">Ordena</string>
     <string name="actionbar_sort_title">Ordena per</string>
+    <string name="sort_by">Ordena per</string>
+    <string name="menu_item_sort_by_name_a_z">A - Z</string>
+    <string name="menu_item_sort_by_name_z_a">Z - A</string>
     <string name="drawer_item_all_files">Tots els fitxers</string>
+    <string name="drawer_item_photos">Fotos</string>
     <string name="drawer_item_on_device">Al dispositiu</string>
     <string name="drawer_item_settings">Configuració</string>
     <string name="drawer_item_uploads_list">Fitxers carregats</string>

+ 64 - 2
src/main/res/values-da/strings.xml

@@ -13,9 +13,27 @@
     <string name="actionbar_send_file">Send</string>
     <string name="actionbar_sort">Sortér</string>
     <string name="actionbar_sort_title">Sortér efter</string>
+    <string name="sort_by">Sortér efter</string>
+    <string name="menu_item_sort_by_name_a_z">A - Z</string>
+    <string name="menu_item_sort_by_name_z_a">Z - A</string>
+    <string name="menu_item_sort_by_date_newest_first">Nyeste først</string>
+    <string name="menu_item_sort_by_date_oldest_first">Ældste først</string>
+    <string name="menu_item_sort_by_size_biggest_first">Største først</string>
+    <string name="menu_item_sort_by_size_smallest_first">Mindste først</string>
+
     <string name="drawer_item_all_files">Alle filer</string>
+    <string name="drawer_item_files">Filer</string>
+    <string name="drawer_item_home">Hjem</string>
+    <string name="drawer_item_favorites">Favoritter</string>
+    <string name="drawer_item_photos">Fotos</string>
+    <string name="drawer_item_on_device">På enhed</string>
+    <string name="drawer_item_recently_added">Tilføjet for nylig</string>
+    <string name="drawer_item_recently_modified">Ændret for nylig</string>
+    <string name="drawer_item_shared">Delt</string>
+    <string name="drawer_item_videos">Videoer</string>
     <string name="drawer_item_settings">Indstillinger</string>
     <string name="drawer_item_uploads_list">Uploade filer</string>
+    <string name="drawer_item_activities">Aktiviteter</string>
     <string name="drawer_quota">%1$s af %2$s brugt</string>
 	<string name="drawer_close">Luk</string>
     <string name="drawer_open">Åbn</string>
@@ -36,6 +54,9 @@
     <string name="prefs_log_delete_history_button">Slet historik</string>
     <string name="prefs_calendar_contacts">Synkroniser kalender &amp; kontakter</string>
     <string name="prefs_calendar_contacts_summary">Indstil DAVdroid (v1.3.0+) for aktiv konto</string>
+    <string name="prefs_calendar_contacts_address_resolve_error">Serveradresse for konto kunne ikke afgøres for DAVdroid</string>
+    <string name="prefs_calendar_contacts_no_store_error">Ingen Google Play-butik eller F-Droid-app er installeret</string>
+    <string name="prefs_calendar_contacts_sync_setup_successful">Synkronisering af kalender &amp; kontakter er nu på plads</string>
     <string name="prefs_help">Hjælp</string>
     <string name="prefs_recommend">Anbefal til en ven</string>
     <string name="prefs_feedback">Feedback</string>
@@ -50,26 +71,44 @@
     <string name="auth_host_url">Serveradresse https://…</string>
     <string name="auth_username">Brugernavn</string>
     <string name="auth_password">Kodeord</string>
+    <string name="auth_register">Har du ikke en server endnu?\nKlik her for at skaffe en udbyder</string>
     <string name="sync_string_files">Filer</string>
     <string name="setup_btn_connect">Tilslut</string>
     <string name="uploader_btn_upload_text">Upload</string>
     <string name="uploader_top_message">Vælg upload mappe</string>
     <string name="uploader_wrn_no_account_title">Ingen konto fundet</string>
+    <string name="uploader_wrn_no_account_text">Der er ingen %1$s-konti på din enhed. Angiv venligst en konto først.</string>
     <string name="uploader_wrn_no_account_setup_btn_text">Opsætning</string>
     <string name="uploader_wrn_no_account_quit_btn_text">Afslut</string>
     <string name="uploader_error_title_no_file_to_upload">Ingen filer til upload</string>
+    <string name="uploader_error_message_received_piece_of_text">%1$s kan ikke uploade et tekststykke som en fil.</string>
+    <string name="uploader_error_message_no_file_to_upload">Modtagne data indeholdt ikke en gyldig fil.</string>
     <string name="uploader_error_title_file_cannot_be_uploaded">Filen kan ikke uploades</string>
+    <string name="uploader_error_message_read_permission_not_granted">%1$s har ikke tilladelse til at læse en modtaget fil</string>
+    <string name="uploader_error_message_source_file_not_found">Filen som skulle uploades blev ikke fundet på dens placering. Tjek venligst om filen findes.</string>
+    <string name="uploader_error_message_source_file_not_copied">Der opstod en fejl under kopiering af filen til midlertidig mappe. Forsøg venligst at sende igen.</string>
     <string name="uploader_upload_files_behaviour">Upload muligheder</string>
     <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Flyt fil til Nextcloud mappe</string>
     <string name="uploader_upload_files_behaviour_only_upload">Behold filen i source-mappen</string>
     <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Slet filen fra source-mappen</string>
     <string name="file_list_seconds_ago">sekunder siden</string>
     <string name="file_list_empty_headline">Ingen filer her</string>
+    <string name="file_list_empty">Upload indhold eller synkronisér med dine enheder!</string>
+    <string name="file_list_empty_favorites">Gør filer til favoritter eller synkronisér med dine enheder!</string>
+    <string name="file_list_empty_favorites_filter_list">Filer og mapper som markeres som favoritter vil blive vist her</string>
+    <string name="file_list_empty_favorites_filter">Fandt ingen favoritter ud fra din søgning! </string>
     <string name="file_list_loading">Indlæser...</string>
     <string name="file_list_no_app_for_file_type">Ingen apps fundet til denne filetype!</string>
     <string name="local_file_list_empty">Der er ingen filer i denne mappe.</string>
     <string name="file_list_empty_headline_search">Intet resultat for denne mappe</string>
+    <string name="file_list_empty_headline_server_search">Ingen resultater</string>
+    <string name="file_list_empty_favorite_headline">Ingen favoritter</string>
+    <string name="file_list_empty_shared_headline">Intet er delt endnu</string>
+    <string name="file_list_empty_shared">Filer og mapper du deler vil blive vist her</string>
+    <string name="file_list_empty_headline_server_search_videos">Ingen videoer</string>
+    <string name="file_list_empty_headline_server_search_photos">Ingen fotos</string>
     <string name="file_list_empty_search">Prøve at kigge i en anden mappe?</string>
+    <string name="file_list_empty_recently_modified">Fandt ikke filer som var ændret i seneste 7 dage</string>
     <string name="file_list_folder">mappe</string>
     <string name="file_list_folders">mapper</string>
     <string name="file_list_file">fil</string>
@@ -113,6 +152,7 @@
     <string name="uploader_upload_failed_content_single">Upload af %1$s kunne ikke gennemføres</string>
     <string name="uploads_view_title">Uploade filer</string>
     <string name="uploads_view_group_current_uploads">Nuværende</string>
+    <string name="uploads_view_group_failed_uploads">Mislykkedes (tryk for forsøge igen)</string>
     <string name="uploads_view_group_finished_uploads">Sendt</string>
     <string name="uploads_view_upload_status_succeeded">Fuldført</string>
     <string name="uploads_view_upload_status_cancelled">Annulléret</string>
@@ -124,7 +164,9 @@
     <string name="uploads_view_upload_status_failed_localfile_error">Lokal fil blev ikke fundet</string>
     <string name="uploads_view_upload_status_failed_permission_error">Manglende rettigheder</string>
     <string name="uploads_view_upload_status_conflict">Konflikt</string>
+    <string name="uploads_view_upload_status_service_interrupted">App blev afsluttet</string>
     <string name="uploads_view_upload_status_unknown_fail">Ukendt fejl</string>
+    <string name="uploads_view_upload_status_waiting_for_wifi">Venter på wifi-forbindelse</string>
     <string name="uploads_view_later_waiting_to_upload">Venter med upload</string>
     <string name="downloader_download_in_progress_ticker">Downloader ...</string>
     <string name="downloader_download_in_progress_content">%1$d%% Downloader %2$s</string>
@@ -133,9 +175,11 @@
     <string name="downloader_download_failed_ticker">Download fejlede</string>
     <string name="downloader_download_failed_content">Download af %1$s kunne ikke fuldføres</string>
     <string name="downloader_not_downloaded_yet">Endnu ikke downloadet</string>
+    <string name="downloader_download_failed_credentials_error">Download mislykkedes, du skal logge ind igen</string>
     <string name="common_choose_account">Vælg konto</string>
     <string name="sync_fail_ticker">Sync fejlede</string>
     <string name="sync_fail_ticker_unauthorized">Synkronisering fejlede. Du er nød til at logge ind igen</string>
+    <string name="sync_fail_content">Synk. af %1$s kunne ikke fuldføres</string>
     <string name="sync_fail_content_unauthorized">Ugyldig adgangskode for %1$s</string>
     <string name="sync_conflicts_in_favourites_ticker">Konflikter fundet</string>
     <string name="sync_conflicts_in_favourites_content">%1$d hold-synkroniseret filer kunne ikke synkroniseres</string>
@@ -209,6 +253,8 @@
 	<string name="auth_unsupported_multiaccount">%1$s understøtter ikke flere konti</string>
 	<string name="auth_account_does_not_exist">Kontoen findes endnu ikke på enheden</string>
     
+    <string name="favorite_real">Angiv som favorit</string>
+    <string name="unset_favorite_real">Fjern som favorit</string>
     <string name="common_rename">Omdøb</string>
     <string name="common_remove">Fjern</string>
     <string name="confirmation_remove_file_alert">Er du sikker på at du vil fjerne %1$s ?</string>
@@ -278,17 +324,21 @@
     <string name="conflict_use_local_version">lokal version</string>
     <string name="conflict_use_server_version">serverversion</string>
 
+    <string name="preview_sorry">Beklager!</string>
     <string name="preview_image_description">Forhåndsvisning af billede</string>
-    <string name="preview_image_error_unknown_format">Dette billede kan ikke vises</string>
+    <string name="preview_image_error_unknown_format">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="prefs_folder_sync_local_path_title">Lokal mappe</string>
     <string name="prefs_folder_sync_remote_path_title">Ekstern mappe</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Benyt undermapper</string>
-    <string name="share_link_file_no_exist">Kan ikke dele. Tjek venligst om filen findes.</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. Tjek venligst om filen findes.</string>
 	<string name="share_link_file_error">Der opstod en fejl ved deling af denne fil eller mappe</string>
 	<string name="unshare_link_file_no_exist">Kan ikke fjerne deling. Tjek venligst om filen findes.</string>
 	<string name="unshare_link_file_error">Der opstod en fejl ved stopning af deling af denne mappe.</string>
+    <string name="update_link_file_no_exist">Kan ikke opdatere. Tjek venligst om filen findes.</string>
+    <string name="update_link_file_error">Der opstod en fejl under forsøg på at opdatering det delte</string>
     <string name="share_link_password_title">Angiv et kodeord</string>
     <string name="share_link_empty_password">Du skal angive et kodeord</string>
 
@@ -319,6 +369,10 @@
     <string name="file_migration_failed_not_writable">FEJL: Fil er ikke skrivbar</string>
     <string name="file_migration_failed_not_readable">FEJL: File ikke læselig</string>
     <string name="file_migration_failed_dir_already_exists">FEJL: Nextcloud mappe eksisterer allerede </string>
+    <string name="file_migration_failed_while_coping">FEJL: Under migrering</string>
+    <string name="file_migration_failed_while_updating_index">FEJL: Under opdatering af indeks</string>
+
+    <string name="file_migration_directory_already_exists">Datamappen findes allerede, hvad skal der ske?</string>
     <string name="file_migration_override_data_folder">Overskriv</string>
     <string name="file_migration_use_data_folder">Benyt eksiterende</string>
 
@@ -451,4 +505,12 @@
     <string name="user_info_full_name">Fulde navn</string>
     <string name="user_info_email">E-post</string>
     <string name="user_info_phone">Telefonnummer</string>
+    <string name="user_info_address">Adresse</string>
+    <string name="user_info_website">Website</string>
+    <string name="user_info_twitter">Twitter</string>
+
+    <string name="user_information_description">Brugerinformation</string>
+
+    <!-- Activities -->
+    <string name="activities_no_results_headline">Endnu ingen aktiviteter</string>
     </resources>

+ 3 - 2
src/main/res/values-de-rDE/strings.xml

@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">Das tut mir leid!</string>
     <string name="preview_image_description">Bildvorschau</string>
+    <string name="preview_image_error_unknown_format">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="prefs_instant_upload_path_title">Pfad zum sofortigen Bilder-Upload</string>
     <string name="prefs_folder_sync_local_path_title">Lokaler Ordner</string>
@@ -609,5 +611,4 @@
     <string name="activities_no_results_headline">Bislang keine Aktivitäten</string>
     <string name="activities_no_results_message">Dieser Stream zeigt Ereignisse wie\nHinzugekommenes, Änderungen &amp; und Freigaben an</string>
 
-
-</resources>
+    </resources>

+ 3 - 2
src/main/res/values-de/strings.xml

@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">Das tut mir leid!</string>
     <string name="preview_image_description">Bildvorschau</string>
+    <string name="preview_image_error_unknown_format">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="prefs_instant_upload_path_title">Sofort-Upload-Ordner</string>
     <string name="prefs_folder_sync_local_path_title">Lokales Verzeichnis</string>
@@ -609,5 +611,4 @@
     <string name="activities_no_results_headline">Bislang keine Aktivitäten</string>
     <string name="activities_no_results_message">Dieser Stream zeigt Ereignisse wie\nHinzugekommenes, Änderungen &amp; und Freigaben an</string>
 
-
-</resources>
+    </resources>

+ 113 - 51
src/main/res/values-el/strings.xml

@@ -13,10 +13,27 @@
     <string name="actionbar_send_file">Αποστολή</string>
     <string name="actionbar_sort">Ταξινόμηση</string>
     <string name="actionbar_sort_title">Ταξινόμηση κατά</string>
+    <string name="sort_by">Ταξινόμηση κατά</string>
+    <string name="menu_item_sort_by_name_a_z">Α - Ω</string>
+    <string name="menu_item_sort_by_name_z_a">Ω - Α</string>
+    <string name="menu_item_sort_by_date_newest_first">Νεότερο πρώτα</string>
+    <string name="menu_item_sort_by_date_oldest_first">Παλαιότερο πρώτα</string>
+    <string name="menu_item_sort_by_size_biggest_first">Μεγαλύτερο πρώτα</string>
+    <string name="menu_item_sort_by_size_smallest_first">Μικρότερο πρώτα</string>
+
     <string name="drawer_item_all_files">Όλα τα αρχεία</string>
+    <string name="drawer_item_files">Αρχεία</string>
+    <string name="drawer_item_home">Αρχική</string>
+    <string name="drawer_item_favorites">Αγαπημένα</string>
+    <string name="drawer_item_photos">Φωτογραφίες</string>
     <string name="drawer_item_on_device">Στην συσκευή</string>
+    <string name="drawer_item_recently_added">Προστέθηκαν πρόσφατα</string>
+    <string name="drawer_item_recently_modified">Τροποποιήθηκαν πρόσφατα</string>
+    <string name="drawer_item_shared">Κοινόχρηστα</string>
+    <string name="drawer_item_videos">Βίντεο</string>
     <string name="drawer_item_settings">Ρυθμίσεις</string>
     <string name="drawer_item_uploads_list">Μεταφορτώσεις</string>
+    <string name="drawer_item_activities">Δραστηριότητες</string>
     <string name="drawer_quota">χρησιμοποιούνται %1$s από %2$s</string>
 	<string name="drawer_close">Κλείσιμο</string>
     <string name="drawer_open">Άνοιγμα</string>
@@ -45,7 +62,7 @@
     <string name="prefs_feedback">Aνάδραση</string>
     <string name="prefs_imprint">Αποτύπωμα</string>
     <string name="prefs_remember_last_share_location">Απομνημόνευση θέσης διαμοιρασμού</string>
-    <string name="prefs_remember_last_upload_location_summary">Απομνημόνευση τελευταιας θέσης μεταφορτώσεων</string>
+    <string name="prefs_remember_last_upload_location_summary">Απομνημόνευση τελευταιας τοποθεσίας διαμοιρασμού μεταφορτώσεων</string>
 
 	<string name="recommend_subject">Δοκιμή %1$s στο κινητό σας!</string>
 	<string name="recommend_text">Θα ήθελα να σε προσκαλέσω να χρησιμοποιήσεις το %1$s στο κινητό σου!\\nΛήψη από εδώ: %2$s</string>
@@ -54,7 +71,7 @@
     <string name="auth_host_url">Διεύθυνση διακομιστή https://…</string>
     <string name="auth_username">Όνομα χρήστη</string>
     <string name="auth_password">Συνθηματικό</string>
-    <string name="auth_register">Δεν έχετε ακόμα διακομιστή;\\nΚάντε κλικ εδώ για να πάρετε ένα από ένα πάροχο</string>
+    <string name="auth_register">Δεν έχετε ακόμα διακομιστή;\\nΚάντε κλικ εδώ για να πάρετε έναν από ένα πάροχο</string>
     <string name="sync_string_files">Αρχεία</string>
     <string name="setup_btn_connect">Σύνδεση</string>
     <string name="uploader_btn_upload_text">Μεταφόρτωση</string>
@@ -63,13 +80,13 @@
     <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_error_title_no_file_to_upload">Δεν υπάρχει αρχείο για μεταφόρτωση</string>
+    <string name="uploader_error_title_no_file_to_upload">Κανένα αρχείο για μεταφόρτωση</string>
     <string name="uploader_error_message_received_piece_of_text">Ο %1$s δεν μπορεί να μεταφορτώσει ένα κομμάτι κειμένου ως αρχείο.</string>
     <string name="uploader_error_message_no_file_to_upload">Τα ληφθέντα δεδομένα δεν περιλαμβάνουν έγκυρο αρχείο.</string>
     <string name="uploader_error_title_file_cannot_be_uploaded">Το αρχείο δεν μπορεί να μεταφορτωθεί</string>
     <string name="uploader_error_message_read_permission_not_granted">Ο %1$s δεν επιτρέπεται να αναγνώσει ένα ληφθέν αρχείο</string>
     <string name="uploader_error_message_source_file_not_found">Το αρχείο προς αποστολή δεν βρέθηκε στην τοποθεσία. Παρακαλώ ελέγξτε εάν υπάρχει το αρχείο.</string>
-    <string name="uploader_error_message_source_file_not_copied">Παρουσιάστηκε σφάλμα κατά την αντιγραφή του αρχείου στον προσωρινό φάκελο. Παρακαλώ δοκιμάστε να στείλετε ξανά.</string>
+    <string name="uploader_error_message_source_file_not_copied">Παρουσιάστηκε σφάλμα κατά την αντιγραφή του αρχείου στον προσωρινό φάκελο. Παρακαλούμε δοκιμάστε να στείλετε ξανά.</string>
     <string name="uploader_upload_files_behaviour">Επιλογή μεταφόρτωσης:</string>
     <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Μετακίνηση αρχείου στον φάκελο Nextcloud</string>
     <string name="uploader_upload_files_behaviour_only_upload">Διατήρηση του αρχείου στον πηγαίο φάκελο</string>
@@ -77,15 +94,36 @@
     <string name="file_list_seconds_ago">δευτερόλεπτα πριν</string>
     <string name="file_list_empty_headline">Δεν υπάρχουν αρχεία</string>
     <string name="file_list_empty">Μεταφόρτωση περιεχομένου ή συγχρονισμός με τις συσκευές σας!</string>
+    <string name="file_list_empty_favorites">Σημειώστε ως αγαπημένα ορισμένα αρχεία ή συγχρονίστε τα με τις συσκευές σας!</string>
+    <string name="file_list_empty_favorites_filter_list">Τα αρχεία ή οι φάκελοι που σημειώσατε ως αγαπημένα θα εμφανιστούν εδώ</string>
+    <string name="file_list_empty_favorites_filter">Δεν βρέθηκαν αρχεία σημειωμένα ως αγαπημένα για το ερώτημά σας!</string>
     <string name="file_list_loading">Φόρτωση&#8230;</string>
     <string name="file_list_no_app_for_file_type">Δεν βρέθηκε εφαρμογή για τον τύπο αρχείου!</string>
     <string name="local_file_list_empty">Δεν υπάρχουν αρχεία σε αυτό τον φάκελο.</string>
+    <string name="file_list_empty_headline_search">Κανενα αποτέλεσμα σε αυτον τον φάκελο</string>
+    <string name="file_list_empty_headline_server_search">Κανένα αποτέλεσμα</string>
+    <string name="file_list_empty_favorite_headline">Κανένα αγαπημένο</string>
+    <string name="file_list_empty_shared_headline">Δεν έχει διαμοιραστεί τίποτα μέχρι στιγμής</string>
+    <string name="file_list_empty_shared">Τα αρχεία και οι φάκελοι που διαμοιράζεστε θα εμφανίζονται εδώ</string>
+    <string name="file_list_empty_headline_server_search_videos">Κανένα βίντεο</string>
+    <string name="file_list_empty_headline_server_search_photos">Καμιά φωτογραφία</string>
+    <string name="file_list_empty_search">Θα δοκιμάστε αναζήτηση και σε άλλο φάκελο;</string>
+    <string name="file_list_empty_recently_modified">Δεν βρέθηκαν αρχεία που να έχουν τροποποιηθεί τις τελευταίες 7 ημέρες</string>
+    <string name="file_list_empty_recently_modified_filter">Δεν βρέθηκαν αρχεία για το αίτημά σας που να έχουν τροποποιηθεί
+        τις τελευταίες 7 ημέρες!</string>
+    <string name="file_list_empty_recently_added">Δεν βρέθηκαν πρόσφατα προστεθημένα αρχεία</string>
+    <string name="file_list_empty_recently_added_filter">Δεν βρέθηκαν πρόσφατα προστεθημένα αρχεία για το αίτημά σας!</string>
+    <string name="file_list_empty_text_photos">Μεταφόρτωση μερικών φωτογραφιών ή ενεργοποιήστε την αυτόματη μεταφόρτωση!</string>
+    <string name="file_list_empty_text_photos_filter">Δεν βρέθηκαν φωτογραφίες για το αίτημά σας!</string>
+    <string name="file_list_empty_text_videos">Μεταφορτώστε μερικά βίντεο ή ενεργοποιήστε την αυτόματη μεταφόρτωση!</string>
+    <string name="file_list_empty_text_videos_filter">Δεν βρέθηκε βίντεο για το αίτημά σας!</string>
     <string name="upload_list_empty_headline">Μη διαθέσιμες μεταφορτώσεις</string>
-    <string name="upload_list_empty_text">Μεταφόρτωση περιεχομένου ή ενεργοποίηση άμεσης μεταφόρτωσης!</string>
+    <string name="upload_list_empty_text">Μεταφορτώστε περιεχόμενο ή ενεργοποιήστε την άμεση μεταφόρτωση!</string>
+    <string name="upload_list_empty_text_auto_upload">Μεταφορτώστε περιεχόμενο ή ενεργοποιήστε την αυτόματη μεταφόρτωση!</string>
     <string name="file_list_folder">φάκελος</string>
     <string name="file_list_folders">φάκελοι</string>
     <string name="file_list_file">αρχείο</string>
-    <string name="file_list_files">αρχείο</string>
+    <string name="file_list_files">αρχεία</string>
     <string name="filedetails_select_file">Αγγίξτε κάποιο αρχείο για να προβάλετε περισσότερες πληροφορίες.</string>
     <string name="filedetails_size">Μέγεθος:</string>
     <string name="filedetails_type">Τύπος:</string>
@@ -98,11 +136,11 @@
     <string name="action_share">Διαμοιρασμός</string>
     <string name="common_yes">Ναι</string>
     <string name="common_no">Όχι</string>
-    <string name="common_ok">ΟΚ</string>
+    <string name="common_ok">Εντάξει</string>
     <string name="common_remove_upload">Αφαίρεση μεταφόρτωσης</string>
     <string name="common_retry_upload">Επανάληψη μεταφόρτωσης</string>
     <string name="common_cancel_sync">Ακύρωση συγχρονισμού</string>
-    <string name="common_cancel">Άκυρο</string>
+    <string name="common_cancel">Ακύρωση</string>
     <string name="common_back">Επιστροφή</string>
     <string name="common_save">Αποθήκευση</string>
     <string name="common_save_exit">Αποθήκευση &amp; έξοδος</string>
@@ -122,7 +160,7 @@
     <string name="uploader_upload_in_progress_content">%1$d%% Μεταφορτώνονται %2$s</string>
     <string name="uploader_upload_succeeded_ticker">Επιτυχημένη μεταφόρτωση</string>
     <string name="uploader_upload_succeeded_content_single">Το %1$s μεταφορτώθηκε</string>
-    <string name="uploader_upload_failed_ticker">Η μεταφορά απέτυχε</string>
+    <string name="uploader_upload_failed_ticker">Η μεταφόρτωση απέτυχε</string>
     <string name="uploader_upload_failed_content_single">Η μεταφόρτωση του %1$s δεν ήταν δυνατόν να ολοκληρωθεί</string>
     <string name="uploader_upload_failed_credentials_error">Η μεταφόρτωση απέτυχε, πρέπει να επανασυνδεθείτε</string>
     <string name="uploads_view_title">Μεταφορτώσεις</string>
@@ -171,11 +209,11 @@
     <string name="foreign_files_local_text">Τοπικά: %1$s</string>
     <string name="foreign_files_remote_text">Απομακρυσμένα: %1$s</string>
     <string name="upload_query_move_foreign_files">Δεν υπάρχει αρκετός διαθέσιμος αποθηκευτικός χώρος για να αντιγραφούν τα επιλεγμένα αρχεία στον φάκελο %1$s. Θα θέλατε να τα μετακινήσετε αντί αυτού;</string>
-    <string name="pass_code_enter_pass_code">Παρακαλώ εισάγετε τον κωδικό πρόσβασης</string>
+    <string name="pass_code_enter_pass_code">Παρακαλούμε εισάγετε τον κωδικό πρόσβασης</string>
     
     <string name="pass_code_configure_your_pass_code">Εισάγετε τον κωδικό πρόσβασης</string>
     <string name="pass_code_configure_your_pass_code_explanation">Ο κωδικός πρόσβασης θα ζητείται κάθε φορά που εκκινεί η εφαρμογή</string>
-    <string name="pass_code_reenter_your_pass_code">Παρακαλώ εισάγετε ξανά τον κωδικό πρόσβασης</string>
+    <string name="pass_code_reenter_your_pass_code">Παρακαλούμε εισάγετε ξανά τον κωδικό πρόσβασης</string>
     <string name="pass_code_remove_your_pass_code">Αφαίρεση του κωδικού πρόσβασης</string>
     <string name="pass_code_mismatch">Οι κωδικοί πρόσβασης δεν ταιριάζουν</string>
     <string name="pass_code_wrong">Εσφαλμένος κωδικός πρόσβασης</string>
@@ -186,7 +224,7 @@
     <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_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>
@@ -224,33 +262,35 @@
 	<string name="auth_unauthorized">Εσφαλμένο όνομα χρήστη ή συνθηματικό</string>
 	<string name="auth_oauth_error">Ανεπιτυχής πιστοποίηση</string>
 	<string name="auth_oauth_error_access_denied">Άρνηση πρόσβασης από τον διακομιστή πιστοποίησης</string>
-	<string name="auth_wtf_reenter_URL">Απροσδόκητη κατάσταση, παρακαλώ εισάγετε ξανά τη διεύθυνση του διακομιστή</string>
-	<string name="auth_expired_oauth_token_toast">Η πιστοποιίησή σας έληξε. Παρακαλώ πιστοποιήστε ξανά</string>
-	<string name="auth_expired_basic_auth_toast">Παρακαλώ εισάγετε το τρέχον συνθηματικό</string>
-	<string name="auth_expired_saml_sso_token_toast">Η συνεδρία σας έληξε. Παρακαλώ συνδεθείτε ξανά</string>
+	<string name="auth_wtf_reenter_URL">Απροσδόκητη κατάσταση, παρακαλoύμε εισάγετε ξανά τη διεύθυνση του διακομιστή</string>
+	<string name="auth_expired_oauth_token_toast">Η πιστοποιίησή σας έληξε. Παρακαλούμε εξουσιοδοτήστε ξανά</string>
+	<string name="auth_expired_basic_auth_toast">Παρακαλούμε εισάγετε το τρέχον συνθηματικό</string>
+	<string name="auth_expired_saml_sso_token_toast">Η συνεδρία σας έληξε. Παρακαλούμε συνδεθείτε ξανά</string>
 	<string name="auth_connecting_auth_server">Σύνδεση με το διακομιστή πιστοποίησης ...</string>
 	<string name="auth_unsupported_auth_method">Ο διακομιστής δεν υποστηρίζει αυτή τη μέθοδο πιστοποίησης</string>
 	<string name="auth_unsupported_multiaccount">Ο %1$s  δεν υποστηρίζει πολλαπλούς λογαριασμούς</string>
-	<string name="auth_fail_get_user_name">Ο διακομιστής σας δεν επιστρέφει το σωστό αναγνωριστικό χρήστη, παρακαλώ επικοινωνήστε με ένα διαχειριστή</string>
+	<string name="auth_fail_get_user_name">Ο διακομιστής σας δεν επιστρέφει το σωστό αναγνωριστικό χρήστη, παρακαλούμε επικοινωνήστε τον διαχειριστή</string>
 	<string name="auth_can_not_auth_against_server">Αδυαμία πιστοποίησης με αυτόν το διακομιστή</string>
-    <string name="auth_account_does_not_exist">Ο λογαριασμός δεν υπάρχει στη συσκευή ακόμα.</string>
+    <string name="auth_account_does_not_exist">Ο λογαριασμός δεν υπάρχει ακόμα στη συσκευή</string>
     
     <string name="favorite">Ορισμός ως διαθέσιμο εκτός σύνδεσης</string>
     <string name="unfavorite">Αναίρεση ορισμού ως διαθέσιμο εκτός σύνδεσης</string>
+    <string name="favorite_real">Ορισμός ως αγαπημένο</string>
+    <string name="unset_favorite_real">Αναίρεση ορισμού ως αγαπημένο</string>
     <string name="common_rename">Μετονομασία</string>
     <string name="common_remove">Αφαίρεση</string>
-    <string name="confirmation_remove_file_alert">Θέλετε σίγουρα να αφαιρέσετε το %1$s;</string>
-    <string name="confirmation_remove_folder_alert">Θέλετε σίγουρα να διαγράψετε το %1$s και τα περιεχόμενά του;</string>
+    <string name="confirmation_remove_file_alert">Θέλετε να αφαιρέσετε το %1$s;</string>
+    <string name="confirmation_remove_folder_alert">Θέλετε να αφαιρέσετε το %1$s και τα περιεχόμενά του;</string>
     <string name="confirmation_remove_local">Μόνο τοπικά</string>
     <string name="remove_success_msg">Επιτυχής αφαίρεση</string>
     <string name="remove_fail_msg">Αποτυχημένη αφαίρεση</string>
     <string name="rename_dialog_title">Εισάγετε νέο όνομα</string>
-    <string name="rename_local_fail_msg">Το τοπικό αντίγραφο δεν ήταν δυνατόν να μετονομαστεί. Παρακαλώ επιλέξτε ένα διαφορετικό όνομα.</string>
-    <string name="rename_server_fail_msg">Η μετονομασία δεν ήταν επιτυχής</string>
+    <string name="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">Ο φάκελος δεν μπορεί να δημιουργηθεί</string>
-    <string name="filename_forbidden_characters">Μη-επιτρεπόμενοι χαρακτήρες: / \\ &lt; &gt; : \" | ? *</string>
+    <string name="filename_forbidden_characters">Απαγορευμένοι χαρακτήρες: / \\ &lt; &gt; : \" | ? *</string>
     <string name="filename_forbidden_charaters_from_server">Το όνομα αρχείου περιέχει έναν τουλάχιστον μη έγκυρο χαρακτήρα</string>
     <string name="filename_empty">Το όνομα αρχείου δεν μπορεί να είναι κενό</string>
     <string name="wait_a_moment">Παρακαλούμε περιμένετε</string>
@@ -266,9 +306,9 @@
     <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">- Η URL δεν ταιριάζει το όνομα του συστήματος στο πιστοποιητικό</string>
-    <string name="ssl_validator_question">Θέλετε να θεωρείται έμπιστο το πιστοποιητικό αυτό παρ\' όλα αυτά;</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="ssl_validator_btn_details_see">Λεπτομέρειες</string>
     <string name="ssl_validator_btn_details_hide">Απόκρυψη</string>
@@ -276,7 +316,7 @@
     <string name="ssl_validator_label_issuer">Εκδόθηκε από:</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_OU">Μονάδα οργανισμού:</string>
     <string name="ssl_validator_label_C">Χώρα:</string>
     <string name="ssl_validator_label_ST">Πολιτεία:</string>
     <string name="ssl_validator_label_L">Τοποθεσία:</string>
@@ -305,12 +345,15 @@
     <string name="instant_upload_on_charging">Μεταφόρτωση μόνο κατά τη διάρκεια φόρτισης</string>
     <string name="instant_upload_path">/InstantUpload</string>
     <string name="conflict_title">Διένεξη αρχείων</string>
-    <string name="conflict_message">Ποια αρχεία θέλετε να διατηρήσετε; Αν επιλέξετε και τις δύο εκδόσεις, στο τοπικό αρχείο θα προστεθεί ένας αριθμός στο όνομά του.</string>
+    <string name="conflict_message">Ποια αρχεία θέλετε να διατηρήσετε; Αν επιλέξετε και τις δύο εκδόσεις, θα προστεθεί ένας αριθμός στο όνομα του τοπικού αρχείου.</string>
     <string name="conflict_keep_both">Διατήρηση και των δύο</string>
     <string name="conflict_use_local_version">τοπική έκδοση</string>
     <string name="conflict_use_server_version">έκδοση διακομιστή</string>
 
+    <string name="preview_sorry">Σας ζητούμε συγγνώμη γι\'αυτό!</string>
     <string name="preview_image_description">Προεπισκόπηση εικόνας</string>
+    <string name="preview_image_error_unknown_format">Αδυναμία προβολής εικόνας</string>
+
     <string name="error__upload__local_file_not_copied">Το %1$s δεν μπόρεσε να αντιγραφεί στον τοπικό φάκελο %2$s</string>
     <string name="prefs_instant_upload_path_title">Φάκελος άμεσης μεταφόρτωσης</string>
     <string name="prefs_folder_sync_local_path_title">Τοπικός φάκελος</string>
@@ -318,12 +361,12 @@
     <string name="prefs_instant_upload_path_use_subfolders_title">Χρήση υποφακέλων</string>
     <string name="prefs_instant_upload_path_use_subfolders_summary">Αποθήκευση σε υποφακέλους με βάση το χρόνο και μήνα</string>
 
-	<string name="share_link_no_support_share_api">Λυπούμαστε, ο διαμοιρασμός δεν είναι διαθέσιμος στο διακομιστή σας. Παρακαλούμε επικοινωνήστε με τον διαχειριστή σας.</string>
-	<string name="share_link_file_no_exist">Αδύνατη η κοινή χρήση. Παρακαλώ ελέγξτε αν υπάρχει ο φάκελος</string>
+	<string name="share_link_no_support_share_api">Λυπούμαστε, ο διαμοιρασμός δεν είναι διαθέσιμος στο διακομιστή σας. Παρακαλούμε επικοινωνήστε με τον διαχειριστή.</string>
+	<string name="share_link_file_no_exist">Αδυναμία διαμοιρασμού. Παρακαλούμε ελέγξτε αν υπάρχει ο φάκελος</string>
 	<string name="share_link_file_error">Παρουσιάστηκε σφάλμα κατά την προσπάθεια διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
-	<string name="unshare_link_file_no_exist">Αδύνατη η διακοπή κοινής χρήσης.  Παρακαλώ ελέγξτε αν το αρχείο υπάρχει</string>
-	<string name="unshare_link_file_error">Παρουσιάρτηκε σφάλμα κατά τη διάρκεια ακύρωσης διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
-    <string name="update_link_file_no_exist">Αδύνατη η ενημέρωση. Παρακαλώ ελέγξτε αν το αρχείο υπάρχει</string>
+	<string name="unshare_link_file_no_exist">Αδυναμία αναίρεσης κοινής χρήσης.  Παρακαλώ ελέγξτε αν το αρχείο υπάρχει</string>
+	<string name="unshare_link_file_error">Παρουσιάστηκε σφάλμα κατά τη διάρκεια αναίρεσης διαμοιρασμού αυτού του αρχείου ή φακέλου</string>
+    <string name="update_link_file_no_exist">Αδυναμία ενημέρωσης. Παρακαλούμε ελέγξτε αν το αρχείο υπάρχει</string>
     <string name="update_link_file_error">Παρουσιάστηκε σφάλμα κατά την προσπάθεια ενημέρωσης του διαμοιρασμού</string>
     <string name="share_link_password_title">Εισάγετε ένα συνθηματικό</string>
     <string name="share_link_empty_password">Πρέπει να εισάγετε ένα συνθηματικό</string>
@@ -346,7 +389,7 @@
     <string name="forbidden_permissions_rename">για να μετονομάσετε αυτό το αρχείο</string>
     <string name="forbidden_permissions_delete">για να διαγράψετε αυτό το αρχείο</string>
     <string name="share_link_forbidden_permissions">για να διαμοιραστείτε αυτό το αρχείο</string>
-    <string name="unshare_link_forbidden_permissions">για να διακόψετε το διαμοιρασμό αυτού του αρχείου</string>
+    <string name="unshare_link_forbidden_permissions">για να αναιρέσετε το διαμοιρασμό αυτού του αρχείου</string>
     <string name="update_link_forbidden_permissions">για να ενημερώσετε αυτό τον διαμοιρασμό</string>
     <string name="forbidden_permissions_create">για να δημιουργήσετε το αρχείο</string>
     <string name="uploader_upload_forbidden_permissions">για να μεταφορτώσετε σε αυτό το φάκελο</string>
@@ -357,7 +400,7 @@
     <string name="file_migration_preparing">Προετοιμασία για μεταφορά&#8230;</string>
     <string name="file_migration_checking_destination">Γίνεται έλεγχος προορισμού&#8230;</string>
     <string name="file_migration_saving_accounts_configuration">Γίνεται αποθήκευση ρυθμίσεων λογαριασμών&#8230;</string>
-    <string name="file_migration_waiting_for_unfinished_sync">Αναμονή για τους ανολοκλήρωτους συγχρονισμούς&#8230;</string>
+    <string name="file_migration_waiting_for_unfinished_sync">Αναμονή για τους μη ολοκληρωμένους συγχρονισμούς&#8230;</string>
     <string name="file_migration_migrating">Γίνεται μετακίνηση δεδομένων&#8230;</string>
     <string name="file_migration_updating_index">Γίνεται ενημέρωση ευρετηρίου&#8230;</string>
     <string name="file_migration_cleaning">Γίνεται εκκαθάριση&#8230;</string>
@@ -377,11 +420,11 @@
     <string name="prefs_category_accounts">Λογαριασμοί</string>
     <string name="prefs_add_account">Προσθήκη λογαριασμού</string>
     <string name="drawer_manage_accounts">Διαχείριση λογαριασμών</string>
-    <string name="auth_redirect_non_secure_connection_title">Ασφαλής σύνδεση ανακατευθύνεται μέσω μιας μη ασφαλούς διαδρομής.</string>
+    <string name="auth_redirect_non_secure_connection_title">Η ασφαλής σύνδεση ανακατευθύνεται μέσω μιας μη ασφαλούς διαδρομής.</string>
 
 	<string name="actionbar_logger">Αρχεία καταγραφών</string>
 	<string name="log_send_history_button">Αποστολή ιστορικού</string>
-	<string name="log_send_no_mail_app">Δεν εντοπίστηκε εφαρμογή αποστολής ιστορικού συστήματος. Παρακαλώ εγκαταστήστε την εφαρμογή ηλεκτρονικού ταχυδρομείου</string>
+	<string name="log_send_no_mail_app">Δεν εντοπίστηκε εφαρμογή αποστολής ιστορικού συστήματος. Παρακαλούμε εγκαταστήστε μια εφαρμογή ηλεκτρονικού ταχυδρομείου.</string>
 	<string name="log_send_mail_subject">%1$s ιστορικό της εφαρμογής Android</string>
 	<string name="log_progress_dialog_text">Φόρτωση δεδομένων &#8230;</string>
 
@@ -399,8 +442,8 @@
     <string name="forbidden_permissions_move">για μετακίνηση αυτού του αρχείου</string>
 
 
-    <string name="copy_file_not_found">Αδύνατη η αντιγραφή. Παρακαλώ ελέγξτε αν το αρχείο υπάρχει</string>
-    <string name="copy_file_invalid_into_descendent">Δεν είναι δυνατό να αντιγραφεί ο φάκελος σε παράγωγό του φάκελο</string>
+    <string name="copy_file_not_found">Αδυναμία αντιγραφής. Παρακαλούμε ελέγξτε αν το αρχείο υπάρχει</string>
+    <string name="copy_file_invalid_into_descendent">Δεν είναι δυνατό να αντιγραφεί ο φάκελος σε απογονικό</string>
     <string name="copy_file_invalid_overwrite">Το αρχείο υπάρχει ήδη στο φάκελο προορισμού</string>
     <string name="copy_file_error">Παρουσιάστηκε σφάλμα κατά την προσπάθεια αντιγραφής αυτού του αρχείου ή φακέλου</string>
     <string name="forbidden_permissions_copy">για αντιγραφή αυτού του αρχείου</string>
@@ -441,7 +484,7 @@
     <string name="pref_behaviour_entries_move">μετακινήθηκε στον φάκελο εφαρμογών</string>
     <string name="pref_behaviour_entries_delete_file">διαγράφηκε</string>
     <string name="prefs_storage_path">Διαδρομή αποθηκευτικού χώρου</string>
-    <string name="prefs_common">Κοινές</string>
+    <string name="prefs_common">Κοινό</string>
 
     <string name="share_dialog_title">Γίνεται διαμοιρασμός</string>
     <string name="share_file">Διαμοιρασμός %1$s</string>
@@ -466,7 +509,7 @@
     <string name="share_email_clarification">%1$s (ηλ. ταχυδρομείο)</string>
     <string name="share_known_remote_clarification">%1$s ( στο %2$s )</string>
 
-    <string name="share_sharee_unavailable">Δεν επιτρέπεται ο διαμοιρασμός μεταξύ χρηστών μέσω εφαρμογής, σε αυτή την έκδοση διακομιστή. \nΕπικοινωνήστε με το διαχειριστή</string>
+    <string name="share_sharee_unavailable">Δεν επιτρέπεται ο διαμοιρασμός μεταξύ χρηστών μέσω πελατών, σε αυτή την έκδοση διακομιστή. \nΕπικοινωνήστε με το διαχειριστή</string>
     <string name="share_privilege_can_share">δυνατότητα διαμοιρασμού</string>
     <string name="share_privilege_can_edit">δυνατότητα επεξεργασίας</string>
     <string name="share_privilege_can_edit_create">δημιουργία</string>
@@ -484,17 +527,17 @@
     <string name="action_switch_list_view">Προβολή λίστας</string>
 
     <string name="manage_space_title">Διαχείριση χώρου</string>
-    <string name="manage_space_description">Τα πιστοποιητικά ρυθμίσεων, βάσης δεδομένων και διακομιστή από τα δεδομένα %1$s\'s θα διαγραφούν μόνιμα.\\n\\nΤα ληφθέντα αρχεία θα παραμείνουν ανέπαφα.\\n\\nΑυτή η διαδικασία ενδεχομένως να διαρκέσει λίγο.</string>
+    <string name="manage_space_description">Τα πιστοποιητικά ρυθμίσεων, βάσης δεδομένων και διακομιστή από τα δεδομένα %1$s\'s θα διαγραφούν μόνιμα.\n\nΤα ληφθέντα αρχεία θα παραμείνουν ανέπαφα.\n\nΑυτή η διαδικασία ενδεχομένως να διαρκέσει αρκετά.</string>
     <string name="manage_space_clear_data">Εκκαθάριση δεδομένων</string>
     <string name="manage_space_error">Μερικά αρχεία δεν μπορούν να διαγραφούν.</string>
 
     <string name="permission_storage_access">Επιπλέον διακαιώματα απαιτούνται για μεταφόρτωση &amp; λήψη αρχείων.</string>
     <string name="local_file_not_found_toast">Το αρχείο δεν βρέθηκε στο τοπικό σύστημα αρχείων</string>
     <string name="confirmation_remove_files_alert">Θέλετε να αφαιρέσετε τα επιλεγμένα αντικείμενα;</string>
-    <string name="confirmation_remove_folders_alert">Θέλετε ν αφαιρέσετε τα επιλεγμένα αντικείμενα με τα περιεχόμενά τους;</string>
+    <string name="confirmation_remove_folders_alert">Θέλετε να αφαιρέσετε τα επιλεγμένα αντικείμενα με τα περιεχόμενά τους;</string>
     <string name="uploads_view_upload_status_waiting_for_charging">Αναμονή για φόρτιση συσκευής</string>
     <string name="actionbar_search">Αναζήτηση</string>
-    <string name="files_drop_not_supported">Αυτό είναι χαρακτηριστικό του Nextcloud, παρακαλώ ενημερώστε.</string>
+    <string name="files_drop_not_supported">Αυτό είναι χαρακτηριστικό του Nextcloud, παρακαλούμε ενημερώστε.</string>
     <string name="learn_more">Μάθετε περισσότερα</string>
     <string name="drawer_folder_sync">Αυτόματη μεταφόρτωση</string>
     <string name="drawer_participate">Συμμετοχή</string>
@@ -502,27 +545,32 @@
     <string name="participate_testing_bug_text">Βρήκατε σφάλμα; Κάτι σας φαίνεται παράξενο;</string>
     <string name="participate_testing_report_text">Αναφέρετε σφάλμα στο Github</string>
     <string name="participate_testing_version_text">Σας ενδιαφέρει να μας βοηθήσετε να δοκιμάσουμε την επόμενη έκδοση;</string>
-    <string name="participate_release_candidate_headline">Υποψήφια κυκλοφορία</string>
-    <string name="participate_release_candidate_text">Η υποψήφια κυκλοφορία (RC) είναι ένα στιγμιότυπο της επερχόμενης έκδοσης και αναμένεται να είναι σταθερό. Η δοκιμή της μεμονωμένης εγκατάστασής σας θα βοηθήσει στην εξασφάλιση αυτού. Εγγραφείτε για δοκιμές στο Play store ή χειροκίνητα αναζητείστε στο τμήμα \"εκδόσεις\" στο F-Droid.</string>
+    <string name="participate_beta_headline">Δοκιμάστε την έκδοση nightly</string>
+    <string name="participate_beta_text">Αυτή περιέχει όλες τις επερχόμενες λειτουργίες και δαθέτει ότι τελευταίο έχει ενσωματωθεί. Σφάλματα/λάθη μπορεί να προκύψουν. Σας παρακαλούμε να μας τα αναφέρετε.</string>
+    <string name="participate_release_candidate_headline">Release candidate</string>
+    <string name="participate_release_candidate_text">Η υποψήφια κυκλοφορία (RC) είναι ένα στιγμιότυπο της επερχόμενης έκδοσης και αναμένεται να είναι σταθερή. Η δοκιμή της μεμονωμένης εγκατάστασής σας θα βοηθήσει στην εξασφάλιση αυτού. Εγγραφείτε για δοκιμές στο Play store ή χειροκίνητα αναζητείστε στο τμήμα \"εκδόσεις\" στο F-Droid.</string>
     <string name="participate_contribute_headline">Ενεργή συνεισφορά</string>
     <string name="participate_contribute_irc_text">Συμμετέχετε στη συνομιλία στο IRC: &lt;a href=\"%1$s\">#nextcloud-mobile&lt;/a></string>
-    <string name="participate_contribute_forum_text">Βοηθήστε τους άλλους στο &lt;a href=\"%1$s\">forum&lt;/a></string>
-    <string name="participate_contribute_translate_text">&lt;a href=\"%1$s\">Μετάφραση&lt;/a> της εφαρμογής</string>
-    <string name="participate_contribute_github_text">Συνεισφέρετε ως προγραμματιστής, για περισσότερες λεπτομέρειες δείτε &lt;a href=\"https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md\">ΣΥΝΕΙΣΦΕΡΕΤΕ.md&lt;/a></string>
+    <string name="participate_contribute_forum_text">Βοηθήστε χρήστες στο &lt;a href=\"%1$s\">forum&lt;/a></string>
+    <string name="participate_contribute_translate_text">&lt;a href=\"%1$s\">Μεταφράστε&lt;/a> την εφαρμογή</string>
+    <string name="participate_contribute_github_text">Συνεισφέρετε ως προγραμματιστής, για περισσότερες λεπτομέρειες δείτε &lt;a href=\"https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md\">CONTRIBUTING.md&lt;/a></string>
     <string name="move_to">Μετακίνηση σε&#8230;</string>
     <string name="copy_to">Αντιγραφή σε&#8230;</string>
     <string name="choose_remote_folder">Επιλογή φακέλου&#8230;</string>
     <string name="folder_sync_loading_folders">Φόρτωση φακέλων&#8230;</string>
     <string name="folder_sync_no_results">Δεν βρέθηκαν φάκελοι πολυμέσων.</string>
-    <string name="folder_sync_preferences">Αυτόματη μεταφόρτωση προτιμήσεων</string>
+    <string name="folder_sync_preferences">Προτιμήσεις αυτόματης μεταφόρτωσης</string>
     <string name="folder_sync_settings">Ρυθμίσεις</string>
-    <string name="folder_sync_new_info">Η άμεση μεταφόρτωση έχει ανανεωθεί τελείως. Παρακαλώ δείτε το κυρίως μενού και ρυθμίστε ξανά την αυτόματη μεταφόρτωση. Σας ζητούμε συγγνώμη για την ενόχληση\\n\\nΑπολαύστε τις νέες και εκτεταμένες δυνατότητες της αυτόματης μεταφόρτωσης!</string>
+    <string name="folder_sync_new_info">Η άμεση μεταφόρτωση έχει ανανεωθεί τελείως. Παρακαλούμε δείτε το κυρίως μενού και ρυθμίστε ξανά την αυτόματη μεταφόρτωση. Σας ζητούμε συγγνώμη για την ενόχληση\n\nΑπολαύστε τις νέες και εκτεταμένες δυνατότητες της αυτόματης μεταφόρτωσης!</string>
     <string name="folder_sync_preferences_folder_path">Για %1$s</string>
     <plurals name="items_selected_count">
         <item quantity="one">%d επιλέχθηκε</item>
         <item quantity="other">%d επιλέχθηκαν</item>
     </plurals>
 
+    <string name="activity_list_loading_activity">Γίνεται φόρτωση δραστηριοτήτων&#8230;</string>
+    <string name="activity_list_no_results">Δεν βρέθηκαν δραστηριότητες.</string>
+
     <string name="upload_file_dialog_title">Εισαγωγή ονόματος και τύπου αρχείου μεταφόρτωσης</string>
     <string name="upload_file_dialog_filename">Όνομα αρχείου</string>
     <string name="upload_file_dialog_filetype">Τύπος αρχείου</string>
@@ -549,4 +597,18 @@
 
     <string name="whats_new_skip">Παράλειψη</string>
 
+    <!-- User information -->
+    <string name="user_info_full_name">Πλήρες όνομα</string>
+    <string name="user_info_email">Ηλεκτρονικό ταχυδρομείο</string>
+    <string name="user_info_phone">Αριθμός τηλεφώνου</string>
+    <string name="user_info_address">Διεύθυνση</string>
+    <string name="user_info_website">Ιστοσελίδα</string>
+    <string name="user_info_twitter">Twitter</string>
+
+    <string name="user_information_description">Πληροφορίες χρήστη</string>
+
+    <!-- Activities -->
+    <string name="activities_no_results_headline">Καμία δραστηριότητα ακόμα</string>
+    <string name="activities_no_results_message">Σε αυτήν τη ροή θα εμφανιστούν γενονότα όπως\nπροσθήκες, αλλαγές &amp; κοινόχρηστα</string>
+
     </resources>

+ 11 - 8
src/main/res/values-es-rMX/strings.xml

@@ -60,7 +60,7 @@
     <string name="prefs_help">Ayuda</string>
     <string name="prefs_recommend">Recomendar a un amigo</string>
     <string name="prefs_feedback">Retroalimentación</string>
-    <string name="prefs_imprint">Imprint</string>
+    <string name="prefs_imprint">Exención de responsabilidad </string>
     <string name="prefs_remember_last_share_location">Recordar la ubicación del elemento compartido</string>
     <string name="prefs_remember_last_upload_location_summary">Recordar la última ubicación del elemento compartido cargado.</string>
 
@@ -108,7 +108,9 @@
     <string name="file_list_empty_headline_server_search_videos">No hay videos</string>
     <string name="file_list_empty_headline_server_search_photos">No hay fotos</string>
     <string name="file_list_empty_search">¿Intentó buscar en otra carpeta?</string>
-    <string name="file_list_empty_recently_modified">Ningún archivo fue modificado en los últimos 7 días</string>
+    <string name="file_list_empty_recently_modified">No se encontraron archivos que hayan sido modificado en los últimos 7 días</string>
+    <string name="file_list_empty_recently_modified_filter">¡No se encontraron archivos para su búsqueda que hayan sido modificados 
+en los últimos 7 días!</string>
     <string name="file_list_empty_recently_added">No se encontraron archivos agregados recientemente</string>
     <string name="file_list_empty_recently_added_filter">¡No se encontraron archivos agregados recientemente para su consulta!</string>
     <string name="file_list_empty_text_photos">¡Cargue algunas fotos o active la carga automática!</string>
@@ -229,8 +231,8 @@
     <string name="media_err_io">El archivo no ha podido ser leído</string>
     <string name="media_err_malformed">El archivo no ha sido codificado correctamente</string>
     <string name="media_err_timeout">Tiempo de espera expirado al intentar reproducir</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_invalid_progressive_playback">Archivo de medio no puede ser transmitido como flujo</string>
+    <string name="media_err_unknown">El archivo de medios no se puede reproducir con el reproductor predeterminado</string>
     <string name="media_err_security_ex">Se presentó un error de seguridad al intentar reproducir %1$s</string>
     <string name="media_err_io_ex">Se presentó un error en la entrada de datos al intentar reproducir %1$s</string>
     <string name="media_err_unexpected">Se presentó un error inesperado al intentar reproducir %1$s</string>
@@ -274,7 +276,7 @@
     <string name="favorite">Establer como disponible fuera de línea</string>
     <string name="unfavorite">Establecer como disponible en línea</string>
     <string name="favorite_real">Establecer como favorito</string>
-    <string name="unset_favorite_real">Desestablecer como favorito</string>
+    <string name="unset_favorite_real">Quitar de los favoritos</string>
     <string name="common_rename">Renombrar</string>
     <string name="common_remove">Eliminar</string>
     <string name="confirmation_remove_file_alert">¿Realmente desea eliminar %1$s?</string>
@@ -350,6 +352,8 @@
 
     <string name="preview_sorry">¡Disculpas por eso!</string>
     <string name="preview_image_description">Vista previa de imagen</string>
+    <string name="preview_image_error_unknown_format">La imagen no puede ser mostrada</string>
+
     <string name="error__upload__local_file_not_copied">%1$s no pudo ser copiado a la carpeta local %2$s</string>
     <string name="prefs_instant_upload_path_title">Carpeta de carga instantánea</string>
     <string name="prefs_folder_sync_local_path_title">Carpeta local</string>
@@ -517,7 +521,7 @@
     <string name="action_retry_uploads">Falla en el reintento</string>
     <string name="action_clear_failed_uploads">Borrar fallidos</string>
     <string name="action_clear_successful_uploads">Borrar exitosos</string>
-    <string name="action_clear_finished_uploads">Borrar completados</string>
+    <string name="action_clear_finished_uploads">Borrar todos</string>
 
     <string name="action_switch_grid_view">Vista de cuadrícula</string>
     <string name="action_switch_list_view">Vista de lista</string>
@@ -607,5 +611,4 @@
     <string name="activities_no_results_headline">Aún no hay actividades</string>
     <string name="activities_no_results_message">Este flujo le mostrará eventos tales como \nagregados, cambios &amp; compartidos</string>
 
-
-</resources>
+    </resources>

+ 118 - 79
src/main/res/values-es/strings.xml

@@ -19,12 +19,21 @@
     <string name="menu_item_sort_by_date_newest_first">El más reciente primero</string>
     <string name="menu_item_sort_by_date_oldest_first">El más viejo primero</string>
     <string name="menu_item_sort_by_size_biggest_first">El más grande primero</string>
-    <string name="menu_item_sort_by_size_smallest_first">El más pequeno primero</string>
+    <string name="menu_item_sort_by_size_smallest_first">El más pequeño primero</string>
 
     <string name="drawer_item_all_files">Todos los archivos</string>
+    <string name="drawer_item_files">Archivos</string>
+    <string name="drawer_item_home">Inicio</string>
+    <string name="drawer_item_favorites">Favoritos</string>
+    <string name="drawer_item_photos">Fotos</string>
     <string name="drawer_item_on_device">En el dispositivo</string>
+    <string name="drawer_item_recently_added">Recientemente añadido</string>
+    <string name="drawer_item_recently_modified">Recientemente modificado</string>
+    <string name="drawer_item_shared">Compartido</string>
+    <string name="drawer_item_videos">Videos</string>
     <string name="drawer_item_settings">Ajustes</string>
     <string name="drawer_item_uploads_list">Subidas</string>
+    <string name="drawer_item_activities">Actividades</string>
     <string name="drawer_quota">%1$s de %2$s utilizado</string>
 	<string name="drawer_close">Cerrar</string>
     <string name="drawer_open">Abrir</string>
@@ -46,56 +55,75 @@
     <string name="prefs_calendar_contacts">Sincronizar calendario y contactos</string>
     <string name="prefs_calendar_contacts_summary">Configurar DAVdroid (v1.3.0 +) de la cuenta actual</string>
     <string name="prefs_calendar_contacts_address_resolve_error">La dirección del Servidor para su cuenta no ha podido ser resuelta por DAVdroid</string>
-    <string name="prefs_calendar_contacts_no_store_error">No está instalada ni la aplicación de Google Play ni la de F-Droid</string>
+    <string name="prefs_calendar_contacts_no_store_error">No está instalada la aplicación de Google Play ni la de F-Droid</string>
     <string name="prefs_calendar_contacts_sync_setup_successful">Calendario &amp; contactos se ha sincronizado exitosamente</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">pie de imprenta</string>
+    <string name="prefs_feedback">Observaciones</string>
+    <string name="prefs_imprint">Pie de imprenta</string>
     <string name="prefs_remember_last_share_location">Recordar la ubicación de los archivos compartidos</string>
     <string name="prefs_remember_last_upload_location_summary">Recordar la ubicación de los últimos archivos compartidos subidos</string>
 
 	<string name="recommend_subject">¡Prueba  %1$s en su smarthphone!</string>
-	<string name="recommend_text">¡Quiero invitarle a usar %1$s en su smartphone!\nDescárguelo aquí: %2$s</string>
+	<string name="recommend_text">¡Quiero invitarle a usar %1$s en su smartphone!\nDescárgalo aquí: %2$s</string>
 
     <string name="auth_check_server">Verificar servidor</string>
     <string name="auth_host_url">Dirección del servidor https://…</string>
     <string name="auth_username">Nombre de usuario</string>
     <string name="auth_password">Contraseña</string>
-    <string name="auth_register">¿No tiene un servidor aun?\nHaga click aquí para obtener uno de un proveedor</string>
+    <string name="auth_register">¿No tiene un servidor aun?\nHaga click aquí para obtener uno desde un proveedor</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">Elige carpeta de subida</string>
     <string name="uploader_wrn_no_account_title">No se encontró la cuenta</string>
-    <string name="uploader_wrn_no_account_text">No hay %1$s cuentas en tu dispositivo. Por favor añade una cuenta primero.</string>
+    <string name="uploader_wrn_no_account_text">No hay %1$s cuentas en su dispositivo. Por favor añada 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_error_title_no_file_to_upload">Ningún archivo para subir</string>
-    <string name="uploader_error_message_received_piece_of_text">%1$s  No puedo subir un texto como si fuera un archivo.</string>
+    <string name="uploader_error_message_received_piece_of_text">%1$s  No se puede subir un texto como si fuera un archivo.</string>
     <string name="uploader_error_message_no_file_to_upload">No hay ningún archivo válido en los datos recibidos.</string>
     <string name="uploader_error_title_file_cannot_be_uploaded">No se puede subir el archivo</string>
     <string name="uploader_error_message_read_permission_not_granted">%1$s No está autorizado a leer el archivo.</string>
-    <string name="uploader_error_message_source_file_not_found">El archivo a subir no se localiza. Compruebe que el archivo existe.</string>
+    <string name="uploader_error_message_source_file_not_found">El archivo a subir no se encuentra. Compruebe que el archivo existe.</string>
     <string name="uploader_error_message_source_file_not_copied">Ha ocurrido un error al copiar a la carpeta temporal. Por favor intentelo de nuevo.</string>
     <string name="uploader_upload_files_behaviour">Configuraciones de subida:</string>
-    <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Mover el fichero a la carpeta de Nextcloud</string>
-    <string name="uploader_upload_files_behaviour_only_upload">Mantener el fichero en la carpeta original</string>
-    <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Borrar fichero de la carpeta original</string>
+    <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Mover el archivo a la carpeta de Nextcloud</string>
+    <string name="uploader_upload_files_behaviour_only_upload">Mantener el archivo en la carpeta original</string>
+    <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Borrar archivo de la carpeta original</string>
     <string name="file_list_seconds_ago">hace segundos</string>
     <string name="file_list_empty_headline">No hay archivos aquí</string>
     <string name="file_list_empty">¡Suba algun contenido o sincronice con sus dispositivos!</string>
+    <string name="file_list_empty_favorites">¡Marque algun archivos como favoritos o sincronice con sus dispositivos!</string>
+    <string name="file_list_empty_favorites_filter_list">Los archivos y carpetas que marque como favoritos aparecerán aquí</string>
+    <string name="file_list_empty_favorites_filter">¡No se encontraron archivos favoritos en su consulta!</string>
     <string name="file_list_loading">Cargando&#8230;</string>
     <string name="file_list_no_app_for_file_type">¡Aplicación no encontrada para el tipo de archivo!</string>
     <string name="local_file_list_empty">No hay archivos en esta carpeta.</string>
     <string name="file_list_empty_headline_search">No hay resultados en esta carpeta</string>
-    <string name="file_list_empty_search">¿A intentado buscar en otra carpeta?</string>
+    <string name="file_list_empty_headline_server_search">Sin resultados</string>
+    <string name="file_list_empty_favorite_headline">Sin favoritos</string>
+    <string name="file_list_empty_shared_headline">Aún no hay nada compartido</string>
+    <string name="file_list_empty_shared">Aquí aparecerán los archivos y carpetas que usted comparta</string>
+    <string name="file_list_empty_headline_server_search_videos">No hay videos</string>
+    <string name="file_list_empty_headline_server_search_photos">No hay fotos</string>
+    <string name="file_list_empty_search">¿Ha intentado buscar en otra carpeta?</string>
+    <string name="file_list_empty_recently_modified">No se modificaron archivos en los últimos 7 días</string>
+    <string name="file_list_empty_recently_modified_filter">¡No se encontraron para su consulta, archivos que 
+ fueran modificados en los últimos 7 días!</string>
+    <string name="file_list_empty_recently_added">No se encontraron archivos agregados recientemente</string>
+    <string name="file_list_empty_recently_added_filter">¡No se encontraron archivos agregados recientemente para su consulta!</string>
+    <string name="file_list_empty_text_photos">¡Suba algunos archivos o active la subida automática!</string>
+    <string name="file_list_empty_text_photos_filter">¡No hay fotos encontradas para su consulta!</string>
+    <string name="file_list_empty_text_videos">¡Suba unos videos o active la subida automática!</string>
+    <string name="file_list_empty_text_videos_filter">¡No se encontraron videos para su consulta!</string>
     <string name="upload_list_empty_headline">No hay subidas disponibles</string>
     <string name="upload_list_empty_text">¡Suba algún contenido o active subidas instantáneas!</string>
+    <string name="upload_list_empty_text_auto_upload">¡Suba con tenido o active la subida automática!</string>
     <string name="file_list_folder">carpeta</string>
     <string name="file_list_folders">carpetas</string>
-    <string name="file_list_file">fichero</string>
-    <string name="file_list_files">ficheros</string>
+    <string name="file_list_file">archivo</string>
+    <string name="file_list_files">archivos</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>
@@ -103,8 +131,8 @@
     <string name="filedetails_modified">Modificado:</string>
     <string name="filedetails_download">Descargar</string>
     <string name="filedetails_sync_file">Sincronizar</string>
-    <string name="filedetails_renamed_in_upload_msg">El fichero fue renombrado como %1$s durante la subida</string>
-    <string name="list_layout">Listar fuente</string>
+    <string name="filedetails_renamed_in_upload_msg">El archivo fue renombrado como %1$s durante la subida</string>
+    <string name="list_layout">Listar diseño</string>
     <string name="action_share">Compartir</string>
     <string name="common_yes">Sí</string>
     <string name="common_no">No</string>
@@ -146,7 +174,7 @@
     <string name="uploads_view_upload_status_failed_retry">La subida se volverá a intentar en breve</string>
     <string name="uploads_view_upload_status_failed_credentials_error">Error de credenciales</string>
     <string name="uploads_view_upload_status_failed_folder_error">Error de carpeta</string>
-    <string name="uploads_view_upload_status_failed_file_error">Error de fichero</string>
+    <string name="uploads_view_upload_status_failed_file_error">Error de archivo</string>
     <string name="uploads_view_upload_status_failed_localfile_error">Archivo local no encontrado</string>
     <string name="uploads_view_upload_status_failed_permission_error">Error de permisos</string>
     <string name="uploads_view_upload_status_conflict">Conflicto</string>
@@ -168,12 +196,12 @@
     <string name="sync_fail_content">La sincronización de %1$s no pudo ser completada</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 ficheros</string>
+    <string name="sync_conflicts_in_favourites_content">No se pudo mantener sincronizados los 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 pudieron sincronizarse (%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 en la carpeta %2$s no pudieron ser copiados a</string>
-    <string name="sync_foreign_files_forgotten_explanation">A partir de la versión 1.3.16, los ficheros subidos desde este dispositivo se copian en la carpeta local %1$s para evitar la pérdida de datos cuando se sincroniza un único archivo con varias cuentas.\n\nDebido a este cambio, todos los ficheros subidos con versiones anteriores de esta aplicación fueron copiados a la carpeta %2$s. Sin embargo, un error impidió que se completara esta operación durante la sincronización de la cuenta. Puede dejar los archivos tal y como están y eliminar el enlace a %3$s o mover los archivos a la carpeta %1$s y mantener el enlace a %4$s.\n\nDebajo se muestran los archivos locales y los archivos remotos en %5$s a los que fueron enlazados.</string>
+    <string name="sync_foreign_files_forgotten_explanation">A partir de la versión 1.3.16, los archivos subidos desde este dispositivo se copian en la carpeta local %1$s para evitar la pérdida de datos cuando se sincroniza un único archivo con varias cuentas.\n\nDebido a este cambio, todos los archivos subidos con versiones anteriores de esta aplicación fueron copiados a la carpeta %2$s. Sin embargo, un error impidió que se completara esta operación durante la sincronización de la cuenta. Puede dejar los archivos tal cual como están y eliminar el enlace hacia %3$s o mover los archivos a la carpeta %1$s y mantener el enlace hacia %4$s.\n\nAbajo se muestran los archivos locales y los archivos remotos en %5$s a los que fueron enlazados.</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>
@@ -183,27 +211,27 @@
     <string name="upload_query_move_foreign_files">No hay suficiente espacio para copiar los archivos seleccionados en la carpeta %1$s. En su lugar, ¿le gustaría moverlos?</string>
     <string name="pass_code_enter_pass_code">Por favor introduzca su código</string>
     
-    <string name="pass_code_configure_your_pass_code">Introduzca su contraseña</string>
-    <string name="pass_code_configure_your_pass_code_explanation">La contraseña será requerida cada vez que la aplicación sea iniciada</string>
+    <string name="pass_code_configure_your_pass_code">Introduzca su código</string>
+    <string name="pass_code_configure_your_pass_code_explanation">El código será requerida cada vez que la aplicación sea iniciada</string>
     <string name="pass_code_reenter_your_pass_code">Por favor reintroduzca su código</string>
-    <string name="pass_code_remove_your_pass_code">Borre su contraseña</string>
+    <string name="pass_code_remove_your_pass_code">Borre su código</string>
     <string name="pass_code_mismatch">Los códigos de acceso no son idénticos</string>
     <string name="pass_code_wrong">Código de acceso incorrecto</string>
     <string name="pass_code_removed">Código de acceso borrado</string>
-    <string name="pass_code_stored">Contraseña almacenada</string>
+    <string name="pass_code_stored">Código almacenado</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 encontró el archivo multimedia</string>
-    <string name="media_err_no_account">No se ha proporcionado cuenta</string>
+    <string name="media_err_no_account">No se ha proporcionado una cuenta</string>
     <string name="media_err_not_in_owncloud">El archivo no está en una cuenta válida </string>
-    <string name="media_err_unsupported">Codec no soportado</string>
+    <string name="media_err_unsupported">Códec 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_invalid_progressive_playback">El 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="media_err_io_ex">Error de entrada al intentar reproducir %1$s</string>
@@ -219,7 +247,7 @@
 	<string name="auth_connection_established">Conexión establecida</string>
 	<string name="auth_testing_connection">Comprobando la conexión</string>
 	<string name="auth_not_configured_title">Configuración de servidor en formato incorrecto</string>
-	<string name="auth_account_not_new">Ya existe una cuenta en este dispositivo con los mismos datos de Usuario y Servidor</string>
+	<string name="auth_account_not_new">Ya existe una cuenta en este dispositivo con los mismos datos de usuario y servidor</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">Error: no se pudo encontrar el host</string>
@@ -232,32 +260,34 @@
 	<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 incorrectos</string>
-	<string name="auth_oauth_error">Autorización no satisfactoria</string>
-	<string name="auth_oauth_error_access_denied">Acceso denegado por servidor de autorización</string>
+	<string name="auth_oauth_error">Autorización fallida</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 introduzca su dirección de correo 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 su 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_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 autentificació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="auth_fail_get_user_name">Su servidor no está retornando una identificación de usuario correcta; contacte a un administrador </string>
-	<string name="auth_can_not_auth_against_server">No se puedo autenticar a este servidor</string>
+	<string name="auth_can_not_auth_against_server">No puede autenticarse a este servidor</string>
     <string name="auth_account_does_not_exist">Aún no existe la cuenta en el dispositivo</string>
     
-    <string name="favorite">Marcar como disponible offline</string>
-    <string name="unfavorite">Desmarcar como disponible offline</string>
+    <string name="favorite">Marcar el modo disponible a desconectado</string>
+    <string name="unfavorite">Desmarcar el modo disponible de desconectado</string>
+    <string name="favorite_real">Marque como favorito</string>
+    <string name="unset_favorite_real">Desmarque como favorito</string>
     <string name="common_rename">Renombrar</string>
     <string name="common_remove">Borrar</string>
-    <string name="confirmation_remove_file_alert">¿Está seguro de que quiere elimintar %1$s?</string>
+    <string name="confirmation_remove_file_alert">¿Está seguro que quiere eliminar %1$s?</string>
     <string name="confirmation_remove_folder_alert">¿Realmente desea eliminar %1$s y todo su contenido?</string>
     <string name="confirmation_remove_local">Sólo local</string>
     <string name="remove_success_msg">Borrado correctamente</string>
     <string name="remove_fail_msg">El borrado no se pudo completar</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_local_fail_msg">No se pudo cambiar el nombre de la copia local, trate 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 se ha podido comprobar el fichero remoto</string>
+    <string name="sync_file_fail_msg">No se ha podido comprobar el archivo remoto</string>
     <string name="sync_file_nothing_to_do_msg">Ya está sincronizado</string>
     <string name="create_dir_fail_msg">No se pudo crear la carpeta</string>
     <string name="filename_forbidden_characters">Caracteres ilegales: / \\ &lt; &gt; : \" | ? *</string>
@@ -266,7 +296,7 @@
     <string name="wait_a_moment">Espere un momento</string>
     <string name="wait_checking_credentials">Comprobando las credenciales guardadas</string>
     <string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, pruebe otra app para seleccionar el archivo</string>
-    <string name="filedisplay_no_file_selected">No hay ficheros seleccionados.</string>
+    <string name="filedisplay_no_file_selected">No hay archivos seleccionados.</string>
     <string name="activity_chooser_title">Enviar enlace a &#8230;</string>
     <string name="wait_for_tmp_copy_from_private_storage">Copiando el archivo desde el almacenamiento privado.</string>
     
@@ -296,13 +326,13 @@
     <string name="ssl_validator_label_signature">Firma:</string>
     <string name="ssl_validator_label_signature_algorithm">Algoritmo:</string>
     <string name="digest_algorithm_not_available">Este algoritmo no está disponible en tu teléfono.</string>
-    <string name="ssl_validator_label_certificate_fingerprint">Firma:</string>
+    <string name="ssl_validator_label_certificate_fingerprint">Huella dactilar:</string>
     <string name="certificate_load_problem">Existe un problema al cargar el certificado.</string>
     <string name="ssl_validator_null_cert">No se ha podido mostrar el certificado</string>
     <string name="ssl_validator_no_info_about_error">- No hay información acerca del error</string>
 
     <string name="placeholder_sentence">Esto es un marcador de posición</string>
-    <string name="placeholder_filename">marcadordeposición.txt</string>
+    <string name="placeholder_filename">placeholder.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>
@@ -313,7 +343,7 @@
     <string name="instant_video_upload_on_wifi">Subir archivos por wifi únicamente</string>
     <string name="instant_video_upload_on_charging">Subir solo al cargar</string>
     <string name="instant_upload_on_charging">Subir solo al cargar</string>
-    <string name="instant_upload_path">/SubidasInstantáneas</string>
+    <string name="instant_upload_path">/InstantUpload</string>
     <string name="conflict_title">Conflicto con archivo</string>
     <string name="conflict_message">¿Qué archivos desea mantener? Si selecciona ambas versiones, el archivo local tendrá un número añadido a su nombre.</string>
     <string name="conflict_keep_both">Mantener ambos</string>
@@ -322,6 +352,8 @@
 
     <string name="preview_sorry">¡Lo sentimos!</string>
     <string name="preview_image_description">Previsualización de imagen</string>
+    <string name="preview_image_error_unknown_format">La foto no puede ser mostrada.</string>
+
     <string name="error__upload__local_file_not_copied">%1$s se pudo copiar a la carpeta local %2$s</string>
     <string name="prefs_instant_upload_path_title">Carpeta para subida instantánea</string>
     <string name="prefs_folder_sync_local_path_title">Carpeta local</string>
@@ -329,11 +361,11 @@
     <string name="prefs_instant_upload_path_use_subfolders_title">Usar subcarpetas</string>
     <string name="prefs_instant_upload_path_use_subfolders_summary">Archivar en subcarpetas basadas en año y mes.</string>
 
-	<string name="share_link_no_support_share_api">La función Compartir no está activada en su servidor. Contacte a su administrador.</string>
+	<string name="share_link_no_support_share_api">Lo siento, la función compartir no está activada en su servidor. Contacte a su administrador.</string>
 	<string name="share_link_file_no_exist">No se puede compartir. Revise si el archivo 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_no_exist">No se puede dejar de compartir. Revise si el archivo existe</string>
-	<string name="unshare_link_file_error">Ocurrió un error al tratar de ya no compartir este archivo o carpeta</string>
+	<string name="unshare_link_file_error">Ocurrió un error al tratar de descompartir este archivo o carpeta</string>
     <string name="update_link_file_no_exist">No se puede actualizar. Revise si el archivo existe</string>
     <string name="update_link_file_error">Un error ha ocurrido mientras se intentaba actualizar el archivo compartido</string>
     <string name="share_link_password_title">Introduzca una contraseña</string>
@@ -357,7 +389,7 @@
     <string name="forbidden_permissions_rename">para renombrar este archivo</string>
     <string name="forbidden_permissions_delete">para eliminar este archivo</string>
     <string name="share_link_forbidden_permissions">para compartir este archivo</string>
-    <string name="unshare_link_forbidden_permissions">para dejar de compartir este archivo</string>
+    <string name="unshare_link_forbidden_permissions">para descompartir este archivo</string>
     <string name="update_link_forbidden_permissions">para actualizar este comparto</string>
     <string name="forbidden_permissions_create">para crear el archivo</string>
     <string name="uploader_upload_forbidden_permissions">para subir archivos a esta carpeta</string>
@@ -392,40 +424,40 @@
 
 	<string name="actionbar_logger">Registros</string>
 	<string name="log_send_history_button">Mandar historial.</string>
-	<string name="log_send_no_mail_app">Aplicación para enviar registros no encontrada. Por favor instale una aplicación de correo.</string>
-	<string name="log_send_mail_subject">Se han encontrado %1$s logs de la app Android</string>
+	<string name="log_send_no_mail_app">Aplicación para enviar registros no fue encontrada. Por favor instale una aplicación de correo.</string>
+	<string name="log_send_mail_subject">Se han encontrado %1$s aplicaciones de registros para Android</string>
 	<string name="log_progress_dialog_text">Cargando datos &#8230;</string>
 
 	<string name="saml_authentication_required_text">Se necesita autenticación</string>
 	<string name="saml_authentication_wrong_pass">Contraseña incorrecta</string>
 	<string name="actionbar_move">Mover</string>
     <string name="actionbar_copy">Copiar</string>
-	<string name="file_list_empty_moving">Aquí no hay nada. ¡Puede agregar una carpeta!</string>
+	<string name="file_list_empty_moving">Nada por aquí. ¡Puede agregar una carpeta!</string>
 	<string name="folder_picker_choose_button_text">Elegir</string>
 
     <string name="move_file_not_found">No se puede mover. Revise si el archivo existe</string>
     <string name="move_file_invalid_into_descendent">No se puede mover una carpeta dentro de una de sus subcarpetas.</string>
     <string name="move_file_invalid_overwrite">El archivo ya existe en la carpeta de destino</string>
     <string name="move_file_error">Hubo un error al tratar de mover este archivo o carpeta</string>
-    <string name="forbidden_permissions_move">para mover este archivo</string>
+    <string name="forbidden_permissions_move">mover este archivo</string>
 
 
     <string name="copy_file_not_found">No se puede copiar. Revise si existe el archivo</string>
     <string name="copy_file_invalid_into_descendent">No se puede copiar una carpeta dentro de una de sus subcarpetas.</string>
-    <string name="copy_file_invalid_overwrite">El fichero ya existe en el directorio de destino</string>
+    <string name="copy_file_invalid_overwrite">El archivo ya existe en el directorio de destino</string>
     <string name="copy_file_error">Hubo un error al tratar de copiar este archivo o carpeta</string>
-    <string name="forbidden_permissions_copy">para copiar este archivo</string>
+    <string name="forbidden_permissions_copy">copiar este archivo</string>
 
     <string name="prefs_category_instant_uploading">Subidas instantáneas</string>
     <string name="prefs_category_details">Detalles</string>
 
-	<string name="prefs_instant_video_upload_path_title">Carpeta para subida instantáneo de vídeos</string>
+	<string name="prefs_instant_video_upload_path_title">Carpeta para subida instantánea de vídeos</string>
     <string name="sync_folder_failed_content">La sincronización de la carpeta %1$s no se pudo completar</string>
 
 	<string name="shared_subject_header">compartido</string>
 	<string name="with_you_subject_header">con usted</string>
     
-	<string name="subject_user_shared_with_you">%1$s compartió \"%2$s\" conmigo</string>
+	<string name="subject_user_shared_with_you">%1$s compartió \"%2$s\" con usted</string>
     <string name="subject_shared_with_you">\"%1$s\" ha sido compartido con usted.</string>
 
     <string name="auth_refresh_button">Refrescar la conexión</string>
@@ -449,7 +481,7 @@
     <string name="select_all">Seleccionar todo</string>
 
     <string name="pref_behaviour_entries_keep_file">dejado en la carpeta original</string>
-    <string name="pref_behaviour_entries_move">movido a la carpeta apps</string>
+    <string name="pref_behaviour_entries_move">movido a la carpeta app</string>
     <string name="pref_behaviour_entries_delete_file">borrado</string>
     <string name="prefs_storage_path">Ruta de almacenamiento</string>
     <string name="prefs_common">Común</string>
@@ -474,72 +506,75 @@
     <string name="search_users_and_groups_hint">Buscar usuarios y grupos</string>
     <string name="share_group_clarification">%1$s (grupo)</string>
     <string name="share_remote_clarification">%1$s (remoto)</string>
-    <string name="share_email_clarification">%1$s (email)</string>
+    <string name="share_email_clarification">%1$s (correo electrónico)</string>
     <string name="share_known_remote_clarification">%1$s ( en %2$s )</string>
 
-    <string name="share_sharee_unavailable">Lo sentimos, su versión de servidor no permite compartir con usuarios desde los clientes.\nPor favor, contacte con su administrador</string>
+    <string name="share_sharee_unavailable">Lo siento, su versión de servidor no permite compartir con usuarios desde los clientes.\nPor favor, contacte con su administrador</string>
     <string name="share_privilege_can_share">puede compartir</string>
     <string name="share_privilege_can_edit">puede editar</string>
     <string name="share_privilege_can_edit_create">crear</string>
-    <string name="share_privilege_can_edit_change">cambio</string>
+    <string name="share_privilege_can_edit_change">cambiar</string>
     <string name="share_privilege_can_edit_delete">borrar</string>
     <string name="edit_share_unshare">Parar de compatir</string>
     <string name="edit_share_done">hecho</string>
 
     <string name="action_retry_uploads">Reintento fallido</string>
-    <string name="action_clear_failed_uploads">Limpiar fallido</string>
-    <string name="action_clear_successful_uploads">Limpiar exitoso</string>
-    <string name="action_clear_finished_uploads">Limpiar todo lo terminado</string>
+    <string name="action_clear_failed_uploads">Limpieza fallida</string>
+    <string name="action_clear_successful_uploads">Limpieza exitosa</string>
+    <string name="action_clear_finished_uploads">Limpiar todo al finalizar</string>
 
-    <string name="action_switch_grid_view">Vista de rejilla</string>
+    <string name="action_switch_grid_view">Vista en cuadrícula</string>
     <string name="action_switch_list_view">Ver lista</string>
 
     <string name="manage_space_title">Gestionar espacio</string>
-    <string name="manage_space_description">Las opciones, certificados y Bases de Datos de %1$s\'s serán borrados permanentemente.\n\nLos archibos bajados no serán tocados.\n\nEste proceso tardará algún tiempo.</string>
+    <string name="manage_space_description">Las opciones, certificados y bases de datos de %1$s serán borrados permanentemente.\n\nLos archivos bajados no se tocarán.\n\nEste proceso tardará algún tiempo.</string>
     <string name="manage_space_clear_data">Limpiar datos</string>
     <string name="manage_space_error">No se han podido eliminar algunos archivos</string>
 
     <string name="permission_storage_access">Se requieren permisos adicionales para subir &amp; y descargar archivos.</string>
     <string name="local_file_not_found_toast">El archivo no se encuentra en el servidor local de archivos.</string>
     <string name="confirmation_remove_files_alert">¿Está seguro de que quiere eliminar los elementos seleccionados?</string>
-    <string name="confirmation_remove_folders_alert">¿Está sguro de que quiere eliminar los elementos seleccionados y sus contenidos?</string>
-    <string name="uploads_view_upload_status_waiting_for_charging">Esperando a la carga del dispositivo</string>
+    <string name="confirmation_remove_folders_alert">¿Está seguro de que quiere eliminar los elementos seleccionados y sus contenidos?</string>
+    <string name="uploads_view_upload_status_waiting_for_charging">Esperando la carga del dispositivo</string>
     <string name="actionbar_search">Buscar</string>
     <string name="files_drop_not_supported">Esta es una característica de Nextcloud. Por favor, actualice.</string>
-    <string name="learn_more">Aprender mas</string>
+    <string name="learn_more">Aprender más</string>
     <string name="drawer_folder_sync">Subida automática</string>
     <string name="drawer_participate">Participar</string>
-    <string name="participate_testing_headline">Ayúdanos a hacer pruebas</string>
+    <string name="participate_testing_headline">Ayúdanos a realizar pruebas</string>
     <string name="participate_testing_bug_text">¿Encontró un error? ¿Algo está mal?</string>
-    <string name="participate_testing_report_text">Informa de un problema en Github</string>
-    <string name="participate_testing_version_text">¿Interesado en ayudarnos a probar la próxima versión?</string>
-    <string name="participate_beta_headline">Pruebe la versión \"nightly\"</string>
-    <string name="participate_beta_text">Esto incluye todas las características por llegar y está al filo de la novedad. Pueden ocurrir fallos y errores y, si sucede, por favor infórmanos.</string>
-    <string name="participate_release_candidate_headline">Release candidate</string>
-    <string name="participate_release_candidate_text">La release candidate (RC) es una instantánea de la próxima versión y se espera que sea estable. Probar su configuración individual podría ayudar a asegurar esto. Regístrese para probarla en Play Store o busque manualmente en la sección \"versiones\" de F-Droid.</string>
-    <string name="participate_contribute_headline">Contribuye activamente</string>
+    <string name="participate_testing_report_text">Informar de un problema en Github</string>
+    <string name="participate_testing_version_text">¿Está interesado en ayudarnos a probar la próxima versión?</string>
+    <string name="participate_beta_headline">Pruebar la versión \"nightly\" (de pruebas)</string>
+    <string name="participate_beta_text">Esto incluye todas las características por llegar y está muy avazado en lo último. Pueden ocurrir fallos y errores, que si sucede, por favor infórmanos.</string>
+    <string name="participate_release_candidate_headline">Versión a ser liberada</string>
+    <string name="participate_release_candidate_text">La versión a ser liberada (release candidate - RC) es una instantánea de la próxima versión y se espera que sea estable. Probar su configuración individual podría ayudar a asegurar esto. Regístrese para probarla en Play Store o busque manualmente en la sección \"versiones\" de F-Droid.</string>
+    <string name="participate_contribute_headline">Colabore activamente</string>
     <string name="participate_contribute_irc_text">Únase a la conversación en IRC:  &lt;a href=\"%1$s\">#nextcloud-mobile&lt;/a></string>
-    <string name="participate_contribute_forum_text">Ayude a otros en el &lt;a href="%1$s">foro&lt;/a>.</string>
-    <string name="participate_contribute_translate_text">&lt;a href="%1$s">Traduzca&lt;/a> la app</string>
-    <string name="participate_contribute_github_text">Contribuya como desarrolador, vea see &lt;a href=\"https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md\">CONTRIBUTING.md&lt;/a> para más detalles</string>
+    <string name="participate_contribute_forum_text">Ayude a otros en el foro &lt;a href=\"%1$s\">&lt;/a>.</string>
+    <string name="participate_contribute_translate_text">&lt;a href=\"%1$s\">Traducen&lt;/a> la app</string>
+    <string name="participate_contribute_github_text">Contribuya como desarrollador, vea &lt;a href=\"https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md\">CONTRIBUTING.md&lt;/a> para más detalles</string>
     <string name="move_to">Mover a&#8230;</string>
     <string name="copy_to">Copiar a&#8230;</string>
-    <string name="choose_remote_folder">Escoja carpeta&#8230;</string>
+    <string name="choose_remote_folder">Elija carpeta&#8230;</string>
     <string name="folder_sync_loading_folders">Cargando carpetas&#8230;</string>
     <string name="folder_sync_no_results">No se han encnotrado carpetas de medios.</string>
     <string name="folder_sync_preferences">Preferencias de subida automática</string>
     <string name="folder_sync_settings">Configuración</string>
-    <string name="folder_sync_new_info">La subida instantánea ha sido completamente renovada. Por favor, vaya al menú principal y reconfigure sus subidas instantáneas. Disculpe las molestias.\n\n¡Disfrute las capacidades de la subida automática, nuevas y extendidas!</string>
+    <string name="folder_sync_new_info">La subida instantánea ha sido completamente renovada. Por favor, vaya al menú principal y reconfigure sus subidas instantáneas. Disculpe las molestias.\n\n¡Disfrute las nuevas y extendidas capacidades de la subida automática!</string>
     <string name="folder_sync_preferences_folder_path">Durante %1$s</string>
     <plurals name="items_selected_count">
         <item quantity="one">%d seleccionado</item>
         <item quantity="other">%d seleccionados</item>
     </plurals>
 
+    <string name="activity_list_loading_activity">Cargando actividades&#8230;</string>
+    <string name="activity_list_no_results">No se encontró actividad.</string>
+
     <string name="upload_file_dialog_title">Ingrese nombre y tipo de archivo de subida</string>
     <string name="upload_file_dialog_filename">Nombre de archivo</string>
     <string name="upload_file_dialog_filetype">Tipo de archivo</string>
-    <string name="upload_file_dialog_filetype_snippet_text">Archivo de texto pequeño (.txt)</string>
+    <string name="upload_file_dialog_filetype_snippet_text">Archivo de texto recortado (.txt)</string>
     <string name="upload_file_dialog_filetype_internet_shortcut">Archivo de atajo a internet (%s)</string>
     <string name="upload_file_dialog_filetype_googlemap_shortcut">Archivo de atajo a Google Maps(%s)</string>
 
@@ -567,9 +602,13 @@
     <string name="user_info_email">Correo electrónico</string>
     <string name="user_info_phone">Número de teléfono</string>
     <string name="user_info_address">Dirección</string>
-    <string name="user_info_website">Sitio web</string>
+    <string name="user_info_website">Página web</string>
     <string name="user_info_twitter">Twitter</string>
 
     <string name="user_information_description">Información de usuario</string>
 
+    <!-- Activities -->
+    <string name="activities_no_results_headline">Aun no hay actividad</string>
+    <string name="activities_no_results_message">Esta secuencia le mostrará eventos como\nagregados, cambios&amp; recursos compartidos</string>
+
     </resources>

+ 2 - 3
src/main/res/values-fi-rFI/strings.xml

@@ -387,7 +387,7 @@
     <string name="file_migration_failed_while_coping">VIRHE: Peilauksen aikana</string>
     <string name="file_migration_failed_while_updating_index">VIRHE: Indeksin päivityksen aikana</string>
 
-    <string name="file_migration_directory_already_exists">Data kansio on jo olemassa, mitä haluat tehdä?</string>
+    <string name="file_migration_directory_already_exists">Data-kansio on jo olemassa, mitä haluat tehdä?</string>
     <string name="file_migration_override_data_folder">Ylikirjoita</string>
     <string name="file_migration_use_data_folder">Käytä olemassa olevaa</string>
 
@@ -562,5 +562,4 @@
     <string name="activities_no_results_headline">Ei vielä toimia</string>
     <string name="activities_no_results_message">Tähän luetteloon ilmestyy tapahtumakuvauksia, kuten\nlisäyksiä, muutoksia&amp; jakamisia</string>
 
-
-</resources>
+    </resources>

+ 4 - 3
src/main/res/values-fr/strings.xml

@@ -52,7 +52,7 @@
     <string name="prefs_log_title_history">Historique de la journalisation</string>
     <string name="prefs_log_summary_history">Cela affiche les logs enregistrés</string>
     <string name="prefs_log_delete_history_button">Effacer le journal</string>
-    <string name="prefs_calendar_contacts">Configurer la synchronisation de l\'agenda &amp; des contacts</string>
+    <string name="prefs_calendar_contacts">Configurer la synchronisation de l\'agenda et des contacts</string>
     <string name="prefs_calendar_contacts_summary">Configurer DAVdroid (v1.3.0+) pour le compte actuel</string>
     <string name="prefs_calendar_contacts_address_resolve_error">Une  adresse de serveur pour le compte pourrait ne pas être résolue par DAVdroid</string>
     <string name="prefs_calendar_contacts_no_store_error">Aucune application Google Play store ou F-Droid installée</string>
@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">Désolé à propos de cela !</string>
     <string name="preview_image_description">Prévisualisation de l\'image</string>
+    <string name="preview_image_error_unknown_format">L\'image ne peut pas être affichée</string>
+
     <string name="error__upload__local_file_not_copied">%1$s n\'a pas pu être copié dans le dossier local %2$s</string>
     <string name="prefs_instant_upload_path_title">Téléversement immédiat du dossier</string>
     <string name="prefs_folder_sync_local_path_title">Dossier local</string>
@@ -609,5 +611,4 @@
     <string name="activities_no_results_headline">Aucune activité pour le moment</string>
     <string name="activities_no_results_message">Ce flux affichera des événements comme\n des ajouts, des changements &amp; des partages</string>
 
-
-</resources>
+    </resources>

+ 16 - 0
src/main/res/values-hu-rHU/strings.xml

@@ -95,10 +95,14 @@
     <string name="file_list_seconds_ago">pár másodperce</string>
     <string name="file_list_empty_headline">Itt nincsenek fájlok</string>
     <string name="file_list_empty">Tölts fel néhány tartalmat, vagy szinkronizálj az eszközöddel!</string>
+    <string name="file_list_empty_favorites">Jelölj kedvencnek néhány fájlt vagy szinkronizálj az eszközeiddel!</string>
+    <string name="file_list_empty_favorites_filter_list">A kedvencnek jelölt fájlokat és mappákat itt találod meg</string>
+    <string name="file_list_empty_favorites_filter">Nincsenek a kérésednek megfelelő kedvenc fájlok!</string>
     <string name="file_list_loading">Betöltés&#8230;</string>
     <string name="file_list_no_app_for_file_type">Nem található alkalmazás a fálj típushoz.</string>
     <string name="local_file_list_empty">Nincs fájl ebben a mappában.</string>
     <string name="file_list_empty_headline_search">Nincsenek eredmények ebben a mappában</string>
+    <string name="file_list_empty_headline_server_search">Nincsenek eredmények</string>
     <string name="file_list_empty_favorite_headline">Nincsenek kedvencek</string>
     <string name="file_list_empty_shared_headline">Nincs semmi megosztva</string>
     <string name="file_list_empty_shared">Az Ön által megosztott fájlok és mappák itt jelennek meg</string>
@@ -106,8 +110,13 @@
     <string name="file_list_empty_headline_server_search_photos">Nincsenek képek</string>
     <string name="file_list_empty_search">Megpróbálsz másik mappában keresni?</string>
     <string name="file_list_empty_recently_modified">Nem talátam az utóbbi 7 napban módosított fájlt</string>
+    <string name="file_list_empty_text_photos">Tölts fel néhány fényképet vagy aktiváld az automatikus feltöltést!</string>
+    <string name="file_list_empty_text_photos_filter">Nincsenek a kérésednek megfelelő fényképek!</string>
+    <string name="file_list_empty_text_videos">Tölts fel néhány videót vagy aktiváld az automatikus feltöltést!</string>
+    <string name="file_list_empty_text_videos_filter">Nincsenek a kérésednek megfelelő videók!</string>
     <string name="upload_list_empty_headline">Nincsenek feltöltések</string>
     <string name="upload_list_empty_text">Tölts fel valami tartalmat vagy kapcsold be az azonnali feltöltést!</string>
+    <string name="upload_list_empty_text_auto_upload">Tölts fel néhány tartalmat vagy aktiváld az automatikus feltöltést!</string>
     <string name="file_list_folder">mappa</string>
     <string name="file_list_folders">mappák</string>
     <string name="file_list_file">fájl</string>
@@ -340,6 +349,8 @@
 
     <string name="preview_sorry">Elnézést kérek!</string>
     <string name="preview_image_description">Előnézeti kép</string>
+    <string name="preview_image_error_unknown_format">A két nem jeleníthető meg</string>
+
     <string name="error__upload__local_file_not_copied">%1$s nem lehet másolni a %2$s helyi mappába</string>
     <string name="prefs_instant_upload_path_title">Azonnali feltöltés mappa</string>
     <string name="prefs_folder_sync_local_path_title">Helyi mappa</string>
@@ -553,6 +564,9 @@
         <item quantity="other">%d kiválasztott</item>
     </plurals>
 
+    <string name="activity_list_loading_activity">Tevékenységek betöltése&#8230;</string>
+    <string name="activity_list_no_results">Nincsenek tevékenységek.</string>
+
     <string name="upload_file_dialog_title">Add meg a feltöltendő fájl nevét és típusát</string>
     <string name="upload_file_dialog_filename">Fájlnév</string>
     <string name="upload_file_dialog_filetype">Fájl típus</string>
@@ -591,4 +605,6 @@
 
     <!-- Activities -->
     <string name="activities_no_results_headline">Nincs még tevékenység</string>
+    <string name="activities_no_results_message">Ez a folyam olyan eseményeket mutat mint\nbővítmények, változások és megosztások</string>
+
     </resources>

+ 6 - 5
src/main/res/values-is/strings.xml

@@ -39,7 +39,7 @@
     <string name="drawer_open">Opna</string>
     <string name="prefs_category_general">Almennt</string>
     <string name="prefs_category_more">Meira</string>
-    <string name="prefs_accounts">Notendaaðgangar</string>
+    <string name="prefs_accounts">Notandaaðgangar</string>
     <string name="prefs_manage_accounts">Sýsla með notandaaðganga</string>
     <string name="prefs_passcode">Læsing með lykilkóða</string>
     <string name="prefs_show_hidden_files">Sýna faldar skrár</string>
@@ -201,7 +201,7 @@
     <string name="sync_fail_in_favourites_content">Efni %1$d skráa var ekki hægt að samstilla (%2$d árekstrar)</string>
     <string name="sync_foreign_files_forgotten_ticker">Sumar staðværar skrár gleymdust</string>
     <string name="sync_foreign_files_forgotten_content">%1$d skrár úr %2$s möppunni var ekki hægt að afrita í</string>
-    <string name="sync_foreign_files_forgotten_explanation">Frá og með útgáfu 1.3.16, munu skrár sem sendar eru frá þessu tæki verða afritaðar inn í staðværu %1$s möppuna, í þeim tilgangi að koma í veg fyrir gagnatap þegar stök skrá er samstillt við marga notendaaðganga.\n\nVegna þessarar breytingar hafa allar skrár sem sendar hafa verið inn í eldri útgáfum kerfisins verið afritaðar í %2$s möppuna. Hins vegar, núna kom villa í veg fyrir að þessari aðgerð lyki um leið og aðgangar voru samstilltir. Þú getur annað hvort látið skrárnar vera eins og þær eru og fjarlægt tengilinn í %3$s, eða fært skrárnar inn í %1$s möppuna og halda tenglinum í %4$s.\n\nStaðværar skrár eru taldar upp hér fyrir neðan, ásamt þeim fjartengdu skrám í %5$s sem þær voru tengdar við.</string>
+    <string name="sync_foreign_files_forgotten_explanation">Frá og með útgáfu 1.3.16, munu skrár sem sendar eru frá þessu tæki verða afritaðar inn í staðværu %1$s möppuna, í þeim tilgangi að koma í veg fyrir gagnatap þegar stök skrá er samstillt við marga notandaaðganga.\n\nVegna þessarar breytingar hafa allar skrár sem sendar hafa verið inn í eldri útgáfum kerfisins verið afritaðar í %2$s möppuna. Hins vegar, núna kom villa í veg fyrir að þessari aðgerð lyki um leið og aðgangar voru samstilltir. Þú getur annað hvort látið skrárnar vera eins og þær eru og fjarlægt tengilinn í %3$s, eða fært skrárnar inn í %1$s möppuna og halda tenglinum í %4$s.\n\nStaðværar skrár eru taldar upp hér fyrir neðan, ásamt þeim fjartengdu skrám í %5$s sem þær voru tengdar við.</string>
     <string name="sync_current_folder_was_removed">Mappan %1$s er ekki lengur til staðar</string>
     <string name="foreign_files_move">Færa allt</string>
     <string name="foreign_files_success">Allar skrár voru færðar</string>
@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">Leiðinlegt þetta!</string>
     <string name="preview_image_description">Forskoðun myndar</string>
+    <string name="preview_image_error_unknown_format">Ekki er hægt að birta myndina</string>
+
     <string name="error__upload__local_file_not_copied">%1$s var ekki hægt að afrita í staðværu %2$s möppuna</string>
     <string name="prefs_instant_upload_path_title">Mappa fyrir beinar innsendingar</string>
     <string name="prefs_folder_sync_local_path_title">Staðvær mappa</string>
@@ -417,7 +419,7 @@
 
     <string name="prefs_category_accounts">Notandaaðgangar</string>
     <string name="prefs_add_account">Bæta við notandaaðgangi</string>
-    <string name="drawer_manage_accounts">Sýsla með notendaaðganga</string>
+    <string name="drawer_manage_accounts">Sýsla með notandaaðganga</string>
     <string name="auth_redirect_non_secure_connection_title">Öruggri tengingu er endurbeint í gegnum óörugga leið.</string>
 
 	<string name="actionbar_logger">Annálar</string>
@@ -609,5 +611,4 @@
     <string name="activities_no_results_headline">Engin virkni ennþá</string>
     <string name="activities_no_results_message">Streymið mun birta atburði á borð við\nviðbætingar, breytingar og deilingar</string>
 
-
-</resources>
+    </resources>

+ 35 - 0
src/main/res/values-it/strings.xml

@@ -13,10 +13,20 @@
     <string name="actionbar_send_file">Invia</string>
     <string name="actionbar_sort">Ordina</string>
     <string name="actionbar_sort_title">Ordina per</string>
+    <string name="sort_by">Ordina per</string>
+    <string name="menu_item_sort_by_name_a_z">A - Z</string>
+    <string name="menu_item_sort_by_name_z_a">Z - A</string>
     <string name="drawer_item_all_files">Tutti i file</string>
+    <string name="drawer_item_files">File</string>
+    <string name="drawer_item_favorites">Preferiti</string>
+    <string name="drawer_item_photos">Foto</string>
     <string name="drawer_item_on_device">Su dispositivo</string>
+    <string name="drawer_item_recently_modified">Modificato di recente</string>
+    <string name="drawer_item_shared">Condiviso</string>
+    <string name="drawer_item_videos">Video</string>
     <string name="drawer_item_settings">Impostazioni</string>
     <string name="drawer_item_uploads_list">Caricamenti</string>
+    <string name="drawer_item_activities">Attività</string>
     <string name="drawer_quota">%1$s di %2$s utilizzati</string>
 	<string name="drawer_close">Chiudi</string>
     <string name="drawer_open">Apri</string>
@@ -81,6 +91,10 @@
     <string name="file_list_no_app_for_file_type">Nessuna applicazione trovata per il tipo di file!</string>
     <string name="local_file_list_empty">Non ci sono file in questa cartella.</string>
     <string name="file_list_empty_headline_search">Nessun risultato in questa cartella</string>
+    <string name="file_list_empty_headline_server_search">Nessun risultato</string>
+    <string name="file_list_empty_favorite_headline">Nessun preferito</string>
+    <string name="file_list_empty_headline_server_search_videos">Nessun video</string>
+    <string name="file_list_empty_headline_server_search_photos">Nessuna foto</string>
     <string name="file_list_empty_search">Vuoi provare a cercare in un\'altra cartella?</string>
     <string name="upload_list_empty_headline">Nessun caricamento disponibile</string>
     <string name="upload_list_empty_text">Carica alcuni contenuti o attiva il caricamento istantaneo!</string>
@@ -312,7 +326,10 @@
     <string name="conflict_use_local_version">Versione locale</string>
     <string name="conflict_use_server_version">versione del server</string>
 
+    <string name="preview_sorry">Spiacenti!</string>
     <string name="preview_image_description">Anteprima dell\'immagine</string>
+    <string name="preview_image_error_unknown_format">L\'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="prefs_instant_upload_path_title">Cartella caricamenti istantanei</string>
     <string name="prefs_folder_sync_local_path_title">Cartella locale</string>
@@ -504,6 +521,7 @@
     <string name="participate_testing_bug_text">Hai trovato un bug? Qualcosa di strano?</string>
     <string name="participate_testing_report_text">Segnala un problema su GitHub</string>
     <string name="participate_testing_version_text">Sei interessato ad aiutarci a provare la nostra prossima versione?</string>
+    <string name="participate_beta_headline">Prova la versione nigthly</string>
     <string name="participate_release_candidate_headline">Candidata al rilascio</string>
     <string name="participate_release_candidate_text">Questa candidata al rilascio (RC) è uno snapshot della prossima versione e dovrebbe essere stabile. La prova della tua specifica configurazione potrebbe aiutare ad assicurare che lo sia. Registrati per i test su Google Play o controlla manualmente nella sezione \"versioni\" su F-Droid.</string>
     <string name="participate_contribute_headline">Contribuisci attivamente</string>
@@ -525,6 +543,9 @@
         <item quantity="other">%d selezionati</item>
     </plurals>
 
+    <string name="activity_list_loading_activity">Caricamento attività in corso &#8230;</string>
+    <string name="activity_list_no_results">Nessuna attività trovata.</string>
+
     <string name="upload_file_dialog_title">Digita il nome e il tipo del file da caricare</string>
     <string name="upload_file_dialog_filename">Nome file</string>
     <string name="upload_file_dialog_filetype">Tipo file</string>
@@ -551,4 +572,18 @@
 
     <string name="whats_new_skip">Salta</string>
 
+    <!-- User information -->
+    <string name="user_info_full_name">Nome completo</string>
+    <string name="user_info_email">Posta elettronica</string>
+    <string name="user_info_phone">Numero di telefono</string>
+    <string name="user_info_address">Indirizzo</string>
+    <string name="user_info_website">Sito web</string>
+    <string name="user_info_twitter">Twitter</string>
+
+    <string name="user_information_description">Informazioni utente</string>
+
+    <!-- Activities -->
+    <string name="activities_no_results_headline">Ancora nessuna attività</string>
+    <string name="activities_no_results_message">Questo flusso mostrerà gli eventi come aggiunte, cambiamenti e condivisioni</string>
+
     </resources>

+ 3 - 2
src/main/res/values-ja-rJP/strings.xml

@@ -353,6 +353,8 @@
 
     <string name="preview_sorry">申し訳ありません!</string>
     <string name="preview_image_description">イメージプレビュー</string>
+    <string name="preview_image_error_unknown_format">画像を表示できません</string>
+
     <string name="error__upload__local_file_not_copied">%1$s は、ローカルフォルダー %2$s  にコピーできませんでした。</string>
     <string name="prefs_instant_upload_path_title">自動アップロードフォルダー</string>
     <string name="prefs_folder_sync_local_path_title">ローカルフォルダー</string>
@@ -610,5 +612,4 @@
     <string name="activities_no_results_message">このタイムラインには、いいね\n
 追加、変更、共有が表示されます</string>
 
-
-</resources>
+    </resources>

+ 69 - 22
src/main/res/values-nb-rNO/strings.xml

@@ -13,10 +13,27 @@
     <string name="actionbar_send_file">Send</string>
     <string name="actionbar_sort">Sorter</string>
     <string name="actionbar_sort_title">Sorter etter</string>
+    <string name="sort_by">Sorter etter</string>
+    <string name="menu_item_sort_by_name_a_z">A - Z</string>
+    <string name="menu_item_sort_by_name_z_a">Z - A</string>
+    <string name="menu_item_sort_by_date_newest_first">Nyeste først</string>
+    <string name="menu_item_sort_by_date_oldest_first">Eldste først</string>
+    <string name="menu_item_sort_by_size_biggest_first">Største først</string>
+    <string name="menu_item_sort_by_size_smallest_first">Minste først</string>
+
     <string name="drawer_item_all_files">Alle filer</string>
+    <string name="drawer_item_files">Filer</string>
+    <string name="drawer_item_home">Hjem</string>
+    <string name="drawer_item_favorites">Favoritter</string>
+    <string name="drawer_item_photos">Bilder</string>
     <string name="drawer_item_on_device">På enheten</string>
+    <string name="drawer_item_recently_added">Nylig lagt til</string>
+    <string name="drawer_item_recently_modified">Nylig endret</string>
+    <string name="drawer_item_shared">Delt</string>
+    <string name="drawer_item_videos">Videoer</string>
     <string name="drawer_item_settings">Innstillinger</string>
     <string name="drawer_item_uploads_list">Opplastinger</string>
+    <string name="drawer_item_activities">Aktiviteter</string>
     <string name="drawer_quota">%1$s av %2$s brukt</string>
 	<string name="drawer_close">Lukk</string>
     <string name="drawer_open">Åpne</string>
@@ -81,7 +98,14 @@
     <string name="file_list_no_app_for_file_type">Ingen applikasjon funnet for filtypen!</string>
     <string name="local_file_list_empty">Det er ingen filer i denne mappen.</string>
     <string name="file_list_empty_headline_search">Ingen resultater i denne mappen</string>
+    <string name="file_list_empty_headline_server_search">Ingen resultater</string>
+    <string name="file_list_empty_favorite_headline">Ingen favoritter</string>
+    <string name="file_list_empty_shared_headline">Ingenting delt enda</string>
+    <string name="file_list_empty_shared">Filer og mapper som du deler vil bli vist her</string>
+    <string name="file_list_empty_headline_server_search_videos">Ingen videoer</string>
+    <string name="file_list_empty_headline_server_search_photos">Ingen bilder</string>
     <string name="file_list_empty_search">Vil du prøve i en annen mappe?</string>
+    <string name="file_list_empty_text_photos">Last opp noen bilder eller aktiver automatisk opplasting!</string>
     <string name="upload_list_empty_headline">Ingen opplastinger tilgjengelig</string>
     <string name="upload_list_empty_text">Last opp innhold eller aktiver umiddelbar opplasting.</string>
     <string name="file_list_folder">mappe</string>
@@ -129,13 +153,13 @@
     <string name="uploader_upload_failed_credentials_error">Opplasting feilet, du må logge inn på nytt</string>
     <string name="uploads_view_title">Opplastinger</string>
     <string name="uploads_view_group_current_uploads">Nåværende</string>
-    <string name="uploads_view_group_failed_uploads">Feilet (trykk for å prøve igjen)</string>
+    <string name="uploads_view_group_failed_uploads">Mislyktes (trykk for å prøve igjen)</string>
     <string name="uploads_view_group_finished_uploads">Opplastet</string>
     <string name="uploads_view_upload_status_succeeded">Ferdig</string>
     <string name="uploads_view_upload_status_cancelled">Avbrutt</string>
     <string name="uploads_view_upload_status_paused">Pauset</string>
     <string name="uploads_view_upload_status_failed_connection_error">Tilkoblingsfeil</string>
-    <string name="uploads_view_upload_status_failed_retry">Opplasting vil prøves igjen snart</string>
+    <string name="uploads_view_upload_status_failed_retry">Nytt forsøk på opplasting vil settes i verk snart</string>
     <string name="uploads_view_upload_status_failed_credentials_error">Legitimasjonsfeil</string>
     <string name="uploads_view_upload_status_failed_folder_error">Mappefeil</string>
     <string name="uploads_view_upload_status_failed_file_error">Filfeil</string>
@@ -145,7 +169,7 @@
     <string name="uploads_view_upload_status_service_interrupted">Applikasjonen ble avsluttet</string>
     <string name="uploads_view_upload_status_unknown_fail">Ukjent feil</string>
     <string name="uploads_view_upload_status_waiting_for_wifi">Venter på trådløstilkobling</string>
-    <string name="uploads_view_later_waiting_to_upload">Venter på å laste op</string>
+    <string name="uploads_view_later_waiting_to_upload">Venter på å laste opp</string>
     <string name="downloader_download_in_progress_ticker">Laster ned &#8230;</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>
@@ -153,7 +177,7 @@
     <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="downloader_download_failed_credentials_error">Nedlasting feilet, du må logge inn på nytt</string>
+    <string name="downloader_download_failed_credentials_error">Nedlasting mislyktes, du må logge inn på nytt</string>
     <string name="common_choose_account">Velg konto</string>
     <string name="sync_fail_ticker">Synkronisering feilet</string>
     <string name="sync_fail_ticker_unauthorized">Synkronisering feilet, du må logge inn på nytt</string>
@@ -161,12 +185,12 @@
     <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_ticker">Holdt-i-synk -filer mislyktes</string>
     <string name="sync_fail_in_favourites_content">Innholdet 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 fra %2$s mappen kunne ikke kopieres til</string>
     <string name="sync_foreign_files_forgotten_explanation">Fra versjon 1.3.16 blir filer som lastes opp fra denne enheten kopiert inn i den lokale mappen %1$s for å forhindre tap av data når samme fil synkroniseres med flere kontoer.\n\nPga. denne endringen ble alle filer som er blitt lastet opp med tidligere versjoner av denne appen, kopiert til mappe %2$s. Imidlertid kunne ikke denne kopieringen fullføres under konto-synkroniseringen pga. en feil. Du kan enten la filen(e) ligge der de ligger og fjerne lenken til %3$s, eller flytte filene til mappe %1$s og beholde lenken til %4$s.\n\nNedenfor finner du en liste over de lokale filene og de eksterne filene i %5$s som de var lenket til.</string>
-    <string name="sync_current_folder_was_removed">Mappen %1$s finnes ikke lengere</string>
+    <string name="sync_current_folder_was_removed">Mappen %1$s finnes ikke lengre</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>
@@ -176,7 +200,7 @@
     <string name="pass_code_enter_pass_code">Sett inn passordet ditt</string>
     
     <string name="pass_code_configure_your_pass_code">Skriv inn passordet ditt</string>
-    <string name="pass_code_configure_your_pass_code_explanation">Passordet vil bli krevd hver gang appen startes</string>
+    <string name="pass_code_configure_your_pass_code_explanation">Passordet vil bli krevd hver gang programmet startes</string>
     <string name="pass_code_reenter_your_pass_code">Skriv inn passordet på nytt</string>
     <string name="pass_code_remove_your_pass_code">Fjern passordet ditt</string>
     <string name="pass_code_mismatch">Passordene er ikke like</string>
@@ -191,14 +215,14 @@
     <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_unsupported">Ustøttet mediakodek</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_unknown">Mediafilen kan ikke spilles med forvalgt 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_io_ex">Inndatafeil under avspilling av %1$s</string>
     <string name="media_err_unexpected">Uventet 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>
@@ -212,13 +236,13 @@
 	<string name="auth_testing_connection">Tester forbindelsen</string>
 	<string name="auth_not_configured_title">Feil i tjenerkonfigurasjon</string>
 	<string name="auth_account_not_new">En konto for samme bruker og tjener 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_account_not_the_same">Den innskrevne brukeren samsvarer ikke med 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 tjenerinstans</string>
 	<string name="auth_timeout_title">Tjeneren brukte for lang tid på å svare</string>
 	<string name="auth_incorrect_address_title">Feil format på tjeneradresse</string>
-	<string name="auth_ssl_general_error_title">Oppstart av SSL feilet</string>
+	<string name="auth_ssl_general_error_title">Oppstart av SSL mislyktes</string>
 	<string name="auth_ssl_unverified_server_title">Kunne ikke verifisere SSL-tjenerens identitet</string>
 	<string name="auth_bad_oc_version_title">Ukjent tjenerversjon</string>
 	<string name="auth_wrong_connection_title">Klarte ikke å opprette tilkobling</string>
@@ -227,9 +251,9 @@
 	<string name="auth_oauth_error">Mislykket autorisasjon</string>
 	<string name="auth_oauth_error_access_denied">Tilgang nektet av autorisasjonstjener</string>
 	<string name="auth_wtf_reenter_URL">Uventet tilstand. Legg inn tjeneradressen på nytt</string>
-	<string name="auth_expired_oauth_token_toast">Autorisasjonen din har gått ut. Vennligt autoriser igjen</string>
+	<string name="auth_expired_oauth_token_toast">Autorisasjonen din har gått ut. Autoriser igjen</string>
 	<string name="auth_expired_basic_auth_toast">Legg inn nåværende passord</string>
-	<string name="auth_expired_saml_sso_token_toast">Sesjonen din har gått ut. Vennligst koble til igjen</string>
+	<string name="auth_expired_saml_sso_token_toast">Økta di har gått ut. Koble til igjen</string>
 	<string name="auth_connecting_auth_server">Kobler til autentiseringstjener..</string>
 	<string name="auth_unsupported_auth_method">Tjeneren støtter ikke denne autorisasjonsmetoden</string>
 	<string name="auth_unsupported_multiaccount">%1$s støtter ikke flere kontoer</string>
@@ -239,6 +263,7 @@
     
     <string name="favorite">Sett som tilgjengelig frakoblet</string>
     <string name="unfavorite">Fjern som tilgjengelig frakoblet</string>
+    <string name="favorite_real">Sett som favoritt</string>
     <string name="common_rename">Endre navn</string>
     <string name="common_remove">Fjern</string>
     <string name="confirmation_remove_file_alert">Vil du virkelig fjerne %1$s?</string>
@@ -265,7 +290,7 @@
     <string name="oauth_check_onoff">Logg inn med oAuth2</string> 
     <string name="oauth_login_connection">Kobler til oAuth2 tjener...</string>    
         
-    <string name="ssl_validator_header">Identiteten til siden kunne ikke verifiseres</string>
+    <string name="ssl_validator_header">Identiteten til siden kunne ikke bekreftes</string>
     <string name="ssl_validator_reason_cert_not_trusted">- Tjenerens sertifikat er ikke til å stole på</string>
     <string name="ssl_validator_reason_cert_expired">- Tjenerens sertifikat er utløpt</string>
     <string name="ssl_validator_reason_cert_not_yet_valid">- Tjenersertifikatets gyldige datoer er i fremtiden</string>
@@ -294,8 +319,8 @@
     <string name="ssl_validator_no_info_about_error">- Ingen informasjon om feilen</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_filename">plassholder.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>
@@ -312,7 +337,10 @@
     <string name="conflict_use_local_version">lokal versjon</string>
     <string name="conflict_use_server_version">tjenerversjon</string>
 
+    <string name="preview_sorry">Beklager!</string>
     <string name="preview_image_description">Bildeforhåndsvisning</string>
+    <string name="preview_image_error_unknown_format">Bildet kan ikke vises</string>
+
     <string name="error__upload__local_file_not_copied">%1$s kunne ikke kopieres til lokal mappe %2$s</string>
     <string name="prefs_instant_upload_path_title">Mappe for umiddelbar opplasting</string>
     <string name="prefs_folder_sync_local_path_title">Lokal mappe</string>
@@ -338,14 +366,14 @@
     <string name="clipboard_uxexpected_error">Uventet feil ved kopiering til utklippstavle</string>
     <string name="clipboard_label">Teksten ble kopiert fra %1$s</string>
 
-    <string name="error_cant_bind_to_operations_service">Kritisk feil: kan ikke utføre operasjonene</string>
+    <string name="error_cant_bind_to_operations_service">Kritisk feil: Kan ikke utføre operasjonene</string>
 
     <string name="network_error_socket_exception">En feil oppstod ved oppretting av forbindelse til tjeneren.</string>
     <string name="network_error_socket_timeout_exception">En feil oppstod ved venting på svar fra tjeneren. Operasjonen kunne ikke utføres</string>
     <string name="network_error_connect_timeout_exception">En feil oppstod ved venting på svar fra tjeneren. Operasjonen kunne ikke utføres</string>
     <string name="network_host_not_available">Operasjonen kunne ikke fullføres. Tjeneren er utilgjengelig</string>
     <string name="forbidden_permissions">Du har ikke tillatelse til %s</string>
-    <string name="forbidden_permissions_rename">å omdøpe denne filen</string>
+    <string name="forbidden_permissions_rename">å gi denne filen nytt navn</string>
     <string name="forbidden_permissions_delete">å slette denne filen</string>
     <string name="share_link_forbidden_permissions">å dele denne filen</string>
     <string name="unshare_link_forbidden_permissions">å avslutte deling av denne filen</string>
@@ -383,7 +411,7 @@
 
 	<string name="actionbar_logger">Logger</string>
 	<string name="log_send_history_button">Send historikk </string>
-	<string name="log_send_no_mail_app">Ingen app for sending av logger funnet. Installer epost-app.</string>
+	<string name="log_send_no_mail_app">Fant inget program til forsendelse av logger. Installer e-post-program.</string>
 	<string name="log_send_mail_subject">%1$s Android applikasjons-logger</string>
 	<string name="log_progress_dialog_text">Laster data &#8230;</string>
 
@@ -440,7 +468,7 @@
     <string name="select_all">Velg alle</string>
 
     <string name="pref_behaviour_entries_keep_file">beholdt i opprinnelig mappe</string>
-    <string name="pref_behaviour_entries_move">flyttet til app-mappe</string>
+    <string name="pref_behaviour_entries_move">flyttet til program-mappe</string>
     <string name="pref_behaviour_entries_delete_file">slettet</string>
     <string name="prefs_storage_path">Lagrings-sti</string>
     <string name="prefs_common">Felles</string>
@@ -504,6 +532,8 @@
     <string name="participate_testing_bug_text">Funnet en feil? Føles noe rart?</string>
     <string name="participate_testing_report_text">Meld en feil på Github</string>
     <string name="participate_testing_version_text">Er du interessert i å hjelpe oss å teste ned neste versjonen?</string>
+    <string name="participate_beta_headline">Test nattskiftversjonen</string>
+    <string name="participate_beta_text">Dette inkluderer alle kommende funksjoner og er helt på kanten. Feil/feilmeldinger kan oppstå og i sådant fall, meld fra om dem til oss.</string>
     <string name="participate_release_candidate_headline">Release candidate</string>
     <string name="participate_release_candidate_text">Release candidate (RC) er en pakke av den kommende utgaven og er forventet å være stabil. Ved å teste denne med ditt oppsett vil kunne hjelpe oss å sikre dette. Meld deg på for testen i Play butikken eller se i versjonsseksjonen i F-Droid.</string>
     <string name="participate_contribute_headline">Bidra aktivt</string>
@@ -525,6 +555,9 @@
         <item quantity="other">%d valgte</item>
     </plurals>
 
+    <string name="activity_list_loading_activity">Laster inn aktiviteter&#8230;</string>
+    <string name="activity_list_no_results">Ingen aktiviteter funnet.</string>
+
     <string name="upload_file_dialog_title">Legg inn filnavn og -type for opplasting</string>
     <string name="upload_file_dialog_filename">Filnavn</string>
     <string name="upload_file_dialog_filetype">Filtype</string>
@@ -541,7 +574,7 @@
 
     <!-- Welcome to Nc intro features -->
     <string name="welcome_feature_1_title">Et trygt hjem for alle dine data</string>
-    <string name="welcome_feature_1_text">Ha tilgang til, del &amp; beskytt filene dine hjemme og i bedriften</string>
+    <string name="welcome_feature_1_text">Benytt, del &amp; beskytt filene dine hjemme og i din bedrift</string>
 
     <string name="welcome_feature_2_title">Multi-konto</string>
     <string name="welcome_feature_2_text">Foren alle skyene dine</string>
@@ -551,4 +584,18 @@
 
     <string name="whats_new_skip">Hopp over</string>
 
+    <!-- User information -->
+    <string name="user_info_full_name">Fullt navn</string>
+    <string name="user_info_email">E-post</string>
+    <string name="user_info_phone">Telefonnummer</string>
+    <string name="user_info_address">Adresse</string>
+    <string name="user_info_website">Nettsted</string>
+    <string name="user_info_twitter">Twitter</string>
+
+    <string name="user_information_description">Brukerinformasjon</string>
+
+    <!-- Activities -->
+    <string name="activities_no_results_headline">Ingen aktivitet enda</string>
+    <string name="activities_no_results_message">Denne strømmen vil vise hendelser som\nf.eks. innlegging, endring og deling</string>
+
     </resources>

+ 17 - 7
src/main/res/values-nl/strings.xml

@@ -94,6 +94,9 @@
     <string name="file_list_seconds_ago">seconden geleden</string>
     <string name="file_list_empty_headline">Hier zijn geen bestanden</string>
     <string name="file_list_empty">Upload bestanden of synchroniseer met je apparaten!</string>
+    <string name="file_list_empty_favorites">Maak enkele bestanden favoriet of synchroniseer met je apparaten!</string>
+    <string name="file_list_empty_favorites_filter_list">Bestanden en mappen gemarkeerd als favoriet worden hier getoond</string>
+    <string name="file_list_empty_favorites_filter">Geen favoriete bestanden gevonden voor je zoekopdracht!</string>
     <string name="file_list_loading">Laden&#160;&#8230;</string>
     <string name="file_list_no_app_for_file_type">Geen app gevonden voor dit bestandsformaat!</string>
     <string name="local_file_list_empty">Er staan geen bestanden in deze map.</string>
@@ -106,10 +109,16 @@
     <string name="file_list_empty_headline_server_search_photos">Geen foto\'s</string>
     <string name="file_list_empty_search">In een andere map kijken?</string>
     <string name="file_list_empty_recently_modified">Geen bestanden gevonden die  de laatste 7 dagen zijn gewijzigd</string>
+    <string name="file_list_empty_recently_modified_filter">Geen gewijzigde bestanden gevonden voor je zoekopdracht die de laatste 7 dagen zijn gewijzigd!</string>
+    <string name="file_list_empty_recently_added">Geen recent toegevoegde bestanden gevoden</string>
+    <string name="file_list_empty_recently_added_filter">Geen recent toegevoegde bestanden gevonden voor je zoekopdracht!</string>
+    <string name="file_list_empty_text_photos">Upload enkele foto\'s of activeer auto-upload!</string>
     <string name="file_list_empty_text_photos_filter">Geen foto\'s voor je zoekopdracht gevonden!</string>
+    <string name="file_list_empty_text_videos">Upload enkele videos of activeer auto-upload!</string>
     <string name="file_list_empty_text_videos_filter">Geen video\'s voor je zoekopdracht gevonden!</string>
     <string name="upload_list_empty_headline">Geen uploads beschikbaar</string>
     <string name="upload_list_empty_text">Upload bestanden of activeer directe uploads!</string>
+    <string name="upload_list_empty_text_auto_upload">Upload enkele gegevens of activeer auto upload!</string>
     <string name="file_list_folder">map</string>
     <string name="file_list_folders">mappen</string>
     <string name="file_list_file">bestand</string>
@@ -342,6 +351,8 @@
 
     <string name="preview_sorry">Sorry hiervoor!</string>
     <string name="preview_image_description">Voorvertoning van afbeelding</string>
+    <string name="preview_image_error_unknown_format">Afbeelding kan niet worden getoond</string>
+
     <string name="error__upload__local_file_not_copied">%1$s kon niet worden gekopieerd naar de lokale map %2$s</string>
     <string name="prefs_instant_upload_path_title">Directe-uploadmap</string>
     <string name="prefs_folder_sync_local_path_title">Lokale map</string>
@@ -349,13 +360,13 @@
     <string name="prefs_instant_upload_path_use_subfolders_title">Gebruik submappen</string>
     <string name="prefs_instant_upload_path_use_subfolders_summary">Opslaan in submappen, gebaseerd op jaar en maand</string>
 
-	<string name="share_link_no_support_share_api">Sorry, delen is niet mogelijk op jouw server. Neem contact op met je beheerder.</string>
+	<string name="share_link_no_support_share_api">Sorry, delen is niet ingeschakeld op jouw server. Neem contact op met je beheerder.</string>
 	<string name="share_link_file_no_exist">Kan dit niet delen. Controleer of dit bestand 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="share_link_file_error">Er trad een fout op bij je poging dit bestand of deze map te delen</string>
 	<string name="unshare_link_file_no_exist">Kan delen niet beëindigen. Ga na of het bestand bestaat</string>
-	<string name="unshare_link_file_error">Er trad een fout op bij uw poging het delen van dit bestand of deze map te beëindigen</string>
+	<string name="unshare_link_file_error">Er trad een fout op bij je poging het delen van dit bestand of deze map te beëindigen</string>
     <string name="update_link_file_no_exist">Kan niet bijwerken. Ga na of het bestand bestaat</string>
-    <string name="update_link_file_error">Er trad een fout op bij uw poging de share bij te werken</string>
+    <string name="update_link_file_error">Er trad een fout op bij je poging de share bij te werken</string>
     <string name="share_link_password_title">Vul het wachtwoord in</string>
     <string name="share_link_empty_password">Je moet een wachtwoord opgeven</string>
 
@@ -597,7 +608,6 @@
 
     <!-- Activities -->
     <string name="activities_no_results_headline">Nog geen activiteit</string>
-    <string name="activities_no_results_message">Deze stream laat gebeurtenissen zoals\toevoegingen, veranderingen &amp; gedeelde bestanden zien</string>
-
+    <string name="activities_no_results_message">Deze stream laat gebeurtenissen zoals\ntoevoegingen, veranderingen &amp; gedeelde bestanden zien</string>
 
-</resources>
+    </resources>

+ 3 - 2
src/main/res/values-pl/strings.xml

@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">Przykro mi z tego powodu!</string>
     <string name="preview_image_description">Podgląd</string>
+    <string name="preview_image_error_unknown_format">Obraz nie może być pokazany</string>
+
     <string name="error__upload__local_file_not_copied">%1$s nie może zostać skopiowany do lokalnego folderu %2$s</string>
     <string name="prefs_instant_upload_path_title">Wysyłka obrazów do folderu</string>
     <string name="prefs_folder_sync_local_path_title">Folder lokalny</string>
@@ -611,5 +613,4 @@
     <string name="activities_no_results_headline">Brak aktywności</string>
     <string name="activities_no_results_message">Ten strumień pokaże zdarzenia jak\ndodatki, zmiany &amp; udziały</string>
 
-
-</resources>
+    </resources>

+ 113 - 113
src/main/res/values-pt-rBR/strings.xml

@@ -22,8 +22,8 @@
     <string name="menu_item_sort_by_size_smallest_first">Menores primeiro</string>
 
     <string name="drawer_item_all_files">Todos os arquivos</string>
-    <string name="drawer_item_files">Files</string>
-    <string name="drawer_item_home">Home</string>
+    <string name="drawer_item_files">Arquivos</string>
+    <string name="drawer_item_home">Início</string>
     <string name="drawer_item_favorites">Favoritos</string>
     <string name="drawer_item_photos">Fotos</string>
     <string name="drawer_item_on_device">No aparelho</string>
@@ -41,14 +41,14 @@
     <string name="prefs_category_more">Mais</string>
     <string name="prefs_accounts">Contas</string>
     <string name="prefs_manage_accounts">Gerenciar contas</string>
-    <string name="prefs_passcode">Bloqueio com senha</string>
+    <string name="prefs_passcode">Bloqueio de código de acesso</string>
     <string name="prefs_show_hidden_files">Mostrar arquivos escondidos</string>
-    <string name="prefs_instant_upload">Envio instantâneo de imagens</string>
-    <string name="prefs_instant_upload_summary">Envia instantaneamente as fotos tiradas com a câmera</string>
-    <string name="prefs_instant_video_upload">Envio instantâneo de vídeos</string>
-    <string name="prefs_instant_video_upload_summary">Envia instantaneamente os vídeos feitos com a câmera</string>
+    <string name="prefs_instant_upload">Envio automático de imagens</string>
+    <string name="prefs_instant_upload_summary">Envia automaticamente as fotos tiradas com a câmera</string>
+    <string name="prefs_instant_video_upload">Envio automático de vídeos</string>
+    <string name="prefs_instant_video_upload_summary">Envia automaticamente os vídeos feitos com a câmera</string>
     <string name="prefs_log_title">Habilitar login</string>
-    <string name="prefs_log_summary">Isto é usado para registrar(log) os problemas</string>
+    <string name="prefs_log_summary">Isto é usado para registrar problemas</string>
     <string name="prefs_log_title_history">Histórico de logins</string>
     <string name="prefs_log_summary_history">Mostra os registros gravados</string>
     <string name="prefs_log_delete_history_button">Excluir histórico</string>
@@ -60,9 +60,9 @@
     <string name="prefs_help">Ajuda</string>
     <string name="prefs_recommend">Recomendar a um amigo</string>
     <string name="prefs_feedback">Feedback</string>
-    <string name="prefs_imprint">Imprint</string>
-    <string name="prefs_remember_last_share_location">Lembre-se do local de compartilhamento</string>
-    <string name="prefs_remember_last_upload_location_summary">Lembrar do último local de envio de compartilhamento</string>
+    <string name="prefs_imprint">Imprimir</string>
+    <string name="prefs_remember_last_share_location">Lembrar o local de compartilhamento</string>
+    <string name="prefs_remember_last_upload_location_summary">Lembrar o último local de envio de compartilhamento</string>
 
 	<string name="recommend_subject">Tentar %1$s em seu smartfone!</string>
 	<string name="recommend_text">Gostaria de convidá-lo a usar %1$s em seu smartphone!\nBaixe aqui: %2$s</string>
@@ -82,7 +82,7 @@
     <string name="uploader_wrn_no_account_quit_btn_text">Sair</string>
     <string name="uploader_error_title_no_file_to_upload">Nenhum arquivo para envio</string>
     <string name="uploader_error_message_received_piece_of_text">%1$s não pode enviar um pedaço de texto como um arquivo.</string>
-    <string name="uploader_error_message_no_file_to_upload">Os dados recebidos não incluem qualquer arquivo válido.</string>
+    <string name="uploader_error_message_no_file_to_upload">Os dados recebidos não incluem um arquivo válido.</string>
     <string name="uploader_error_title_file_cannot_be_uploaded">O arquivo não pode ser enviado</string>
     <string name="uploader_error_message_read_permission_not_granted">%1$s não tem permissão para ler um arquivo recebido</string>
     <string name="uploader_error_message_source_file_not_found">Arquivo para envio não foi encontrado em sua localização. Verifique se o arquivo existe.</string>
@@ -113,13 +113,13 @@
 foram encontrados para sua pesquisa! </string>
     <string name="file_list_empty_recently_added">Nenhum arquivo recentemente adicionado foi encontrado!</string>
     <string name="file_list_empty_recently_added_filter">Nenhum arquivo recentemente adicionado foi encontrado para sua pesquisa!</string>
-    <string name="file_list_empty_text_photos">Envie algumas fotos ou ative o auto-envio!</string>
+    <string name="file_list_empty_text_photos">Envie algumas fotos ou ative o envio automático!</string>
     <string name="file_list_empty_text_photos_filter">Nenhuma foto foi encontrada para sua pesquisa!</string>
-    <string name="file_list_empty_text_videos">Envie alguns vídeos ou ative o auto-envio!</string>
+    <string name="file_list_empty_text_videos">Envie alguns vídeos ou ative o envio automático!</string>
     <string name="file_list_empty_text_videos_filter">Nenhum vídeo encontrado para sua pesquisa!</string>
     <string name="upload_list_empty_headline">Nenhum envio disponível</string>
-    <string name="upload_list_empty_text">Envie algum conteúdo ou ative o envio instantâneo!</string>
-    <string name="upload_list_empty_text_auto_upload">Envie algum conteúdo ou ative o auto-envio!</string>
+    <string name="upload_list_empty_text">Envie algum conteúdo ou ative o envio automático!</string>
+    <string name="upload_list_empty_text_auto_upload">Envie algum conteúdo ou ative o envio automático!</string>
     <string name="file_list_folder">pasta</string>
     <string name="file_list_folders">pastas</string>
     <string name="file_list_file">arquivo</string>
@@ -137,7 +137,7 @@ foram encontrados para sua pesquisa! </string>
     <string name="common_yes">Sim</string>
     <string name="common_no">Não</string>
     <string name="common_ok">OK</string>
-    <string name="common_remove_upload">Remover arquivo enviado</string>
+    <string name="common_remove_upload">Excluir arquivo enviado</string>
     <string name="common_retry_upload">Tentar enviar de novo</string>
     <string name="common_cancel_sync">Cancelar sincronização</string>
     <string name="common_cancel">Cancelar</string>
@@ -151,7 +151,7 @@ foram encontrados para sua pesquisa! </string>
     <string name="common_pending">Pendente</string>
     <string name="about_title">Sobre</string>
     <string name="change_password">Alterar senha</string>
-    <string name="delete_account">Remover conta</string>
+    <string name="delete_account">Excluir conta</string>
     <string name="delete_account_warning">Excluir a conta %s?\n\nNão é possível desfazer a exclusão.</string>
     <string name="create_account">Criar conta</string>
     <string name="upload_chooser_title">Enviar de &#8230;</string>
@@ -162,7 +162,7 @@ foram encontrados para sua pesquisa! </string>
     <string name="uploader_upload_succeeded_content_single">%1$s enviado</string>
     <string name="uploader_upload_failed_ticker">Falha no envio</string>
     <string name="uploader_upload_failed_content_single">O envio de %1$s não pôde ser finalizado</string>
-    <string name="uploader_upload_failed_credentials_error">Falha no envio, você precisa se conectar novamente</string>
+    <string name="uploader_upload_failed_credentials_error">Falha no envio, você precisa reautenticar-se</string>
     <string name="uploads_view_title">Envios</string>
     <string name="uploads_view_group_current_uploads">Atual</string>
     <string name="uploads_view_group_failed_uploads">Falha (toque para tentar novamente)</string>
@@ -171,10 +171,10 @@ foram encontrados para sua pesquisa! </string>
     <string name="uploads_view_upload_status_cancelled">Cancelado</string>
     <string name="uploads_view_upload_status_paused">Pausado</string>
     <string name="uploads_view_upload_status_failed_connection_error">Erro de conexão</string>
-    <string name="uploads_view_upload_status_failed_retry">O arquivo enviado será recarregado em breve</string>
+    <string name="uploads_view_upload_status_failed_retry">O envio será retentado em breve</string>
     <string name="uploads_view_upload_status_failed_credentials_error">Erro de credenciais</string>
-    <string name="uploads_view_upload_status_failed_folder_error">Erro de pasta</string>
-    <string name="uploads_view_upload_status_failed_file_error">Erro de arquivo</string>
+    <string name="uploads_view_upload_status_failed_folder_error">Erro na pasta</string>
+    <string name="uploads_view_upload_status_failed_file_error">Erro no arquivo</string>
     <string name="uploads_view_upload_status_failed_localfile_error">Arquivo local não encontrado</string>
     <string name="uploads_view_upload_status_failed_permission_error">Erro de permissão</string>
     <string name="uploads_view_upload_status_conflict">Conflito</string>
@@ -189,10 +189,10 @@ foram encontrados para sua pesquisa! </string>
     <string name="downloader_download_failed_ticker">Download falhou</string>
     <string name="downloader_download_failed_content">Download de %1$s não pôde ser concluído</string>
     <string name="downloader_not_downloaded_yet">Ainda não baixado</string>
-    <string name="downloader_download_failed_credentials_error">Falha em baixar o arquivo, você precisa se conectar novamente</string>
+    <string name="downloader_download_failed_credentials_error">Falha em baixar o arquivo, você precisa reautenticar-se</string>
     <string name="common_choose_account">Escolha a conta</string>
     <string name="sync_fail_ticker">Falha na sincronização</string>
-    <string name="sync_fail_ticker_unauthorized">Falha na sincronização, você precisa se conectar novamente</string>
+    <string name="sync_fail_ticker_unauthorized">Falha na sincronização, você precisa se logar novamente</string>
     <string name="sync_fail_content">A sincronização de %1$s não pode ser finalizada</string>
     <string name="sync_fail_content_unauthorized">Senha inválida para %1$s</string>
     <string name="sync_conflicts_in_favourites_ticker">Conflitos encontrados</string>
@@ -213,12 +213,12 @@ foram encontrados para sua pesquisa! </string>
     
     <string name="pass_code_configure_your_pass_code">Digite o código de acesso</string>
     <string name="pass_code_configure_your_pass_code_explanation">O código de acesso será solicitado toda vez que o aplicativo for iniciado</string>
-    <string name="pass_code_reenter_your_pass_code">Por favor, digite sua senha novamente</string>
-    <string name="pass_code_remove_your_pass_code">Remover seu código de acesso</string>
+    <string name="pass_code_reenter_your_pass_code">Por favor, digite seu códigode acesso novamente</string>
+    <string name="pass_code_remove_your_pass_code">Excluir seu código de acesso</string>
     <string name="pass_code_mismatch">Os códigos de acesso não são os mesmos</string>
     <string name="pass_code_wrong">Código de acesso incorreto</string>
-    <string name="pass_code_removed">Código de acesso removido</string>
-    <string name="pass_code_stored">Código de acesso armazenado</string>
+    <string name="pass_code_removed">Código de acesso excluído</string>
+    <string name="pass_code_stored">Código de acesso salvo</string>
     
     <string name="media_notif_ticker">%1$s reprodutor de música</string>
     <string name="media_state_playing">%1$s (reproduzindo)</string>
@@ -226,13 +226,13 @@ foram encontrados para sua pesquisa! </string>
     <string name="media_event_done">%1$s reprodução finalizada</string>
     <string name="media_err_nothing_to_play">Nenhum arquivo de mídia encontrado</string>
     <string name="media_err_no_account">Nenhuma conta fornecida</string>
-    <string name="media_err_not_in_owncloud">Arquivo não está em uma conta válida</string>
-    <string name="media_err_unsupported">Codec de mídia não suportado</string>
-    <string name="media_err_io">Arquivo de mídia não pode ser lido</string>
-    <string name="media_err_malformed">Arquivo de mídia não corretamente codificado</string>
-    <string name="media_err_timeout">Expirou o tempo durante a tentativa</string>
-    <string name="media_err_invalid_progressive_playback">Arquivo de mídia não pode ser transmitido</string>
-    <string name="media_err_unknown">Arquivo de mídia não pode ser reproduzido com o stock media player</string>
+    <string name="media_err_not_in_owncloud">O arquivo não está em uma conta válida</string>
+    <string name="media_err_unsupported">O codec de mídia não é suportado</string>
+    <string name="media_err_io">O arquivo de mídia não pode ser lido</string>
+    <string name="media_err_malformed">O arquivo de mídia não está corretamente codificado</string>
+    <string name="media_err_timeout">Expirou o tempo durante a tentativa de reproduzir</string>
+    <string name="media_err_invalid_progressive_playback">O arquivo de mídia não pode ser transmitido</string>
+    <string name="media_err_unknown">O arquivo de mídia não pode ser reproduzido com o stock media player</string>
     <string name="media_err_security_ex">Erro de segurança tentando reproduzir %1$s</string>
     <string name="media_err_io_ex">Erro de entrada tentando reproduzir %1$s</string>
     <string name="media_err_unexpected">Erro inesperado tentando reproduzir %1$s</string>
@@ -250,7 +250,7 @@ foram encontrados para sua pesquisa! </string>
 	<string name="auth_account_not_new">Já existe no dispositivo uma conta para o mesmo usuário e servidor</string>
 	<string name="auth_account_not_the_same">As informações que o usuário digitou não correspondem ao usuário da conta</string>
 	<string name="auth_unknown_error_title">Ocorreu um erro desconhecido!</string>
-	<string name="auth_unknown_host_title">Não pôde encontrar host</string>
+	<string name="auth_unknown_host_title">Não pode encontrar host</string>
 	<string name="auth_incorrect_path_title">Instância de servidor não encontrada</string>
 	<string name="auth_timeout_title">O servidor demorou demais a responder</string>
 	<string name="auth_incorrect_address_title">Formato de endereço de servidor errado</string>
@@ -259,17 +259,17 @@ foram encontrados para sua pesquisa! </string>
 	<string name="auth_bad_oc_version_title">Versão do servidor desconhecida</string>
 	<string name="auth_wrong_connection_title">Não foi possível estabelecer conexão</string>
 	<string name="auth_secure_connection">Conexão segura estabelecida</string>
-	<string name="auth_unauthorized">Nome de usuário e/ou senha está errada!</string>
+	<string name="auth_unauthorized">Nome de usuário ou senha errado</string>
 	<string name="auth_oauth_error">Autorização sem sucesso</string>
 	<string name="auth_oauth_error_access_denied">Acesso negado pelo servidor de autorização</string>
-	<string name="auth_wtf_reenter_URL">Estado inesperado; por favor insira o endereço do servidor novamente</string>
-	<string name="auth_expired_oauth_token_toast">Sua autorização expirou. Por favor, solicite autorizar novamente</string>
+	<string name="auth_wtf_reenter_URL">Estado inesperado. Insira o endereço do servidor novamente</string>
+	<string name="auth_expired_oauth_token_toast">Sua autorização expirou. Por favor, autorize novamente</string>
 	<string name="auth_expired_basic_auth_toast">Por favor, digite a senha atual</string>
 	<string name="auth_expired_saml_sso_token_toast">Sua sessão expirou. Por favor, conecte-se novamente</string>
-	<string name="auth_connecting_auth_server">Conectando ao servidor de autenticação ...</string>
+	<string name="auth_connecting_auth_server">Conectando ao servidor de autenticação...</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 múltiplas contas</string>
-	<string name="auth_fail_get_user_name">Seu servidor não está retornando um id de usuário correto, entre em contato com um administrador</string>
+	<string name="auth_fail_get_user_name">Seu servidor não está retornando um id de usuário correto. Por favor, entre em contato com um administrador</string>
 	<string name="auth_can_not_auth_against_server">Não é possível obter autenticação neste servidor</string>
     <string name="auth_account_does_not_exist">Conta ainda não existe no dispositivo</string>
     
@@ -278,37 +278,37 @@ foram encontrados para sua pesquisa! </string>
     <string name="favorite_real">Marque como favorito</string>
     <string name="unset_favorite_real">Desmarque como favorito</string>
     <string name="common_rename">Renomear</string>
-    <string name="common_remove">Remover</string>
-    <string name="confirmation_remove_file_alert">Deseja realmente remover %1$s?</string>
-    <string name="confirmation_remove_folder_alert">Você realmente deseja remover %1$s e seus conteúdos?</string>
+    <string name="common_remove">Excluir</string>
+    <string name="confirmation_remove_file_alert">Deseja realmente excluir %1$s?</string>
+    <string name="confirmation_remove_folder_alert">Você deseja realmente excluir %1$s e seu conteúdo?</string>
     <string name="confirmation_remove_local">Somente local</string>
-    <string name="remove_success_msg">Removido com sucesso</string>
-    <string name="remove_fail_msg">Erro ao remover</string>
+    <string name="remove_success_msg">Excluído com sucesso</string>
+    <string name="remove_fail_msg">Erro ao excluir</string>
     <string name="rename_dialog_title">Digite um novo nome</string>
-    <string name="rename_local_fail_msg">Cópia local não pôde ser renomeada; tente outro nome</string>
+    <string name="rename_local_fail_msg">Cópia local não pôde ser renomeada. Tente outro nome</string>
     <string name="rename_server_fail_msg">Renomeação não pôde ser finalizada</string>
     <string name="sync_file_fail_msg">Arquivo remoto não pode ser verificado</string>
     <string name="sync_file_nothing_to_do_msg">Conteúdo do arquivo já foi sincronizado</string>
     <string name="create_dir_fail_msg">A pasta não pode ser criada</string>
     <string name="filename_forbidden_characters">Caracteres proibidos: / \\ &lt; &gt; : \" | ? *</string>
-    <string name="filename_forbidden_charaters_from_server">O nome do arquivo contem pelo menos um caractere inválido  </string>
+    <string name="filename_forbidden_charaters_from_server">O nome do arquivo contém pelo menos um caractere inválido  </string>
     <string name="filename_empty">O nome do arquivo não pode estar vazio</string>
     <string name="wait_a_moment">Aguarde um momento</string>
     <string name="wait_checking_credentials">Verificando credenciais salvas</string>
-    <string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor, tente selecionar o arquivo com outro app</string>
+    <string name="filedisplay_unexpected_bad_get_content">Problema inesperado. Por favor, tente selecionar o arquivo com outro aplicativo</string>
     <string name="filedisplay_no_file_selected">Nenhum arquivo foi selecionado</string>
     <string name="activity_chooser_title">Enviar o link para &#8230;</string>
-    <string name="wait_for_tmp_copy_from_private_storage">Copiando o arquivo de armazenagem privada</string>
+    <string name="wait_for_tmp_copy_from_private_storage">Copiando o arquivo da armazenagem privada</string>
     
     <string name="oauth_check_onoff">Login com oAuth2</string> 
-    <string name="oauth_login_connection">Conectando-se a oAuth2 servidor ...</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="ssl_validator_reason_cert_not_trusted">- O certificado do servidor não é confiável</string>
     <string name="ssl_validator_reason_cert_expired">- O certificado do servidor expirou</string>
-    <string name="ssl_validator_reason_cert_not_yet_valid">- O datas de validade do certificado do servidor estão no futuro</string>
-    <string name="ssl_validator_reason_hostname_not_verified">- O URL do host não confere com o host do certificado</string>
-    <string name="ssl_validator_question">Você mesmo assim confia nesse certificado?</string>
+    <string name="ssl_validator_reason_cert_not_yet_valid">- A data de validade do certificado do servidor está no futuro</string>
+    <string name="ssl_validator_reason_hostname_not_verified">- A URL do host não confere com o host do certificado</string>
+    <string name="ssl_validator_question">Você confia nesse certificado mesmo assim?</string>
     <string name="ssl_validator_not_saved">O certificado não pode ser salvo</string>
     <string name="ssl_validator_btn_details_see">Detalhes</string>
     <string name="ssl_validator_btn_details_hide">Ocultar</string>
@@ -339,11 +339,11 @@ foram encontrados para sua pesquisa! </string>
     <string name="placeholder_media_time">12:23:45</string>
 
     <string name="auto_upload_on_wifi">Enviar somente via wifi</string>
-    <string name="instant_upload_on_wifi">Envio de fotos apenas por Wi-Fi</string>
-    <string name="instant_video_upload_on_wifi">Envio de vídeos apenas por Wi-Fi</string>
+    <string name="instant_upload_on_wifi">Enviar fotos apenas por Wi-Fi</string>
+    <string name="instant_video_upload_on_wifi">Enviar vídeos apenas por Wi-Fi</string>
     <string name="instant_video_upload_on_charging">Enviar apenas quando solicitado</string>
     <string name="instant_upload_on_charging">Enviar apenas quando solicitado</string>
-    <string name="instant_upload_path">/Upload instantâneo</string>
+    <string name="instant_upload_path">/Upload automático</string>
     <string name="conflict_title">Conflito de arquivo</string>
     <string name="conflict_message">Quais arquivos você deseja manter? Se você selecionar ambas as versões, o arquivo local terá um número adicionado ao seu nome.</string>
     <string name="conflict_keep_both">Manter ambos</string>
@@ -352,17 +352,19 @@ foram encontrados para sua pesquisa! </string>
 
     <string name="preview_sorry">Desculpe isso!</string>
     <string name="preview_image_description">Pré-visualização da imagem</string>
+    <string name="preview_image_error_unknown_format">Imagem não pode ser mostrada</string>
+
     <string name="error__upload__local_file_not_copied">%1$s não pôde ser copiado para pasta local %2$s</string>
-    <string name="prefs_instant_upload_path_title">Pasta de envio instantâneo</string>
+    <string name="prefs_instant_upload_path_title">Pasta de envio automático</string>
     <string name="prefs_folder_sync_local_path_title">Pasta local</string>
     <string name="prefs_folder_sync_remote_path_title">Pasta remota</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Usar subpastas</string>
-    <string name="prefs_instant_upload_path_use_subfolders_summary">Armazena em subpastas com base no ano e mês</string>
+    <string name="prefs_instant_upload_path_use_subfolders_summary">Armazena em subpastas baseado no ano e mês</string>
 
-	<string name="share_link_no_support_share_api">Lamentamos, mas o compartilhamento não está ativada no servidor. Entre em contato com o administrador.</string>
+	<string name="share_link_no_support_share_api">Lamentamos, mas o compartilhamento não está ativado no servidor. Entre em contato com o administrador.</string>
 	<string name="share_link_file_no_exist">Não é possível compartilhar. Por favor verifique se o arquivo 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_no_exist">Não é possível cancelar o compartilhamento. Por favor verifique se o arquivo existe</string>
+	<string name="unshare_link_file_no_exist">Não é possível descompartilhar. Por favor verifique se o arquivo existe</string>
 	<string name="unshare_link_file_error">Ocorreu um erro ao tentar descompartilhar este arquivo ou pasta</string>
     <string name="update_link_file_no_exist">Não é possível atualizar. Verifique se o arquivo existe</string>
     <string name="update_link_file_error">Ocorreu um erro enquanto tentava atualizar o compartilhamento</string>
@@ -377,23 +379,23 @@ foram encontrados para sua pesquisa! </string>
     <string name="clipboard_uxexpected_error">Erro inesperado ao copiar para a área de transferência</string>
     <string name="clipboard_label">Texto copiado de %1$s</string>
 
-    <string name="error_cant_bind_to_operations_service">Erro crítico: não pode executar operações</string>
+    <string name="error_cant_bind_to_operations_service">Erro crítico: não pode executar as operações</string>
 
     <string name="network_error_socket_exception">Ocorreu um erro durante a conexão com o servidor.</string>
-    <string name="network_error_socket_timeout_exception">Ocorreu um erro enquanto se espera pelo servidor, a operação não poderia ter sido executada</string>
-    <string name="network_error_connect_timeout_exception">Ocorreu um erro enquanto se espera pelo servidor, a operação não poderia ter sido executada</string>
-    <string name="network_host_not_available">A operação não pôde ser concluída, o servidor está indisponível</string>
-    <string name="forbidden_permissions">Você não tem premissão %s</string>
+    <string name="network_error_socket_timeout_exception">Ocorreu um erro enquanto se espera pelo servidor, a operação pode não ter sido executada</string>
+    <string name="network_error_connect_timeout_exception">Ocorreu um erro enquanto se espera pelo servidor, a operação pode não ter sido executada</string>
+    <string name="network_host_not_available">A operação não pode ser concluída, o servidor está indisponível</string>
+    <string name="forbidden_permissions">Você não tem permissão %s</string>
     <string name="forbidden_permissions_rename">para renomear este arquivo</string>
     <string name="forbidden_permissions_delete">para excluir este arquivo</string>
     <string name="share_link_forbidden_permissions">para compartilhar este arquivo</string>
     <string name="unshare_link_forbidden_permissions">para descompartilhar este arquivo</string>
     <string name="update_link_forbidden_permissions">atualizar este compartilhamento</string>
     <string name="forbidden_permissions_create">para criar este arquivo</string>
-    <string name="uploader_upload_forbidden_permissions">para enviar para esta pasta</string>
+    <string name="uploader_upload_forbidden_permissions">para enviar a esta pasta</string>
     <string name="downloader_download_file_not_found">Este arquivo não mais está disponível neste servidor</string>
 
-    <string name="file_migration_dialog_title">Atualizando caminho do storage</string>
+    <string name="file_migration_dialog_title">Atualizando caminho do armazenamento</string>
     <string name="file_migration_finish_button">Finalizado</string>
     <string name="file_migration_preparing">Preparando para migração&#8230;</string>
     <string name="file_migration_checking_destination">Verificando destino&#8230;</string>
@@ -407,13 +409,13 @@ foram encontrados para sua pesquisa! </string>
     <string name="file_migration_failed_not_enough_space">ERRO: Espaço Insuficiente</string>
     <string name="file_migration_failed_not_writable">ERRO: Arquivo não permite gravação</string>
     <string name="file_migration_failed_not_readable">ERRO: Arquivo não permite leitura</string>
-    <string name="file_migration_failed_dir_already_exists">ERRO: diretório ou pasta já existe</string>
+    <string name="file_migration_failed_dir_already_exists">ERRO: Diretório Nexcloud já existe</string>
     <string name="file_migration_failed_while_coping">ERRO: Durante a Migração</string>
-    <string name="file_migration_failed_while_updating_index">ERRO: Durante atualização de indice</string>
+    <string name="file_migration_failed_while_updating_index">ERRO: Durante atualização de índice</string>
 
     <string name="file_migration_directory_already_exists">Pasta de dados já existe, o que fazer?</string>
     <string name="file_migration_override_data_folder">Sobrescrever</string>
-    <string name="file_migration_use_data_folder">Usar existente</string>
+    <string name="file_migration_use_data_folder">Usar a existente</string>
 
     <string name="prefs_category_accounts">Contas</string>
     <string name="prefs_add_account">Adicionar uma conta</string>
@@ -422,11 +424,11 @@ foram encontrados para sua pesquisa! </string>
 
 	<string name="actionbar_logger">Logs</string>
 	<string name="log_send_history_button">Enviar histórico</string>
-	<string name="log_send_no_mail_app">Nenhum aplicativo para envio de logs foi encontrado. Por favor, instale um aplicativo de e-mail.</string>
+	<string name="log_send_no_mail_app">Nenhum aplicativo para envio de logs foi encontrado. Por favor, instale um aplicativo de email.</string>
 	<string name="log_send_mail_subject">%1$s logs do aplicativo Android</string>
 	<string name="log_progress_dialog_text">Carregando dados &#8230;</string>
 
-	<string name="saml_authentication_required_text">Autenticação obrigatória</string>
+	<string name="saml_authentication_required_text">Autenticação necessária</string>
 	<string name="saml_authentication_wrong_pass">Senha incorreta</string>
 	<string name="actionbar_move">Mover</string>
     <string name="actionbar_copy">Copiar</string>
@@ -444,25 +446,25 @@ foram encontrados para sua pesquisa! </string>
     <string name="copy_file_invalid_into_descendent">Não é possível copiar uma pasta em uma descendente</string>
     <string name="copy_file_invalid_overwrite">O arquivo já existe na pasta de destino</string>
     <string name="copy_file_error">Ocorreu um erro ao tentar copiar este arquivo ou pasta</string>
-    <string name="forbidden_permissions_copy">para copiar este arquivo</string>
+    <string name="forbidden_permissions_copy">copiar este arquivo</string>
 
-    <string name="prefs_category_instant_uploading">Envio instantâneo</string>
+    <string name="prefs_category_instant_uploading">Envio automático</string>
     <string name="prefs_category_details">Detalhes</string>
 
-	<string name="prefs_instant_video_upload_path_title">Pasta de envio instantâneo de vídeos</string>
-    <string name="sync_folder_failed_content">A sincronização da pasta %1$s não pode ser finalizada</string>
+	<string name="prefs_instant_video_upload_path_title">Pasta de envio automático de vídeos</string>
+    <string name="sync_folder_failed_content">A sincronização da pasta %1$s não pôde ser finalizada</string>
 
 	<string name="shared_subject_header">compartilhado</string>
 	<string name="with_you_subject_header">com você</string>
     
-	<string name="subject_user_shared_with_you">%1$s compartilhado \"%2$s\" com você</string>
+	<string name="subject_user_shared_with_you">%1$s compartilhou \"%2$s\" com você</string>
     <string name="subject_shared_with_you">\"%1$s\" foi compartilhado com você</string>
 
     <string name="auth_refresh_button">Reinicializar conexão</string>
     <string name="auth_host_address">Endereço do servidor</string>
-    <string name="common_error_out_memory">Não há memoria suficiente</string>
+    <string name="common_error_out_memory">Não há memória suficiente</string>
 
-    <string name="username">Nome do Usuário</string>
+    <string name="username">Nome do usuário</string>
 
     <string name="file_list__footer__folder">1 pasta</string>
     <string name="file_list__footer__folders">%1$d pastas</string>
@@ -479,9 +481,9 @@ foram encontrados para sua pesquisa! </string>
     <string name="select_all">Selecionar tudo</string>
 
     <string name="pref_behaviour_entries_keep_file">mantido na pasta original</string>
-    <string name="pref_behaviour_entries_move">movido para a pasta app</string>
+    <string name="pref_behaviour_entries_move">movido para a pasta aplicativos</string>
     <string name="pref_behaviour_entries_delete_file">excluído</string>
-    <string name="prefs_storage_path">Caminho do storage</string>
+    <string name="prefs_storage_path">Caminho do armazenamento</string>
     <string name="prefs_common">Comum</string>
 
     <string name="share_dialog_title">Compartilhamento</string>
@@ -492,7 +494,7 @@ foram encontrados para sua pesquisa! </string>
     <string name="share_via_link_section_title">Compartilhar link</string>
     <string name="share_via_link_expiration_date_label">Definir data de expiração</string>
     <string name="share_via_link_password_label">Proteger com senha</string>
-    <string name="share_via_link_password_title">Asegurado</string>
+    <string name="share_via_link_password_title">Seguro</string>
     <string name="share_via_link_edit_permission_label">Permitir edição</string>
     <string name="share_via_link_hide_file_listing_permission_label">Ocultar a listagem de arquivos</string>
     <string name="share_get_public_link_button">Obter link</string>
@@ -507,38 +509,37 @@ foram encontrados para sua pesquisa! </string>
     <string name="share_email_clarification">%1$s (email)</string>
     <string name="share_known_remote_clarification">%1$s ( em %2$s )</string>
 
-    <string name="share_sharee_unavailable">Lamentamos, mas a versão do seu servidor não permite partilhar com utilizadores nos clientes. 
-Contacte o administrador</string>
+    <string name="share_sharee_unavailable">Lamentamos, mas a versão do seu servidor não permite compartilhar com utilizadores nos clientes.\nPor favor contacte o administrador</string>
     <string name="share_privilege_can_share">pode compartilhar</string>
     <string name="share_privilege_can_edit">pode editar</string>
     <string name="share_privilege_can_edit_create">criar</string>
-    <string name="share_privilege_can_edit_change">mudança</string>
+    <string name="share_privilege_can_edit_change">alterar</string>
     <string name="share_privilege_can_edit_delete">excluir</string>
     <string name="edit_share_unshare">Parar compartilhamento</string>
     <string name="edit_share_done">finalizado</string>
 
-    <string name="action_retry_uploads">Falha, tentar novamente</string>
-    <string name="action_clear_failed_uploads">Limpar falha</string>
-    <string name="action_clear_successful_uploads">Limpado com sucesso</string>
+    <string name="action_retry_uploads">Retentar falhou</string>
+    <string name="action_clear_failed_uploads">Limpeza falhou</string>
+    <string name="action_clear_successful_uploads">Limpo com sucesso</string>
     <string name="action_clear_finished_uploads">Limpar tudo finalizado</string>
 
     <string name="action_switch_grid_view">Grade de exibição</string>
     <string name="action_switch_list_view">Lista de visualização</string>
 
     <string name="manage_space_title">Gerenciar o espaço</string>
-    <string name="manage_space_description">Definições, certificados de banco de dados e servidor de dados %1$s serão excluído permanentemente. \n\nArquivos baixados serão mantidos inalterados. \n\nEste processo pode demorar algum tempo.</string>
+    <string name="manage_space_description">Definições, certificados de banco de dados e servidor de dados %1$s serão excluídos permanentemente. \n\nArquivos baixados serão mantidos inalterados. \n\nEste processo pode demorar algum tempo.</string>
     <string name="manage_space_clear_data">Limpar dados</string>
     <string name="manage_space_error">Alguns arquivos não puderam ser excluídos.</string>
 
     <string name="permission_storage_access">Permissões adicionais são necessárias para se enviar e baixar arquivos &amp; .</string>
     <string name="local_file_not_found_toast">O arquivo não foi encontrado no sistema de arquivos local</string>
-    <string name="confirmation_remove_files_alert">Deseja realmente remover os itens selecionados?</string>
-    <string name="confirmation_remove_folders_alert">Deseja realmente remover os itens selecionados e seus conteúdos?</string>
-    <string name="uploads_view_upload_status_waiting_for_charging">À espera de carregamento do dispositivo</string>
+    <string name="confirmation_remove_files_alert">Deseja realmente excluir os itens selecionados?</string>
+    <string name="confirmation_remove_folders_alert">Deseja realmente excluir os itens selecionados e seus conteúdos?</string>
+    <string name="uploads_view_upload_status_waiting_for_charging">Esperando o carregamento do dispositivo</string>
     <string name="actionbar_search">Pesquisar</string>
-    <string name="files_drop_not_supported">Este é um recurso do Nextcloud, atualize.</string>
-    <string name="learn_more">Aprender mais</string>
-    <string name="drawer_folder_sync">Upload automatico</string>
+    <string name="files_drop_not_supported">Este é um recurso do Nextcloud. Por favor, atualize.</string>
+    <string name="learn_more">Saiba mais</string>
+    <string name="drawer_folder_sync">Envio automático</string>
     <string name="drawer_participate">Participar</string>
     <string name="participate_testing_headline">Ajude-nos a testar</string>
     <string name="participate_testing_bug_text">Encontrou um erro? Algo está estranho?</string>
@@ -546,8 +547,8 @@ Contacte o administrador</string>
     <string name="participate_testing_version_text">Está interessado em ajudar-nos a testar a próxima versão?</string>
     <string name="participate_beta_headline">Teste a versão beta</string>
     <string name="participate_beta_text">Isso inclui todos os próximos recursos. Erros pode ocorrer e irão. Por favor, reporte-os a nós.</string>
-    <string name="participate_release_candidate_headline">Release candidate</string>
-    <string name="participate_release_candidate_text">O release candidate (RC) é um instantâneo do lançamento e espera-se que seja estável. Testar a sua configuração individual pode ajudar a garantir isso. Registre-se para testar na Play Store ou procure manualmente na seção \"versões\" na F-Droid.</string>
+    <string name="participate_release_candidate_headline">Candidato a versão</string>
+    <string name="participate_release_candidate_text">O candidato à versão (RC) é um instantâneo do lançamento e espera-se que seja estável. Testar a sua configuração individual pode ajudar a garantir isso. Registre-se para testar na Play Store ou procure manualmente na seção \"versões\" na F-Droid.</string>
     <string name="participate_contribute_headline">Contribuir ativamente</string>
     <string name="participate_contribute_irc_text">Participe de uma conversa no IRC: &lt;a href="%1$s">#nextcloud-mobile&lt;/a></string>
     <string name="participate_contribute_forum_text">Ajude outros no &lt;a href="%1$s">forum&lt;/a></string>
@@ -557,10 +558,10 @@ Contacte o administrador</string>
     <string name="copy_to">Copiar para&#8230;</string>
     <string name="choose_remote_folder">Escolha a pasta&#8230;</string>
     <string name="folder_sync_loading_folders">Carregando pastas&#8230;</string>
-    <string name="folder_sync_no_results">Nenhuma pasta de media encontrada.</string>
-    <string name="folder_sync_preferences">Preferencias de upload automatico</string>
+    <string name="folder_sync_no_results">Nenhuma pasta de mídia encontrada.</string>
+    <string name="folder_sync_preferences">Preferências do envio automático</string>
     <string name="folder_sync_settings">Configurações</string>
-    <string name="folder_sync_new_info">Upload instantaneo foi completamente renovado. Favor veja o menu principal e reconfigure o seu auto upload. Desculpe o inconveniente. \n\nAproveite o novo e mais completo auto upload</string>
+    <string name="folder_sync_new_info">O envio automático foi completamente renovado.  Por favor veja o menu principal e reconfigure o seu envio automático. Desculpe o inconveniente. \n\nAproveite as novas e extendidas capacidades do envio automático!</string>
     <string name="folder_sync_preferences_folder_path">Para %1$s</string>
     <plurals name="items_selected_count">
         <item quantity="one">%d selecionado</item>
@@ -570,29 +571,29 @@ Contacte o administrador</string>
     <string name="activity_list_loading_activity">Carregando atividades&#8230;</string>
     <string name="activity_list_no_results">Nenhuma atividade encontrada.</string>
 
-    <string name="upload_file_dialog_title">Forneça o nome e o tipo de arquivo que será enviado </string>
+    <string name="upload_file_dialog_title">Forneça o nome e o tipo de arquivo a enviar</string>
     <string name="upload_file_dialog_filename">Nome do Arquivo</string>
     <string name="upload_file_dialog_filetype">Tipo do Arquivo</string>
-    <string name="upload_file_dialog_filetype_snippet_text">Fragmento de texto arquivo(.txt)</string>
-    <string name="upload_file_dialog_filetype_internet_shortcut">Atalho da Internet arquivo(%s) </string>
-    <string name="upload_file_dialog_filetype_googlemap_shortcut">Atalho da Internet Google Maps arquivo(%s)</string>
+    <string name="upload_file_dialog_filetype_snippet_text">Fragmento de arquivo texto (.txt)</string>
+    <string name="upload_file_dialog_filetype_internet_shortcut">Arquivo de atalho da internet (%s) </string>
+    <string name="upload_file_dialog_filetype_googlemap_shortcut">Arquivo de atalho do Google Maps (%s)</string>
 
     <string name="storage_description_default">Padrão</string>
     <string name="storage_description_sd_no">Cartão SD %1$d</string>
     <string name="storage_description_unknown">Desconhecido</string>
 
     <!-- What's new feature and texts to show -->
-    <string name="whats_new_title">O que há denovo no Nextcloud</string>
+    <string name="whats_new_title">O que há de novo no Nextcloud</string>
 
     <!-- Welcome to Nc intro features -->
-    <string name="welcome_feature_1_title">Uma casa segura para todos os seus dados</string>
-    <string name="welcome_feature_1_text">Acesso, compartilhamento &amp; proteja seus dados em casa e em sua empresa</string>
+    <string name="welcome_feature_1_title">Um local seguro para seus dados</string>
+    <string name="welcome_feature_1_text">Acesse, compartilhe &amp; proteja seus dados em casa e na sua empresa</string>
 
     <string name="welcome_feature_2_title">Multi contas</string>
-    <string name="welcome_feature_2_text">Conectar em todos os seus clouds</string>
+    <string name="welcome_feature_2_text">Conectar em todas as suas Nuvens</string>
 
-    <string name="welcome_feature_3_title">Upload Instantaneo</string>
-    <string name="welcome_feature_3_text">Guarde suas fotos de forma segura</string>
+    <string name="welcome_feature_3_title">Envio automático</string>
+    <string name="welcome_feature_3_text">Mantenha seguras as suas fotos</string>
 
     <string name="whats_new_skip">Pular</string>
 
@@ -610,5 +611,4 @@ Contacte o administrador</string>
     <string name="activities_no_results_headline">Nenhuma atividade ainda</string>
     <string name="activities_no_results_message">O stream irá mostrar eventos como\nadições, mudanças &amp; compartilhamentos</string>
 
-
-</resources>
+    </resources>

+ 19 - 2
src/main/res/values-ro/strings.xml

@@ -2,7 +2,7 @@
 <resources>
     <string name="about_android">%1$s aplicație Android</string>
     <string name="about_version">versiunea %1$s</string>
-    <string name="actionbar_sync">Reîmprospătare cont</string>
+    <string name="actionbar_sync">Reîmprospătează contul</string>
     <string name="actionbar_upload">Încarcă</string>
     <string name="actionbar_upload_from_apps">Conținut de la alte aplicații</string>
     <string name="actionbar_upload_files">Fișiere</string>
@@ -11,12 +11,29 @@
     <string name="actionbar_settings">Setări</string>
     <string name="actionbar_see_details">Detalii</string>
     <string name="actionbar_send_file">Expediază</string>
-    <string name="actionbar_sort">Sortare</string>
+    <string name="actionbar_sort">Sortează</string>
     <string name="actionbar_sort_title">Sortare după</string>
+    <string name="sort_by">Sortare după</string>
+    <string name="menu_item_sort_by_name_a_z">A - Z</string>
+    <string name="menu_item_sort_by_name_z_a">Z - A</string>
+    <string name="menu_item_sort_by_date_newest_first">Cel mai nou mai întâi</string>
+    <string name="menu_item_sort_by_date_oldest_first">Cel mai vechi mai întâi</string>
+    <string name="menu_item_sort_by_size_biggest_first">Cel mai mare mai întâi</string>
+    <string name="menu_item_sort_by_size_smallest_first">Cel mai mic mai întâi</string>
+
     <string name="drawer_item_all_files">Toate fișierele</string>
+    <string name="drawer_item_files">Fișiere</string>
+    <string name="drawer_item_home">Acasă</string>
+    <string name="drawer_item_favorites">Favorite</string>
+    <string name="drawer_item_photos">Poze</string>
     <string name="drawer_item_on_device">Pe dispozitiv</string>
+    <string name="drawer_item_recently_added">Adăugate recent</string>
+    <string name="drawer_item_recently_modified">Modificate recent</string>
+    <string name="drawer_item_shared">Partajate</string>
+    <string name="drawer_item_videos">Fișiere video</string>
     <string name="drawer_item_settings">Setări</string>
     <string name="drawer_item_uploads_list">Încărcări</string>
+    <string name="drawer_item_activities">Activități</string>
     <string name="drawer_quota">%1$s din %2$s folosiți</string>
 	<string name="drawer_close">Închide</string>
     <string name="drawer_open">Deschide</string>

+ 21 - 20
src/main/res/values-ru/strings.xml

@@ -11,7 +11,7 @@
     <string name="actionbar_settings">Настройки</string>
     <string name="actionbar_see_details">Подробно</string>
     <string name="actionbar_send_file">Отправить</string>
-    <string name="actionbar_sort">Сортировать</string>
+    <string name="actionbar_sort">Сортировка</string>
     <string name="actionbar_sort_title">Сортировать по</string>
     <string name="sort_by">Порядок сортировки</string>
     <string name="menu_item_sort_by_name_a_z">А–Я</string>
@@ -43,10 +43,10 @@
     <string name="prefs_manage_accounts">Управление аккаунтами</string>
     <string name="prefs_passcode">Блокировка кодом</string>
     <string name="prefs_show_hidden_files">Показать скрытые файлы</string>
-    <string name="prefs_instant_upload">Автозагрузка изображений</string>
-    <string name="prefs_instant_upload_summary">Автоматически загружать изображения, сделанные камерой</string>
-    <string name="prefs_instant_video_upload">Автозагрузка видео</string>
-    <string name="prefs_instant_video_upload_summary">Автоматически загружать видео, сделанные камерой</string>
+    <string name="prefs_instant_upload">Моментальная загрузка изображений</string>
+    <string name="prefs_instant_upload_summary">Немедленно загружать изображения, сделанные камерой</string>
+    <string name="prefs_instant_video_upload">Моментальная загрузка видео</string>
+    <string name="prefs_instant_video_upload_summary">Немедленно загружать видео, сделанные камерой</string>
     <string name="prefs_log_title">Включить журналирование</string>
     <string name="prefs_log_summary">Используется для регистрации ошибок</string>
     <string name="prefs_log_title_history">История журналирования</string>
@@ -72,8 +72,8 @@
     <string name="auth_host_url">Адрес сервера https://...</string>
     <string name="auth_username">Имя пользователя</string>
     <string name="auth_password">Пароль</string>
-    <string name="auth_register">Ещё нет сервера?
-Щёлкните здесь чтобы получить от провайдера</string>
+    <string name="auth_register">Нет сервера?
+Нажмите здесь чтобы получить от провайдера</string>
     <string name="sync_string_files">Файлы</string>
     <string name="setup_btn_connect">Подключиться</string>
     <string name="uploader_btn_upload_text">Загрузить</string>
@@ -90,9 +90,9 @@
     <string name="uploader_error_message_source_file_not_found">Файл для загрузки не был найден в положенном месте. Пожалуйста проверьте, существует ли файл.</string>
     <string name="uploader_error_message_source_file_not_copied">Произошла ошибка во время копирования этого файла во временный каталог. Пожалуйста попробуйте отправить его заново.</string>
     <string name="uploader_upload_files_behaviour">Настройка загрузки:</string>
-    <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Переместить файл в папку Nextcloud</string>
-    <string name="uploader_upload_files_behaviour_only_upload">Хранить файл в исходном каталоге</string>
-    <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Удалить файл из исходного каталога</string>
+    <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Перемещать файл в папку Nextcloud</string>
+    <string name="uploader_upload_files_behaviour_only_upload">Сохранять файл в исходном каталоге</string>
+    <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Удалять файл из исходного каталога</string>
     <string name="file_list_seconds_ago">несколько секунд назад</string>
     <string name="file_list_empty_headline">Здесь нет файлов</string>
     <string name="file_list_empty">Загрузите что-нибудь или синхронизируйте со своими устройствами!</string>
@@ -105,7 +105,7 @@
     <string name="file_list_empty_headline_search">В этой папке результатов не найдено</string>
     <string name="file_list_empty_headline_server_search">Результаты отсутствуют</string>
     <string name="file_list_empty_favorite_headline">Избранное отсутствует</string>
-    <string name="file_list_empty_shared_headline">Вы еще ничем не поделились</string>
+    <string name="file_list_empty_shared_headline">Вы ещё ничем не поделились</string>
     <string name="file_list_empty_shared">Здесь появятся файлы и каталоги, которыми вы поделитесь</string>
     <string name="file_list_empty_headline_server_search_videos">Видео отсуствуют</string>
     <string name="file_list_empty_headline_server_search_photos">Фотографии отсутствуют</string>
@@ -120,7 +120,7 @@
     <string name="file_list_empty_text_videos">Загрузите видео или включите автоматическую загрузку!</string>
     <string name="file_list_empty_text_videos_filter">По запросу видео–файлов не найдено!</string>
     <string name="upload_list_empty_headline">Нет доступных загрузок</string>
-    <string name="upload_list_empty_text">Загрузите что-нибудь или включите автоматическую загрузку!</string>
+    <string name="upload_list_empty_text">Загрузите что-нибудь или включите моментальную загрузку!</string>
     <string name="upload_list_empty_text_auto_upload">Загрузите что-нибудь или включите автоматическую загрузку!</string>
     <string name="file_list_folder">каталог</string>
     <string name="file_list_folders">каталоги</string>
@@ -328,7 +328,7 @@
     <string name="ssl_validator_label_signature">Подпись:</string>
     <string name="ssl_validator_label_signature_algorithm">Алгоритм:</string>
     <string name="digest_algorithm_not_available">Этот хэш алгоритм не доступен на этом телефоне.</string>
-    <string name="ssl_validator_label_certificate_fingerprint">Слепок:</string>
+    <string name="ssl_validator_label_certificate_fingerprint">Отпечаток:</string>
     <string name="certificate_load_problem">Проблема при загрузке сертификата.</string>
     <string name="ssl_validator_null_cert">Сертификат не может быть показан.</string>
     <string name="ssl_validator_no_info_about_error">- Нет информации об ошибке</string>
@@ -354,8 +354,10 @@
 
     <string name="preview_sorry">Приносим извинения.</string>
     <string name="preview_image_description">Предпросмотр</string>
+    <string name="preview_image_error_unknown_format">Это изображение не может быть отображено</string>
+
     <string name="error__upload__local_file_not_copied">%1$s невозможно скопировать в локальный каталог %2$s </string>
-    <string name="prefs_instant_upload_path_title">Каталог для автоматической загрузки</string>
+    <string name="prefs_instant_upload_path_title">Каталог для немедленной загрузки</string>
     <string name="prefs_folder_sync_local_path_title">Локальный каталог</string>
     <string name="prefs_folder_sync_remote_path_title">Удалённый каталог</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Использовать подкаталоги</string>
@@ -448,10 +450,10 @@
     <string name="copy_file_error">Произошла ошибка при попытке копирования этого файла или каталога</string>
     <string name="forbidden_permissions_copy">для копирования этого файла</string>
 
-    <string name="prefs_category_instant_uploading">Автоматическая загрузка</string>
+    <string name="prefs_category_instant_uploading">Немедленная загрузка</string>
     <string name="prefs_category_details">Подробно</string>
 
-	<string name="prefs_instant_video_upload_path_title">Каталог для автоматической загрузки видео</string>
+	<string name="prefs_instant_video_upload_path_title">Каталог для немедленной загрузки видео</string>
     <string name="sync_folder_failed_content">Синхронизация каталога %1$s не может быть завершена</string>
 
 	<string name="shared_subject_header">поделился</string>
@@ -561,7 +563,7 @@
     <string name="folder_sync_no_results">Не найдены каталоги с медиа(файлами).</string>
     <string name="folder_sync_preferences">Настройки автозагрузки</string>
     <string name="folder_sync_settings">Настройки</string>
-    <string name="folder_sync_new_info">Автоматическая загрузка была полностью переделана. Используйте главное меню для перенастройки автоматической загрузки. Извините за неудобства.\n\nВам понравятся новые расширенные возможности автоматической загрузки!</string>
+    <string name="folder_sync_new_info">Моментальная загрузка была полностью переделана. Используйте главное меню для перенастройки автоматической загрузки. Извините за неудобства.\n\nВам понравятся новые расширенные возможности автоматической загрузки!</string>
     <string name="folder_sync_preferences_folder_path">Для %1$s</string>
     <plurals name="items_selected_count">
         <item quantity="one">%d выбран</item>
@@ -594,7 +596,7 @@
     <string name="welcome_feature_2_title">Работа с несколькими аккаунтами</string>
     <string name="welcome_feature_2_text">Доступ ко всем вашим облакам</string>
 
-    <string name="welcome_feature_3_title">Автозагрузка</string>
+    <string name="welcome_feature_3_title">Немедленная загрузка</string>
     <string name="welcome_feature_3_text">Держите ваши фотографии в безопасности</string>
 
     <string name="whats_new_skip">Пропустить</string>
@@ -613,5 +615,4 @@
     <string name="activities_no_results_headline">Событий ещё нет</string>
     <string name="activities_no_results_message">Этот поток будет отображать события вроде\nдобавления, изменения файлов или открытия доступа к ним</string>
 
-
-</resources>
+    </resources>

+ 1 - 1
src/main/res/values-sq/strings.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
-    <string name="about_android">Aplikacioni %1$s Android</string>
+    <string name="about_android">%1$s Android app</string>
     <string name="about_version">version %1$s</string>
     <string name="actionbar_sync">Rifreskoni llogarinë</string>
     <string name="actionbar_upload">Ngarkoni</string>

+ 3 - 2
src/main/res/values-tr/strings.xml

@@ -351,6 +351,8 @@
 
     <string name="preview_sorry">Bundan dolayı üzgünüz!</string>
     <string name="preview_image_description">Görsel önizleme</string>
+    <string name="preview_image_error_unknown_format">Görsel görüntülenemedi</string>
+
     <string name="error__upload__local_file_not_copied">%1$s, %2$s yerel klasörüne kopyalanamadı</string>
     <string name="prefs_instant_upload_path_title">Anında yükleme klasörü</string>
     <string name="prefs_folder_sync_local_path_title">Yerel klasör</string>
@@ -608,5 +610,4 @@
     <string name="activities_no_results_headline">Henüz bir işlem yapılmamış</string>
     <string name="activities_no_results_message">Bu akışta ekleme, değiştirme\nve paylaşım gibi işlemler görüntülenir</string>
 
-
-</resources>
+    </resources>

+ 3 - 2
src/main/res/values-zh-rCN/strings.xml

@@ -352,6 +352,8 @@
 
     <string name="preview_sorry">对此抱歉!</string>
     <string name="preview_image_description">图片预览</string>
+    <string name="preview_image_error_unknown_format">无法显示图片</string>
+
     <string name="error__upload__local_file_not_copied">无法复制 %1$s 到本地目录 %2$s</string>
     <string name="prefs_instant_upload_path_title">实时上传文件夹</string>
     <string name="prefs_folder_sync_local_path_title">本地文件夹</string>
@@ -608,5 +610,4 @@
     <string name="activities_no_results_headline">暂无动态</string>
     <string name="activities_no_results_message">瀑布流中会显示诸如添加\n更改&amp;分享之类的事件</string>
 
-
-</resources>
+    </resources>

+ 2 - 9
src/main/res/values/colors.xml

@@ -19,8 +19,8 @@
 -->
 <resources>
 
-    <color name="owncloud_blue">@color/actionbar_start_color</color>
-    <color name="owncloud_blue_accent">#007CC2</color>
+    <color name="nc_blue">@color/navigation_bar_start_color</color>
+    <color name="nc_blue_accent">#007CC2</color>
     <color name="owncloud_blue_bright">#00ddff</color>
 
     <color name="list_item_lastmod_and_filesize_text">@color/secondaryTextColor</color>
@@ -43,17 +43,10 @@
     <color name="highlightTextColor">#55739a</color>
 
     <!-- Colors -->
-    <color name="color_accent">@color/owncloud_blue_accent</color>
     <color name="dialog_list_item">#1F1F1F</color>
     <color name="standard_grey">#757575</color>
 
     <!-- standard material color definitions -->
-    <color name="primary">@color/owncloud_blue</color>
-    <color name="primary_dark">#006AA3</color>
-
-    <!-- special transparent action bar colors for image preview -->
-    <color name="owncloud_blue_transparent">#201D2D44</color>
-    <color name="owncloud_blue_dark_transparent">#40162233</color>
 
     <!-- level colors for info notifications/visualisations -->
     <color name="infolevel_info">@color/color_accent</color>

+ 22 - 6
src/main/res/values/setup.xml

@@ -39,19 +39,33 @@
     <bool name="show_whats_new">true</bool>
     <bool name="show_external_links">true</bool>
     
+    // Contacts backup
+    <bool name="contacts_backup">true</bool>
+    <string name="contacts_backup_folder">/Contacts-Backup</string>
+    <integer name="contacts_backup_expire">-1</integer>
+    
+    <!-- To fill if you want to show webviews instead of regular welcome views -->
+    <array name="whatsnew_urls"></array>
+    
     <!-- Colors -->
+    <color name="primary">@color/nc_blue</color>
+    <color name="primary_dark">#006AA3</color>
+    <color name="color_accent">@color/nc_blue_accent</color>
     <color name="login_text_color">@color/white</color>
     <color name="login_text_hint_color">#7fC0E3</color>
-    <color name="login_background_color">#FFFFFF</color>
-    <color name="login_logo_background_color">#FFFFFF</color>
+    <color name="login_background_color">#FFFFFF</color>    <!-- Not used at the moment -->
+    <color name="login_logo_background_color">#FFFFFF</color>    <!-- Not used at the moment -->
     <color name="background_color">#FFFFFF</color>
-    <color name="actionbar_start_color">#0082c9</color>
-    <color name="actionbar_end_color">#0082c9</color>
-    <color name="primary_button_background_color">@color/owncloud_blue_accent</color>
+    <color name="navigation_bar_start_color">#0082c9</color>
+    <color name="navigation_bar_end_color">#0082c9</color>
+    <color name="primary_button_background_color">@color/nc_blue_accent</color>
     <color name="primary_button_text_color">@color/white</color>
     <color name="secondary_button_background_color">#D6D7D7</color>
     <color name="secondary_button_text_color">@color/black</color>
-    <color name="drawer_header_color">@color/owncloud_blue_accent</color>
+
+    <!-- special transparent action bar colors for image preview -->
+    <color name="color_transparent">#201D2D44</color>
+    <color name="color_dark_transparent">#40162233</color>
 
     <!-- Button -->
     <color name="button_text_color">#000000</color>
@@ -115,6 +129,8 @@
     <!-- Files becomes Home -->
     <bool name="use_home">false</bool>
 
+    <!-- Push server url -->
+    <string name="push_server_url" translatable="false"></string>
 </resources>
 
 

+ 28 - 0
src/main/res/values/strings.xml

@@ -34,6 +34,7 @@
     <string name="drawer_item_settings">Settings</string>
     <string name="drawer_item_uploads_list">Uploads</string>
     <string name="drawer_item_activities">Activities</string>
+    <string name="drawer_item_notifications">Notifications</string>
     <string name="drawer_quota">%1$s of %2$s used</string>
 	<string name="drawer_close">Close</string>
     <string name="drawer_open">Open</string>
@@ -182,6 +183,7 @@
     <string name="uploads_view_upload_status_unknown_fail">Unknown error</string>
     <string name="uploads_view_upload_status_waiting_for_wifi">Waiting for wifi connectivity</string>
     <string name="uploads_view_later_waiting_to_upload">Waiting to upload</string>
+    <string name="uploads_view_group_header" translatable="false">%1$s (%2$d)</string>
     <string name="downloader_download_in_progress_ticker">Downloading &#8230;</string>
     <string name="downloader_download_in_progress_content">%1$d%% Downloading %2$s</string>
     <string name="downloader_download_succeeded_ticker">Download succeeded</string>
@@ -580,6 +582,10 @@
     <string name="activity_list_loading_activity">Loading activities&#8230;</string>
     <string name="activity_list_no_results">No activities found.</string>
 
+    <string name="notifications_loading_activity">Loading notifications&#8230;</string>
+    <string name="notifications_no_results_headline">No notifications</string>
+    <string name="notifications_no_results_message">Please check back later.</string>
+
     <string name="upload_file_dialog_title">Input upload filename and filetype</string>
     <string name="upload_file_dialog_filename">Filename</string>
     <string name="upload_file_dialog_filetype">Filetype</string>
@@ -622,5 +628,27 @@
     <string name="webview_error">Error occurred</string>
     <string name="prefs_category_about">About</string>
 
+    <string name="actionbar_contacts">Contacts backup</string>
+    <string name="contacts_backup_button">Backup now</string>
+    <string name="contacts_restore_button">Restore last backup</string>
+    <string name="contacts_header_restore">Restore</string>
+    <string name="contacts_header_backup">Backup</string>
+    <string name="contacts_automatic_backup">Contacts backup</string>
+    <string name="contacts_last_backup">Last backup</string>
+    <string name="contacts_read_permission">Read permission of contacts is needed</string>
+    <string name="contacts_write_permission">Write permission for contacts is needed</string>
+    <string name="contactlist_title">Restore contacts</string>
+    <string name="contaclist_restore_selected">Restore selected contacts</string>
+    <string name="contactlist_account_chooser_title">Choose account for import</string>
+    <string name="contactlist_no_permission">No permission, nothing imported!</string>
+    <string name="contacts_preference_choose_date">Choose date</string>
+    <string name="contacts_preference_backup_never">never</string>
+    <string name="contacts_preferences_no_file_found">No file found</string>
+    <string name="contacts_preferences_backup_scheduled">Backup scheduled and will start shortly</string>
+    <string name="contacts_preferences_import_scheduled">Import scheduled and will start shortly</string>
+
+    <!-- Notifications -->
+    <string name="new_notification_received">New notification received</string>
+
 
 </resources>

+ 9 - 9
src/main/res/values/styles.xml

@@ -106,7 +106,7 @@
 
 	<style name="Button.Login" parent="Button">
 		<item name="colorButtonNormal">@color/white</item>
-		<item name="android:textColor">@color/owncloud_blue</item>
+		<item name="android:textColor">@color/nc_blue</item>
 	</style>
 
 	<style name="Button.Borderless" parent="Base.Widget.AppCompat.Button.Borderless">
@@ -129,10 +129,10 @@
 
 	<!-- ACTION BAR STYLES -->
 	<style name="Theme.ownCloud.Overlay.ActionBar" parent="@style/Widget.AppCompat.ActionBar">
-		<item name="android:background">@color/owncloud_blue_transparent</item>
+		<item name="android:background">@color/color_transparent</item>
 		<item name="android:windowActionBarOverlay">true</item>
 		<!-- Support library compatibility -->
-		<item name="background">@color/owncloud_blue_transparent</item>
+		<item name="background">@color/color_transparent</item>
 		<item name="windowActionBarOverlay">true</item>
 	</style>
 
@@ -150,8 +150,8 @@
 	
 	<style name="Theme.ownCloud.Widget.ActionBar"
 		parent="style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
-    	<item name="android:background">@color/owncloud_blue</item>
-    	<item name="background">@drawable/main_header_bg</item>
+		<item name="android:background">@drawable/main_header_bg</item>
+		<item name="background">@drawable/main_header_bg</item>
     	<item name="android:textColor">#ffffff</item>
     	<item name="android:shadowColor">#222222</item>
     	<item name="android:shadowRadius">1</item>
@@ -162,19 +162,19 @@
 	<!-- Dialogs -->
 	<style name="Theme.ownCloud.Dialog" parent="style/Theme.AppCompat.Light.Dialog.Alert">
 		<item name="windowNoTitle">false</item>
-		<item name="colorAccent">@color/owncloud_blue_accent</item>
+		<item name="colorAccent">@color/color_accent</item>
 		<item name="buttonBarButtonStyle">@style/Theme.ownCloud.Dialog.ButtonBar.Button</item>
 		<item name="buttonBarStyle">@style/Theme.ownCloud.Dialog.ButtonBar</item>
 	</style>
 
 	<style name="Theme.ownCloud.Dialog.NoTitle" parent="style/Theme.ownCloud.Dialog">
 		<item name="windowNoTitle">true</item>
-		<item name="colorAccent">@color/owncloud_blue_accent</item>
+		<item name="colorAccent">@color/color_accent</item>
 	</style>
 
 	<style name="Theme.ownCloud.Dialog.NoButtonBarStyle" parent="style/Theme.AppCompat.Light.Dialog.Alert">
 		<item name="windowNoTitle">false</item>
-		<item name="colorAccent">@color/owncloud_blue_accent</item>
+		<item name="colorAccent">@color/color_accent</item>
 	</style>
 
 	<style name="menu_labels_style">
@@ -271,7 +271,7 @@
 	
 	<style name="OAuthDialog" parent="style/Theme.AppCompat.Light.Dialog.Alert">
 		<item name="windowNoTitle">false</item>
-		<item name="colorAccent">@color/owncloud_blue_accent</item>
+		<item name="colorAccent">@color/nc_blue_accent</item>
 	</style>    
 		
 	<color name="setup_text_hint">#777777</color>

+ 3 - 2
src/main/res/xml/exposed_filepaths.xml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <paths xmlns:android="http://schemas.android.com/apk/res/android">
-    <external-path name="file" path="/" />
+    <external-path name="external_files" path="."/>
+    <root-path name="external_files" path="/storage/" />
     <!-- yes, valid for ALL external storage and not only our app folder, since we can't use @string/data_folder
     as a value for 'path' attribute; in practice, we will only generate URIs in our folders, of course -->
-</paths>
+</paths>

+ 100 - 0
src/modified/AndroidManifest.xml

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Nextcloud Android client application
+
+  Copyright (C) 2017 Mario Danic
+
+  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/>.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.owncloud.android"
+          android:versionCode="10040299"
+          android:versionName="1.4.2">
+
+    <application
+        android:name=".MainApp"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:fullBackupContent="@xml/backup_config"
+        android:theme="@style/Theme.ownCloud.Toolbar"
+        android:manageSpaceActivity="com.owncloud.android.ui.activity.ManageSpaceActivity">
+        <activity
+            android:name=".ui.activity.ModifiedFileDisplayActivity"
+            android:label="@string/app_name"
+            android:theme="@style/Theme.ownCloud.Toolbar.Drawer">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".authentication.ModifiedAuthenticatorActivity"
+            android:exported="true"
+            android:launchMode="singleTask"
+            android:theme="@style/Theme.ownCloud.noActionBar.Login">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="@string/oauth2_redirect_scheme" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.owncloud.android.workaround.accounts.CREATE" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="@string/login_data_own_scheme" android:host="login"/>
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".ui.activity.FileDisplayActivity"
+            tools:node="remove"/>
+
+        <activity-alias
+            android:name=".ui.activity.FileDisplayActivity"
+            android:targetActivity=".ui.activity.ModifiedFileDisplayActivity"
+            tools:replace="android:targetActivity"/>
+
+        <activity-alias
+            android:name=".authentication.AuthenticatorActivity"
+            android:targetActivity=".authentication.ModifiedAuthenticatorActivity"
+            tools:replace="android:targetActivity"/>
+
+
+        <service
+            android:name=".services.firebase.NCFirebaseMessagingService">
+            <intent-filter>
+                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
+            </intent-filter>
+        </service>
+
+        <service
+            android:name=".services.firebase.NCFirebaseInstanceIDService">
+            <intent-filter>
+                <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
+            </intent-filter>
+        </service>
+
+    </application>
+
+</manifest>

+ 41 - 0
src/modified/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java

@@ -0,0 +1,41 @@
+package com.owncloud.android.authentication;
+
+import android.os.Bundle;
+
+import com.owncloud.android.utils.GooglePlayUtils;
+
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class ModifiedAuthenticatorActivity extends AuthenticatorActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        GooglePlayUtils.checkPlayServices(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        GooglePlayUtils.checkPlayServices(this);
+    }
+
+}

+ 45 - 0
src/modified/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java

@@ -0,0 +1,45 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.services.firebase;
+
+import android.text.TextUtils;
+
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.FirebaseInstanceIdService;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.db.PreferenceManager;
+import com.owncloud.android.utils.PushUtils;
+
+public class NCFirebaseInstanceIDService extends FirebaseInstanceIdService {
+    private static final String TAG = "NCFirebaseInstanceID";
+
+    @Override
+    public void onTokenRefresh() {
+        //You can implement this method to store the token on your server
+        if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) {
+            PreferenceManager.setPushToken(MainApp.getAppContext(), FirebaseInstanceId.getInstance().getToken());
+            PreferenceManager.setPushTokenUpdateTime(MainApp.getAppContext(), System.currentTimeMillis());
+
+            PushUtils.pushRegistrationToServer();
+        }
+    }
+}
+

+ 66 - 0
src/modified/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java

@@ -0,0 +1,66 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.services.firebase;
+
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.ui.activity.NotificationsActivity;
+
+public class NCFirebaseMessagingService extends FirebaseMessagingService {
+    private static final String TAG = "NCFirebaseMessaging";
+
+    @Override
+    public void onMessageReceived(RemoteMessage remoteMessage) {
+        super.onMessageReceived(remoteMessage);
+
+        sendNotification(MainApp.getAppContext().getString(R.string.new_notification_received));
+    }
+
+    private void sendNotification(String contentTitle) {
+        Intent intent = new Intent(this, NotificationsActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
+                PendingIntent.FLAG_ONE_SHOT);
+
+        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setContentTitle(contentTitle)
+                .setSound(defaultSoundUri)
+                .setAutoCancel(true)
+                .setContentIntent(pendingIntent);
+
+        NotificationManager notificationManager =
+                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+
+        notificationManager.notify(0, notificationBuilder.build());
+    }
+
+}

+ 36 - 0
src/modified/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java

@@ -0,0 +1,36 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.activity;
+
+import com.owncloud.android.ui.events.TokenPushEvent;
+import com.owncloud.android.utils.PushUtils;
+
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+public class ModifiedFileDisplayActivity extends FileDisplayActivity {
+
+    @Subscribe(threadMode = ThreadMode.BACKGROUND)
+    public void onMessageEvent(TokenPushEvent event) {
+        PushUtils.pushRegistrationToServer();
+    }
+
+}

+ 49 - 0
src/modified/java/com/owncloud/android/utils/GooglePlayUtils.java

@@ -0,0 +1,49 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.utils;
+
+import android.app.Activity;
+import android.util.Log;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+
+public class GooglePlayUtils {
+    private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
+    private static final String TAG = "GooglePlayUtils";
+
+    public static boolean checkPlayServices(Activity activity) {
+        GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
+        int resultCode = apiAvailability.isGooglePlayServicesAvailable(activity);
+        if (resultCode != ConnectionResult.SUCCESS) {
+            if (apiAvailability.isUserResolvableError(resultCode)) {
+                apiAvailability.getErrorDialog(activity, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)
+                        .show();
+            } else {
+                Log.i(TAG, "This device is not supported.");
+                activity.finish();
+            }
+            return false;
+        }
+        return true;
+    }
+
+}

+ 248 - 0
src/modified/java/com/owncloud/android/utils/PushUtils.java

@@ -0,0 +1,248 @@
+/**
+ * Nextcloud Android client application
+ *
+ * @author Mario Danic
+ * Copyright (C) 2017 Mario Danic
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.utils;
+
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.db.PreferenceManager;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.OwnCloudClient;
+import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForNotificationsOperation;
+import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForProxyOperation;
+import com.owncloud.android.lib.resources.notifications.models.PushResponse;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+
+public class PushUtils {
+
+    private static final String TAG = "PushUtils";
+    private static final String KEYPAIR_FOLDER = "nc-keypair";
+    private static final String KEYPAIR_FILE_NAME = "push_key";
+    private static final String KEYPAIR_PRIV_EXTENSION = ".priv";
+    private static final String KEYPAIR_PUB_EXTENSION = ".pub";
+
+    public static String generateSHA512Hash(String pushToken) {
+        MessageDigest messageDigest = null;
+        try {
+            messageDigest = MessageDigest.getInstance("SHA-512");
+            messageDigest.update(pushToken.getBytes());
+            return bytesToHex(messageDigest.digest());
+        } catch (NoSuchAlgorithmException e) {
+            Log_OC.d(TAG, "SHA-512 algorithm not supported");
+        }
+        return "";
+    }
+
+    private static String bytesToHex(byte[] bytes) {
+        StringBuilder result = new StringBuilder();
+        for (byte individualByte : bytes) {
+            result.append(Integer.toString((individualByte & 0xff) + 0x100, 16)
+                    .substring(1));
+        }
+        return result.toString();
+    }
+
+    public static int generateRsa2048KeyPair() {
+        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
+                + KEYPAIR_FOLDER;
+
+        String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
+        String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
+        File keyPathFile = new File(keyPath);
+
+        if (!new File(privateKeyPath).exists() && !new File(publicKeyPath).exists()) {
+            try {
+                if (!keyPathFile.exists()) {
+                    keyPathFile.mkdir();
+                }
+                KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+                keyGen.initialize(2048);
+
+                KeyPair pair = keyGen.generateKeyPair();
+                int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyPath);
+                int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyPath);
+
+                if (statusPrivate == 0 && statusPublic == 0) {
+                    // all went well
+                    return 0;
+                } else {
+                    return -2;
+                }
+            } catch (NoSuchAlgorithmException e) {
+                Log_OC.d(TAG, "RSA algorithm not supported");
+            }
+        } else {
+            // we already have the key
+            return -1;
+        }
+
+        // we failed to generate the key
+        return -2;
+    }
+
+    public static void pushRegistrationToServer() {
+        String token = PreferenceManager.getPushToken(MainApp.getAppContext());
+        if (!TextUtils.isEmpty(MainApp.getAppContext().getResources().getString(R.string.push_server_url)) &&
+                !TextUtils.isEmpty(token)) {
+            PushUtils.generateRsa2048KeyPair();
+            String pushTokenHash = PushUtils.generateSHA512Hash(token).toLowerCase();
+            PublicKey devicePublicKey = (PublicKey) PushUtils.readKeyFromFile(true);
+            if (devicePublicKey != null) {
+                byte[] publicKeyBytes = Base64.encode(devicePublicKey.getEncoded(), Base64.NO_WRAP);
+                String publicKey = new String(publicKeyBytes);
+                publicKey = publicKey.replaceAll("(.{64})", "$1\n");
+
+                publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n";
+
+                Context context = MainApp.getAppContext();
+                for (Account account : AccountUtils.getAccounts(context)) {
+                    try {
+                        OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
+                        OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                                getClientFor(ocAccount, context);
+
+                        RemoteOperation registerAccountDeviceForNotificationsOperation =
+                                new RegisterAccountDeviceForNotificationsOperation(pushTokenHash,
+                                        publicKey,
+                                        context.getResources().getString(R.string.push_server_url));
+
+                        RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
+                                execute(mClient);
+
+                        if (remoteOperationResult.isSuccess()) {
+                            PushResponse pushResponse = remoteOperationResult.getPushResponseData();
+
+                            RemoteOperation registerAccountDeviceForProxyOperation = new
+                                    RegisterAccountDeviceForProxyOperation(
+                                    context.getResources().getString(R.string.push_server_url),
+                                    token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
+                                    pushResponse.getPublicKey());
+
+                            remoteOperationResult = registerAccountDeviceForProxyOperation.execute(mClient);
+                            PreferenceManager.setPushTokenLastSentTime(MainApp.getAppContext(),
+                                    System.currentTimeMillis());
+
+                        }
+                    } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
+                        Log_OC.d(TAG, "Failed to find an account");
+                    } catch (AuthenticatorException e) {
+                        Log_OC.d(TAG, "Failed via AuthenticatorException");
+                    } catch (IOException e) {
+                        Log_OC.d(TAG, "Failed via IOException");
+                    } catch (OperationCanceledException e) {
+                        Log_OC.d(TAG, "Failed via OperationCanceledException");
+                    }
+                }
+            }
+        }
+    }
+
+    public static Key readKeyFromFile(boolean readPublicKey) {
+        String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
+                + KEYPAIR_FOLDER;
+        ;
+        String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION;
+        String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION;
+
+        String path;
+
+        if (readPublicKey) {
+            path = publicKeyPath;
+        } else {
+            path = privateKeyPath;
+        }
+
+        FileInputStream fileInputStream = null;
+        try {
+            fileInputStream = new FileInputStream(path);
+            byte[] bytes = new byte[fileInputStream.available()];
+            fileInputStream.read(bytes);
+            fileInputStream.close();
+
+            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+
+            if (readPublicKey) {
+                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
+                return keyFactory.generatePublic(keySpec);
+            } else {
+                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
+                return keyFactory.generatePrivate(keySpec);
+            }
+
+        } catch (FileNotFoundException e) {
+            Log_OC.d(TAG, "Failed to find path while reading the Key");
+        } catch (IOException e) {
+            Log_OC.d(TAG, "IOException while reading the key");
+        } catch (InvalidKeySpecException e) {
+            Log_OC.d(TAG, "InvalidKeySpecException while reading the key");
+        } catch (NoSuchAlgorithmException e) {
+            Log_OC.d(TAG, "RSA algorithm not supported");
+        }
+
+        return null;
+    }
+
+    private static int saveKeyToFile(Key key, String path) {
+        byte[] encoded = key.getEncoded();
+        FileOutputStream keyFileOutputStream = null;
+        try {
+            if (!new File(path).exists()) {
+                new File(path).createNewFile();
+            }
+            keyFileOutputStream = new FileOutputStream(path);
+            keyFileOutputStream.write(encoded);
+            keyFileOutputStream.close();
+            return 0;
+        } catch (FileNotFoundException e) {
+            Log_OC.d(TAG, "Failed to save key to file");
+        } catch (IOException e) {
+            Log_OC.d(TAG, "Failed to save key to file via IOException");
+        }
+
+        return -1;
+    }
+}

+ 25 - 5
src/modified/res/values/setup.xml

@@ -24,7 +24,7 @@
     <bool name="show_welcome_link">false</bool>
 	<string name="welcome_link_url">"https://nextcloud.com/providers"</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="auth_method_saml_web_sso">off</string>
@@ -34,20 +34,35 @@
     <bool name = "share_via_link_feature">true</bool>
     <bool name = "share_with_users_feature">true</bool>
     <bool name="show_external_links">true</bool>
+  
+    <bool name="show_whats_new">true</bool>
+    <!-- To fill if you want to show webviews instead of regular welcome views -->
+    <array name="whatsnew_urls"></array>
+    
+    // Contacts backup
+    <bool name="contacts_backup">true</bool>
+    <string name="contacts_backup_folder">/Contacts-Backup</string>
+    <integer name="contacts_backup_expire">30</integer>
 
     <!-- Colors -->
+    <color name="primary">@color/nc_blue</color>
+    <color name="primary_dark">#006AA3</color>
+    <color name="color_accent">@color/nc_blue_accent</color>
     <color name="login_text_color">@color/white</color>
     <color name="login_text_hint_color">#7fC0E3</color>
     <color name="login_background_color">#FFFFFF</color>
     <color name="login_logo_background_color">#FFFFFF</color>
     <color name="background_color">#FFFFFF</color>
-    <color name="actionbar_start_color">#0082c9</color>
-    <color name="actionbar_end_color">#0082c9</color>
-    <color name="primary_button_background_color">@color/owncloud_blue_accent</color>
+    <color name="navigation_bar_start_color">#0082c9</color>
+    <color name="navigation_bar_end_color">#0082c9</color>
+    <color name="primary_button_background_color">@color/nc_blue_accent</color>
     <color name="primary_button_text_color">@color/white</color>
     <color name="secondary_button_background_color">#D6D7D7</color>
     <color name="secondary_button_text_color">@color/black</color>
-    <color name="drawer_header_color">@color/owncloud_blue_accent</color>
+
+    <!-- special transparent action bar colors for image preview -->
+    <color name="color_transparent">#201D2D44</color>
+    <color name="color_dark_transparent">#40162233</color>
 
     <!-- Button -->
     <color name="button_text_color">#000000</color>
@@ -101,6 +116,9 @@
 
     <!-- login data links -->
     <string name="login_data_own_scheme" translatable="false">cloud</string>
+    <!-- url for webview login, with the protocol prefix
+    If set, will replace all other login methods available -->
+    <string name="webview_login_url" translatable="false"></string>
 
     <!-- analytics enabled -->
     <bool name="analytics_enabled">false</bool>
@@ -108,6 +126,8 @@
     <!-- Files becomes Home -->
     <bool name="use_home">true</bool>
 
+    <!-- Push server url -->
+    <string name="push_server_url" translatable="false"></string>
 </resources>