Browse Source

Merge remote-tracking branch 'remotes/origin/master' into pinchZoom_new

tobiasKaminsky 8 years ago
parent
commit
47b5748b74
100 changed files with 3922 additions and 3096 deletions
  1. 30 6
      .drone.yml
  2. 0 0
      .drone.yml.sig
  3. 1 0
      .gitignore
  4. 2 2
      .tx/config
  5. 11 1
      CHANGELOG.md
  6. 17 11
      CONTRIBUTING.md
  7. 99 0
      ICONS.txt
  8. 1 1
      README.md
  9. 12 3
      SETUP.md
  10. 24 12
      THIRD_PARTY.txt
  11. 2 2
      androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java
  12. 2 2
      automationTest/README.md
  13. 1 1
      automationTest/src/test/java/com/owncloud/android/test/ui/models/FileListView.java
  14. 119 82
      build.gradle
  15. 74 0
      drawable_resources/alphabetical_asc.svg
  16. 69 0
      drawable_resources/alphabetical_desc.svg
  17. 1 0
      drawable_resources/arrow-right.svg
  18. 1 0
      drawable_resources/calendar.svg
  19. 119 0
      drawable_resources/get_it_as_apk.svg
  20. 58 0
      drawable_resources/ic_activity.svg
  21. 58 0
      drawable_resources/ic_activity_light_grey.svg
  22. 56 0
      drawable_resources/ic_list_empty_error.svg
  23. 62 0
      drawable_resources/ic_list_empty_home.svg
  24. 58 0
      drawable_resources/ic_list_empty_image.svg
  25. 62 0
      drawable_resources/ic_list_empty_recent.svg
  26. 56 0
      drawable_resources/ic_list_empty_shared.svg
  27. 58 0
      drawable_resources/ic_list_empty_video.svg
  28. 58 0
      drawable_resources/ic_notification.svg
  29. 58 0
      drawable_resources/ic_notification_light_grey.svg
  30. 58 0
      drawable_resources/ic_search_light_blue.svg
  31. 58 0
      drawable_resources/ic_search_light_grey.svg
  32. 58 0
      drawable_resources/ic_star_light_yellow.svg
  33. 1 0
      drawable_resources/ic_twitter.svg
  34. 1 0
      drawable_resources/ic_web.svg
  35. 0 69
      drawable_resources/launcher_beta.svg
  36. 64 0
      drawable_resources/logout.svg
  37. 64 0
      drawable_resources/modification_asc.svg
  38. 63 0
      drawable_resources/modification_desc.svg
  39. 77 0
      drawable_resources/size_asc.svg
  40. 76 0
      drawable_resources/size_desc.svg
  41. 2 2
      gradle/wrapper/gradle-wrapper.properties
  42. 1 0
      issue_template.md
  43. BIN
      res/drawable/ic_synced.png
  44. 0 64
      res/layout/file_preview.xml
  45. 0 316
      res/values-da/strings.xml
  46. 0 425
      res/values-es-rMX/strings.xml
  47. 5 0
      res/xml/backup_config.xml
  48. 5 0
      scripts/lint/getBranchName.sh
  49. 2 0
      scripts/lint/lint-results.txt
  50. 180 0
      scripts/lint/lint-up.rb
  51. 0 93
      src/com/owncloud/android/services/SyncedFolderJobService.java
  52. 0 78
      src/com/owncloud/android/services/observer/SyncedFolderObserver.java
  53. 0 84
      src/com/owncloud/android/services/observer/SyncedFolderObserverService.java
  54. 0 39
      src/com/owncloud/android/ui/RadioButtonPreference.java
  55. 0 44
      src/com/owncloud/android/ui/SquareImageView.java
  56. 0 27
      src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java
  57. 0 27
      src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java
  58. 0 598
      src/com/owncloud/android/ui/fragment/ExtendedListFragment.java
  59. 0 590
      src/com/owncloud/android/ui/preview/PreviewImageFragment.java
  60. 0 26
      src/com/owncloud/android/utils/MimeType.java
  61. 0 56
      src/com/owncloud/android/utils/OwnCloudSession.java
  62. 0 109
      src/com/owncloud/android/utils/RecursiveFileObserver.java
  63. 35 0
      src/generic/google-services.json
  64. 33 0
      src/generic/java/com/owncloud/android/utils/AnalyticsUtils.java
  65. 29 0
      src/generic/java/com/owncloud/android/utils/PushUtils.java
  66. 84 0
      src/gplay/AndroidManifest.xml
  67. 35 0
      src/gplay/google-services.json
  68. 41 0
      src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java
  69. 43 0
      src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java
  70. 66 0
      src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java
  71. 35 0
      src/gplay/java/com/owncloud/android/utils/AnalyticsUtils.java
  72. 49 0
      src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.java
  73. 332 0
      src/gplay/java/com/owncloud/android/utils/PushUtils.java
  74. 7 0
      src/gplay/res/values/setup.xml
  75. 26 9
      src/main/AndroidManifest.xml
  76. 0 0
      src/main/java/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java
  77. 109 50
      src/main/java/com/owncloud/android/MainApp.java
  78. 13 8
      src/main/java/com/owncloud/android/authentication/AccountAuthenticator.java
  79. 0 0
      src/main/java/com/owncloud/android/authentication/AccountAuthenticatorActivity.java
  80. 0 0
      src/main/java/com/owncloud/android/authentication/AccountAuthenticatorService.java
  81. 11 11
      src/main/java/com/owncloud/android/authentication/AccountUtils.java
  82. 346 172
      src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java
  83. 0 0
      src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.java
  84. 0 0
      src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java
  85. 0 0
      src/main/java/com/owncloud/android/authentication/OAuth2Constants.java
  86. 30 6
      src/main/java/com/owncloud/android/authentication/PassCodeManager.java
  87. 5 7
      src/main/java/com/owncloud/android/authentication/SsoWebViewClient.java
  88. 244 0
      src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java
  89. 175 0
      src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java
  90. 16 10
      src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java
  91. 0 0
      src/main/java/com/owncloud/android/datamodel/MediaFolder.java
  92. 1 2
      src/main/java/com/owncloud/android/datamodel/MediaProvider.java
  93. 24 12
      src/main/java/com/owncloud/android/datamodel/OCFile.java
  94. 81 0
      src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java
  95. 4 1
      src/main/java/com/owncloud/android/datamodel/SyncedFolder.java
  96. 8 0
      src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java
  97. 112 12
      src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java
  98. 2 2
      src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
  99. 39 21
      src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java
  100. 3 3
      src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java

+ 30 - 6
.drone.yml

@@ -1,19 +1,24 @@
 pipeline:
   test:
-    image: nextcloudci/android:android-7
+    image: nextcloudci/android:android-18
     commands:
+      # uncomment gplay for Gplay, Modified only
+      - sh -c "if [ '$FLAVOUR' != 'Generic' ]; then sed -i '/com.google.*.gms/s/^.*\/\///g' build.gradle; fi"
+
       - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI -c 20M
       - emulator -avd test -no-window &
       - ./wait_for_emulator.sh
+
       # build app and assemble APK, in debug mode
-      - ./gradlew assembleDebug
-      # run all the local unit tests of app module
-      - ./gradlew :testDebug
+      - ./gradlew assemble${FLAVOUR}
+
       # 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 :installDebug
-      - ./gradlew :installDebugAndroidTest
+      - ./gradlew :install${FLAVOUR}Debug
+      - ./gradlew :install${FLAVOUR}DebugAndroidTest
+
       # 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
@@ -22,3 +27,22 @@ pipeline:
       - ANDROID_TARGET=android-24
       - ANDROID_ABI=armeabi-v7a
       - LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/:/opt/android-sdk-linux/tools/lib64/gles_mesa/
+
+  lint:
+      image: nextcloudci/android:android-18
+      commands:
+        # needs gplay
+        - sed -i '/com.google.*.gms/s/^.*\/\///g' build.gradle
+        - export BRANCH=$(scripts/lint/getBranchName.sh ${GIT_USERNAME} ${GIT_TOKEN} ${DRONE_PULL_REQUEST})
+        - ruby scripts/lint/lint-up.rb ${GIT_USERNAME} ${GIT_TOKEN} $BRANCH
+      when:
+        matrix:
+          FLAVOUR: Modified
+
+matrix:
+  FLAVOUR:
+    - Generic
+    - Gplay
+    - Modified
+
+branches: master

File diff suppressed because it is too large
+ 0 - 0
.drone.yml.sig


+ 1 - 0
.gitignore

@@ -33,3 +33,4 @@ tests/proguard-project.txt
 *.iml
 build
 /gradle.properties
+

File diff suppressed because it is too large
+ 2 - 2
.tx/config


+ 11 - 1
CHANGELOG.md

@@ -1,3 +1,13 @@
+## 1.4.2 (March 14, 2017)
+- Auto Upload for newly taken photos/images (Android 6+)
+- Auto Upload improvements and fixes
+- Filtering improvements
+- Fix for Android permissions (removed read phone state permission)
+- Fix re-upload of files
+- Avoid toggling favourite for all selected files
+- Link to providers list in the setup screen
+- further bugfixes and improvements
+
 ## 1.4.1 (January 27, 2017)
 - Share URLs to Nextcloud
 - Improve performance of Auto Upload view
@@ -36,7 +46,7 @@
 - Display quota if configured/available in navigation drawer
 - Resume chunked uploads instead of complete restarts
 - Filter remote and local file lists
-- Simple integration with DAVdroid for calender and contacts sync
+- Simple integration with DAVdroid for calendar and contacts sync
 - Mix folders and files on sort by date
 - Upload when charging option
 - Revamp upload options Move/Copy/Just-Upload

+ 17 - 11
CONTRIBUTING.md

@@ -22,13 +22,13 @@
     1. Types
         1. Stable
         1. Release Candidate
-        1. Beta
+        1. Dev
     1. Version Name and number
     1. Release cycle
     1. Release Process
         1. Stable
         1. Release Candidate
-        1. Development Beta
+        1. Development Dev
 
 
 # Guidelines
@@ -58,7 +58,7 @@ If your issue appears to be a bug, and hasn't been reported, open a new issue.
 # Contributing to Source Code
 Thanks for wanting to contribute source code to Nextcloud. That's great!
 
-New contributions are addded under AGPL version 3.
+New contributions are added under AGPL version 3.
 
 ## Developing process
 We are all about quality while not sacrificing speed so we use a very pragmatic workflow.
@@ -79,6 +79,12 @@ Our formatter setup is rather simple:
 * Auto optimize imports (Settings->Editor->Auto Import->Optimize imports on the fly)
 
 
+### Build variants
+There are three build variants
+* generic: no Google Stuff, used for FDroid
+* gplay: with Google Stuff (Push notification) and Analytics disabled, used for Google Play Store
+* modified: custom, with Google Stuff and Analytics enabled, used for branded releases
+
 ## Contribution process
 * Contribute your code in the branch 'master'. It will give us a better chance to test your code before merging it with stable code.
 * For your first contribution start a pull request on master.
@@ -128,10 +134,10 @@ _stable beta_ releases done via the Beta program of the Google Play store and f-
 Whenever a PR is reviewed/approved we put it on master.
 Before releasing a new stable version there is at least one release candidate. It is based on the current master and during this phase the master is feature freezed. After ~2 weeks with no error a stable version will be releaded, which is identically to the latest release candidate. 
 
-### Development Beta
+### Dev
 Done as a standalone app that can be installed in parallel to the stable app.
-Any PR which is labelled "ready for beta" will be automatically included in the beta app. This label should only set by the main developers. 
-Same applies for the android-library. This repository also has a branch called beta which includes all upcoming features. The beta branch on this repository must always use the android-library beta branch.
+Any PR which is labelled "ready for dev" will be automatically included in the dev app. This label should only set by the main developers. 
+Same applies for the android-library. This repository also has a branch called dev which includes all upcoming features. The dev branch on this repository must always use the android-library dev branch.
 
 ## Version Name and number
 ### Stable / Release candidate
@@ -151,8 +157,8 @@ Examples for different versions:
 
 beware, that beta releases for an upcoming version will always use the minor and hotfix version of the release they are targeting. So to make sure the version code of the upcoming stable release will always be higher stable releases set the 2 beta digits to '99' as seen above in the examples.
 
-### Development Beta
-For development beta the version name is in format YYYYMMDD. It is mainly as a reference for reporting bugs and is not related to stable/release candidates as it is an independent app.
+### Dev
+For dev the version name is in format YYYYMMDD. It is mainly as a reference for reporting bugs and is not related to stable/release candidates as it is an independent app.
 
 ## Release cycle
 * for each release we choose several PRs that will be included in the next release. Currently there are many open PRs from ownCloud, but after merging them, the intention is to choose the PRs that are ready (reviewed, tested) to get them merged very soon.
@@ -180,7 +186,7 @@ Release Candidate releases are based on the git [master](https://github.com/next
 2. Create a [release/tag](https://github.com/nextcloud/android/releases) in git. Tag name following the naming schema: ```rc-Mayor.Minor.Hotfix-betaIncrement``` (e.g. rc-1.2.0-12) naming the version number following the [semantic versioning schema](http://semver.org/)
 
 
-###Development Beta Release
-Beta releases are based on the git [beta](https://github.com/nextcloud/android/tree/beta) and are done independently from stable releases and integrate open PRs that might not be production ready or heavily tested but being put out there for people willing to test new features and provide valuable feedback on new features to be incorporated before a feature gets released in the stable app.
+### Dev Release
+Dev releases are based on the [dev](https://github.com/nextcloud/android/tree/dev) branch and are done independently from stable releases and integrate open PRs that might not be production ready or heavily tested but being put out there for people willing to test new features and provide valuable feedback on new features to be incorporated before a feature gets released in the stable app.
 
-The deployment/build is done once a day automatically. If code has changed a new apk will be published on https://github.com/nextcloud/android/tree/beta/apks and it will also be available on f-droid.
+The deployment/build is done once a day automatically. If code has changed a new apk will be published [here](https://download.nextcloud.com/android/dev) and it will, with a little delay, be available on f-droid.

+ 99 - 0
ICONS.txt

@@ -0,0 +1,99 @@
+Standard Google Material Design icons
+Copyright (c) 2014, Google (http://www.google.com/design/)
+uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE
+
+Twitter icon graphic
+Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
+with Reserved Font Name Material Design Icons.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

+ 1 - 1
README.md

@@ -29,7 +29,7 @@ if you want to join the Github organization just let us know and we’ll add you
 Make sure you read [SETUP.md](https://github.com/nextcloud/android/blob/master/SETUP.md) and [CONTRIBUTING.md](https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md) when you start working on this project. Basically: Fork this repository and contribute back using pull requests to the master branch.
 Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/android/pulls) and working on [starter issue](https://github.com/nextcloud/android/issues?q=is%3Aopen+is%3Aissue+label%3A%22starter+issue%22).
 
-**Beta version** [beta version](https://github.com/nextcloud/android/raw/beta/apks/latest.apk)
+**Dev version** [dev version](https://download.nextcloud.com/android/dev/latest.apk)
 
 **License:** [GPLv2](https://github.com/nextcloud/android/blob/master/LICENSE.txt)
 

+ 12 - 3
SETUP.md

@@ -28,7 +28,7 @@ For other software dependencies check the details in the section corresponding t
 
 You will need [git][1] to access to the different versions of the Nextcloud's source code. The source code is hosted in Github and may be read by anybody without needing a Github account. You will need a Github account if you want to contribute to the development of the app with your own code.
 
-Next steps will assume you have a Github account and that you will get the code from your own fork. 
+Next steps will assume you have a Github account and that you will get the code from your own fork.
 
 * In a web browser, go to https://github.com/nextcloud/android, and click the 'Fork' button near the top right corner.
 * Open a terminal and go on with the next steps in it.
@@ -63,11 +63,20 @@ To set up the project in Android Studio follow the next steps:
 * Run the 'clean' and 'build' tasks using the Gradle wrapper provided
     - Windows: ```gradlew.bat clean build```
     - Mac OS/Linux: ```./gradlew clean build```
-	
+
 The first time the Gradle wrapper is called, the correct Gradle version will be downloaded automatically. An Internet connection is needed for it works.
-	
+
 The generated APK file is saved in android/build/outputs/apk as android-debug.apk
 
+### 4. App flavours
+
+The app is currently equipped to be built with two flavours:
+* generic - the regular build, released as a Nextcloud Android app on the Play store
+* custom - a customized build, to be used by people who need features we can't or
+  won't include into the traditional build (like Firebase Analytics)
+
+When building the *generic*, you will *not* get the dependencies imposed by the *custom*
+build.
 
 [0]: https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md
 [1]: https://git-scm.com/

+ 24 - 12
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
 ###################################################################
 
@@ -12,7 +12,7 @@
 ###########
 
 This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License verions 2,
+it under the terms of the GNU General Public License versions 2,
 as published by the Free Software Foundation.
 
 This program is distributed in the hope that it will be useful,
@@ -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

+ 2 - 2
androidTest/java/com/owncloud/android/datamodel/OCFileUnitTest.java

@@ -85,7 +85,7 @@ public class OCFileUnitTest {
         mFile.setModificationTimestampAtLastSyncForData(MODIFICATION_TIMESTAMP_AT_LAST_SYNC_FOR_DATA);
         mFile.setLastSyncDateForProperties(LAST_SYNC_DATE_FOR_PROPERTIES);
         mFile.setLastSyncDateForData(LAST_SYNC_DATE_FOR_DATA);
-        mFile.setFavorite(true);
+        mFile.setAvailableOffline(true);
         mFile.setEtag(ETAG);
         mFile.setShareViaLink(true);
         mFile.setShareWithSharee(true);
@@ -120,7 +120,7 @@ public class OCFileUnitTest {
         );
         assertThat(fileReadFromParcel.getLastSyncDateForProperties(), is(LAST_SYNC_DATE_FOR_PROPERTIES));
         assertThat(fileReadFromParcel.getLastSyncDateForData(), is(LAST_SYNC_DATE_FOR_DATA));
-        assertThat(fileReadFromParcel.isFavorite(), is(true));
+        assertThat(fileReadFromParcel.setAvailableOffline(), is(true));
         assertThat(fileReadFromParcel.getEtag(), is(ETAG));
         assertThat(fileReadFromParcel.isSharedViaLink(), is(true));
         assertThat(fileReadFromParcel.isSharedWithSharee(), is(true));

+ 2 - 2
automationTest/README.md

@@ -4,7 +4,7 @@ This project contains a set of automatic tests operating in the UI level.
 
 Tests are to be run with the tool Appium. Check [here][0] to install it and all its dependencies (including Maven).
 
-You will need to modify the constants in automationTest/src/test/java/com/owncloud/android/test/ui/testSuites/Config.java to assign appropiate values for your test server and accounts.
+You will need to modify the constants in automationTest/src/test/java/com/owncloud/android/test/ui/testSuites/Config.java to assign appropriate values for your test server and accounts.
 You will need to include the ownCloud.apk to test in automationTest/src/test/resources/.
 
 To run the tests from command line, plug a device to your computer or start and emulator. Then type 
@@ -15,6 +15,6 @@ To run only one category of the test
 
 mvn clean -Dtest=RunSmokeTests test
 
-The project may also be imported in Eclipse, with the appropiate plug-ins, and run from it.
+The project may also be imported in Eclipse, with the appropriate plug-ins, and run from it.
 
 [0]: http://appium.io/slate/en/master/?java#about-appium

+ 1 - 1
automationTest/src/test/java/com/owncloud/android/test/ui/models/FileListView.java

@@ -89,7 +89,7 @@ public class FileListView {
 	private static String localFileIndicator = 
 			"com.owncloud.android:id/localFileIndicator";
 	private static String favoriteFileIndicator = 
-			"com.owncloud.android:id/favoriteIcon";
+			"com.owncloud.android:id/keptOfflineIcon";
 	private static String sharedElementIndicator = 
 			"com.owncloud.android:id/sharedIcon";
 	

+ 119 - 82
build.gradle

@@ -8,9 +8,13 @@
 buildscript {
     repositories {
         jcenter()
+        maven {
+            url 'https://oss.sonatype.org/content/repositories/snapshots/'
+        }
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.2.3'
+        classpath 'com.android.tools.build:gradle:2.3.1'
+        classpath 'com.google.gms:google-services:3.0.0'
     }
 }
 
@@ -19,8 +23,13 @@ apply plugin: 'checkstyle'
 apply plugin: 'pmd'
 apply plugin: 'findbugs'
 
+configurations.all {
+    // check for updates every build
+    resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
+}
+
 ext {
-    supportLibraryVersion = '24.2.1'
+    supportLibraryVersion = '25.0.0'
 
     travisBuild = System.getenv("TRAVIS") == "true"
 
@@ -31,66 +40,30 @@ ext {
 repositories {
     jcenter()
     maven { url "https://jitpack.io" }
+    maven {
+        url 'https://oss.sonatype.org/content/repositories/snapshots/'
+    }
 
     flatDir {
         dirs 'libs'
     }
 }
 
-dependencies {
-    /// dependencies for app building
-    compile name: 'touch-image-view'
-
-    compile 'com.github.nextcloud:android-library:1.0.11'
-    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.getbase:floatingactionbutton:1.10.1'
-    compile 'com.google.code.findbugs:annotations:2.0.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}"
-
-
-}
-
-tasks.withType(Test) {
-    /// increased logging for tests
-    testLogging {
-        events "passed", "skipped", "failed"
-    }
-}
-
 android {
     lintOptions {
-        abortOnError true
+        abortOnError false
         lintConfig file("${project.rootDir}/lint.xml")
         htmlReport true
         htmlOutput file("$project.buildDir/reports/lint/lint.html")
+        disable 'MissingTranslation'
     }
-    compileSdkVersion 24
-    buildToolsVersion "24.0.2"
+
+    dexOptions {
+        javaMaxHeapSize "4g"
+    }
+
+    compileSdkVersion 25
+    buildToolsVersion '25.0.0'
 
     defaultConfig {
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -100,40 +73,39 @@ android {
         testInstrumentationRunnerArgument "TEST_PASSWORD", "\"$System.env.OCTEST_APP_PASSWORD\""
         testInstrumentationRunnerArgument "TEST_SERVER_URL", "\"$System.env.OCTEST_SERVER_BASE_URL\""
 
-        applicationId "com.nextcloud.client"
-    }
+        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
-    sourceSets {
-        main {
-            manifest.srcFile 'AndroidManifest.xml'
-            java.srcDirs = ['src']
-            resources.srcDirs = ['src']
-            aidl.srcDirs = ['src']
-            renderscript.srcDirs = ['src']
-            res.srcDirs = ['res']
-            assets.srcDirs = ['assets']
-        }
+        // 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 {
+            // used for f-droid
+            generic {
+                applicationId 'com.nextcloud.client'
+            }
 
-        // move whole local unit tests structure as a whole from src/test/* to test/*
-        test.setRoot('test')
-
-        // move whole instrumented tests structure as a whole from src/androidTest/* to androidTest/*
-        androidTest.setRoot('androidTest')
+            gplay {
+                applicationId 'com.nextcloud.client'
+            }
 
+            modified {
+                // structure is:
+                // domain tld
+                // domain name
+                // .client
+                applicationId 'com.custom.client'
+            }
+        }
 
-        // Move the build types to build-types/<type>
-        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
-        // This moves them out of them default location under src/<type>/... which would
-        // conflict with src/ being used by the main source set.
-        // Adding new build types or product flavors should be accompanied
-        // by a similar customization.
-        debug.setRoot('build-types/debug')
-        release.setRoot('build-types/release')
+        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 when running on Travis CI or when disabled via -Dpre-dex=false.
         preDexLibraries = preDexEnabled && !travisBuild
@@ -144,13 +116,11 @@ android {
         targetCompatibility JavaVersion.VERSION_1_7
     }
 
-    lintOptions {
-        abortOnError false
-    }
-
     packagingOptions {
         exclude 'META-INF/LICENSE.txt'
+        exclude 'META-INF/LICENSE'
     }
+
     task checkstyle(type: Checkstyle) {
         configFile = file("${rootProject.projectDir}/checkstyle.xml")
         configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/config/quality/checkstyle/suppressions.xml").absolutePath
@@ -203,3 +173,70 @@ android {
     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:1.0.18'
+    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.googlecode.ez-vcard:ez-vcard:0.10.2'
+
+    // uncomment for gplay, modified
+    //compile 'com.google.android.gms:play-services:10.2.4'
+
+    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"
+    }
+}
+
+// uncomment for gplay, modified (must be at the bottom)
+//apply plugin: 'com.google.gms.google-services'

+ 74 - 0
drawable_resources/alphabetical_asc.svg

@@ -0,0 +1,74 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="alphabetical_asc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_alphabetical_asc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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="27.81"
+     inkscape:cx="5.6317334"
+     inkscape:cy="12"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <g
+     id="g4174"
+     transform="matrix(0.92801764,0,0,0.92801764,3.1474097,-0.40986228)"
+     style="fill:#757575;fill-opacity:1">
+    <path
+       inkscape:connector-curvature="0"
+       id="path4"
+       d="m 4.9604453,12.687998 v 1.805869 h 4.0659743 v 0.05724 L 4.535752,20.951046 v 1.190987 H 11.86263 V 20.326932 H 7.4088934 v -0.07571 L 11.796157,13.954685 V 12.687992 H 4.9604453 Z"
+       style="fill:#757575;fill-opacity:1" />
+    <path
+       inkscape:connector-curvature="0"
+       id="path4-2"
+       d="M 6.9569297,2.3102604 4.0357798,11.764298 h 2.2416409 l 0.679509,-2.5536989 h 2.7328076 l 0.7367497,2.5536989 h 2.326579 L 9.7931408,2.3102604 Z m 1.314702,1.539974 h 0.027698 L 8.5356806,4.7587082 8.781264,5.6764144 9.3776808,7.6798582 H 7.2689873 L 7.8358603,5.6948791 8.0629788,4.7679408 Z"
+       style="fill:#757575;fill-opacity:1" />
+  </g>
+  <path
+     inkscape:connector-curvature="0"
+     d="m 19.12569,12.771205 -1.428741,-0.0014 -0.0086,6.184289 -1.53692,-1.544835 -1.015429,1.013386 3.263957,3.275355 3.275354,-3.263958 -1.013385,-1.015428 -1.544836,1.53692 z"
+     id="path4-3"
+     sodipodi:nodetypes="cccccccccc"
+     style="fill:#757575;fill-opacity:1" />
+</svg>

+ 69 - 0
drawable_resources/alphabetical_desc.svg

@@ -0,0 +1,69 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="alphabetical_desc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_alphabetical_desc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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="27.81"
+     inkscape:cx="-2.5868955"
+     inkscape:cy="12"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <path
+     style="fill:#757575;fill-opacity:1"
+     d="m 7.7507904,1.734489 v 1.675878 h 3.7732956 v 0.05312 L 7.3566676,9.402743 V 10.508 H 14.15614 V 8.823554 H 10.022993 V 8.753294 L 14.094451,2.909997 V 1.734483 H 7.7507904 Z"
+     id="path4"
+     inkscape:connector-curvature="0" />
+  <path
+     style="fill:#757575;fill-opacity:1"
+     d="M 9.6035632,11.364486 6.8926845,20.138 H 8.9729668 L 9.6035632,17.768122 H 12.139657 L 12.823374,20.138 h 2.159106 l -2.746863,-8.773514 z m 1.2200668,1.429123 h 0.0257 l 0.219338,0.84308 0.227906,0.851647 0.553485,1.859232 H 9.8931541 l 0.5260679,-1.842096 0.21077,-0.860215 0.193634,-0.851648 z"
+     id="path4-2"
+     inkscape:connector-curvature="0" />
+  <path
+     inkscape:connector-curvature="0"
+     d="m 19.12569,12.771205 -1.428741,-0.0014 -0.0086,6.184289 -1.53692,-1.544835 -1.015429,1.013386 3.263957,3.275355 3.275354,-3.263958 -1.013385,-1.015428 -1.544836,1.53692 z"
+     id="path4-3"
+     sodipodi:nodetypes="cccccccccc"
+     style="fill:#757575;fill-opacity:1" />
+</svg>

+ 1 - 0
drawable_resources/arrow-right.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" /></svg>

+ 1 - 0
drawable_resources/calendar.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M19,19H5V8H19M16,1V3H8V1H6V3H5C3.89,3 3,3.89 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5C21,3.89 20.1,3 19,3H18V1M17,12H12V17H17V12Z" /></svg>

+ 119 - 0
drawable_resources/get_it_as_apk.svg

@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<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="http://www.w3.org/2000/svg"
+    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+    id="svg2"
+    version="1.1"
+    inkscape:version="0.91 r13725"
+    width="646"
+    height="250"
+    viewBox="0 0 646 250"
+    sodipodi:docname="get it as apk.svg"
+    inkscape:export-filename="/home/tobi/Daten/projekt/nextcloud/graphic/get it as apk.png"
+    inkscape:export-xdpi="90"
+    inkscape:export-ydpi="90">
+  <metadata
+     id="metadata8">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs6" />
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1600"
+     inkscape:window-height="835"
+     id="namedview4"
+     showgrid="false"
+     inkscape:zoom="0.70710678"
+     inkscape:cx="-365.55383"
+     inkscape:cy="112.63522"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="layer2"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       position="195.16147,-94.752308"
+       orientation="0,1"
+       id="guide4794" />
+    <sodipodi:guide
+       position="159.80613,-198.697"
+       orientation="0,1"
+       id="guide4796" />
+  </sodipodi:namedview>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="apk"
+     style="display:inline">
+    <rect
+       style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#000000;fill-opacity:1;stroke:#a6a6a6;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate"
+       id="rect4142"
+       width="558.33984"
+       height="163.47433"
+       x="44.59919"
+       y="43.897827"
+       ry="20.533007" />
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.70548439px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="222.50375"
+       y="90.160751"
+       id="text4163"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4165"
+         x="222.50375"
+         y="90.160751"
+         style="font-size:32.55822754px;fill:#ffffff">GET IT AS</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:35.25382233px;line-height:125%;font-family:Helvetica;-inkscape-font-specification:Helvetica;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       x="223.56383"
+       y="176.70526"
+       id="text4167"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4169"
+         x="223.56383"
+         y="176.70526"
+         style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-size:88.134552px;font-family:'Open Sans';-inkscape-font-specification:'Open Sans Semi-Bold'">APK-File</tspan></text>
+    <g
+       style="fill:#000000"
+       id="g4784"
+       transform="matrix(1.0640299,0,0,1.0640299,-61.475209,-106.93774)">
+      <path
+         id="path4774"
+         d="m 126.7489,161.76071 110.43377,0 0,110.43377 -110.43377,0 z"
+         inkscape:connector-curvature="0"
+         style="fill:none" />
+      <path
+         id="path4776"
+         d="m 154.35734,244.58604 c 0,2.53077 2.07064,4.60141 4.60141,4.60141 l 4.6014,0 0,16.10493 c 0,3.81916 3.08294,6.9021 6.90212,6.9021 3.81916,0 6.9021,-3.08294 6.9021,-6.9021 l 0,-16.10493 9.20283,0 0,16.10493 c 0,3.81916 3.08294,6.9021 6.9021,6.9021 3.81917,0 6.90212,-3.08294 6.90212,-6.9021 l 0,-16.10493 4.60139,0 c 2.53078,0 4.60141,-2.07064 4.60141,-4.60141 l 0,-46.01408 -55.21688,0 0,46.01408 z m -11.50351,-46.01408 c -3.81918,0 -6.90212,3.08295 -6.90212,6.90212 l 0,32.20986 c 0,3.81915 3.08294,6.9021 6.90212,6.9021 3.81916,0 6.9021,-3.08295 6.9021,-6.9021 l 0,-32.20986 c 0,-3.81917 -3.08294,-6.90212 -6.9021,-6.90212 z m 78.22391,0 c -3.81916,0 -6.9021,3.08295 -6.9021,6.90212 l 0,32.20986 c 0,3.81915 3.08294,6.9021 6.9021,6.9021 3.81917,0 6.90212,-3.08295 6.90212,-6.9021 l 0,-32.20986 c 0,-3.81917 -3.08295,-6.90212 -6.90212,-6.90212 z m -22.86899,-26.87221 5.98182,-5.98182 c 0.9203,-0.92028 0.9203,-2.34672 0,-3.26701 -0.92027,-0.92028 -2.34671,-0.92028 -3.26699,0 l -6.81008,6.81009 c -3.63512,-1.84057 -7.77637,-2.89888 -12.14772,-2.89888 -4.41735,0 -8.55862,1.05831 -12.23974,2.89888 l -6.8561,-6.81009 c -0.92027,-0.92028 -2.34671,-0.92028 -3.267,0 -0.92028,0.92029 -0.92028,2.34673 0,3.26701 l 6.02785,6.02783 c -6.81009,5.01554 -11.27345,13.06801 -11.27345,22.22481 l 55.21688,0 c 0,-9.1568 -4.46335,-17.25529 -11.36547,-22.27082 z m -25.44578,13.06799 -4.60141,0 0,-4.60139 4.60141,0 0,4.60139 z m 23.00703,0 -4.60141,0 0,-4.60139 4.60141,0 0,4.60139 z"
+         inkscape:connector-curvature="0"
+         style="fill:#ffffff" />
+    </g>
+  </g>
+</svg>

+ 58 - 0
drawable_resources/ic_activity.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="32"
+   width="32"
+   version="1.0"
+   viewBox="0 0 32 32"
+   id="svg4"
+   sodipodi:docname="ic_activity.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\activities\src\main\res\drawable-mdpi\ic_activity.png"
+   inkscape:export-xdpi="72"
+   inkscape:export-ydpi="72">
+  <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="7.375"
+     inkscape:cx="16"
+     inkscape:cy="16"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="M 16,1.9491526 6,18.810169 H 17 L 16,30.050847 26,13.189831 H 15 Z"
+     id="path2"
+     style="fill:#757575;fill-opacity:1;stroke-width:0.96784461"
+     inkscape:connector-curvature="0" />
+</svg>

+ 58 - 0
drawable_resources/ic_activity_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="32"
+   width="32"
+   version="1.0"
+   viewBox="0 0 32 32"
+   id="svg4"
+   sodipodi:docname="ic_activity_light_grey.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\activities\src\main\res\drawable-mdpi\ic_activity_light_grey.png"
+   inkscape:export-xdpi="216"
+   inkscape:export-ydpi="216">
+  <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="7.375"
+     inkscape:cx="-20.40678"
+     inkscape:cy="16"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="M 16,1.9491526 6,18.810169 H 17 L 16,30.050847 26,13.189831 H 15 Z"
+     id="path2"
+     style="fill:#000000;fill-opacity:1;stroke-width:0.96784461;opacity:0.5"
+     inkscape:connector-curvature="0" />
+</svg>

+ 56 - 0
drawable_resources/ic_list_empty_error.svg

@@ -0,0 +1,56 @@
+<?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"
+   id="svg4"
+   sodipodi:docname="ic_list_empty_error.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-mdpi\ic_list_empty_error.png"
+   inkscape:export-xdpi="432"
+   inkscape:export-ydpi="432"
+   inkscape:version="0.92.1 r15371">
+  <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="M5.516 2L2 5.516v4.968L5.516 14h4.968L14 10.484V5.516L10.484 2H5.516zM7 4h2v5H7V4zm0 6h2v2H7v-2z"
+     id="path2"
+     style="fill:#000000;fill-opacity:1;opacity:0.5" />
+</svg>

+ 62 - 0
drawable_resources/ic_list_empty_home.svg

@@ -0,0 +1,62 @@
+<?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"
+   id="svg6"
+   sodipodi:docname="ic_list_empty_home.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-xxxhdpi\ic_home.png"
+   inkscape:export-xdpi="576"
+   inkscape:export-ydpi="576">
+  <metadata
+     id="metadata12">
+    <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="defs10" />
+  <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="namedview8"
+     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="svg6" />
+  <path
+     color="#000"
+     fill="none"
+     d="M-62.897-32.993h163.31v97.986h-163.31z"
+     id="path2" />
+  <path
+     d="M 8,1.4367797 0.46588938,8.9426374 H 3.2911808 V 14.59322 H 12.708819 V 8.9426374 h 2.825292 L 12.708819,6.089093 V 2.3502906 H 9.8835281 v 1.0171049 z"
+     id="path4"
+     inkscape:connector-curvature="0"
+     style="opacity:1;fill-rule:evenodd;stroke-width:0.94176388;fill:#757575;fill-opacity:1" />
+</svg>

+ 58 - 0
drawable_resources/ic_list_empty_image.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"
+   id="svg4"
+   sodipodi:docname="ic_list_empty_image.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-mdpi\ic_list_empty_image.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="8"
+     inkscape:cy="8"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     style="block-progression:tb;text-transform:none;text-indent:0;fill:#000000;fill-opacity:1;opacity:0.5"
+     d="M1.344 2A.445.445 0 0 0 1 2.438v11.124c0 .23.212.44.438.44h13.125a.457.457 0 0 0 .437-.44V2.61c0-.337-.264-.61-.515-.61zM2 3h12v5l-1-1-3 4-3-3-4 4H2zm2.5 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3z"
+     fill="#969696"
+     color="#000"
+     id="path2" />
+</svg>

+ 62 - 0
drawable_resources/ic_list_empty_recent.svg

@@ -0,0 +1,62 @@
+<?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"
+   id="svg6"
+   sodipodi:docname="ic_list_empty_recent.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-mdpi\ic_recent.png"
+   inkscape:export-xdpi="144"
+   inkscape:export-ydpi="144"
+   inkscape:version="0.92.1 r15371">
+  <metadata
+     id="metadata12">
+    <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="defs10" />
+  <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="namedview8"
+     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="svg6" />
+  <path
+     color="#000"
+     fill="none"
+     d="M-62.897-32.993h163.31v97.986h-163.31z"
+     id="path2" />
+  <path
+     style="color:#000000;text-indent:0;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;white-space:normal;isolation:auto;mix-blend-mode:normal;stroke-width:0.93220341;fill:#757575;fill-opacity:1;opacity:1"
+     d="M 8,0.54237288 C 3.8927119,0.54237288 0.54237288,3.8927119 0.54237288,8 c 0,4.107288 3.35033902,7.457627 7.45762712,7.457627 4.107288,0 7.457627,-3.350339 7.457627,-7.457627 C 15.457627,3.8927119 12.107288,0.54237288 8,0.54237288 Z M 8,2.4067797 c 3.100508,0 5.59322,2.4927118 5.59322,5.5932203 0,3.100508 -2.492712,5.59322 -5.59322,5.59322 C 4.8994915,13.59322 2.4067797,11.100508 2.4067797,8 2.4067797,4.8994915 4.8994915,2.4067797 8,2.4067797 Z M 7.5898305,3.2755932 A 0.57516949,0.57516949 0 0 0 7.0258475,3.8470339 L 6.2316102,7.9860169 V 8 C 6.0610169,8.7038136 6.5457627,8.9322034 7.0864407,9.260339 h 0.00466 L 9.8858475,10.73322 C 10.494576,11.201186 11.197458,10.289492 10.588729,9.819661 L 8.9322034,8.009322 V 8 L 8.1771186,3.8516949 A 0.57516949,0.57516949 0 0 0 7.5898305,3.2755932 Z"
+     id="path4"
+     inkscape:connector-curvature="0" />
+</svg>

+ 56 - 0
drawable_resources/ic_list_empty_shared.svg

@@ -0,0 +1,56 @@
+<?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"
+   id="svg4"
+   sodipodi:docname="ic_list_empty_shared.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-mdpi\ic_shared.png"
+   inkscape:export-xdpi="144"
+   inkscape:export-ydpi="144"
+   inkscape:version="0.92.1 r15371">
+  <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="M12.228 1a2.457 2.457 0 0 0-2.46 2.454c0 .075.01.15.016.224L5.05 6.092a2.445 2.445 0 0 0-1.596-.586A2.453 2.453 0 0 0 1 7.96a2.453 2.453 0 0 0 2.454 2.455 2.45 2.45 0 0 0 1.46-.477l4.865 2.474c-.004.044-.01.09-.01.134a2.457 2.457 0 1 0 .804-1.818l-4.696-2.4c.02-.123.035-.25.035-.378 0-.072-.01-.144-.015-.214l4.74-2.414A2.457 2.457 0 1 0 12.228.99z"
+     id="path2"
+     style="fill:#757575;fill-opacity:1;opacity:1" />
+</svg>

+ 58 - 0
drawable_resources/ic_list_empty_video.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"
+   id="svg4"
+   sodipodi:docname="ic_list_empty_video.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\favourites\src\main\res\drawable-mdpi\ic_list_empty_video.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="8"
+     inkscape:cy="8"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     style="block-progression:tb;text-transform:none;text-indent:0;fill:#000000;fill-opacity:1;opacity:0.5"
+     d="M1.344 2A.445.445 0 0 0 1 2.438v11.124c0 .23.212.44.438.44h13.124a.457.457 0 0 0 .438-.44V2.61c0-.336-.265-.61-.516-.61zM2 3h12v10H2zm3 2v6l6-3z"
+     fill="#969696"
+     color="#000"
+     id="path2" />
+</svg>

+ 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>

+ 58 - 0
drawable_resources/ic_search_light_blue.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"
+   version="1.1"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   id="svg4"
+   sodipodi:docname="ic_search_light_blue.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\filtering-improvements\res\drawable-xxxhdpi\ic_search_light_blue.png"
+   inkscape:export-xdpi="1152"
+   inkscape:export-ydpi="1152"
+   inkscape:version="0.92.0 r15299">
+  <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" />
+        <dc:title></dc:title>
+      </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="9.8333333"
+     inkscape:cx="12"
+     inkscape:cy="12"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"
+     id="path2"
+     style="fill:#99cde9;fill-opacity:1" />
+</svg>

+ 58 - 0
drawable_resources/ic_search_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"
+   version="1.1"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   id="svg4"
+   sodipodi:docname="ic_search_light_grey.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\new-filtering-magic\res\drawable-xxxhdpi\ic_search_light_grey.png"
+   inkscape:export-xdpi="1152"
+   inkscape:export-ydpi="1152"
+   inkscape:version="0.92.0 r15299">
+  <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" />
+        <dc:title />
+      </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="9.8333333"
+     inkscape:cx="-15.305085"
+     inkscape:cy="12"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"
+     id="path2"
+     style="fill:#000000;fill-opacity:1;opacity:0.5" />
+</svg>

+ 58 - 0
drawable_resources/ic_star_light_yellow.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"
+   version="1.1"
+   width="24"
+   height="24"
+   viewBox="0 0 24 24"
+   id="svg4"
+   sodipodi:docname="ic_star_light_yellow.svg"
+   inkscape:version="0.92.1 r15371"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\showSearchViewHeaders\src\main\res\drawable-mdpi\ic_star_light_yellow.png"
+   inkscape:export-xdpi="288"
+   inkscape:export-ydpi="288">
+  <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" />
+        <dc:title />
+      </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="9.8333333"
+     inkscape:cx="-42.61017"
+     inkscape:cy="12"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z"
+     id="path2"
+     style="fill:#ffcc00;fill-opacity:1;opacity:0.5" />
+</svg>

+ 1 - 0
drawable_resources/ic_twitter.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" /></svg>

+ 1 - 0
drawable_resources/ic_web.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></svg>

+ 0 - 69
drawable_resources/launcher_beta.svg

@@ -1,69 +0,0 @@
-<?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"
-   enable-background="new 0 0 595.275 311.111"
-   xml:space="preserve"
-   height="32"
-   width="32"
-   version="1.1"
-   y="0px"
-   x="0px"
-   viewBox="0 0 32 31.999997"
-   id="svg2"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="launcher_beta.svg"
-   inkscape:export-filename="C:\DEV\src\Android\nextcloud_android_beta\res\mipmap-xhdpi\ic_launcher.png"
-   inkscape:export-xdpi="270"
-   inkscape:export-ydpi="270"><metadata
-     id="metadata12"><rdf:RDF><cc:Work
-         rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
-     id="defs10" /><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="namedview8"
-     showgrid="false"
-     inkscape:zoom="3.6875"
-     inkscape:cx="1.0680032"
-     inkscape:cy="40.080182"
-     inkscape:window-x="-9"
-     inkscape:window-y="-9"
-     inkscape:window-maximized="1"
-     inkscape:current-layer="svg2" /><rect
-     rx="5"
-     ry="5"
-     height="32"
-     width="32"
-     y="-.0000052588"
-     x="0"
-     fill="#0082c9"
-     id="rect4" /><path
-     style="enable-background:accumulate;color-rendering:auto;text-decoration-color:#000000;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;block-progression:tb;text-decoration-line:none;image-rendering:auto;white-space:normal;text-indent:0;text-transform:none;text-decoration-style:solid"
-     d="m16.023 9.342c-3.1256 0.0003-5.7501 2.1404-6.552 5.0194-0.69991-1.5399-2.2405-2.6312-4.0305-2.6314-2.4366 0.000277-4.44 2.0038-4.4403 4.4405-0.00048201 2.4372 2.0032 4.4411 4.4403 4.4414 1.7902-0.000217 3.3298-1.0922 4.0296-2.6323 0.80161 2.8795 3.427 5.02 6.553 5.0203 3.1085 0.00024 5.7201-2.117 6.5383-4.9719 0.71125 1.5116 2.2282 2.5836 3.9976 2.5838 2.4376 0.000431 4.4416-2.0037 4.4412-4.4414-0.000235-2.4372-2.0041-4.4409-4.4412-4.4405-1.7695 0.000205-3.2873 1.072-3.9985 2.5838-0.8182-2.8548-3.4289-4.9721-6.5374-4.9719zm0 2.6067c2.3473-0.000156 4.2224 1.8745 4.2226 4.2219 0.000118 2.3477-1.875 4.223-4.2226 4.2228-2.3473-0.000228-4.2219-1.8754-4.2217-4.2228 0.000247-2.3471 1.8747-4.2216 4.2217-4.2219zm-10.583 2.388c1.0283 0.000118 1.8336 0.8055 1.8337 1.8338 0.000182 1.0285-0.80524 1.8346-1.8337 1.8347-1.0285-0.00012-1.8339-0.8062-1.8337-1.8347 0.000118-1.0283 0.80546-1.8337 1.8337-1.8338zm21.119 0c1.0285-0.00018 1.8345 0.80527 1.8346 1.8338 0.000245 1.0288-0.8059 1.8349-1.8346 1.8347-1.0285-0.00012-1.8339-0.8062-1.8338-1.8347 0.000118-1.0283 0.80549-1.8337 1.8338-1.8338z"
-     fill="#fff"
-     id="path6" /><text
-     xml:space="preserve"
-     style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5px;line-height:125%;font-family:Roboto;-inkscape-font-specification:'Roboto, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-     x="8.3471136"
-     y="31.050848"
-     id="text4140"
-     sodipodi:linespacing="125%"><tspan
-       sodipodi:role="line"
-       id="tspan4144"
-       x="8.3471136"
-       y="31.050848">B E T A</tspan></text>
-</svg>

+ 64 - 0
drawable_resources/logout.svg

@@ -0,0 +1,64 @@
+<?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="25.6"
+   viewBox="0 0 24 24"
+   width="25.6"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.92.1 r15371"
+   sodipodi:docname="logout.svg"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   style="fill:#000000"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\logoutButton\src\main\res\drawable-mdpi\logout.png">
+  <metadata
+     id="metadata12">
+    <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="defs10" />
+  <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="namedview8"
+     showgrid="false"
+     inkscape:zoom="13.906433"
+     inkscape:cx="-2.7503911"
+     inkscape:cy="14.502003"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <path
+     d="M 0,0 H 24 V 24 H 0 Z"
+     id="path4"
+     inkscape:connector-curvature="0"
+     style="fill:none" />
+  <path
+     d="m 10.09,15.59 1.41,1.41 5,-5 -5,-5 -1.41,1.41 2.58,2.59 H 3 v 2 h 9.67 z M 19,3 H 5 C 3.89,3 3,3.9 3,5 V 9 H 5 V 5 H 19 V 19 H 5 V 15 H 3 v 4 c 0,1.1 0.89,2 2,2 h 14 c 1.1,0 2,-0.9 2,-2 V 5 C 21,3.9 20.1,3 19,3 Z"
+     id="path6"
+     style="fill:#757575;fill-opacity:1"
+     inkscape:connector-curvature="0" />
+</svg>

+ 64 - 0
drawable_resources/modification_asc.svg

@@ -0,0 +1,64 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="modified_asc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_modification_asc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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" />
+        <dc:title></dc:title>
+      </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="22.63"
+     inkscape:cx="15.581769"
+     inkscape:cy="6.7845462"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <path
+     style="fill:#757575;fill-opacity:1;stroke-width:1.06666672"
+     d="M 11.589844 2.1328125 A 9.4570668 9.4570668 0 0 0 2.1328125 11.589844 C 2.1328125 16.79123 6.3600875 21.048828 11.589844 21.048828 A 9.4570668 9.4570668 0 0 0 13.015625 20.921875 L 13.015625 19.017578 A 7.5656534 7.5656534 0 0 1 11.589844 19.15625 A 7.5656534 7.5656534 0 0 1 4.0253906 11.589844 A 7.5656534 7.5656534 0 0 1 11.589844 4.0253906 A 7.5656534 7.5656534 0 0 1 18.978516 10 L 20.902344 10 A 9.4570668 9.4570668 0 0 0 11.589844 2.1328125 z M 10.644531 6.8632812 L 10.644531 12.535156 L 13.015625 13.957031 L 13.015625 12.392578 L 12.0625 11.826172 L 12.0625 6.8632812 L 10.644531 6.8632812 z "
+     transform="scale(0.93749999)"
+     id="path4" />
+  <path
+     inkscape:connector-curvature="0"
+     d="m 13.720313,16.930993 h 5.580275 v -1.736354 h -5.580275 m 0,-4.340882 v 1.736354 h 8.370413 v -1.736354 m -8.370413,10.418118 h 2.790138 v -1.736351 h -2.790138 z"
+     id="path4-8"
+     style="fill:#757575;fill-opacity:1;stroke-width:1.16980636" />
+</svg>

+ 63 - 0
drawable_resources/modification_desc.svg

@@ -0,0 +1,63 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="modified_desc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_modified_desc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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="22.63"
+     inkscape:cx="15.581769"
+     inkscape:cy="6.7845462"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <path
+     style="fill:#757575;fill-opacity:1;stroke-width:1.06666672"
+     d="M 11.589844 2.1328125 A 9.4570668 9.4570668 0 0 0 2.1328125 11.589844 C 2.1328125 16.79123 6.3600875 21.048828 11.589844 21.048828 A 9.4570668 9.4570668 0 0 0 13.015625 20.921875 L 13.015625 19.017578 A 7.5656534 7.5656534 0 0 1 11.589844 19.15625 A 7.5656534 7.5656534 0 0 1 4.0253906 11.589844 A 7.5656534 7.5656534 0 0 1 11.589844 4.0253906 A 7.5656534 7.5656534 0 0 1 18.978516 10 L 20.902344 10 A 9.4570668 9.4570668 0 0 0 11.589844 2.1328125 z M 10.644531 6.8632812 L 10.644531 12.535156 L 13.015625 13.957031 L 13.015625 12.392578 L 12.0625 11.826172 L 12.0625 6.8632812 L 10.644531 6.8632812 z "
+     transform="scale(0.93749999)"
+     id="path4" />
+  <path
+     inkscape:connector-curvature="0"
+     d="m 13.720313,15.194639 h 5.580275 v 1.736354 h -5.580275 m 0,4.340882 v -1.736354 h 8.370413 v 1.736354 M 13.720313,10.853757 h 2.790138 v 1.736351 h -2.790138 z"
+     id="path4-8"
+     style="fill:#757575;fill-opacity:1;stroke-width:1.16980636" />
+</svg>

+ 77 - 0
drawable_resources/size_asc.svg

@@ -0,0 +1,77 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="size_asc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_size_asc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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" />
+        <dc:title />
+      </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="22.627418"
+     inkscape:cx="15.398362"
+     inkscape:cy="7.3901816"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Layer 3" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Layer 2"
+     style="display:inline" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:label="Layer 1" />
+  <path
+     inkscape:connector-curvature="0"
+     d="m 13.720501,15.194819 h 5.580275 v 1.736354 h -5.580275 m 0,4.340882 v -1.736354 h 8.370414 v 1.736354 M 13.720501,10.853937 h 2.790138 v 1.736351 h -2.790138 z"
+     id="path4-8"
+     style="fill:#757575;fill-opacity:1;stroke-width:1.16980636" />
+  <path
+     style="fill:#757575;fill-opacity:1;stroke-width:1.06666672"
+     d="M 10.412109 1.8046875 A 9.4570668 9.4570668 0 0 0 0.95507812 11.259766 A 9.4570668 9.4570668 0 0 0 10.412109 20.71875 A 9.4570668 9.4570668 0 0 0 13.015625 20.337891 L 13.015625 18.351562 A 7.5656534 7.5656534 0 0 1 10.412109 18.826172 A 7.5656534 7.5656534 0 0 1 2.8457031 11.259766 A 7.5656534 7.5656534 0 0 1 10.412109 3.6953125 A 7.5656534 7.5656534 0 0 1 17.869141 10 L 19.773438 10 A 9.4570668 9.4570668 0 0 0 10.412109 1.8046875 z M 10.412109 5.5878906 L 10.412109 11.259766 L 6.4023438 15.269531 C 8.191236 17.058424 10.881148 17.400168 13.015625 16.296875 L 13.015625 10 L 15.941406 10 C 15.711784 8.9928477 15.206905 8.0355871 14.419922 7.2519531 C 13.313445 6.1360192 11.859041 5.5878906 10.412109 5.5878906 z "
+     transform="scale(0.93749999)"
+     id="path4-88" />
+</svg>

+ 76 - 0
drawable_resources/size_desc.svg

@@ -0,0 +1,76 @@
+<?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"
+   version="1.1"
+   width="25.6"
+   height="25.6"
+   viewBox="0 0 24 24"
+   id="svg2"
+   inkscape:version="0.92.0 r15299"
+   sodipodi:docname="size_desc.svg"
+   inkscape:export-filename="C:\DEV\src\Android\Nextcloud\newSortingLayout\res\drawable-xxxhdpi\ic_size_desc.png"
+   inkscape:export-xdpi="360"
+   inkscape:export-ydpi="360">
+  <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="22.63"
+     inkscape:cx="11.152894"
+     inkscape:cy="8.874615"
+     inkscape:window-x="-9"
+     inkscape:window-y="-9"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer3"
+     inkscape:label="Layer 3" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Layer 2"
+     style="display:inline" />
+  <g
+     inkscape:groupmode="layer"
+     id="layer1"
+     inkscape:label="Layer 1" />
+  <path
+     style="fill:#757575;fill-opacity:1;stroke-width:1.06666672"
+     d="M 10.412109 1.8046875 A 9.4570668 9.4570668 0 0 0 0.95507812 11.259766 A 9.4570668 9.4570668 0 0 0 10.412109 20.71875 A 9.4570668 9.4570668 0 0 0 13.015625 20.337891 L 13.015625 18.351562 A 7.5656534 7.5656534 0 0 1 10.412109 18.826172 A 7.5656534 7.5656534 0 0 1 2.8457031 11.259766 A 7.5656534 7.5656534 0 0 1 10.412109 3.6953125 A 7.5656534 7.5656534 0 0 1 17.869141 10 L 19.773438 10 A 9.4570668 9.4570668 0 0 0 10.412109 1.8046875 z M 10.412109 5.5878906 L 10.412109 11.259766 L 6.4023438 15.269531 C 8.191236 17.058424 10.881148 17.400168 13.015625 16.296875 L 13.015625 10 L 15.941406 10 C 15.711784 8.9928477 15.206905 8.0355871 14.419922 7.2519531 C 13.313445 6.1360192 11.859041 5.5878906 10.412109 5.5878906 z "
+     transform="scale(0.93749999)"
+     id="path4-88" />
+  <path
+     inkscape:connector-curvature="0"
+     d="m 13.720312,16.930993 h 5.580275 v -1.736354 h -5.580275 m 0,-4.340882 v 1.736354 h 8.370413 v -1.736354 m -8.370413,10.418118 h 2.790138 v -1.736351 h -2.790138 z"
+     id="path4-8"
+     style="fill:#757575;fill-opacity:1;stroke-width:1.16980636" />
+</svg>

+ 2 - 2
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
-#Tue Aug 16 10:44:46 CEST 2016
+#Wed Mar 15 00:10:20 CET 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip

+ 1 - 0
issue_template.md

@@ -31,3 +31,4 @@ Insert your webserver log here
 ```
 Insert your Nextcloud log here
 ```
+**NOTE:** Be super sure to remove sensitive data like passwords, note that everybody can look here! You can use the Issue Template application to prefill some of the required information: https://apps.nextcloud.com/apps/issuetemplate

BIN
res/drawable/ic_synced.png


+ 0 - 64
res/layout/file_preview.xml

@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ownCloud Android client application
-
-  Copyright (C) 2015 ownCloud Inc.
-  
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License version 2,
-  as published by the Free Software Foundation.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-  
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/top"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center"
-    tools:context=".ui.fragment.FilePreviewFragment">
-
-    <FrameLayout
-        android:id="@+id/visual_area"
-   	    android:layout_width="match_parent"
-   	    android:layout_height="0dp"
-   	    android:layout_alignParentTop="true"
-   	    android:layout_above="@+id/media_controller"
-        >
-    
-	    <ImageView
-	        android:id="@+id/image_preview"
-	        android:layout_width="match_parent"
-	        android:layout_height="match_parent"
-	        android:layout_margin="@dimen/standard_margin"
-	        android:layout_gravity="center"
-	        android:contentDescription="@string/preview_image_description"
-	        android:src="@drawable/logo" />
-	    
-	   	<VideoView  
-	   	    android:id="@+id/video_preview"
-			android:layout_width="match_parent"
-			android:layout_height="match_parent"  
-			android:layout_gravity="center" 
-			android:visibility="gone"
-			/>
-	   	
-   	</FrameLayout>
-   	
-   	<com.owncloud.android.media.MediaControlView 
-   	    android:id="@id/media_controller"
-   	    android:layout_width="match_parent"
-   	    android:layout_height="wrap_content"
-   	    android:layout_alignParentBottom="true"
-		android:layout_margin="@dimen/standard_margin"
-   	    />
-
-</RelativeLayout>

+ 0 - 316
res/values-da/strings.xml

@@ -1,316 +0,0 @@
-<?xml version='1.0' encoding='UTF-8'?>
-<resources>
-  <string name="about_version">version %1$s</string>
-  <string name="actionbar_sync">Genindlæs konto</string>
-  <string name="actionbar_upload">Upload</string>
-  <string name="actionbar_upload_from_apps">Indhold fra andre apps</string>
-  <string name="actionbar_upload_files">Filer</string>
-  <string name="actionbar_open_with">Åben med</string>
-  <string name="actionbar_mkdir">Ny mappe</string>
-  <string name="actionbar_settings">Indstillinger</string>
-  <string name="actionbar_see_details">Detaljer</string>
-  <string name="actionbar_send_file">Send</string>
-  <string name="actionbar_sort">Sortér</string>
-  <string name="actionbar_sort_title">Sortér efter</string>
-  <string-array name="actionbar_sortby">
-    <item>A-Å</item>
-    <item>Nyeste - ældste</item>
-  </string-array>
-  <!--TODO re-enable when server-side folder size calculation is available   
-    	<item>Biggest - Smallest</item>-->
-  <!--TODO re-enable when "Accounts" is available in Navigation Drawer-->
-  <!--<string name="drawer_item_accounts">Accounts</string>-->
-  <string name="drawer_item_all_files">Alle filer</string>
-  <!--TODO re-enable when "On Device" is available
-    <string name="drawer_item_on_device">On device</string>-->
-  <string name="drawer_item_settings">Indstillinger</string>
-  <string name="drawer_item_uploads_list">Uploade filer</string>
-  <string name="drawer_close">Luk</string>
-  <string name="drawer_open">Åbn</string>
-  <string name="prefs_category_general">Generel</string>
-  <string name="prefs_category_more">Mere</string>
-  <string name="prefs_accounts">Konti</string>
-  <string name="prefs_manage_accounts">Administrer konti</string>
-  <string name="prefs_passcode">Passcode-lås</string>
-  <string name="prefs_instant_upload">Upload billeder straks</string>
-  <string name="prefs_instant_upload_summary">Upload straks billeder taget med kameraet</string>
-  <string name="prefs_instant_video_upload">Upload videoer straks</string>
-  <string name="prefs_instant_video_upload_summary">Upload straks videor optaget med kameraet</string>
-  <string name="prefs_log_title">Slå logning til</string>
-  <string name="prefs_log_summary">Dette bruges til at logregistrere problemer</string>
-  <string name="prefs_log_title_history">Logningshistorik</string>
-  <string name="prefs_log_summary_history">Dette viser de optagne logger</string>
-  <string name="prefs_log_delete_history_button">Slet historik</string>
-  <string name="prefs_help">Hjælp</string>
-  <string name="prefs_recommend">Anbefal til en ven</string>
-  <string name="prefs_feedback">Feedback</string>
-  <string name="prefs_imprint">Aftryk</string>
-  <string name="prefs_remember_last_share_location">Husk delt placering</string>
-  <string name="prefs_remember_last_upload_location_summary">Husk seneste delte placering for overførsel</string>
-  <string name="recommend_subject">Prøv %1$s på din smartphone!</string>
-  <string name="recommend_text">Jeg ønsker at invitere dig til at bruge %1$s på din smartphone!\nHent den her: %2$s</string>
-  <string name="auth_host_url">Serveradresse https://…</string>
-  <string name="auth_username">Brugernavn</string>
-  <string name="auth_password">Kodeord</string>
-  <string name="auth_register">Ny med %1$s</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_wrn_no_account_title">Ingen konto fundet</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="file_list_seconds_ago">sekunder siden</string>
-  <string name="file_list_empty">Her er tomt. Upload noget!</string>
-  <string name="file_list_loading">Indlæser...</string>
-  <string name="local_file_list_empty">Der er ingen filer i denne mappe.</string>
-  <string name="file_list_folder">mappe</string>
-  <string name="file_list_folders">mapper</string>
-  <string name="file_list_file">fil</string>
-  <string name="file_list_files">fil</string>
-  <string name="filedetails_select_file">Tryk på en fil for at vise yderligere information.</string>
-  <string name="filedetails_size">Størelse:</string>
-  <string name="filedetails_type">Type:</string>
-  <string name="filedetails_created">Oprettet:</string>
-  <string name="filedetails_modified">Ændret:</string>
-  <string name="filedetails_download">Hent</string>
-  <string name="filedetails_renamed_in_upload_msg">Filen blev omdøbt til %1$s under upload</string>
-  <string name="action_share">Del</string>
-  <string name="common_yes">Ja</string>
-  <string name="common_no">Nej</string>
-  <string name="common_ok">OK</string>
-  <string name="common_cancel">Annuller</string>
-  <string name="common_back">Tilbage</string>
-  <string name="common_error">Fejl</string>
-  <string name="common_loading">Indlæser...</string>
-  <string name="common_unknown">ukendt</string>
-  <string name="common_error_unknown">Ukendt fejl</string>
-  <string name="about_title">Om</string>
-  <string name="change_password">Skift kodeord</string>
-  <string name="create_account">Opret konto</string>
-  <string name="upload_chooser_title">Upload fra ...</string>
-  <string name="uploader_info_dirname">Mappenavn</string>
-  <string name="uploader_upload_in_progress_ticker">Uploader ...</string>
-  <string name="uploader_upload_in_progress_content">%1$d%% Uploader %2$s</string>
-  <string name="uploader_upload_succeeded_ticker">Upload færdig</string>
-  <string name="uploader_upload_failed_ticker">Upload fejlede</string>
-  <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_finished_uploads">Sendt</string>
-  <string name="uploads_view_upload_status_succeeded">Fuldført</string>
-  <string name="uploads_view_upload_status_failed_credentials_error">Godkendelsesfejl</string>
-  <string name="uploads_view_upload_status_failed_folder_error">Mappefejl</string>
-  <string name="uploads_view_upload_status_failed_file_error">Filfejl</string>
-  <string name="uploads_view_upload_status_failed_permission_error">Manglende rettigheder</string>
-  <string name="uploads_view_upload_status_unknown_fail">Ukendt fejl</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>
-  <string name="downloader_download_succeeded_ticker">Download fuldført</string>
-  <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="common_choose_account">Vælg konto</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>
-  <string name="sync_fail_in_favourites_ticker">Hold-synkroniseret filer mislykkedes</string>
-  <string name="sync_fail_in_favourites_content">Indholdet af %1$d filer ikke kunne synkroniseres (%2$d konflikter)</string>
-  <string name="sync_foreign_files_forgotten_ticker">Visse lokale filer blev glemt</string>
-  <string name="sync_foreign_files_forgotten_content">%1$d filer ud af %2$s mappe kunne ikke kopieres ind i</string>
-  <string name="sync_foreign_files_forgotten_explanation">Fra version 1.3.16 bliver filer uploadet fra denne enhed kopieret til mappen %1$s for at forhindre datatab når en enkelt fil synkroniseres med flere konti.\n\nPå grund af denne ændring er alle filer som var uploadet i tidligere versioner af denne app kopieret til mappen %2$s. Imidlertid forhindrede en fejl færdiggørelsen af denne operation under konto-synkronisering. Du kan enten lade filen (el. filerne) være som de er og fjerne linket til %3$s eller flytte filen (el. filerne) til mappen %1$s og beholde linket til %4$s.\n\nHerunder er en liste med den lokale fil(er), og den fjerne mappe(r) i %5$s, som de var knyttet til.</string>
-  <string name="sync_current_folder_was_removed">Mappen %1$s eksistere ikke længere</string>
-  <string name="foreign_files_move">Flyt alle</string>
-  <string name="foreign_files_success">Alle filer blev flyttet</string>
-  <string name="foreign_files_fail">Visse filer kunne ikke flyttes</string>
-  <string name="foreign_files_local_text">Lokal: %1$s</string>
-  <string name="foreign_files_remote_text">Fjernplacering: %1$s</string>
-  <string name="pass_code_configure_your_pass_code">Angiv din passcode</string>
-  <string name="pass_code_configure_your_pass_code_explanation">Denne passcode vil blive forespurgt hver gang app\'en startes</string>
-  <string name="pass_code_remove_your_pass_code">Fjern din passcode</string>
-  <string name="pass_code_mismatch">Passcode\'erne er ikke ens</string>
-  <string name="pass_code_wrong">Ukorrekt passcode</string>
-  <string name="pass_code_removed">Passcode blev fjernet</string>
-  <string name="pass_code_stored">Passcode blev gendannet</string>
-  <string name="media_notif_ticker">%1$s musikafspiller</string>
-  <string name="media_state_playing">%1$s (afspiller)</string>
-  <string name="media_state_loading">%1$s (indlæser)</string>
-  <string name="media_event_done">%1$s afspilning færdig</string>
-  <string name="media_err_nothing_to_play">Mediefil ikke fundet</string>
-  <string name="media_err_no_account">Ingen konto angivet</string>
-  <string name="media_err_not_in_owncloud">Fil er ikke en gyldig konto</string>
-  <string name="media_err_unsupported">Ikke-understøttet medie codec</string>
-  <string name="media_err_io">Mediefilen kunne ikke læses</string>
-  <string name="media_err_malformed">Mediefilen er ikke korrekt \"encoded\"</string>
-  <string name="media_err_timeout">Tiden udløb under forsøg på at afspille</string>
-  <string name="media_err_invalid_progressive_playback">Mediefilen kan ikke streames</string>
-  <string name="media_err_unknown">Mediefil kan ikke afspilles med tilgængelige medieafspiller</string>
-  <string name="media_err_security_ex">Sikkerhedsfejl ved forsøg på afspilning af </string>
-  <string name="media_err_io_ex">Inputfejl ved forsøg på afspilning af %1$s</string>
-  <string name="media_err_unexpected">Uventet fejl ved forsøg på afspilning af %1$s</string>
-  <string name="media_rewind_description">Tilbagespolingsknap</string>
-  <string name="media_play_pause_description">Afspil eller pause knap</string>
-  <string name="media_forward_description">Hurtigt fremad-knap</string>
-  <string name="auth_no_net_conn_title">Ingen netværksforbindelse</string>
-  <string name="auth_nossl_plain_ok_title">Sikker forbindelse ikke tilgængelig.</string>
-  <string name="auth_connection_established">Forbindelse oprettet</string>
-  <string name="auth_testing_connection">Tester forbindelsen</string>
-  <string name="auth_not_configured_title">Misdannet server konfiguration</string>
-  <string name="auth_account_not_new">En konto for den samme bruger og server eksisterer allerede på enheden</string>
-  <string name="auth_account_not_the_same">Den indtastede bruger passer ikke til brugeren for denne konto</string>
-  <string name="auth_unknown_error_title">Ukendt fejl opstod!</string>
-  <string name="auth_unknown_host_title">Kunne ikke finde værten</string>
-  <string name="auth_incorrect_path_title">Server instans blev ikke fundet</string>
-  <string name="auth_timeout_title">Serveren var for længe om at svare</string>
-  <string name="auth_ssl_general_error_title">SSL-initialiseringen fejlede</string>
-  <string name="auth_ssl_unverified_server_title">Kunne ikke bekræfte SSl-serverens identitet</string>
-  <string name="auth_bad_oc_version_title">Ikke genkendt serverversion</string>
-  <string name="auth_wrong_connection_title">Kunne ikke oprette forbindelse</string>
-  <string name="auth_secure_connection">Sikker forbindelse oprettet</string>
-  <string name="auth_unauthorized">Forkert brugernavn eller kodeord</string>
-  <string name="auth_oauth_error">Mislykket godkendelse</string>
-  <string name="auth_oauth_error_access_denied">Adgang afvist af autorisationsserver</string>
-  <string name="auth_expired_oauth_token_toast">Din godkendelse udløb. Gentag godkendelse</string>
-  <string name="auth_expired_saml_sso_token_toast">Din session udløb. Forbind venligst igen</string>
-  <string name="auth_unsupported_auth_method">Serveren understøtter ikke denne godkendelsesmetode</string>
-  <string name="auth_unsupported_multiaccount">%1$s understøtter ikke flere konti</string>
-  <string name="auth_fail_get_user_name">Din server retunere ikke et korrekt bruger-id. Kontakt venligst din administrator</string>
-  <string name="auth_account_does_not_exist">Kontoen findes endnu ikke på enheden</string>
-  <string name="common_rename">Omdøb</string>
-  <string name="common_remove">Fjern</string>
-  <string name="confirmation_remove_alert">Er du sikker på at du vil fjerne %1$s ?</string>
-  <string name="confirmation_remove_folder_alert">Ønsker du virkelig at slette %1$s og dets indhold?</string>
-  <string name="confirmation_remove_local">Kun lokal</string>
-  <string name="remove_success_msg">Vellykket fjernelse</string>
-  <string name="remove_fail_msg">Fjernelse kunne ikke fuldføres</string>
-  <string name="rename_dialog_title">Indtast et nyt navn</string>
-  <string name="rename_local_fail_msg">Lokal kopi kunne ikke omdøbes; prøv et andet nyt navn</string>
-  <string name="rename_server_fail_msg">Omdøbning kunne ikke gennemføres</string>
-  <string name="sync_file_fail_msg">Ekstern fil kunne ikke kontrolleres</string>
-  <string name="sync_file_nothing_to_do_msg">Filindholdet allerede synkroniseret</string>
-  <string name="create_dir_fail_msg">Kunne ikke oprette mappe</string>
-  <string name="filename_forbidden_characters">Ugyldige tegn: / \\ &lt; &gt; : \" | ? *</string>
-  <string name="filename_forbidden_charaters_from_server">Filnavnet indeholder mindst ét ugyldigt tegn</string>
-  <string name="filename_empty">Filnavnet kan ikke stå tomt.</string>
-  <string name="wait_a_moment">Vent et øjeblik</string>
-  <string name="filedisplay_unexpected_bad_get_content">Uventet problem; prøv venligst en anden applikation til at vælge filen</string>
-  <string name="filedisplay_no_file_selected">Ingen fil blev valgt</string>
-  <string name="activity_chooser_title">Send link til ...</string>
-  <string name="wait_for_tmp_copy_from_private_storage">Kopierer fil fra privat lager.</string>
-  <string name="oauth_check_onoff">Log på med oAuth2</string>
-  <string name="oauth_login_connection">Forbinder til oAuth2-server...</string>
-  <string name="ssl_validator_header">Sidens identitet kunne ikke verificeres</string>
-  <string name="ssl_validator_reason_cert_not_trusted">- Serverens certifikat er ikke troværdigt</string>
-  <string name="ssl_validator_reason_cert_expired">- Serverens certifikat er udløbet</string>
-  <string name="ssl_validator_reason_cert_not_yet_valid">- Serverens certifikat er for ung</string>
-  <string name="ssl_validator_reason_hostname_not_verified">- URL adressen stemmer ikke overens med værtsnavnet i certifikatet</string>
-  <string name="ssl_validator_question">Stoler du på certifikatet alligevel?</string>
-  <string name="ssl_validator_not_saved">Certifikatet kunne ikke gemmes</string>
-  <string name="ssl_validator_btn_details_see">Detaljer</string>
-  <string name="ssl_validator_btn_details_hide">Gem</string>
-  <string name="ssl_validator_label_subject">Udstedt til:</string>
-  <string name="ssl_validator_label_issuer">Udstedt af:</string>
-  <string name="ssl_validator_label_CN">Almindeligt navn:</string>
-  <string name="ssl_validator_label_O">Organisation:</string>
-  <string name="ssl_validator_label_OU">Organisatorisk enhed:</string>
-  <string name="ssl_validator_label_C">Land:</string>
-  <string name="ssl_validator_label_ST">Stat:</string>
-  <string name="ssl_validator_label_L">Sted:</string>
-  <string name="ssl_validator_label_validity">Gyldighed:</string>
-  <string name="ssl_validator_label_validity_from">Fra:</string>
-  <string name="ssl_validator_label_validity_to">Til:</string>
-  <string name="ssl_validator_label_signature">Signatur:</string>
-  <string name="ssl_validator_label_signature_algorithm">Algoritme:</string>
-  <string name="ssl_validator_null_cert">Certifikatet kunne ikke vises.</string>
-  <string name="ssl_validator_no_info_about_error">- Ingen information om fejlen</string>
-  <string name="placeholder_sentence">Dette er en pladsholder</string>
-  <string name="placeholder_filename">pladsholder.txt</string>
-  <string name="placeholder_filetype">PNG Billede</string>
-  <string name="placeholder_filesize">389 KB</string>
-  <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
-  <string name="placeholder_media_time">12:23:45</string>
-  <string name="instant_upload_path">/Øjeblikkelig upload</string>
-  <string name="conflict_title">Filkonflikt</string>
-  <string name="conflict_message">Hvilke filer ønsker du at beholde? Hvis du vælger begge versioner, så vil den lokale fil få et tal tilføjet til sit navn.</string>
-  <string name="conflict_keep_both">Behold begge</string>
-  <string name="conflict_use_local_version">lokal version</string>
-  <string name="conflict_use_server_version">serverversion</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="error__upload__local_file_not_copied">%1$s kunne ikke kopieres til %2$s lokale mappe</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="share_link_password_title">Angiv et kodeord</string>
-  <string name="share_link_empty_password">Du skal angive et kodeord</string>
-  <string name="activity_chooser_send_file_title">Send</string>
-  <string name="copy_link">Kopiér link</string>
-  <string name="clipboard_text_copied">Kopieret til udklipsholder</string>
-  <string name="error_cant_bind_to_operations_service">Kritisk fejl: kan ikke udføre handlingerne</string>
-  <string name="network_error_socket_exception">Der opstod en fejl under tilslutningen til serveren.</string>
-  <string name="network_error_socket_timeout_exception">Der opstod en fejl mens vi ventede på serveren, handlingen kunne ikke gennemføres</string>
-  <string name="network_error_connect_timeout_exception">Der opstod en fejl mens vi ventede på serveren, handlingen kunne ikke gennemføres</string>
-  <string name="network_host_not_available">Handlingen kunne ikke gennemføres, serveren er utilgængelig</string>
-  <string name="empty"></string>
-  <string name="forbidden_permissions">Du har ikke rettigheden %s</string>
-  <string name="forbidden_permissions_rename">til at omdøbe denne fil</string>
-  <string name="forbidden_permissions_delete">til at slette denne fil</string>
-  <string name="share_link_forbidden_permissions">til at dele denne fil</string>
-  <string name="unshare_link_forbidden_permissions">til at stoppe deling af denne fil</string>
-  <string name="forbidden_permissions_create">til at oprette filen</string>
-  <string name="uploader_upload_forbidden_permissions">til at overføre til denne mappe</string>
-  <string name="downloader_download_file_not_found">Filen er ikke længere tilgængelig på serveren</string>
-  <string name="prefs_category_accounts">Konti</string>
-  <string name="prefs_add_account">Tilføj konto</string>
-  <string name="auth_redirect_non_secure_connection_title">Sikker forbindelse videredirigeres gennem en usikker rute.</string>
-  <string name="actionbar_logger">Logge</string>
-  <string name="log_send_mail_subject">%1$s Android-app - logge</string>
-  <string name="saml_authentication_required_text">Godkendelse påkrævet</string>
-  <string name="saml_authentication_wrong_pass">Forkert kodeord</string>
-  <string name="actionbar_move">Flyt</string>
-  <string name="file_list_empty_moving">Der er intet her. Du kan tilføje en mappe!</string>
-  <string name="folder_picker_choose_button_text">Vælg</string>
-  <string name="move_file_not_found">Kan ikke flytte. Tjek venligst om filen findes</string>
-  <string name="move_file_invalid_into_descendent">Det er ikke muligt at flytte en mappe til en undermappe</string>
-  <string name="move_file_invalid_overwrite">Filen findes allerede i destinationsmappen</string>
-  <string name="move_file_error">Der opstod en fejl under forsøg på at flytte denne mappe eller fil</string>
-  <string name="forbidden_permissions_move">til at flytte denne fil</string>
-  <string name="copy_file_not_found">Kunne ikke kopiere. Tjek venligst om filerne findes</string>
-  <string name="copy_file_invalid_into_descendent">Det er ikke muligt at kopiere en mappe til én af dens undermapper</string>
-  <string name="copy_file_invalid_overwrite">Filen findes allerede i destinationsmappen</string>
-  <string name="copy_file_error">Der opstod en fejl under forsøg på at kopiere denne fil eller mappe</string>
-  <string name="forbidden_permissions_copy">til at kopiere denne fil</string>
-  <string name="prefs_category_details">Detaljer</string>
-  <string name="shared_subject_header">delt</string>
-  <string name="with_you_subject_header">med dig</string>
-  <string name="subject_user_shared_with_you">%1$s delte \"%2$s\" med dig</string>
-  <string name="subject_shared_with_you">\"%1$s\" er blevet delt med dig</string>
-  <string name="auth_refresh_button">Genopfrisk forbindelsen</string>
-  <string name="auth_host_address">Serveradresse</string>
-  <string name="common_error_out_memory">Ikke tilstrækkelig hukommelse</string>
-  <string name="username">Brugernavn</string>
-  <string name="file_list__footer__folder">1 mappe</string>
-  <string name="file_list__footer__folders">%1$d mapper</string>
-  <string name="file_list__footer__file">1 fil</string>
-  <string name="file_list__footer__file_and_folder">1 fil, 1 mappe</string>
-  <string name="file_list__footer__file_and_folders">1 fil, %1$d mapper</string>
-  <string name="file_list__footer__files">%1$d filer</string>
-  <string name="file_list__footer__files_and_folder">%1$d filer, 1 mape</string>
-  <string name="file_list__footer__files_and_folders">%1$d filer, %2$d mapper</string>
-  <string name="share_dialog_title">Deling</string>
-  <string name="share_via_link_section_title">Del link</string>
-  <string name="share_via_link_expiration_date_label">Vælg udløbsdato</string>
-  <string name="share_via_link_password_label">Beskyt med adgangskode</string>
-  <string name="share_via_link_password_title">Sikret</string>
-  <string name="share_via_link_edit_permission_label">Tillad redigering</string>
-  <string name="share_search">Søg</string>
-  <string name="share_privilege_can_share">kan dele</string>
-  <string name="share_privilege_can_edit">kan redigere</string>
-  <string name="share_privilege_can_edit_create">opret</string>
-  <string name="share_privilege_can_edit_change">tilpas</string>
-  <string name="share_privilege_can_edit_delete">slet</string>
-  <string name="edit_share_unshare">Stop deling</string>
-</resources>

+ 0 - 425
res/values-es-rMX/strings.xml

@@ -1,425 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <string name="about_android">%1$s Android app</string>
-    <string name="about_version">versión %1$s</string>
-    <string name="actionbar_sync">Actualizar cuenta</string>
-    <string name="actionbar_upload">Subir archivo</string>
-    <string name="actionbar_upload_from_apps">Contenido de otras aplicaciones</string>
-    <string name="actionbar_upload_files">Archivos</string>
-    <string name="actionbar_open_with">Abrir con</string>
-    <string name="actionbar_mkdir">Nueva carpeta</string>
-    <string name="actionbar_settings">Ajustes</string>
-    <string name="actionbar_see_details">Detalles</string>
-    <string name="actionbar_send_file">Enviar</string>
-    <string name="actionbar_sort">Filtrar</string>
-    <string name="actionbar_sort_title">Filtrar por</string>
-    <string name="menu_item_sort_by_name">A - Z</string>
-    <string name="menu_item_sort_by_date">Más reciente - Más antiguo</string>
-    <string name="menu_item_sort_by_size">Más grande - Más pequeño</string>
-    <string name="drawer_item_all_files">Todos los archivos</string>
-    <string name="drawer_item_on_device">En su dispositivo</string>
-    <string name="drawer_item_settings">Ajustes</string>
-    <string name="drawer_item_uploads_list">Subidas</string>
-    <string name="drawer_quota">%1$s de %2$s usados</string>
-	<string name="drawer_close">Cerrar</string>
-    <string name="drawer_open">Abrir</string>
-    <string name="prefs_category_general">General</string>
-    <string name="prefs_category_more">Más</string>
-    <string name="prefs_accounts">Cuentas</string>
-    <string name="prefs_manage_accounts">Administrar cuentas</string>
-    <string name="prefs_passcode">Código de seguridad</string>
-    <string name="prefs_show_hidden_files">Mostrar archivos escondidos</string>
-    <string name="prefs_instant_upload">Subida instantánea de imágenes</string>
-    <string name="prefs_instant_upload_summary">Subida instantánea de fotografías tomadas por la cámara</string>
-    <string name="prefs_instant_video_upload">Subida instantánea de video</string>
-    <string name="prefs_instant_video_upload_summary">Subida instantánea de videos grabados por la cámara</string>
-    <string name="prefs_log_title">Activar registro</string>
-    <string name="prefs_log_summary">Esto es usado para registrar problemas</string>
-    <string name="prefs_log_title_history">Historial de registro</string>
-    <string name="prefs_log_summary_history">Esto muestra los registros grabados</string>
-    <string name="prefs_log_delete_history_button">Borrar historial</string>
-    <string name="prefs_calendar_contacts">Sincronizar calendario  &amp; contactos</string>
-    <string name="prefs_calendar_contacts_summary">Configure DAVdroid (v1.3.0+) para la cuenta actual</string>
-    <string name="prefs_calendar_contacts_address_resolve_error">La dirección del servidor para la cuenta no se pudo resolver para DAVdroid</string>
-    <string name="prefs_calendar_contacts_no_store_error">No se encuentran instaladas las aplicaciones Google Play o F-Droid</string>
-    <string name="prefs_calendar_contacts_sync_setup_successful">La configuración de Calendario &amp; contactos se sincronizaron 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">Imprint</string>
-    <string name="prefs_remember_last_share_location">Recordar la ubicación compartida</string>
-    <string name="prefs_remember_last_upload_location_summary">Recordar la última ubicación de subida</string>
-
-	<string name="recommend_subject">Prueba  %1$s en tu smarthphone!</string>
-	<string name="recommend_text">¡Quisiera invitarlo a usar %1$s en su smartphone!\\nDescargue aquí: %2$s</string>
-
-    <string name="auth_check_server">Probar 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="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">Elegir carpeta de subida</string>
-    <string name="uploader_wrn_no_account_title">No se encontraron cuentas</string>
-    <string name="uploader_wrn_no_account_text">No hay %1$s cuentas en su dispositivo. Por favor configure una cuenta primero.</string>
-    <string name="uploader_wrn_no_account_setup_btn_text">Configuración</string>
-    <string name="uploader_wrn_no_account_quit_btn_text">Salir</string>
-    <string name="uploader_error_title_no_file_to_upload">No hay archivo para subir</string>
-    <string name="uploader_error_message_received_piece_of_text">%1$s no puede subir una pieza de texto como un archivo.</string>
-    <string name="uploader_error_message_no_file_to_upload">La información recibida no incluye un archivo válido.</string>
-    <string name="uploader_error_title_file_cannot_be_uploaded">El archivo no puede ser subido</string>
-    <string name="uploader_error_message_read_permission_not_granted">%1$s no tiene permitido leer el archivo recibido</string>
-    <string name="uploader_error_message_source_file_not_found">El archivo a subir no fue encontrado en esta localización. Por favor revise si existe el archivo.</string>
-    <string name="uploader_error_message_source_file_not_copied">Un error ocurrió mientras se copiaba el archivo a una carpeta temporal. Por favor intente enviarlo de nuevo.</string>
-    <string name="uploader_upload_files_behaviour">Opción de subida:</string>
-    <string name="uploader_upload_files_behaviour_move_to_nextcloud_folder">Mover archivo a carpeta de Nextcloud</string>
-    <string name="uploader_upload_files_behaviour_only_upload">Mantener archivo en carpeta de origen</string>
-    <string name="uploader_upload_files_behaviour_upload_and_delete_from_source">Borrar archivo de la carpeta de origen</string>
-    <string name="file_list_seconds_ago">hace segundos</string>
-    <string name="file_list_loading">Cargando&#8230;</string>
-    <string name="file_list_no_app_for_file_type">¡No hay aplicación para este tipo de archivo!</string>
-    <string name="local_file_list_empty">No hay archivos en esta carpeta</string>
-    <string name="file_list_folder">carpeta</string>
-    <string name="file_list_folders">carpetas</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>
-    <string name="filedetails_created">Creado:</string>
-    <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 archivo fue renombrado como %1$s durante la subida</string>
-    <string name="list_layout">Vista de lista</string>
-    <string name="action_share">Compartir</string>
-    <string name="common_yes">Sí</string>
-    <string name="common_no">No</string>
-    <string name="common_ok">Aceptar</string>
-    <string name="common_remove_upload">Eliminar subida</string>
-    <string name="common_retry_upload">Reintentar subida</string>
-    <string name="common_cancel_sync">Cancelar sincronización</string>
-    <string name="common_cancel">Cancelar</string>
-    <string name="common_back">Atrás</string>
-    <string name="common_save">Guardar</string>
-    <string name="common_save_exit">Guardar &amp; salir</string>
-    <string name="common_error">Error</string>
-    <string name="common_loading">Cargando ...</string>
-    <string name="common_unknown">desconocido</string>
-    <string name="common_error_unknown">Error desconocido</string>
-    <string name="common_pending">Pendiente</string>
-    <string name="about_title">Acerca de</string>
-    <string name="change_password">Cambiar contraseña</string>
-    <string name="delete_account">Eliminar cuenta</string>
-    <string name="delete_account_warning">Eliminar cuenta %s?\\n\\nEl borrado no se puede deshacer.</string>
-    <string name="create_account">Crear cuenta</string>
-    <string name="upload_chooser_title">Subir</string>
-    <string name="uploader_info_dirname">Nombre de la carpeta</string>
-    <string name="uploader_upload_in_progress_ticker">Subiendo...</string>
-    <string name="uploader_upload_in_progress_content">%1$d%% Subiendo %2$s</string>
-    <string name="uploader_upload_succeeded_ticker">Subido con éxito</string>
-    <string name="uploader_upload_succeeded_content_single">%1$s subidos</string>
-    <string name="uploader_upload_failed_ticker">Error en la subida</string>
-    <string name="uploader_upload_failed_content_single">La subida de %1$s no se pudo completar</string>
-    <string name="uploader_upload_failed_credentials_error">Subida fallida, necesita iniciar sesión de nuevo</string>
-    <string name="uploads_view_title">Subidas</string>
-    <string name="uploads_view_group_current_uploads">Actual</string>
-    <string name="uploads_view_group_failed_uploads">Falló (toque para reintentar)</string>
-    <string name="uploads_view_group_finished_uploads">Subido</string>
-    <string name="uploads_view_upload_status_succeeded">Completado</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">Error de conexión</string>
-    <string name="uploads_view_upload_status_failed_retry">La subida se reintentará 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 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>
-    <string name="uploads_view_upload_status_service_interrupted">La aplicación a terminado</string>
-    <string name="uploads_view_upload_status_unknown_fail">Error desconocido</string>
-    <string name="uploads_view_upload_status_waiting_for_wifi">Esperando conexión Wi-Fi</string>
-    <string name="uploads_view_later_waiting_to_upload">Esperando para subir</string>
-    <string name="downloader_download_in_progress_ticker">Descargando ...</string>
-    <string name="downloader_download_in_progress_content">%1$s Descargada de %2$s</string>
-    <string name="downloader_download_succeeded_ticker">Descarga completa</string>
-    <string name="downloader_download_succeeded_content">%1$s descargado</string>
-    <string name="downloader_download_failed_ticker">Falló la descarga</string>
-    <string name="downloader_download_failed_content">La descarga de %1$s no se pudo completar</string>
-    <string name="downloader_not_downloaded_yet">No descargado</string>
-    <string name="downloader_download_failed_credentials_error">Descarga fallida, necesita iniciar sesión de nuevo</string>
-    <string name="common_choose_account">Elige una cuenta</string>
-    <string name="sync_fail_ticker">Sincronización fallida</string>
-    <string name="sync_fail_ticker_unauthorized">Sincronización fallida, necesita iniciar sesión de nuevo</string>
-    <string name="sync_fail_content">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 archivos</string>
-    <string name="sync_fail_in_favourites_ticker">Fallos en la sincronización de contenidos</string>
-    <string name="sync_fail_in_favourites_content">Los contenidos de %1$d archivos no fueron sincronizados (%2$d conflictos)</string>
-    <string name="sync_foreign_files_forgotten_ticker">Algunos archivos locales se han perdido</string>
-    <string name="sync_foreign_files_forgotten_content">%1$d archivos de las %2$s carpetas no pudieron ser copiadas</string>
-    <string name="sync_foreign_files_forgotten_explanation">A partir de la versión 1.3.16, los archivos subidos desde este dispositivo son copiados a la carpeta local %1$s para prevenir pérdidas cuando un archivo es sincronizado en múltiples cuentas.\\n\\nDebido a este cambio, todos los archivos subidos en versiones previas de esta aplicación eran copiadas en la carpeta %2$s. Sin embargo, un error prevenía completar esta operación durante la sincronización de la cuenta. Puede elegir dejar los archivos como tal y remover el enlace a %3$s, o mover el archivo dentro de la carpeta %1$s y mantener el enlace a %4$s.\\n\\nEnlistados debajo están los archivos locales, y los archivos remotos en %5$s donde 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>
-    <string name="foreign_files_fail">Algunos archivos no han podido ser movidos</string>
-    <string name="foreign_files_local_text">Local: %1$s</string>
-    <string name="foreign_files_remote_text">Remoto: %1$s</string>
-    <string name="upload_query_move_foreign_files">No hay suficiente espacio para copiar los archivos seleccionados en la carpeta %1$s. ¿Le gustaría moverlos en su lugar?</string>
-    <string name="pass_code_enter_pass_code">Por favor entre su código de seguridad</string>
-    
-    <string name="pass_code_configure_your_pass_code">Ingrese su código de seguridad</string>
-    <string name="pass_code_configure_your_pass_code_explanation">El código de seguridad será solicitado en cada ocasión que inicie la aplicación</string>
-    <string name="pass_code_reenter_your_pass_code">Por favor reingrese el código de seguridad</string>
-    <string name="pass_code_remove_your_pass_code">Eliminar su código de seguridad</string>
-    <string name="pass_code_mismatch">Los códigos de seguridad no son iguales</string>
-    <string name="pass_code_wrong">Código de seguridad incorrecto</string>
-    <string name="pass_code_removed">Código de seguridad eliminado</string>
-    <string name="pass_code_stored">Código de seguridad 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 encuentra archivo de medio</string>
-    <string name="media_err_no_account">No se ha proporcionado cuenta</string>
-    <string name="media_err_not_in_owncloud">El archivo no esta en una cuenta valida </string>
-    <string name="media_err_unsupported">Codec No Soportado</string>
-    <string name="media_err_io">El archivo de medios no pudo ser leído </string>
-    <string name="media_err_malformed">Archivo no codificado correctamente</string>
-    <string name="media_err_timeout">Tiempo de espera agotado en el intento de reproducción</string>
-    <string name="media_err_invalid_progressive_playback">Archivo de medio no puede ser transmitido</string>
-    <string name="media_err_unknown">El archivo de medios no se puede reproducir con el reproductor de medios por defecto </string>
-    <string name="media_err_security_ex">Error de seguridad al intentar reproducir %1$s</string>
-    <string name="media_err_io_ex">Error de entrada al intentar reproducir %1$s</string>
-    <string name="media_err_unexpected">Error inesperado intentando reproducir %1$s</string>
-    <string name="media_rewind_description">Botón Rebobinado</string>
-    <string name="media_play_pause_description">Botón de reproducción o pausa </string>
-    <string name="media_forward_description">Botón avance rápido</string>
-
-	<string name="auth_getting_authorization">Obteniendo autorización &#8230;</string>
-	<string name="auth_trying_to_login">Intentando iniciar en sesión &#8230;</string>
-	<string name="auth_no_net_conn_title">Sin conexión de red</string>
-	<string name="auth_nossl_plain_ok_title">Conexión segura no disponible.</string>
-	<string name="auth_connection_established">Conexión establecida</string>
-	<string name="auth_testing_connection">Probando conexión</string>
-	<string name="auth_not_configured_title">Configuración de servidor en formato incorrecto</string>
-	<string name="auth_account_not_new">Una cuenta para el mismo usuario y servidor ya existen en el dispositivo</string>
-	<string name="auth_account_not_the_same">El usuario introducido no concuerda con el usuario de esta cuenta</string>
-	<string name="auth_unknown_error_title">Ocurrió un error desconocido</string>
-	<string name="auth_unknown_host_title">No se pudo encontrar la dirección</string>
-	<string name="auth_incorrect_path_title">Instancia de servidor no encontrada</string>
-	<string name="auth_timeout_title">El servidor ha tardado demasiado en responder</string>
-	<string name="auth_incorrect_address_title">Formato erróneo de la dirección del servidor</string>
-	<string name="auth_ssl_general_error_title">Falló la inicialización SSL</string>
-	<string name="auth_ssl_unverified_server_title">No fue posible verificar la identidad del servidor SLL</string>
-	<string name="auth_bad_oc_version_title">No se reconoce  la versión del servidor </string>
-	<string name="auth_wrong_connection_title">No se ha podido establecer la conexión</string>
-	<string name="auth_secure_connection">Conexión segura establecida</string>
-	<string name="auth_unauthorized">Nombre de usuario o contraseña incorrecta</string>
-	<string name="auth_oauth_error">Autorización no satisfactoria</string>
-	<string name="auth_oauth_error_access_denied">Acceso denegado por servidor de autorización</string>
-	<string name="auth_wtf_reenter_URL">Estado inesperado; por favor ingrese la dirección del servidor de nuevo</string>
-	<string name="auth_expired_oauth_token_toast">Su autorización ha expirado. Por favor, autorice de nuevo</string>
-	<string name="auth_expired_basic_auth_toast">Por favor ingrese la contraseña actual</string>
-	<string name="auth_expired_saml_sso_token_toast">Su sesión ha expirado. Favor de conectarse de nuevo</string>
-	<string name="auth_connecting_auth_server">Conectando al servidor de autenticación ...</string>
-	<string name="auth_unsupported_auth_method">El servidor no soporta este método de autenticación</string>
-	<string name="auth_unsupported_multiaccount">%1$s no soporta cuentas múltiples</string>
-	<string name="auth_fail_get_user_name">Su servidor no esta regresando un id de usuario correcto, por favor contacte un administrador
-	</string>
-	<string name="auth_can_not_auth_against_server">No se puede autenticar a este servidor</string>
-    <string name="auth_account_does_not_exist">La cuenta no existe aun en el dispositivo</string>
-    
-    <string name="favorite">Fijar como disponible localmente</string>
-    <string name="unfavorite">Remover como disponible localmente</string>
-    <string name="common_rename">Renombrar</string>
-    <string name="common_remove">Borrar</string>
-    <string name="confirmation_remove_file_alert">¿Realmente desea eliminar %1$s?</string>
-    <string name="confirmation_remove_folder_alert">¿Realmente desea eliminar %1$s y sus contenidos?</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 pudo ser completado</string>
-    <string name="rename_dialog_title">Introduzca un nombre nuevo</string>
-    <string name="rename_local_fail_msg">No se pudo cambiar el nombre de la copia local, trata con un nombre differente</string>
-    <string name="rename_server_fail_msg">No se pudo cambiar el nombre</string>
-    <string name="sync_file_fail_msg">No pudo comprobarse el archivo remoto</string>
-    <string name="sync_file_nothing_to_do_msg">Ya está sincronizado</string>
-    <string name="create_dir_fail_msg">La carpeta no pudo ser creada</string>
-    <string name="filename_forbidden_characters">Carácteres ilegales: / \\ &lt; &gt; : \" | ? *</string>
-    <string name="filename_forbidden_charaters_from_server">El nombre del archivo contiene al menos un carácter inválido.</string>
-    <string name="filename_empty">El nombre del archivo no puede estar vacío</string>
-    <string name="wait_a_moment">Espere un momento</string>
-    <string name="wait_checking_credentials">Revisando credenciales almacenadas</string>
-    <string name="filedisplay_unexpected_bad_get_content">Problema inesperado; por favor seleccione el archivo de una diferente aplicación</string>
-    <string name="filedisplay_no_file_selected">No fué seleccionado ningún archivo</string>
-    <string name="activity_chooser_title">Enviar enlace a &#8230;</string>
-    <string name="wait_for_tmp_copy_from_private_storage">Copiar archivo desde un alojamiento privado</string>
-    
-    <string name="oauth_check_onoff">Ingresar con oAuth2</string> 
-    <string name="oauth_login_connection">Conectando al servidor oAuth2...</string>    
-        
-    <string name="ssl_validator_header">La identidad del sitio no puede ser verificada</string>
-    <string name="ssl_validator_reason_cert_not_trusted">- El certificado del servidor no es de confianza</string>
-    <string name="ssl_validator_reason_cert_expired">- El certificado del servidor expiró</string>
-    <string name="ssl_validator_reason_cert_not_yet_valid">- El certificado del servidor es demasiado reciente</string>
-    <string name="ssl_validator_reason_hostname_not_verified">- La URL no coincide con el nombre de dominio del certificado</string>
-    <string name="ssl_validator_question">¿Confías de todas formas en este certificado?</string>
-    <string name="ssl_validator_not_saved">El certificado no pudo ser guardado</string>
-    <string name="ssl_validator_btn_details_see">Detalles</string>
-    <string name="ssl_validator_btn_details_hide">Ocultar</string>
-    <string name="ssl_validator_label_subject">Emitido para:</string>
-    <string name="ssl_validator_label_issuer">Emitido por:</string>
-    <string name="ssl_validator_label_CN">Nombre común:</string>
-    <string name="ssl_validator_label_O">Organización:</string>
-    <string name="ssl_validator_label_OU">Unidad organizativa</string>
-    <string name="ssl_validator_label_C">Pais:</string>
-    <string name="ssl_validator_label_ST">Estado:</string>
-    <string name="ssl_validator_label_L">Ubicación:</string>
-    <string name="ssl_validator_label_validity">Validez:</string>
-    <string name="ssl_validator_label_validity_from">De:</string>
-    <string name="ssl_validator_label_validity_to">A:</string>
-    <string name="ssl_validator_label_signature">Firma:</string>
-    <string name="ssl_validator_label_signature_algorithm">Algoritmo:</string>
-    <string name="digest_algorithm_not_available">El algoritmo de análisis no está disponible en su teléfono</string>
-    <string name="ssl_validator_label_certificate_fingerprint">Huella digital:</string>
-    <string name="certificate_load_problem">Hay un problema cargando el certificado.</string>
-    <string name="ssl_validator_null_cert">El certificado no pudo ser mostrado.</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_filetype">Imagen PNG</string>
-    <string name="placeholder_filesize">389 KB</string>
-    <string name="placeholder_timestamp">2012/05/18 12:23 PM</string>
-    <string name="placeholder_media_time">12:23:45</string>
-
-    <string name="instant_upload_on_wifi">Subir imágenes a través de wifi únicamente</string>
-    <string name="instant_video_upload_on_wifi">Subir videos a través de wifi únicamente</string>
-    <string name="instant_video_upload_on_charging">Subir al cargar batería únicamente</string>
-    <string name="instant_upload_on_charging">Subir al cargar batería únicamente</string>
-    <string name="instant_upload_path">/SubidasInstantáneas</string>
-    <string name="conflict_title">Conflicto de archivo</string>
-    <string name="conflict_message">¿Que archivos desea mantener? Si selecciona ambas versiones, el archivo local tendrá un numero agregado a su nombre.</string>
-    <string name="conflict_keep_both">Mantener ambas</string>
-    <string name="conflict_use_local_version">versión local</string>
-    <string name="conflict_use_server_version">versión de servidor</string>
-    
-    <string name="preview_image_description">Previsualización de imagen</string>
-    <string name="preview_image_error_unknown_format">Esta imagen no puede ser mostrada</string>
-
-    <string name="error__upload__local_file_not_copied">%1$s no pudo ser copiada a la carpeta local %2$s </string>
-    <string name="prefs_instant_upload_path_title">Carpeta de subida instantánea</string>
-    <string name="prefs_folder_sync_local_path_title">Carpeta local</string>
-    <string name="prefs_folder_sync_remote_path_title">Carpeta remota</string>
-    <string name="prefs_instant_upload_path_use_subfolders_title">Use subdirectorios</string>
-    <string name="prefs_instant_upload_path_use_subfolders_summary">Almacenar en subdirectorios basados en año y mes</string>
-
-	<string name="share_link_no_support_share_api">Lo sentimos, compartir no esta habilitado en su servidor. Por favor contacte a su 
-		administrador.</string>
-	<string name="share_link_file_no_exist">Incapaz de compartir. Por favor revise si existe el archivo</string>
-	<string name="share_link_file_error">Ocurrió un error mientras se trataba de compartir este archivo o carpeta</string>
-	<string name="unshare_link_file_no_exist">Incapaz de dejar de compartir. Por favor revise si existe el archivo</string>
-	<string name="unshare_link_file_error">Ocurrió un error al tratar de dejar de compartir este archivo o carpeta</string>
-    <string name="update_link_file_no_exist">Incapaz de actualizar. Por favor revise si existe el archivo</string>
-    <string name="update_link_file_error">Ocurrió un error mientras se intentaba actualizar el recurso compartido</string>
-    <string name="share_link_password_title">Ingrese una contraseña</string>
-    <string name="share_link_empty_password">Debe ingresar una contraseña</string>
-
-    <string name="activity_chooser_send_file_title">Enviar</string>
-
-    <string name="copy_link">Copiar enlace</string>
-    <string name="clipboard_text_copied">Copiado al portapapeles</string>
-    <string name="clipboard_no_text_to_copy">No se recibió texto para copiar a este portapapeles</string>
-    <string name="clipboard_uxexpected_error">Error inesperado al copiar a portapapeles</string>
-    <string name="clipboard_label">Texto copiado de %1$s</string>
-
-    <string name="error_cant_bind_to_operations_service">Error crítico: no se pueden realizar las operaciones</string>
-
-    <string name="network_error_socket_exception">Ocurrió un error mientras se conectaba con el servidor</string>
-    <string name="network_error_socket_timeout_exception">Ocurrió un error mientras se esperaba por el servidor; la operación no pudo ser completada</string>
-    <string name="network_error_connect_timeout_exception">Ocurrió un error mientras se esperaba por el servidor; la operación no pudo ser completada</string>
-    <string name="network_host_not_available">La operación no pudo ser completada; el servidor no esta disponible</string>
-    <string name="forbidden_permissions">No tiene los permisos %s</string>
-    <string name="forbidden_permissions_rename">para renombrar este archivo</string>
-    <string name="forbidden_permissions_delete">para borrar este archivo</string>
-    <string name="share_link_forbidden_permissions">para compartir este archivo</string>
-    <string name="unshare_link_forbidden_permissions">para dejar de compartir este archivo</string>
-    <string name="update_link_forbidden_permissions">para actualizar este recurso compartido</string>
-    <string name="forbidden_permissions_create">para crear este archivo</string>
-    <string name="uploader_upload_forbidden_permissions">para subir esta carpeta</string>
-    <string name="downloader_download_file_not_found">El archivo ya no esta disponible en el servidor</string>
-
-    <string name="file_migration_dialog_title">Actualizando ruta de almacenamiento</string>
-    <string name="file_migration_finish_button">Terminar</string>
-    <string name="file_migration_preparing">Preparando para la migración&#8230;</string>
-    <string name="file_migration_checking_destination">Revisar destino&#8230;</string>
-    <string name="file_migration_saving_accounts_configuration">Guardando la configuración de las cuentas&#8230;</string>
-    <string name="file_migration_waiting_for_unfinished_sync">Esperando por sincronizaciones incompletas&#8230;</string>
-    <string name="file_migration_migrating">Moviendo información&#8230;</string>
-    <string name="file_migration_updating_index">Actualizando índice&#8230;</string>
-    <string name="file_migration_cleaning">Limpiando&#8230;</string>
-    <string name="file_migration_restoring_accounts_configuration">Restaurando configuración de cuentas&#8230;</string>
-    <string name="file_migration_ok_finished">Completado</string>
-    <string name="file_migration_failed_not_enough_space">ERROR: No hay suficiente espacio</string>
-    <string name="file_migration_failed_not_writable">ERROR: El archivo no se puede escribir</string>
-    <string name="file_migration_failed_not_readable">ERROR: El archivo no se puede leer</string>
-    <string name="file_migration_failed_dir_already_exists">ERROR: El directorio de Nextcloud ya existe</string>
-    <string name="file_migration_failed_while_coping">ERROR: Durante la migración</string>
-    <string name="file_migration_failed_while_updating_index">ERROR: Durante la actualización del índice</string>
-
-    <string name="file_migration_directory_already_exists">La carpeta de la información ya existe, ¿que hacer?</string>
-    <string name="file_migration_override_data_folder">Sobrescribir</string>
-    <string name="file_migration_use_data_folder">Usar el existente</string>
-
-    <string name="prefs_category_accounts">Cuentas</string>
-    <string name="prefs_add_account">Agregar cuenta</string>
-    <string name="drawer_manage_accounts">Administrar cuentas</string>
-    <string name="auth_redirect_non_secure_connection_title">La conexión segura esta redirigiendo a una ruta insegura</string>
-
-	<string name="actionbar_logger">Registros</string>
-	<string name="log_send_history_button">Enviar historial</string>
-	<string name="log_send_no_mail_app">No hay aplicación para mandar registros encontrados. Por favor instale una aplicación de correo</string>
-	<string name="log_send_mail_subject">Aplicación de registros %1$s Android</string>
-	<string name="log_progress_dialog_text">Cargando información &#8230;</string>
-
-	<string name="saml_authentication_required_text">Autenticación requerida</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">No hay nada aquí. ¡Puede agregar una carpeta!</string>
-	<string name="folder_picker_choose_button_text">Seleccionar</string>
-
-    <string name="move_file_not_found">Incapaz de mover. Por favor revise si existe el archivo</string>
-    <string name="move_file_invalid_into_descendent">No es posible mover una carpeta dentro de un subdirectorio</string>
-    <string name="move_file_invalid_overwrite">El archivo ya existe en la carpeta de destino</string>
-    <string name="move_file_error">Ocurrió un error mientras se intentaba mover este archivo o carpeta</string>
-    <string name="forbidden_permissions_move">mover este archivo</string>
-
-
-    <string name="copy_file_not_found">Incapaz de copiar. Por favor revise si existe el archivo</string>
-    <string name="copy_file_invalid_into_descendent">No es posible copiar una carpeta dentro de un subdirectorio</string>
-    <string name="copy_file_invalid_overwrite">El archivo ya existe en la carpeta de destino</string>
-    <string name="copy_file_error">Ocurrió un error mientras se intentaba copiar este archivo o carpeta</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 de subida instantánea de video</string>
-    <string name="auth_host_address">Dirección del servidor</string>
-    <string name="share_dialog_title">Compartiendo</string>
-    <string name="share_via_link_section_title">Enlace compartido</string>
-    <string name="share_via_link_expiration_date_label">Establecer fecha de caducidad</string>
-    <string name="share_via_link_password_label">Protección con contraseña</string>
-    <string name="share_search">Buscar</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_delete">eliminar</string>
-    <string name="whats_new_skip">Saltar</string>
-</resources>

+ 5 - 0
res/xml/backup_config.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<full-backup-content>
+    <exclude domain="sharedpref" path="evernote_jobs.xml" />
+    <exclude domain="database" path="evernote_jobs.db" />
+</full-backup-content>

+ 5 - 0
scripts/lint/getBranchName.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+# $1: username, $2: password/token, $3: pull request number
+
+curl 2>/dev/null -u $1:$2 https://api.github.com/repos/nextcloud/android/pulls/$3 | grep \"ref\": | grep -v master | cut -d"\"" -f4

+ 2 - 0
scripts/lint/lint-results.txt

@@ -0,0 +1,2 @@
+DO NOT TOUCH; GENERATED BY DRONE
+      <span class="mdl-layout-title">Lint Report: 75 errors and 852 warnings</span>

+ 180 - 0
scripts/lint/lint-up.rb

@@ -0,0 +1,180 @@
+## Script from https://github.com/tir38/android-lint-entropy-reducer at 07.05.2017
+# adapts to drone, use git username / token as parameter
+
+puts "=================== starting Android Lint Entropy Reducer ===================="
+
+# get args
+git_user, git_token, git_branch = ARGV
+
+# ========================  SETUP ============================
+
+# User name for git commits made by this script.
+TRAVIS_GIT_USERNAME = String.new("Drone CI server")
+
+# File name and relative path of generated Lint report. Must match build.gradle file:
+#   lintOptions {
+#       htmlOutput file("[FILE_NAME].html")
+#   }
+LINT_REPORT_FILE = String.new("build/reports/lint/lint.html")
+
+# File name and relative path of previous results of this script.
+PREVIOUS_LINT_RESULTS_FILE=String.new("scripts/lint/lint-results.txt")
+
+# Flag to evaluate warnings. true = check warnings; false = ignore warnings
+CHECK_WARNINGS = true
+
+# File name and relative path to custom lint rules; Can be null or "".
+CUSTOM_LINT_FILE = String.new("")
+
+# ================ SETUP DONE; DON'T TOUCH ANYTHING BELOW  ================
+
+require 'fileutils'
+require 'pathname'
+require 'open3'
+
+# since we need the xml-simple gem, and we want this script self-contained, let's grab it just when we need it
+begin
+    gem "xml-simple"
+    rescue LoadError
+    system("gem install xml-simple")
+    Gem.clear_paths
+end
+
+require 'xmlsimple'
+
+# add custom Lint jar
+if !CUSTOM_LINT_FILE.nil? &&
+    CUSTOM_LINT_FILE.length > 0
+
+    ENV["ANDROID_LINT_JARS"] = Dir.pwd + "/" + CUSTOM_LINT_FILE
+    puts "adding custom lint rules to default set: "
+    puts ENV["ANDROID_LINT_JARS"]
+end
+
+# run Lint
+puts "running Lint..."
+system './gradlew clean lint'
+
+# confirm that Lint ran w/out error
+result = $?.to_i
+if result != 0
+    puts "FAIL: failed to run ./gradlew clean lint"
+    exit 1
+end
+
+# find Lint report file
+lint_reports = Dir.glob(LINT_REPORT_FILE)
+if lint_reports.length == 0
+    puts "Lint HTML report not found."
+    exit 1
+end
+lint_report = String.new(lint_reports[0])
+
+# find error/warning count string in HTML report
+error_warning_string = ""
+File.open lint_report do |file|
+  error_warning_string = file.find { |line| line =~ /[0-9]* errors and [0-9]* warnings/ }
+end
+
+# find number of errors
+error_string = error_warning_string.match(/[0-9]* errors/)[0]
+current_error_count = error_string.match(/[0-9]*/)[0].to_i
+puts "found errors: " + current_error_count.to_s
+
+# find number of warnings
+if CHECK_WARNINGS == true
+    warning_string = error_warning_string.match(/[0-9]* warnings/)[0]
+    current_warning_count = warning_string.match(/[0-9]*/)[0].to_i
+    puts "found warnings: " + current_warning_count.to_s
+end
+
+# get previous error and warning counts from last successful build
+
+previous_results = false
+
+previous_lint_reports = Dir.glob(PREVIOUS_LINT_RESULTS_FILE)
+if previous_lint_reports.nil? ||
+    previous_lint_reports.length == 0
+
+    previous_lint_report = File.new(PREVIOUS_LINT_RESULTS_FILE, "w") # create for writing to later
+else
+    previous_lint_report = String.new(previous_lint_reports[0])
+
+    previous_error_warning_string = ""
+    File.open previous_lint_report do |file|
+      previous_error_warning_string = file.find { |line| line =~ /[0-9]* errors and [0-9]* warnings/ }
+    end
+
+    unless previous_error_warning_string.nil?
+        previous_results = true
+
+        previous_error_string = previous_error_warning_string.match(/[0-9]* errors/)[0]
+        previous_error_count = previous_error_string.match(/[0-9]*/)[0].to_i
+        puts "previous errors: " + previous_error_count.to_s
+
+        if CHECK_WARNINGS == true
+            previous_warning_string = previous_error_warning_string.match(/[0-9]* warnings/)[0]
+            previous_warning_count = previous_warning_string.match(/[0-9]*/)[0].to_i
+            puts "previous warnings: " + previous_warning_count.to_s
+        end
+    end
+end
+
+# compare previous error count with current error count
+if previous_results == true  &&
+    current_error_count > previous_error_count
+    puts "FAIL: error count increased"
+    exit 1
+end
+
+# compare previous warning count with current warning count
+if CHECK_WARNINGS  == true &&
+    previous_results == true &&
+    current_warning_count > previous_warning_count
+
+    puts "FAIL: warning count increased"
+    exit 1
+end
+
+# check if warning and error count stayed the same
+if  previous_results == true &&
+    current_error_count == previous_error_count &&
+    current_warning_count == previous_warning_count
+
+    puts "SUCCESS: count stayed the same"
+    exit 0
+end
+
+# either error count or warning count DECREASED
+
+# write new results to file (will overwrite existing, or create new)
+File.write(previous_lint_report, "DO NOT TOUCH; GENERATED BY DRONE\n" + error_warning_string)
+
+# push changes to github (if this script is run locally, we don't want to overwrite git username and email, so save temporarily)
+previous_git_username, _ = Open3.capture2('git config user.name')
+previous_git_username = previous_git_username.strip
+
+previous_git_email, _ = Open3.capture3('git config user.email')
+previous_git_email = previous_git_email.strip
+
+# update git user name and email for this script
+system ("git config --local user.name '"  + git_user + "'")
+system ("git config --local user.email '.'") # set email blank
+system ("git remote rm origin")
+system ("git remote add origin https://" + git_user + ":" + git_token + "@github.com/nextcloud/android")
+
+# add previous Lint result file to git
+system ('git add ' + PREVIOUS_LINT_RESULTS_FILE)
+
+# commit changes; Add "skip ci" so that we don't accidentally trigger another Drone build
+system ('git commit -m "Drone: update Lint results to reflect reduced error/warning count [skip ci]" ')
+
+# push to origin
+system ('git push origin HEAD:' + git_branch)
+
+# restore previous git user name and email
+system("git config --local user.name '#{previous_git_username}'")
+system("git config --local user.email '#{previous_git_email}'")
+
+puts "SUCCESS: count was reduced"
+exit 0 # success

+ 0 - 93
src/com/owncloud/android/services/SyncedFolderJobService.java

@@ -1,93 +0,0 @@
-/**
- *   Nextcloud Android client application
- *
- *   @author Tobias Kaminsky
- *   Copyright (C) 2016 Tobias Kaminsky
- *   Copyright (C) 2016 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/>.
- */
-
-package com.owncloud.android.services;
-
-import android.accounts.Account;
-import android.annotation.TargetApi;
-import android.app.job.JobParameters;
-import android.app.job.JobService;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.PersistableBundle;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.authentication.AccountUtils;
-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.utils.FileStorageUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-
-import java.io.File;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class SyncedFolderJobService extends JobService {
-    private static final String TAG = "SyncedFolderJobService";
-
-    public static final String LOCAL_PATH = "filePath";
-    public static final String REMOTE_PATH = "remotePath";
-    public static final String ACCOUNT = "account";
-    public static final String UPLOAD_BEHAVIOUR = "uploadBehaviour";
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        return START_REDELIVER_INTENT;
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        Context context = MainApp.getAppContext();
-        PersistableBundle bundle = params.getExtras();
-        String filePath = bundle.getString(LOCAL_PATH);
-        String remotePath = bundle.getString(REMOTE_PATH);
-        Account account = AccountUtils.getOwnCloudAccountByName(context, bundle.getString(ACCOUNT));
-        Integer uploadBehaviour = bundle.getInt(UPLOAD_BEHAVIOUR);
-
-        Log_OC.d(TAG, "startJob: " + params.getJobId() + ", filePath: " + filePath);
-
-        File file = new File(filePath);
-
-        // File can be deleted between job generation and job execution. If file does not exist, just ignore it
-        if (file.exists()) {
-            String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(file.getAbsolutePath());
-
-            FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
-            requester.uploadNewFile(
-                    context,
-                    account,
-                    filePath,
-                    remotePath,
-                    uploadBehaviour,
-                    mimeType,
-                    true,           // create parent folder if not existent
-                    UploadFileOperation.CREATED_AS_INSTANT_PICTURE
-            );
-        }
-        return false;
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters params) {
-        return false;
-    }
-}

+ 0 - 78
src/com/owncloud/android/services/observer/SyncedFolderObserver.java

@@ -1,78 +0,0 @@
-package com.owncloud.android.services.observer;
-
-import android.annotation.TargetApi;
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Build;
-import android.os.FileObserver;
-import android.os.PersistableBundle;
-import android.util.Log;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.datamodel.SyncedFolder;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.services.SyncedFolderJobService;
-import com.owncloud.android.utils.FileStorageUtils;
-import com.owncloud.android.utils.RecursiveFileObserver;
-
-import java.io.File;
-import java.util.Date;
-
-class SyncedFolderObserver extends RecursiveFileObserver {
-
-    private Context context;
-
-    public static final String TAG = "SyncedFolderObserver";
-    private SyncedFolder syncedFolder;
-
-
-    public SyncedFolderObserver(SyncedFolder syncedFolder) {
-        super(syncedFolder.getLocalPath(), FileObserver.CREATE + FileObserver.MOVED_TO);
-
-        context = MainApp.getAppContext();
-        this.syncedFolder = syncedFolder;
-        Log_OC.d("SyncedFolderObserver", "Started watching: " + syncedFolder.getLocalPath());
-    }
-
-
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    @Override
-    public void onEvent(int event, String path) {
-        Log.d(TAG, "Event: " + event + " Path: " + path);
-
-        File temp = new File(path);
-
-        // do not upload "null"-files, test if file exists and is a real file
-        if (!temp.getName().equalsIgnoreCase("null") && temp.isFile() && !temp.getName().endsWith(".tmp")) {
-            PersistableBundle bundle = new PersistableBundle();
-            // TODO extract
-            bundle.putString(SyncedFolderJobService.LOCAL_PATH, path);
-            bundle.putString(SyncedFolderJobService.REMOTE_PATH, FileStorageUtils.getInstantUploadFilePath(
-                                                                 syncedFolder.getRemotePath(), temp.getName(),
-                                                                 new Date().getTime(),
-                                                                 syncedFolder.getSubfolderByDate()));
-            bundle.putString(SyncedFolderJobService.ACCOUNT, syncedFolder.getAccount());
-            bundle.putInt(SyncedFolderJobService.UPLOAD_BEHAVIOUR, syncedFolder.getUploadAction());
-
-            JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
-            Long date = new Date().getTime();
-            JobInfo job = new JobInfo.Builder(
-                    date.intValue(),
-                    new ComponentName(context, SyncedFolderJobService.class))
-                    .setRequiresCharging(syncedFolder.getChargingOnly())
-                    .setMinimumLatency(10000)
-                    .setRequiredNetworkType(syncedFolder.getWifiOnly() ? JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
-                    .setExtras(bundle)
-                    .setPersisted(true)
-                    .build();
-
-            Integer result = js.schedule(job);
-            if (result <= 0) {
-                Log_OC.d(TAG, "Job failed to start: " + result);
-            }
-        }
-    }
-}

+ 0 - 84
src/com/owncloud/android/services/observer/SyncedFolderObserverService.java

@@ -1,84 +0,0 @@
-package com.owncloud.android.services.observer;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.IBinder;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.datamodel.SyncedFolder;
-import com.owncloud.android.datamodel.SyncedFolderProvider;
-import com.owncloud.android.lib.common.utils.Log_OC;
-
-import java.util.HashMap;
-
-public class SyncedFolderObserverService extends Service {
-    private static final String TAG = "SyncedFolderObserverService";
-    private SyncedFolderProvider mProvider;
-    private HashMap<String, SyncedFolderObserver> syncedFolderMap = new HashMap<>();
-    private final IBinder mBinder = new SyncedFolderObserverBinder();
-
-    @Override
-    public void onCreate() {
-        mProvider = new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
-    }
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        Log_OC.d(TAG, "start");
-        for (SyncedFolder syncedFolder : mProvider.getSyncedFolders()) {
-            if (syncedFolder.isEnabled()) {
-                Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
-                SyncedFolderObserver observer = new SyncedFolderObserver(syncedFolder);
-                observer.startWatching();
-                syncedFolderMap.put(syncedFolder.getLocalPath(), observer);
-            }
-        }
-
-        return Service.START_NOT_STICKY;
-    }
-
-    @Override
-    public void onDestroy() {
-        for (SyncedFolderObserver observer : syncedFolderMap.values()) {
-            observer.stopWatching();
-            syncedFolderMap.remove(observer);
-        }
-    }
-
-    /**
-     * Restart oberver if it is enabled
-     * If syncedFolder exists already, use it, otherwise create new observer
-     * @param syncedFolder
-     */
-    public void restartObserver(SyncedFolder syncedFolder){
-        if (syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
-            Log_OC.d(TAG, "stop observer: " + syncedFolder.getLocalPath());
-            syncedFolderMap.get(syncedFolder.getLocalPath()).stopWatching();
-            syncedFolderMap.remove(syncedFolder.getLocalPath());
-        }
-
-        if (syncedFolder.isEnabled()) {
-            Log_OC.d(TAG, "start observer: " + syncedFolder.getLocalPath());
-            if (syncedFolderMap.containsKey(syncedFolder.getLocalPath())) {
-                syncedFolderMap.get(syncedFolder.getLocalPath()).startWatching();
-            } else {
-                SyncedFolderObserver observer = new SyncedFolderObserver(syncedFolder);
-                observer.startWatching();
-                syncedFolderMap.put(syncedFolder.getLocalPath(), observer);
-            }
-        }
-    }
-
-    @Override
-    public IBinder onBind(Intent arg0) {
-        return mBinder;
-    }
-
-    public class SyncedFolderObserverBinder extends Binder {
-        public SyncedFolderObserverService getService() {
-            return SyncedFolderObserverService.this;
-        }
-    }
-
-}

+ 0 - 39
src/com/owncloud/android/ui/RadioButtonPreference.java

@@ -1,39 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui;
-
-import android.content.Context;
-import android.preference.CheckBoxPreference;
-import android.view.View;
-
-import com.owncloud.android.R;
-
-public class RadioButtonPreference extends CheckBoxPreference implements View.OnLongClickListener {
-    
-    public RadioButtonPreference(Context context) {
-        super(context, null, android.R.attr.checkBoxPreferenceStyle);
-        setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
-    }
-  
-    @Override
-    public boolean onLongClick(View v) {
-        return true;
-    }
-}

+ 0 - 44
src/com/owncloud/android/ui/SquareImageView.java

@@ -1,44 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-public class SquareImageView extends ImageView {
-
-    public SquareImageView(Context context) {
-        super(context);
-    }
-
-    public SquareImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
-    }
-}

+ 0 - 27
src/com/owncloud/android/ui/fragment/AuthenticatorAccountDetailsFragment.java

@@ -1,27 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.fragment;
-
-import android.support.v4.app.Fragment;
-
-public class AuthenticatorAccountDetailsFragment extends Fragment {
-
-}

+ 0 - 27
src/com/owncloud/android/ui/fragment/AuthenticatorGetStartedFragment.java

@@ -1,27 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.fragment;
-
-import android.support.v4.app.Fragment;
-
-public class AuthenticatorGetStartedFragment extends Fragment {
-
-}

+ 0 - 598
src/com/owncloud/android/ui/fragment/ExtendedListFragment.java

@@ -1,598 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2012-2016 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.fragment;
-
-import android.os.Bundle;
-import android.support.annotation.DrawableRes;
-import android.support.annotation.StringRes;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v4.widget.SwipeRefreshLayout;
-import android.view.GestureDetector;
-import android.support.v7.widget.SearchView;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.GridView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.getbase.floatingactionbutton.FloatingActionButton;
-import com.getbase.floatingactionbutton.FloatingActionsMenu;
-import com.owncloud.android.MainApp;
-import com.owncloud.android.R;
-import com.owncloud.android.db.PreferenceManager;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.ui.ExtendedListView;
-import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
-import com.owncloud.android.ui.adapter.FilterableListAdapter;
-
-import java.util.ArrayList;
-
-import third_parties.in.srain.cube.GridViewWithHeaderAndFooter;
-
-public class ExtendedListFragment extends Fragment
-        implements OnItemClickListener, OnEnforceableRefreshListener, SearchView.OnQueryTextListener {
-
-    protected static final String TAG = ExtendedListFragment.class.getSimpleName();
-
-    protected static final String KEY_SAVED_LIST_POSITION = "SAVED_LIST_POSITION"; 
-
-    private static final String KEY_INDEXES = "INDEXES";
-    private static final String KEY_FIRST_POSITIONS= "FIRST_POSITIONS";
-    private static final String KEY_TOPS = "TOPS";
-    private static final String KEY_HEIGHT_CELL = "HEIGHT_CELL";
-    private static final String KEY_EMPTY_LIST_MESSAGE = "EMPTY_LIST_MESSAGE";
-    private static final String KEY_IS_GRID_VISIBLE = "IS_GRID_VISIBLE";
-    public static final float minColumnSize = 2.0f;
-    public static final float maxColumnSize = 10.0f;
-
-    private ScaleGestureDetector mScaleGestureDetector = null;
-    protected SwipeRefreshLayout mRefreshListLayout;
-    protected SwipeRefreshLayout mRefreshGridLayout;
-    protected SwipeRefreshLayout mRefreshEmptyLayout;
-    protected LinearLayout mEmptyListContainer;
-    protected TextView mEmptyListMessage;
-    protected TextView mEmptyListHeadline;
-    protected ImageView mEmptyListIcon;
-    protected ProgressBar mEmptyListProgress;
-
-    private FloatingActionsMenu mFabMain;
-    private FloatingActionButton mFabUpload;
-    private FloatingActionButton mFabMkdir;
-    private FloatingActionButton mFabUploadFromApp;
-
-    // Save the state of the scroll in browsing
-    private ArrayList<Integer> mIndexes;
-    private ArrayList<Integer> mFirstPositions;
-    private ArrayList<Integer> mTops;
-    private int mHeightCell = 0;
-
-    private SwipeRefreshLayout.OnRefreshListener mOnRefreshListener = null;
-
-    protected AbsListView mCurrentListView;
-    private ExtendedListView mListView;
-    private View mListFooterView;
-    private GridViewWithHeaderAndFooter mGridView;
-    private View mGridFooterView;
-
-    private FilterableListAdapter mAdapter;
-
-    private float mScale = -1f;
-
-    protected void setListAdapter(FilterableListAdapter listAdapter) {
-        mAdapter = listAdapter;
-        mCurrentListView.setAdapter(listAdapter);
-        mCurrentListView.invalidateViews();
-    }
-
-    protected AbsListView getListView() {
-        return mCurrentListView;
-    }
-
-    public FloatingActionButton getFabUpload() {
-        return mFabUpload;
-    }
-
-    public FloatingActionButton getFabUploadFromApp() {
-        return mFabUploadFromApp;
-    }
-
-    public FloatingActionButton getFabMkdir() {
-        return mFabMkdir;
-    }
-
-    public FloatingActionsMenu getFabMain() {
-        return mFabMain;
-    }
-
-    public void switchToGridView() {
-        if (!isGridEnabled()) {
-            mListView.setAdapter(null);
-            mRefreshListLayout.setVisibility(View.GONE);
-            mRefreshGridLayout.setVisibility(View.VISIBLE);
-            mCurrentListView = mGridView;
-            setListAdapter(mAdapter);
-        }
-    }
-
-    public void switchToListView() {
-        if (isGridEnabled()) {
-            mGridView.setAdapter(null);
-            mRefreshGridLayout.setVisibility(View.GONE);
-            mRefreshListLayout.setVisibility(View.VISIBLE);
-            mCurrentListView = mListView;
-            setListAdapter(mAdapter);
-        }
-    }
-
-    public boolean isGridEnabled(){
-        return (mCurrentListView != null && mCurrentListView.equals(mGridView));
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        final MenuItem item = menu.findItem(R.id.action_search);
-        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
-        searchView.setOnQueryTextListener(this);
-    }
-
-    public boolean onQueryTextChange(String query) {
-        mAdapter.filter(query);
-        return true;
-    }
-
-    @Override
-    public boolean onQueryTextSubmit(String query) {
-        mAdapter.filter(query);
-        return true;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreateView");
-
-        View v = inflater.inflate(R.layout.list_fragment, null);
-        setupEmptyList(v);
-
-        mListView = (ExtendedListView)(v.findViewById(R.id.list_root));
-        mListView.setOnItemClickListener(this);
-        mListFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
-        mGridView = (GridViewWithHeaderAndFooter) (v.findViewById(R.id.grid_root));
-
-        mScale = PreferenceManager.getGridColumns(getContext());
-        setGridViewColumns(1f);
-        //mGridView.setNumColumns(GridView.AUTO_FIT);
-        mGridView.setOnItemClickListener(this);
-
-        mGridFooterView = inflater.inflate(R.layout.list_footer, null, false);
-
-        mScaleGestureDetector = new ScaleGestureDetector(MainApp.getAppContext(),new ScaleListener());
-//        gestureDetector = new GestureDetector(MainApp.getAppContext(), new SingleTapConfirm());
-
-        mGridView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent motionEvent) {
-                mScaleGestureDetector.onTouchEvent(motionEvent);
-                return false;
-            }
-        });
-
-        if (savedInstanceState != null) {
-            int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-            if (mCurrentListView!= null && mCurrentListView.equals(mListView)) {
-                Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
-                mListView.setAndCenterSelection(referencePosition);
-            } else {
-                Log_OC.v(TAG, "Setting grid position " + referencePosition);
-                mGridView.setSelection(referencePosition);
-            }
-        }
-
-        // Pull-down to refresh layout
-        mRefreshListLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_list);
-        mRefreshGridLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_grid);
-        mRefreshEmptyLayout = (SwipeRefreshLayout) v.findViewById(R.id.swipe_containing_empty);
-        
-        onCreateSwipeToRefresh(mRefreshListLayout);
-        onCreateSwipeToRefresh(mRefreshGridLayout);
-        onCreateSwipeToRefresh(mRefreshEmptyLayout);
-
-        mListView.setEmptyView(mRefreshEmptyLayout);
-        mGridView.setEmptyView(mRefreshEmptyLayout);
-
-        mFabMain = (FloatingActionsMenu) v.findViewById(R.id.fab_main);
-        mFabUpload = (FloatingActionButton) v.findViewById(R.id.fab_upload);
-        mFabMkdir = (FloatingActionButton) v.findViewById(R.id.fab_mkdir);
-        mFabUploadFromApp = (FloatingActionButton) v.findViewById(R.id.fab_upload_from_app);
-
-        mCurrentListView = mListView;   // list by default
-        if (savedInstanceState != null) {
-            if (savedInstanceState.getBoolean(KEY_IS_GRID_VISIBLE, false)) {
-                switchToGridView();
-            }
-            int referencePosition = savedInstanceState.getInt(KEY_SAVED_LIST_POSITION);
-            if (isGridEnabled()) {
-                Log_OC.v(TAG, "Setting grid position " + referencePosition);
-                mGridView.setSelection(referencePosition);
-            } else {
-                Log_OC.v(TAG, "Setting and centering around list position " + referencePosition);
-                mListView.setAndCenterSelection(referencePosition);
-            }
-        }
-
-        return v;
-    }
-
-    private class SingleTapConfirm extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public boolean onSingleTapUp(MotionEvent e) {
-            return true;
-        }
-    }
-
-    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
-        @Override
-        public boolean onScale(ScaleGestureDetector detector) {
-            setGridViewColumns(detector.getScaleFactor());
-
-            PreferenceManager.setGridColumns(getContext(), mScale);
-
-            return true;
-        }
-    }
-
-    private void setGridViewColumns(float scaleFactor) {
-        if (mScale == -1f) {
-            mGridView.setNumColumns(GridView.AUTO_FIT);
-            mScale = mGridView.getNumColumns();
-        }
-        mScale *= 1.f - (scaleFactor - 1.f);
-        mScale = Math.max(minColumnSize, Math.min(mScale, maxColumnSize));
-        Integer scaleInt = Math.round(mScale);
-        mGridView.setNumColumns(scaleInt);
-        mGridView.invalidateViews();
-    }
-
-    protected void setupEmptyList(View view) {
-        mEmptyListContainer = (LinearLayout) view.findViewById(R.id.empty_list_view);
-        mEmptyListMessage = (TextView) view.findViewById(R.id.empty_list_view_text);
-        mEmptyListHeadline = (TextView) view.findViewById(R.id.empty_list_view_headline);
-        mEmptyListIcon = (ImageView) view.findViewById(R.id.empty_list_icon);
-        mEmptyListProgress = (ProgressBar) view.findViewById(R.id.empty_list_progress);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        
-        if (savedInstanceState != null) {
-            mIndexes = savedInstanceState.getIntegerArrayList(KEY_INDEXES);
-            mFirstPositions = savedInstanceState.getIntegerArrayList(KEY_FIRST_POSITIONS);
-            mTops = savedInstanceState.getIntegerArrayList(KEY_TOPS);
-            mHeightCell = savedInstanceState.getInt(KEY_HEIGHT_CELL);
-            setMessageForEmptyList(savedInstanceState.getString(KEY_EMPTY_LIST_MESSAGE));
-            
-        } else {
-            mIndexes = new ArrayList<>();
-            mFirstPositions = new ArrayList<>();
-            mTops = new ArrayList<>();
-            mHeightCell = 0;
-        }
-
-        mScale = PreferenceManager.getGridColumns(getContext());
-    }    
-    
-    
-    @Override
-    public void onSaveInstanceState(Bundle savedInstanceState) {
-        super.onSaveInstanceState(savedInstanceState);
-        Log_OC.d(TAG, "onSaveInstanceState()");
-        savedInstanceState.putBoolean(KEY_IS_GRID_VISIBLE, isGridEnabled());
-        savedInstanceState.putInt(KEY_SAVED_LIST_POSITION, getReferencePosition());
-        savedInstanceState.putIntegerArrayList(KEY_INDEXES, mIndexes);
-        savedInstanceState.putIntegerArrayList(KEY_FIRST_POSITIONS, mFirstPositions);
-        savedInstanceState.putIntegerArrayList(KEY_TOPS, mTops);
-        savedInstanceState.putInt(KEY_HEIGHT_CELL, mHeightCell);
-        savedInstanceState.putString(KEY_EMPTY_LIST_MESSAGE, getEmptyViewText());
-
-        PreferenceManager.setGridColumns(getContext(), mScale);
-    }
-
-    /**
-     * Calculates the position of the item that will be used as a reference to
-     * reposition the visible items in the list when the device is turned to
-     * other position.
-     * 
-     * The current policy is take as a reference the visible item in the center
-     * of the screen.
-     * 
-     * @return The position in the list of the visible item in the center of the
-     *         screen.
-     */
-    protected int getReferencePosition() {
-        if (mCurrentListView != null) {
-            return (mCurrentListView.getFirstVisiblePosition() +
-                    mCurrentListView.getLastVisiblePosition()) / 2;
-        } else {
-            return 0;
-        }
-    }
-
-    public int getColumnSize() {
-        return Math.round(mScale);
-    }
-
-
-    /*
-     * Restore index and position
-     */
-    protected void restoreIndexAndTopPosition() {
-        if (mIndexes.size() > 0) {  
-            // needs to be checked; not every browse-up had a browse-down before 
-            
-            int index = mIndexes.remove(mIndexes.size() - 1);
-            final int firstPosition = mFirstPositions.remove(mFirstPositions.size() -1);
-            int top = mTops.remove(mTops.size() - 1);
-
-            Log_OC.v(TAG, "Setting selection to position: " + firstPosition + "; top: "
-                    + top + "; index: " + index);
-
-            if (mCurrentListView!= null && mCurrentListView.equals(mListView)) {
-                if (mHeightCell*index <= mListView.getHeight()) {
-                    mListView.setSelectionFromTop(firstPosition, top);
-                } else {
-                    mListView.setSelectionFromTop(index, 0);
-                }
-
-            } else {
-                if (mHeightCell*index <= mGridView.getHeight()) {
-                    mGridView.setSelection(firstPosition);
-                    //mGridView.smoothScrollToPosition(firstPosition);
-                } else {
-                    mGridView.setSelection(index);
-                    //mGridView.smoothScrollToPosition(index);
-                }
-            }
-
-        }
-    }
-    
-    /*
-     * Save index and top position
-     */
-    protected void saveIndexAndTopPosition(int index) {
-        
-        mIndexes.add(index);
-        
-        int firstPosition = mCurrentListView.getFirstVisiblePosition();
-        mFirstPositions.add(firstPosition);
-        
-        View view = mCurrentListView.getChildAt(0);
-        int top = (view == null) ? 0 : view.getTop() ;
-
-        mTops.add(top);
-        
-        // Save the height of a cell
-        mHeightCell = (view == null || mHeightCell != 0) ? mHeightCell : view.getHeight();
-    }
-    
-    
-    @Override
-    public void onItemClick (AdapterView<?> parent, View view, int position, long id) {
-        // to be @overriden
-    }
-
-    @Override
-    public void onRefresh() {
-        mRefreshListLayout.setRefreshing(false);
-        mRefreshGridLayout.setRefreshing(false);
-        mRefreshEmptyLayout.setRefreshing(false);
-
-        if (mOnRefreshListener != null) {
-            mOnRefreshListener.onRefresh();
-        }
-    }
-    public void setOnRefreshListener(OnEnforceableRefreshListener listener) {
-        mOnRefreshListener = listener;
-    }
-    
-
-    /**
-     * Disables swipe gesture.
-     *
-     * Sets the 'enabled' state of the refresh layouts contained in the fragment.
-     *
-     * When 'false' is set, prevents user gestures but keeps the option to refresh programatically,
-     *
-     * @param   enabled     Desired state for capturing swipe gesture.
-     */
-    public void setSwipeEnabled(boolean enabled) {
-        mRefreshListLayout.setEnabled(enabled);
-        mRefreshGridLayout.setEnabled(enabled);
-        mRefreshEmptyLayout.setEnabled(enabled);
-    }
-
-    /**
-     * Sets the 'visibility' state of the FAB contained in the fragment.
-     *
-     * When 'false' is set, FAB visibility is set to View.GONE programmatically,
-     *
-     * @param   enabled     Desired visibility for the FAB.
-     */
-    public void setFabEnabled(boolean enabled) {
-        if(enabled) {
-            mFabMain.setVisibility(View.VISIBLE);
-        } else {
-            mFabMain.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Set message for empty list view.
-     */
-    public void setMessageForEmptyList(String message) {
-        if (mEmptyListContainer != null && mEmptyListMessage != null) {
-            mEmptyListMessage.setText(message);
-        }
-    }
-
-    /**
-     * displays an empty list information with a headline, a message and an icon.
-     *
-     * @param headline the headline
-     * @param message  the message
-     * @param icon     the icon to be shown
-     */
-    public void setMessageForEmptyList(@StringRes int headline, @StringRes int message, @DrawableRes int icon) {
-        if (mEmptyListContainer != null && mEmptyListMessage != null) {
-            mEmptyListHeadline.setText(headline);
-            mEmptyListMessage.setText(message);
-            mEmptyListIcon.setImageResource(icon);
-
-            mEmptyListIcon.setVisibility(View.VISIBLE);
-            mEmptyListProgress.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Set message for empty list view.
-     */
-    public void setEmptyListMessage() {
-        setMessageForEmptyList(
-                R.string.file_list_empty_headline,
-                R.string.file_list_empty,
-                R.drawable.ic_list_empty_folder
-        );
-    }
-
-    /**
-     * Set message for empty list view.
-     */
-    public void setEmptyListLoadingMessage() {
-        if (mEmptyListContainer != null && mEmptyListMessage != null) {
-            mEmptyListHeadline.setText(R.string.file_list_loading);
-            mEmptyListMessage.setText("");
-
-            mEmptyListIcon.setVisibility(View.GONE);
-            mEmptyListProgress.setVisibility(View.VISIBLE);
-        }
-    }
-
-    /**
-     * Get the text of EmptyListMessage TextView.
-     * 
-     * @return String empty text view text-value
-     */
-    public String getEmptyViewText() {
-        return (mEmptyListContainer != null && mEmptyListMessage != null) ? mEmptyListMessage.getText().toString() : "";
-    }
-
-    protected void onCreateSwipeToRefresh(SwipeRefreshLayout refreshLayout) {
-        // Colors in animations
-        refreshLayout.setColorSchemeResources(R.color.color_accent, R.color.primary, R.color.primary_dark);
-
-        refreshLayout.setOnRefreshListener(this);
-    }
-
-    @Override
-    public void onRefresh(boolean ignoreETag) {
-        mRefreshListLayout.setRefreshing(false);
-        mRefreshGridLayout.setRefreshing(false);
-        mRefreshEmptyLayout.setRefreshing(false);
-
-        if (mOnRefreshListener != null) {
-            mOnRefreshListener.onRefresh();
-        }
-    }
-
-    protected void setChoiceMode(int choiceMode) {
-        mListView.setChoiceMode(choiceMode);
-        mGridView.setChoiceMode(choiceMode);
-    }
-
-    protected void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener listener) {
-        mListView.setMultiChoiceModeListener(listener);
-        mGridView.setMultiChoiceModeListener(listener);
-    }
-
-    /**
-     * TODO doc
-     * To be called before setAdapter, or GridViewWithHeaderAndFooter will throw an exception
-     *
-     * @param enabled flag if footer should be shown/calculated
-     */
-    protected void setFooterEnabled(boolean enabled) {
-        if (enabled) {
-            if (mGridView.getFooterViewCount() == 0 && mGridView.isCorrectAdapter()) {
-                if (mGridFooterView.getParent() != null ) {
-                    ((ViewGroup) mGridFooterView.getParent()).removeView(mGridFooterView);
-                }
-                mGridView.addFooterView(mGridFooterView, null, false);
-            }
-            mGridFooterView.invalidate();
-
-            if (mListView.getFooterViewsCount() == 0) {
-                if (mListFooterView.getParent() != null ) {
-                    ((ViewGroup) mListFooterView.getParent()).removeView(mListFooterView);
-                }
-                mListView.addFooterView(mListFooterView, null, false);
-            }
-            mListFooterView.invalidate();
-
-        } else {
-            mGridView.removeFooterView(mGridFooterView);
-            mListView.removeFooterView(mListFooterView);
-        }
-    }
-
-    /**
-     * set the list/grid footer text.
-     *
-     * @param text the footer text
-     */
-    protected void setFooterText(String text) {
-        if (text != null && text.length() > 0) {
-            ((TextView)mListFooterView.findViewById(R.id.footerText)).setText(text);
-            ((TextView)mGridFooterView.findViewById(R.id.footerText)).setText(text);
-            setFooterEnabled(true);
-
-        } else {
-            setFooterEnabled(false);
-        }
-    }
-
-}

+ 0 - 590
src/com/owncloud/android/ui/preview/PreviewImageFragment.java

@@ -1,590 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   @author David A. Velasco
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.ui.preview;
-
-import android.accounts.Account;
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.owncloud.android.R;
-import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.FileMenuFilter;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
-import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment;
-import com.owncloud.android.ui.fragment.FileFragment;
-import com.owncloud.android.utils.BitmapUtils;
-import com.owncloud.android.utils.DisplayUtils;
-import com.owncloud.android.utils.MimeTypeUtil;
-
-import java.lang.ref.WeakReference;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import third_parties.michaelOrtiz.TouchImageViewCustom;
-
-
-/**
- * This fragment shows a preview of a downloaded image.
- *
- * Trying to get an instance with a NULL {@link OCFile} will produce an
- * {@link IllegalStateException}.
- * 
- * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on
- * instantiation too.
- */
-public class PreviewImageFragment extends FileFragment {
-
-    public static final String EXTRA_FILE = "FILE";
-
-    private static final String ARG_FILE = "FILE";
-    private static final String ARG_IGNORE_FIRST = "IGNORE_FIRST";
-
-    private TouchImageViewCustom mImageView;
-    private TextView mMessageView;
-    private ProgressBar mProgressWheel;
-
-    public Bitmap mBitmap = null;
-
-    private static final String TAG = PreviewImageFragment.class.getSimpleName();
-
-    private boolean mIgnoreFirstSavedState;
-
-    private LoadBitmapTask mLoadBitmapTask = null;
-
-
-    /**
-     * Public factory method to create a new fragment that previews an image.
-     *
-     * Android strongly recommends keep the empty constructor of fragments as the only public
-     * constructor, and
-     * use {@link #setArguments(Bundle)} to set the needed arguments.
-     *
-     * 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
-     */
-    public static PreviewImageFragment newInstance(OCFile imageFile, boolean ignoreFirstSavedState){
-        PreviewImageFragment frag = new PreviewImageFragment();
-        Bundle args = new Bundle();
-        args.putParcelable(ARG_FILE, imageFile);
-        args.putBoolean(ARG_IGNORE_FIRST, ignoreFirstSavedState);
-        frag.setArguments(args);
-        return frag;
-    }
-
-
-    
-    /**
-     *  Creates an empty fragment for image previews.
-     * 
-     *  MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically
-     *  (for instance, when the device is turned a aside).
-     * 
-     *  DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful
-     *  construction
-     */
-    public PreviewImageFragment() {
-        mIgnoreFirstSavedState = false;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        Bundle args = getArguments();
-        setFile((OCFile)args.getParcelable(ARG_FILE));
-            // TODO better in super, but needs to check ALL the class extending FileFragment;
-            // not right now
-
-        mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST);
-        setHasOptionsMenu(true);
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-                             Bundle savedInstanceState) {
-        super.onCreateView(inflater, container, savedInstanceState);
-        View view = inflater.inflate(R.layout.preview_image_fragment, container, false);
-        mImageView = (TouchImageViewCustom) view.findViewById(R.id.image);
-        mImageView.setVisibility(View.GONE);
-        mImageView.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                ((PreviewImageActivity) getActivity()).toggleFullScreen();
-            }
-
-        });
-        mMessageView = (TextView)view.findViewById(R.id.message);
-        mMessageView.setVisibility(View.GONE);
-        mProgressWheel = (ProgressBar)view.findViewById(R.id.progressWheel);
-        mProgressWheel.setVisibility(View.VISIBLE);
-        return view;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
-        if (savedInstanceState != null) {
-            if (!mIgnoreFirstSavedState) {
-                OCFile file = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE);
-                setFile(file);
-            } else {
-                mIgnoreFirstSavedState = false;
-            }
-        }
-        if (getFile() == null) {
-            throw new IllegalStateException("Instanced with a NULL OCFile");
-        }
-        if (!getFile().isDown()) {
-            throw new IllegalStateException("There is no local file to preview");
-        }
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onSaveInstanceState(Bundle outState) {
-        super.onSaveInstanceState(outState);
-        outState.putParcelable(PreviewImageFragment.EXTRA_FILE, getFile());
-    }
-
-
-    @Override
-    public void onStart() {
-        super.onStart();
-        if (getFile() != null) {
-            mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel);
-            //mLoadBitmapTask.execute(new String[]{getFile().getStoragePath()});
-//            mLoadBitmapTask.execute(getFile().getStoragePath());
-            mLoadBitmapTask.execute(getFile());
-        }
-    }
-
-
-    @Override
-    public void onStop() {
-        Log_OC.d(TAG, "onStop starts");
-        if (mLoadBitmapTask != null) {
-            mLoadBitmapTask.cancel(true);
-            mLoadBitmapTask = null;
-        }
-        super.onStop();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        super.onCreateOptionsMenu(menu, inflater);
-        inflater.inflate(R.menu.file_actions_menu, menu);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        super.onPrepareOptionsMenu(menu);
-
-        if (mContainerActivity.getStorageManager() != null && getFile() != null) {
-            // Update the file
-            setFile(mContainerActivity.getStorageManager().getFileById(getFile().getFileId()));
-
-            FileMenuFilter mf = new FileMenuFilter(
-                getFile(),
-                mContainerActivity.getStorageManager().getAccount(),
-                mContainerActivity,
-                getActivity()
-            );
-            mf.filter(menu);
-        }
-
-        // additional restriction for this fragment 
-        // TODO allow renaming in PreviewImageFragment
-        MenuItem item = menu.findItem(R.id.action_rename_file);
-        if (item != null) {
-            item.setVisible(false);
-            item.setEnabled(false);
-        }
-
-        // additional restriction for this fragment 
-        // TODO allow refresh file in PreviewImageFragment
-        item = menu.findItem(R.id.action_sync_file);
-        if (item != null) {
-            item.setVisible(false);
-            item.setEnabled(false);
-        }
-
-        // additional restriction for this fragment
-        item = menu.findItem(R.id.action_move);
-        if (item != null) {
-            item.setVisible(false);
-            item.setEnabled(false);
-        }
-
-        // additional restriction for this fragment
-        item = menu.findItem(R.id.action_copy);
-        if (item != null) {
-            item.setVisible(false);
-            item.setEnabled(false);
-        }
-
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.action_share_file: {
-                mContainerActivity.getFileOperationsHelper().showShareFile(getFile());
-                return true;
-            }
-            case R.id.action_open_file_with: {
-                openFile();
-                return true;
-            }
-            case R.id.action_remove_file: {
-                RemoveFilesDialogFragment dialog = RemoveFilesDialogFragment.newInstance(getFile());
-                dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION);
-                return true;
-            }
-            case R.id.action_see_details: {
-                seeDetails();
-                return true;
-            }
-            case R.id.action_send_file: {
-                mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile());
-                return true;
-            }
-            case R.id.action_sync_file: {
-                mContainerActivity.getFileOperationsHelper().syncFile(getFile());
-                return true;
-            }
-            case R.id.action_favorite_file:{
-                mContainerActivity.getFileOperationsHelper().toggleFavorite(getFile(), true);
-                return true;
-            }
-            case R.id.action_unfavorite_file:{
-                mContainerActivity.getFileOperationsHelper().toggleFavorite(getFile(), false);
-                return true;
-            }
-            default:
-                return super.onOptionsItemSelected(item);
-        }
-    }
-
-
-    private void seeDetails() {
-        mContainerActivity.showDetails(getFile());
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-    }
-
-
-    @Override
-    public void onPause() {
-        super.onPause();
-    }
-
-    @SuppressFBWarnings("Dm")
-    @Override
-    public void onDestroy() {
-        if (mBitmap != null) {
-            mBitmap.recycle();
-            System.gc();
-                // putting this in onStop() is just the same; the fragment is always destroyed by
-                // {@link FragmentStatePagerAdapter} when the fragment in swiped further than the
-                // valid offscreen distance, and onStop() is never called before than that
-        }
-        super.onDestroy();
-    }
-
-
-    /**
-     * Opens the previewed image with an external application.
-     */
-    private void openFile() {
-        mContainerActivity.getFileOperationsHelper().openFile(getFile());
-        finish();
-    }
-
-    
-    private class LoadBitmapTask extends AsyncTask<OCFile, Void, LoadImage> {
-
-        /**
-         * Weak reference to the target {@link ImageView} where the bitmap will be loaded into.
-         *
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
-         * memory before the load finishes.
-         */
-        private final WeakReference<ImageViewCustom> mImageViewRef;
-
-        /**
-         * Weak reference to the target {@link TextView} where error messages will be written.
-         *
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
-         * memory before the load finishes.
-         */
-        private final WeakReference<TextView> mMessageViewRef;
-
-
-        /**
-         * Weak reference to the target {@link ProgressBar} shown while the load is in progress.
-         * 
-         * Using a weak reference will avoid memory leaks if the target ImageView is retired from
-         * memory before the load finishes.
-         */
-        private final WeakReference<ProgressBar> mProgressWheelRef;
-
-
-        /**
-         * Error message to show when a load fails
-         */
-        private int mErrorMessageId;
-
-
-        /**
-         * Constructor.
-         *
-         * @param imageView Target {@link ImageView} where the bitmap will be loaded into.
-         */
-        public LoadBitmapTask(ImageViewCustom imageView, TextView messageView,
-                              ProgressBar progressWheel) {
-            mImageViewRef = new WeakReference<ImageViewCustom>(imageView);
-            mMessageViewRef = new WeakReference<TextView>(messageView);
-            mProgressWheelRef = new WeakReference<ProgressBar>(progressWheel);
-        }
-
-        @Override
-        protected LoadImage doInBackground(OCFile... params) {
-            Bitmap result = null;
-            if (params.length != 1) {
-                return null;
-            }
-            OCFile ocFile = params[0];
-            String storagePath = ocFile.getStoragePath();
-            try {
-
-                int maxDownScale = 3;   // could be a parameter passed to doInBackground(...)
-                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);
-
-                        if (isCancelled()) {
-                            return new LoadImage(result, ocFile);
-                        }
-
-                        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 {
-                            // Rotate image, obeying exif tag.
-                            result = BitmapUtils.rotateImage(result, 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 (result != null) {
-                            result.recycle();
-                        }
-                        result = null;
-                    }
-                }
-
-            } catch (NoSuchFieldError e) {
-                mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Error from access to unexisting field despite protection; file "
-                        + storagePath, e);
-
-            } catch (Throwable t) {
-                mErrorMessageId = R.string.common_error_unknown;
-                Log_OC.e(TAG, "Unexpected error loading " + getFile().getStoragePath(), t);
-
-            }
-
-            return new LoadImage(result, ocFile);
-        }
-
-        @Override
-        protected void onCancelled(LoadImage result) {
-            if (result != null && result.bitmap != null) {
-                result.bitmap.recycle();
-            }
-        }
-
-        @Override
-        protected void onPostExecute(LoadImage result) {
-            hideProgressWheel();
-            if (result.bitmap != null) {
-                showLoadedImage(result);
-            }
-            else {
-                showErrorMessage();
-            }
-            if (result.bitmap != null && mBitmap != result.bitmap)  {
-                // unused bitmap, release it! (just in case)
-                result.bitmap.recycle();
-            }
-        }
-
-        @SuppressLint("InlinedApi")
-        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")) {
-                    Drawable backrepeat = getResources().getDrawable(R.drawable.backrepeat);
-                    imageView.setBackground(backrepeat);
-                }
-
-                if (result.ocFile.getMimetype().equalsIgnoreCase("image/gif")) {
-                    imageView.setGIFImageFromStoragePath(result.ocFile.getStoragePath());
-                } else {
-                    imageView.setImageBitmap(bitmap);
-                }
-
-                imageView.setVisibility(View.VISIBLE);
-                mBitmap  = bitmap;  // needs to be kept for recycling when not useful
-            }
-
-            final TextView messageView = mMessageViewRef.get();
-            if (messageView != null) {
-                messageView.setVisibility(View.GONE);
-            } // else , silently finish, the fragment was destroyed
-        }
-
-        private void showErrorMessage() {
-            final ImageView imageView = mImageViewRef.get();
-            if (imageView != null) {
-                // shows the default error icon
-                imageView.setVisibility(View.VISIBLE);
-            } // else , silently finish, the fragment was destroyed
-
-            final TextView messageView = mMessageViewRef.get();
-            if (messageView != null) {
-                messageView.setText(mErrorMessageId);
-                messageView.setVisibility(View.VISIBLE);
-            } // else , silently finish, the fragment was destroyed
-        }
-
-        private void hideProgressWheel() {
-            final ProgressBar progressWheel = mProgressWheelRef.get();
-            if (progressWheel != null) {
-                progressWheel.setVisibility(View.GONE);
-            }
-        }
-
-    }
-
-    /**
-     * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment}
-     * to be previewed.
-     * 
-     * @param file      File to test if can be previewed.
-     * @return          'True' if the file can be handled by the fragment.
-     */
-    public static boolean canBePreviewed(OCFile file) {
-        return (file != null && MimeTypeUtil.isImage(file));
-    }
-
-
-    /**
-     * Finishes the preview
-     */
-    private void finish() {
-        Activity container = getActivity();
-        container.finish();
-    }
-
-    public TouchImageViewCustom getImageView() {
-        return mImageView;
-    }
-
-    private class LoadImage {
-        private Bitmap bitmap;
-        private OCFile ocFile;
-
-        public LoadImage(Bitmap bitmap, OCFile ocFile){
-            this.bitmap = bitmap;
-            this.ocFile = ocFile;
-        }
-
-    }
-
-}

+ 0 - 26
src/com/owncloud/android/utils/MimeType.java

@@ -1,26 +0,0 @@
-/**
- *   Nextcloud Android client application
- *
- *   Copyright (C) 2016 Nextcloud
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2+,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.utils;
-
-/**
- * Class containing the mime types.
- */
-public class MimeType {
-    public static final String DIRECTORY = "DIR";
-}

+ 0 - 56
src/com/owncloud/android/utils/OwnCloudSession.java

@@ -1,56 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   @author Bartek Przybylski
- *   Copyright (C) 2011  Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-package com.owncloud.android.utils;
-
-/**
- * Represents a session to an ownCloud instance
- */
-public class OwnCloudSession {
-    private String mSessionName;
-    private String mSessionUrl;
-    private int mEntryId;
-
-    public OwnCloudSession(String name, String url, int entryId) {
-        mSessionName = name;
-        mSessionUrl = url;
-        mEntryId = entryId;
-    }
-
-    public void setName(String name) {
-        mSessionName = name;
-    }
-
-    public String getName() {
-        return mSessionName;
-    }
-
-    public void setUrl(String url) {
-        mSessionUrl = url;
-    }
-
-    public String getUrl() {
-        return mSessionUrl;
-    }
-
-    public int getEntryId() {
-        return mEntryId;
-    }
-}

+ 0 - 109
src/com/owncloud/android/utils/RecursiveFileObserver.java

@@ -1,109 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   Copyright (C) 2012 Bartek Przybylski
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.utils;
-
-import android.os.FileObserver;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Stack;
-
-public class RecursiveFileObserver extends FileObserver {
-
-    private final List<SingleFileObserver> mObservers =  new ArrayList<>();
-    private boolean watching = false;
-    private String mPath;
-    private int mMask;
-    
-    public RecursiveFileObserver(String path) {
-        this(path, ALL_EVENTS);
-    }
-    
-    public RecursiveFileObserver(String path, int mask) {
-        super(path, mask);
-        mPath = path;
-        mMask = mask;
-    }
-
-    @Override
-    public void startWatching() {
-        if (watching) {
-            return;
-        }
-        watching = true;
-        final Stack<String> stack = new Stack<String>();
-        stack.push(mPath);
-        
-        while (!stack.empty()) {
-            String parent = stack.pop();
-            mObservers.add(new SingleFileObserver(parent, mMask));
-            File path = new File(parent);
-            File[] files = path.listFiles();
-            if (files == null) {
-                continue;
-            }
-            for (final File file : files) {
-                if (file.isDirectory() && !file.getName().equals(".")
-                        && !file.getName().equals("..")) {
-                    stack.push(file.getPath());
-                }
-            }
-        }
-        for (int i = 0; i < mObservers.size(); i++) {
-            mObservers.get(i).startWatching();
-        }
-    }
-    
-    @Override
-    public void stopWatching() {
-        if (!watching) {
-            return;
-        }
-
-        for (int i = 0; i < mObservers.size(); ++i) {
-            mObservers.get(i).stopWatching();
-        }
-        mObservers.clear();
-        watching = false;
-    }
-    
-    @Override
-    public void onEvent(int event, String path) {
-        
-    }
-    
-    private class SingleFileObserver extends FileObserver {
-        private String mPath;
-
-        SingleFileObserver(String path, int mask) {
-            super(path, mask);
-            mPath = path;
-        }
-        
-        @Override
-        public void onEvent(int event, String path) {
-            String newPath = mPath + "/" + path;
-            RecursiveFileObserver.this.onEvent(event, newPath);
-        } 
-        
-    }
-}

+ 35 - 0
src/generic/google-services.json

@@ -0,0 +1,35 @@
+{
+  "project_info": {
+    "project_number": "",
+    "project_id": ""
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "",
+        "android_client_info": {
+          "package_name": "com.nextcloud.client"
+        }
+      },
+      "oauth_client": [],
+      "api_key": [
+        {
+          "current_key": ""
+        }
+      ],
+      "services": {
+        "analytics_service": {
+          "status": 1
+        },
+        "appinvite_service": {
+          "status": 1,
+          "other_platform_oauth_client": []
+        },
+        "ads_service": {
+          "status": 1
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 33 - 0
src/generic/java/com/owncloud/android/utils/AnalyticsUtils.java

@@ -0,0 +1,33 @@
+/**
+ * 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;
+
+public class AnalyticsUtils {
+    public static void setCurrentScreenName(Activity activity, String s, String s1) {
+        // do nothing
+    }
+
+    public static void disableAnalytics() {
+        // do nothing
+    }
+
+}

+ 29 - 0
src/generic/java/com/owncloud/android/utils/PushUtils.java

@@ -0,0 +1,29 @@
+/**
+ * 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;
+
+public class PushUtils {
+    public static final String KEY_PUSH = "push";
+
+    public static void pushRegistrationToServer() {
+        // do nothing
+    }
+}

+ 84 - 0
src/gplay/AndroidManifest.xml

@@ -0,0 +1,84 @@
+<?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">
+
+        <meta-data android:name="com.google.android.gms.version"
+                   android:value="@integer/google_play_services_version" />
+
+        <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-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>

+ 35 - 0
src/gplay/google-services.json

@@ -0,0 +1,35 @@
+{
+  "project_info": {
+    "project_number": "",
+    "project_id": ""
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "",
+        "android_client_info": {
+          "package_name": "com.nextcloud.client"
+        }
+      },
+      "oauth_client": [],
+      "api_key": [
+        {
+          "current_key": ""
+        }
+      ],
+      "services": {
+        "analytics_service": {
+          "status": 1
+        },
+        "appinvite_service": {
+          "status": 1,
+          "other_platform_oauth_client": []
+        },
+        "ads_service": {
+          "status": 1
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 41 - 0
src/gplay/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);
+    }
+
+}

+ 43 - 0
src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java

@@ -0,0 +1,43 @@
+/**
+ * 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());
+            PushUtils.pushRegistrationToServer();
+        }
+    }
+}
+

+ 66 - 0
src/gplay/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());
+    }
+
+}

+ 35 - 0
src/gplay/java/com/owncloud/android/utils/AnalyticsUtils.java

@@ -0,0 +1,35 @@
+/**
+ * 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 com.google.firebase.analytics.FirebaseAnalytics;
+import com.owncloud.android.MainApp;
+
+public class AnalyticsUtils {
+    public static void setCurrentScreenName(Activity activity, String s, String s1) {
+        // do nothing
+    }
+
+    public static void disableAnalytics() {
+        FirebaseAnalytics.getInstance(MainApp.getAppContext()).setAnalyticsCollectionEnabled(false);
+    }
+}

+ 49 - 0
src/gplay/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;
+    }
+
+}

+ 332 - 0
src/gplay/java/com/owncloud/android/utils/PushUtils.java

@@ -0,0 +1,332 @@
+/**
+ * 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.google.gson.Gson;
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.ArbitraryDataProvider;
+import com.owncloud.android.datamodel.PushConfigurationState;
+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.UnregisterAccountDeviceForNotificationsOperation;
+import com.owncloud.android.lib.resources.notifications.UnregisterAccountDeviceForProxyOperation;
+import com.owncloud.android.lib.resources.notifications.models.PushResponse;
+
+import org.apache.commons.httpclient.HttpStatus;
+
+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 final String KEY_PUSH = "push";
+
+    private static ArbitraryDataProvider arbitraryDataProvider;
+
+    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 "";
+    }
+
+    public 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();
+    }
+    
+    private 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;
+    }
+
+    private static void deleteRegistrationForAccount(Account account) {
+        Context context = MainApp.getAppContext();
+        OwnCloudAccount ocAccount = null;
+        arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
+
+        try {
+            ocAccount = new OwnCloudAccount(account, context);
+            OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                    getClientFor(ocAccount, context);
+
+            RemoteOperation unregisterAccountDeviceForNotificationsOperation = new
+                    UnregisterAccountDeviceForNotificationsOperation();
+
+            RemoteOperationResult remoteOperationResult = unregisterAccountDeviceForNotificationsOperation.
+                    execute(mClient);
+
+            if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) {
+                String arbitraryValue;
+                if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) {
+                    Gson gson = new Gson();
+                    PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue,
+                            PushConfigurationState.class);
+                    RemoteOperation unregisterAccountDeviceForProxyOperation =
+                            new UnregisterAccountDeviceForProxyOperation(context.getResources().
+                                    getString(R.string.push_server_url),
+                                    pushArbitraryData.getDeviceIdentifier(),
+                                    pushArbitraryData.getDeviceIdentifierSignature(),
+                                    pushArbitraryData.getUserPublicKey());
+
+                    remoteOperationResult = unregisterAccountDeviceForProxyOperation.execute(mClient);
+
+                    if (remoteOperationResult.isSuccess()) {
+                        arbitraryDataProvider.deleteKeyForAccount(account, KEY_PUSH);
+                    }
+                }
+            }
+
+
+        } 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 void pushRegistrationToServer() {
+        String token = PreferenceManager.getPushToken(MainApp.getAppContext());
+        arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
+
+        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();
+                String providerValue;
+                PushConfigurationState accountPushData = null;
+                Gson gson = new Gson();
+                for (Account account : AccountUtils.getAccounts(context)) {
+                    providerValue = arbitraryDataProvider.getValue(account, KEY_PUSH);
+                    if (!TextUtils.isEmpty(providerValue)) {
+                        accountPushData = gson.fromJson(providerValue,
+                                PushConfigurationState.class);
+                    } else {
+                        accountPushData = null;
+                    }
+
+                    if (accountPushData != null && !accountPushData.getPushToken().equals(token) &&
+                            !accountPushData.isShouldBeDeleted() ||
+                            TextUtils.isEmpty(providerValue)) {
+                        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);
+
+                                if (remoteOperationResult.isSuccess()) {
+                                    PushConfigurationState pushArbitraryData = new PushConfigurationState(token,
+                                            pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
+                                            pushResponse.getPublicKey(), false);
+                                    arbitraryDataProvider.storeOrUpdateKeyValue(account, KEY_PUSH,
+                                            gson.toJson(pushArbitraryData));
+                                }
+                            }
+                        } 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");
+                        }
+                    } else if (accountPushData != null && accountPushData.isShouldBeDeleted()) {
+                        deleteRegistrationForAccount(account);
+                    }
+                }
+            }
+        }
+    }
+
+    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;
+    }
+}

+ 7 - 0
src/gplay/res/values/setup.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Push server url -->
+    <string name="push_server_url" translatable="false">https://push-notifications.nextcloud.com</string>
+</resources>
+
+

+ 26 - 9
AndroidManifest.xml → src/main/AndroidManifest.xml

@@ -19,13 +19,14 @@
   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="10040201"
-    android:versionName="1.4.2 RC1">
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.owncloud.android"
+          android:versionCode="10040299"
+          android:versionName="1.4.2">
 
     <uses-sdk
         android:minSdkVersion="14"
-        android:targetSdkVersion="24" />
+        android:targetSdkVersion="25" />
 
     <!-- GET_ACCOUNTS is needed for API < 23.
         For API >= 23 results in the addition of CONTACTS group to the list of permissions that may be
@@ -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" />
@@ -55,12 +59,14 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 
 
     <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
@@ -74,9 +80,16 @@
             </intent-filter>
         </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" />
+        <activity android:name=".ui.activity.ContactsPreferenceActivity"
+            android:launchMode="singleInstance"/>
         <activity android:name=".ui.activity.ReceiveExternalFilesActivity"
                   
                   android:taskAffinity=""
@@ -128,10 +141,6 @@
                 android:name="android.content.SyncAdapter"
                 android:resource="@xml/syncadapter_files" />
         </service>
-        <service
-            android:name=".services.SyncedFolderJobService"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:exported="true"/>
 
         <provider
             android:name=".providers.FileContentProvider"
@@ -143,7 +152,7 @@
 
         <provider
             android:name=".providers.UsersAndGroupsSearchProvider"
-            android:authorities="com.nextcloud.android.providers.UsersAndGroupsSearchProvider"
+            android:authorities="@string/users_and_groups_search_authority"
             android:enabled="true"
             android:exported="false"
             android:label="@string/search_users_and_groups_hint" />
@@ -203,6 +212,7 @@
         <service android:name=".media.MediaService" />
 
         <activity android:name=".ui.activity.PassCodeActivity" />
+        <activity android:name=".ui.activity.FingerprintActivity"/>
         <activity android:name=".ui.activity.ConflictsResolveActivity"/>
         <activity android:name=".ui.activity.GenericExplanationActivity"/>
         <activity android:name=".ui.activity.ErrorsWhileCopyingHandlerActivity"/>
@@ -244,6 +254,13 @@
                 <action android:name="android.intent.action.BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
+        <receiver android:name=".services.ShutdownReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_SHUTDOWN" />
+                <action android:name="android.intent.action.QUICKBOOT_POWEROFF" />
+            </intent-filter>
+        </receiver>
+
 
         <service android:name=".services.observer.FileObserverService" />
 

+ 0 - 0
src/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java → src/main/java/com/afollestad/sectionedrecyclerview/SectionedRecyclerViewAdapter.java


+ 109 - 50
src/com/owncloud/android/MainApp.java → src/main/java/com/owncloud/android/MainApp.java

@@ -1,27 +1,25 @@
 /**
- *   ownCloud Android client application
- *
- *   @author masensio
- *   @author David A. Velasco
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   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/>.
+ * ownCloud Android client application
  *
+ * @author masensio
+ * @author David A. Velasco
+ * Copyright (C) 2015 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;
 
 import android.app.Activity;
-import android.app.Application;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,29 +30,39 @@ import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
-import android.preference.PreferenceManager;
+import android.support.multidex.MultiDexApplication;
+import android.support.v4.util.Pair;
 
+import com.evernote.android.job.JobManager;
 import com.owncloud.android.authentication.PassCodeManager;
+import com.owncloud.android.datamodel.SyncedFolder;
+import com.owncloud.android.datamodel.SyncedFolderProvider;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory.Policy;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.NCJobCreator;
 import com.owncloud.android.services.observer.SyncedFolderObserverService;
 import com.owncloud.android.ui.activity.Preferences;
 import com.owncloud.android.ui.activity.WhatsNewActivity;
+import com.owncloud.android.utils.AnalyticsUtils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 
 /**
  * Main Application of the project
- * 
+ *
  * Contains methods to build the "static" strings. These strings were before constants in different
  * classes
  */
-public class MainApp extends Application {
+public class MainApp extends MultiDexApplication {
 
     private static final String TAG = MainApp.class.getSimpleName();
 
@@ -76,14 +84,20 @@ public class MainApp extends Application {
     @SuppressWarnings("unused")
     private boolean mBound;
 
-    @SuppressFBWarnings("ST")    public void onCreate(){
+    @SuppressFBWarnings("ST")
+    public void onCreate() {
         super.onCreate();
+        JobManager.create(this).addJobCreator(new NCJobCreator());
         MainApp.mContext = getApplicationContext();
 
+        if (!getResources().getBoolean(R.bool.analytics_enabled)) {
+            AnalyticsUtils.disableAnalytics();
+        }
+
         SharedPreferences appPrefs =
                 PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
         MainApp.storagePath = appPrefs.getString(Preferences.PreferenceKeys.STORAGE_PATH, Environment.
-                              getExternalStorageDirectory().getAbsolutePath());
+                getExternalStorageDirectory().getAbsolutePath());
 
         boolean isSamlAuth = AUTH_ON.equals(getString(R.string.auth_method_saml_web_sso));
 
@@ -96,42 +110,41 @@ public class MainApp extends Application {
 
         // initialise thumbnails cache on background thread
         new ThumbnailsCacheManager.InitDiskCacheTask().execute();
-        
-        if (BuildConfig.DEBUG) {
-
-            String dataFolder = getDataFolder();
 
-            // Set folder for store logs
-            Log_OC.setLogDataFolder(dataFolder);
-
-            Log_OC.startLogging(MainApp.storagePath);
+        if (BuildConfig.DEBUG) {
+            // use app writable dir, no permissions needed
+            Log_OC.startLogging(getAppContext());
             Log_OC.d("Debug", "start logging");
         }
 
+        cleanOldEntries();
+        updateAutoUploadEntries();
+
         Log_OC.d("SyncedFolderObserverService", "Start service SyncedFolderObserverService");
         Intent i = new Intent(this, SyncedFolderObserverService.class);
         startService(i);
         bindService(i, syncedFolderObserverServiceConnection, Context.BIND_AUTO_CREATE);
 
+
         // register global protection with pass code
-        registerActivityLifecycleCallbacks( new ActivityLifecycleCallbacks() {
+        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
 
             @Override
             public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
-                Log_OC.d(activity.getClass().getSimpleName(),  "onCreate(Bundle) starting" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onCreate(Bundle) starting");
                 WhatsNewActivity.runIfNeeded(activity);
                 PassCodeManager.getPassCodeManager().onActivityCreated(activity);
             }
 
             @Override
             public void onActivityStarted(Activity activity) {
-                Log_OC.d(activity.getClass().getSimpleName(),  "onStart() starting" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onStart() starting");
                 PassCodeManager.getPassCodeManager().onActivityStarted(activity);
             }
 
             @Override
             public void onActivityResumed(Activity activity) {
-                Log_OC.d(activity.getClass().getSimpleName(), "onResume() starting" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onResume() starting");
             }
 
             @Override
@@ -141,18 +154,18 @@ public class MainApp extends Application {
 
             @Override
             public void onActivityStopped(Activity activity) {
-                Log_OC.d(activity.getClass().getSimpleName(), "onStop() ending" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onStop() ending");
                 PassCodeManager.getPassCodeManager().onActivityStopped(activity);
             }
 
             @Override
             public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
-                Log_OC.d(activity.getClass().getSimpleName(), "onSaveInstanceState(Bundle) starting" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onSaveInstanceState(Bundle) starting");
             }
 
             @Override
             public void onActivityDestroyed(Activity activity) {
-                Log_OC.d(activity.getClass().getSimpleName(), "onDestroy() ending" );
+                Log_OC.d(activity.getClass().getSimpleName(), "onDestroy() ending");
             }
         });
     }
@@ -161,11 +174,11 @@ public class MainApp extends Application {
         return MainApp.mContext;
     }
 
-    public static String getStoragePath(){
+    public static String getStoragePath() {
         return MainApp.storagePath;
     }
 
-    public static void setStoragePath(String path){
+    public static void setStoragePath(String path) {
         MainApp.storagePath = path;
     }
 
@@ -192,42 +205,42 @@ public class MainApp extends Application {
     public static String getAuthority() {
         return getAppContext().getResources().getString(R.string.authority);
     }
-    
+
     //  From AccountAuthenticator
     //  public static final String AUTH_TOKEN_TYPE = "org.owncloud";
     public static String getAuthTokenType() {
         return getAppContext().getResources().getString(R.string.authority);
     }
-    
+
     //  From ProviderMeta 
     //  public static final String DB_FILE = "owncloud.db";
     public static String getDBFile() {
         return getAppContext().getResources().getString(R.string.db_file);
     }
-    
+
     //  From ProviderMeta
     //  private final String mDatabaseName = "ownCloud";
     public static String getDBName() {
         return getAppContext().getResources().getString(R.string.db_name);
     }
-     
+
     /**
      * name of data_folder, e.g., "owncloud"
      */
     public static String getDataFolder() {
         return getAppContext().getResources().getString(R.string.data_folder);
     }
-    
+
     // log_name
     public static String getLogName() {
         return getAppContext().getResources().getString(R.string.log_name);
     }
 
-    public static void showOnlyFilesOnDevice(boolean state){
+    public static void showOnlyFilesOnDevice(boolean state) {
         mOnlyOnDevice = state;
     }
 
-    public static boolean isOnlyOnDevice(){
+    public static boolean isOnlyOnDevice() {
         return mOnlyOnDevice;
     }
 
@@ -257,12 +270,58 @@ public class MainApp extends Application {
         return userAgent;
     }
 
-    /** Defines callbacks for service binding, passed to bindService() */
+    private void updateAutoUploadEntries() {
+        // updates entries to reflect their true paths
+        if (!PreferenceManager.getAutoUploadPathsUpdate(this)) {
+            SyncedFolderProvider syncedFolderProvider =
+                    new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
+            syncedFolderProvider.updateAutoUploadPaths(mContext);
+        }
+    }
+
+    private void cleanOldEntries() {
+        // previous versions of application created broken entries in the SyncedFolderProvider
+        // database, and this cleans all that and leaves 1 (newest) entry per synced folder
+
+        if (!PreferenceManager.getLegacyClean(this)) {
+            SyncedFolderProvider syncedFolderProvider =
+                    new SyncedFolderProvider(MainApp.getAppContext().getContentResolver());
+
+            List<SyncedFolder> syncedFolderList = syncedFolderProvider.getSyncedFolders();
+            Map<Pair<String, String>, Long> syncedFolders = new HashMap<>();
+            ArrayList<Long> ids = new ArrayList<>();
+            for (SyncedFolder syncedFolder : syncedFolderList) {
+                Pair<String, String> checkPair = new Pair<>(syncedFolder.getAccount(), syncedFolder.getLocalPath());
+                if (syncedFolders.containsKey(checkPair)) {
+                    if (syncedFolder.getId() > syncedFolders.get(checkPair)) {
+                        syncedFolders.put(checkPair, syncedFolder.getId());
+                    }
+                } else {
+                    syncedFolders.put(checkPair, syncedFolder.getId());
+                }
+            }
+
+            for (Long idValue : syncedFolders.values()) {
+                ids.add(idValue);
+            }
+
+            if (ids.size() > 0) {
+                syncedFolderProvider.deleteSyncedFoldersNotInList(mContext, ids);
+            } else {
+                PreferenceManager.setLegacyClean(this, true);
+            }
+        }
+    }
+
+    /**
+     * Defines callbacks for service binding, passed to bindService()
+     */
     private ServiceConnection syncedFolderObserverServiceConnection = new ServiceConnection() {
 
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
-            SyncedFolderObserverService.SyncedFolderObserverBinder binder = (SyncedFolderObserverService.SyncedFolderObserverBinder) service;
+            SyncedFolderObserverService.SyncedFolderObserverBinder binder =
+                    (SyncedFolderObserverService.SyncedFolderObserverBinder) service;
             mObserverService = binder.getService();
             mBound = true;
         }

+ 13 - 8
src/com/owncloud/android/authentication/AccountAuthenticator.java → 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);

+ 0 - 0
src/com/owncloud/android/authentication/AccountAuthenticatorActivity.java → src/main/java/com/owncloud/android/authentication/AccountAuthenticatorActivity.java


+ 0 - 0
src/com/owncloud/android/authentication/AccountAuthenticatorService.java → src/main/java/com/owncloud/android/authentication/AccountAuthenticatorService.java


+ 11 - 11
src/com/owncloud/android/authentication/AccountUtils.java → src/main/java/com/owncloud/android/authentication/AccountUtils.java

@@ -96,7 +96,8 @@ public class AccountUtils {
             int lastAtPos = account.name.lastIndexOf("@");
             String hostAndPort = account.name.substring(lastAtPos + 1);
             String username = account.name.substring(0, lastAtPos);
-            String otherHostAndPort, otherUsername;
+            String otherHostAndPort;
+            String otherUsername;
             Locale currentLocale = context.getResources().getConfiguration().locale;
             for (Account otherAccount : ocAccounts) {
                 lastAtPos = otherAccount.name.lastIndexOf("@");
@@ -155,7 +156,7 @@ public class AccountUtils {
                             .getDefaultSharedPreferences(context).edit();
                     appPrefs.putString("select_oc_account", accountName);
     
-                    appPrefs.commit();
+                    appPrefs.apply();
                     result = true;
                     break;
                 }
@@ -182,6 +183,7 @@ public class AccountUtils {
             if (AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).equals(authTokenType)) {
                 return SAML_SSO_PATH;
             }
+
             return WEBDAV_PATH_4_0_AND_LATER;
         }
         return null;
@@ -322,14 +324,12 @@ public class AccountUtils {
     }
 
     public static boolean hasSearchUsersSupport(Account account){
-        OwnCloudVersion serverVersion = null;
-        if (account != null) {
-            AccountManager accountMgr = AccountManager.get(MainApp.getAppContext());
-            String serverVersionStr = accountMgr.getUserData(account, Constants.KEY_OC_VERSION);
-            if (serverVersionStr != null) {
-                serverVersion = new OwnCloudVersion(serverVersionStr);
-            }
-        }
-        return (serverVersion != null ? serverVersion.isSearchUsersSupported() : false);
+        OwnCloudVersion serverVersion = getServerVersion(account);
+        return (serverVersion != null && serverVersion.isSearchUsersSupported());
+    }
+
+    public static boolean hasSearchSupport(Account account) {
+        OwnCloudVersion serverVersion = getServerVersion(account);
+        return (serverVersion != null && serverVersion.isSearchSupported());
     }
 }

+ 346 - 172
src/com/owncloud/android/authentication/AuthenticatorActivity.java → src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -4,8 +4,10 @@
  * @author Bartek Przybylski
  * @author David A. Velasco
  * @author masensio
+ * @author Mario Danic
  * Copyright (C) 2012  Bartek Przybylski
  * Copyright (C) 2015 ownCloud Inc.
+ * 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,
@@ -18,6 +20,22 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * All changes by Mario Danic are distributed under the following terms:
+ *
+ * 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.authentication;
@@ -30,6 +48,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -38,12 +57,14 @@ import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.text.Editable;
 import android.text.InputType;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -54,6 +75,7 @@ import android.view.inputmethod.EditorInfo;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
@@ -85,11 +107,13 @@ import com.owncloud.android.operations.GetServerInfoOperation;
 import com.owncloud.android.operations.OAuth2GetAccessToken;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
+import com.owncloud.android.ui.components.CustomEditText;
 import com.owncloud.android.ui.dialog.CredentialsDialogFragment;
 import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
 import com.owncloud.android.ui.dialog.SamlWebViewDialog;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
+import com.owncloud.android.utils.AnalyticsUtils;
 import com.owncloud.android.utils.DisplayUtils;
 
 import java.security.cert.X509Certificate;
@@ -107,6 +131,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
     private static final String TAG = AuthenticatorActivity.class.getSimpleName();
 
+    private static final String SCREEN_NAME = "Login";
+
     public static final String EXTRA_ACTION = "ACTION";
     public static final String EXTRA_ACCOUNT = "ACCOUNT";
 
@@ -144,8 +170,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private static final String KEY_ASYNC_TASK_IN_PROGRESS = "AUTH_IN_PROGRESS";
     public static final String PROTOCOL_SUFFIX = "://";
     public static final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":";
-    private static final String HTTPS_PROTOCOL = "https://";
-    private static final String HTTP_PROTOCOL = "http://";
+    public static final String HTTPS_PROTOCOL = "https://";
+    public static final String HTTP_PROTOCOL = "http://";
+
+    public static final String REGULAR_SERVER_INPUT_TYPE = "regular";
+    public static final String SUBDOMAIN_SERVER_INPUT_TYPE = "prefix";
+    public static final String DIRECTORY_SERVER_INPUT_TYPE = "suffix";
 
     /// parameters from EXTRAs in starter Intent
     private byte mAction;
@@ -162,7 +192,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
     /// Server PRE-Fragment elements 
-    private EditText mHostUrlInput;
+    private CustomEditText mHostUrlInput;
     private View mRefreshButton;
     private TextView mServerStatusView;
 
@@ -184,6 +214,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private View mOkButton;
     private TextView mAuthStatusView;
 
+    private WebView mLoginWebView;
+
     private int mAuthStatusText = 0, mAuthStatusIcon = 0;
 
     private String mAuthToken = "";
@@ -198,6 +230,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private final String OAUTH_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
     private final String SAML_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
 
+    private boolean webViewLoginMethod;
+    private String webViewUser;
+    private String webViewPassword;
+
     /**
      * {@inheritDoc}
      *
@@ -235,42 +271,91 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG);
         }
 
+        webViewLoginMethod = !TextUtils.isEmpty(getResources().getString(R.string.webview_login_url));
+
+        if (webViewLoginMethod) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        }
+
         /// load user interface
-        setContentView(R.layout.account_setup);
+        if (!webViewLoginMethod) {
+            setContentView(R.layout.account_setup);
 
-        /// initialize general UI elements
-        initOverallUi();
+            /// initialize general UI elements
+            initOverallUi();
 
-        mOkButton = findViewById(R.id.buttonOK);
-        mOkButton.setOnClickListener(new View.OnClickListener() {
+            mOkButton = findViewById(R.id.buttonOK);
+            mOkButton.setOnClickListener(new View.OnClickListener() {
 
-            @Override
-            public void onClick(View v) {
-                onOkClick();
-            }
-        });
+                @Override
+                public void onClick(View v) {
+                    onOkClick();
+                }
+            });
+
+            findViewById(R.id.centeredRefreshButton).setOnClickListener(new View.OnClickListener() {
+
+                @Override
+                public void onClick(View v) {
+                    checkOcServer();
+                }
+            });
+
+            findViewById(R.id.embeddedRefreshButton).setOnClickListener(new View.OnClickListener() {
+
+                @Override
+                public void onClick(View v) {
+                    checkOcServer();
+                }
+            });
+
+            /// initialize block to be moved to single Fragment to check server and get info about it
+
+            /// initialize block to be moved to single Fragment to retrieve and validate credentials
+            initAuthorizationPreFragment(savedInstanceState);
 
-        findViewById(R.id.centeredRefreshButton).setOnClickListener(new View.OnClickListener() {
+        } else {
+            setContentView(R.layout.account_setup_webview);
+            mLoginWebView = (WebView) findViewById(R.id.login_webview);
+            initWebViewLogin();
+        }
 
+        initServerPreFragment(savedInstanceState);
+    }
+
+    private void initWebViewLogin() {
+        mLoginWebView.getSettings().setAllowFileAccess(false);
+        mLoginWebView.getSettings().setJavaScriptEnabled(true);
+        mLoginWebView.getSettings().setUserAgentString(MainApp.getUserAgent());
+        mLoginWebView.loadUrl(getResources().getString(R.string.webview_login_url));
+        mLoginWebView.setWebViewClient(new WebViewClient() {
             @Override
-            public void onClick(View v) {
-                checkOcServer();
+            public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                if (url.startsWith(getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/")) {
+                    parseAndLoginFromWebView(url);
+                    return true;
+                }
+                return false;
             }
-        });
 
-        findViewById(R.id.embeddedRefreshButton).setOnClickListener(new View.OnClickListener() {
+            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
 
-            @Override
-            public void onClick(View v) {
-                checkOcServer();
+                mLoginWebView.loadData(DisplayUtils.getData(getResources().openRawResource(R.raw.custom_error)),"text/html; charset=UTF-8", null);
             }
         });
+    }
 
-        /// initialize block to be moved to single Fragment to check server and get info about it 
-        initServerPreFragment(savedInstanceState);
+    private void parseAndLoginFromWebView(String dataString) {
+        String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/";
+        LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString);
+
+        if (loginUrlInfo != null) {
+            mServerInfo.mBaseUrl = normalizeUrlSuffix(loginUrlInfo.serverAddress);
+            webViewUser = loginUrlInfo.username;
+            webViewPassword = loginUrlInfo.password;
+            checkOcServer();
+        }
 
-        /// initialize block to be moved to single Fragment to retrieve and validate credentials 
-        initAuthorizationPreFragment(savedInstanceState);
     }
 
     private void populateLoginFields(String dataString) throws IllegalArgumentException {
@@ -294,7 +379,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     /**
      * parses a URI string and returns a login data object with the information from the URI string.
      *
-     * @param prefix URI beginning, e.g. cloud://login/
+     * @param prefix     URI beginning, e.g. cloud://login/
      * @param dataString the complete URI
      * @return login data
      * @throws IllegalArgumentException when
@@ -413,7 +498,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
                 mServerInfo.mVersion = AccountUtils.getServerVersion(mAccount);
             } else {
-                mServerInfo.mBaseUrl = getString(R.string.server_url).trim();
+                if (!webViewLoginMethod) {
+                    mServerInfo.mBaseUrl = getString(R.string.server_url).trim();
+                } else {
+                    mServerInfo.mBaseUrl = getString(R.string.webview_login_url).trim();
+                }
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
             }
         } else {
@@ -435,72 +524,75 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
         }
 
-        /// step 2 - set properties of UI elements (text, visibility, enabled...)
-        mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);
-        // Convert IDN to Unicode
-        mHostUrlInput.setText(DisplayUtils.convertIdn(mServerInfo.mBaseUrl, false));
-        if (mAction != ACTION_CREATE) {
-            /// lock things that should not change
-            mHostUrlInput.setEnabled(false);
-            mHostUrlInput.setFocusable(false);
-        }
-        if (isUrlInputAllowed) {
-            mRefreshButton = findViewById(R.id.embeddedRefreshButton);
-        } else {
-            findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);
-            mRefreshButton = findViewById(R.id.centeredRefreshButton);
-        }
-        showRefreshButton(mServerIsChecked && !mServerIsValid &&
-                mWaitingForOpId > Integer.MAX_VALUE);
-        mServerStatusView = (TextView) findViewById(R.id.server_status_text);
-        showServerStatus();
+        if (!webViewLoginMethod) {
+            /// step 2 - set properties of UI elements (text, visibility, enabled...)
+            mHostUrlInput = (CustomEditText) findViewById(R.id.hostUrlInput);
+            // Convert IDN to Unicode
+            mHostUrlInput.setText(DisplayUtils.convertIdn(mServerInfo.mBaseUrl, false));
+            if (mAction != ACTION_CREATE) {
+                /// lock things that should not change
+                mHostUrlInput.setEnabled(false);
+                mHostUrlInput.setFocusable(false);
+            }
+            if (isUrlInputAllowed) {
+                mRefreshButton = findViewById(R.id.embeddedRefreshButton);
+            } else {
+                findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);
+                mRefreshButton = findViewById(R.id.centeredRefreshButton);
+            }
+            showRefreshButton(mServerIsChecked && !mServerIsValid &&
+                    mWaitingForOpId > Integer.MAX_VALUE);
+            mServerStatusView = (TextView) findViewById(R.id.server_status_text);
+            showServerStatus();
 
-        /// step 3 - bind some listeners and options
-        mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
-        mHostUrlInput.setOnEditorActionListener(this);
+            /// step 3 - bind some listeners and options
+            mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
+            mHostUrlInput.setOnEditorActionListener(this);
 
-        /// step 4 - create listeners that will be bound at onResume
-        mHostUrlInputWatcher = new TextWatcher() {
+            /// step 4 - create listeners that will be bound at onResume
+            mHostUrlInputWatcher = new TextWatcher() {
 
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (mOkButton.isEnabled() &&
-                        !mServerInfo.mBaseUrl.equals(
-                                normalizeUrl(s.toString(), mServerInfo.mIsSslConn))) {
-                    mOkButton.setEnabled(false);
+                @Override
+                public void afterTextChanged(Editable s) {
+                    if (mOkButton.isEnabled() &&
+                            !mServerInfo.mBaseUrl.equals(
+                                    normalizeUrl(s.toString(), mServerInfo.mIsSslConn))) {
+                        mOkButton.setEnabled(false);
+                    }
                 }
-            }
 
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                if (mAuthStatusIcon != 0) {
-                    Log_OC.d(TAG, "onTextChanged: hiding authentication status");
-                    mAuthStatusIcon = 0;
-                    mAuthStatusText = 0;
-                    showAuthStatus();
+                @Override
+                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+                    // not used at the moment
                 }
-            }
-        };
 
-
-        // TODO find out if this is really necessary, or if it can done in a different way
-        findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent event) {
-                if (event.getAction() == MotionEvent.ACTION_DOWN &&
-                        AccountTypeUtils
-                                .getAuthTokenTypeSamlSessionCookie(MainApp
-                                        .getAccountType()).equals(mAuthTokenType) &&
-                        mHostUrlInput.hasFocus()) {
-                    checkOcServer();
+                @Override
+                public void onTextChanged(CharSequence s, int start, int before, int count) {
+                    if (mAuthStatusIcon != 0) {
+                        Log_OC.d(TAG, "onTextChanged: hiding authentication status");
+                        mAuthStatusIcon = 0;
+                        mAuthStatusText = 0;
+                        showAuthStatus();
+                    }
                 }
-                return false;
-            }
-        });
+            };
+
+
+            // TODO find out if this is really necessary, or if it can done in a different way
+            findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
+                @Override
+                public boolean onTouch(View view, MotionEvent event) {
+                    if (event.getAction() == MotionEvent.ACTION_DOWN &&
+                            AccountTypeUtils
+                                    .getAuthTokenTypeSamlSessionCookie(MainApp
+                                            .getAccountType()).equals(mAuthTokenType) &&
+                            mHostUrlInput.hasFocus()) {
+                        checkOcServer();
+                    }
+                    return false;
+                }
+            });
+        }
     }
 
 
@@ -629,11 +721,20 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         outState.putString(KEY_AUTH_TOKEN_TYPE, mAuthTokenType);
         outState.putLong(KEY_WAITING_FOR_OP_ID, mWaitingForOpId);
 
-        /// Server PRE-fragment state
-        outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);
-        outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);
-        outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);
-        outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);
+        if (!webViewLoginMethod) {
+            /// Server PRE-fragment state
+            outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);
+            outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);
+            outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);
+            outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);
+
+            /// Authentication PRE-fragment state
+            outState.putBoolean(KEY_PASSWORD_EXPOSED, isPasswordVisible());
+            outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);
+            outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);
+            outState.putString(KEY_AUTH_TOKEN, mAuthToken);
+        }
+
         outState.putBoolean(KEY_IS_SSL_CONN, mServerInfo.mIsSslConn);
         outState.putString(KEY_HOST_URL_TEXT, mServerInfo.mBaseUrl);
         if (mServerInfo.mVersion != null) {
@@ -641,18 +742,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
         outState.putString(KEY_SERVER_AUTH_METHOD, mServerInfo.mAuthMethod.name());
 
-        /// Authentication PRE-fragment state
-        outState.putBoolean(KEY_PASSWORD_EXPOSED, isPasswordVisible());
-        outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);
-        outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);
-        outState.putString(KEY_AUTH_TOKEN, mAuthToken);
-
         /// authentication
         outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt);
 
         /// AsyncTask (User and password)
-        outState.putString(KEY_USERNAME, mUsernameInput.getText().toString().trim());
-        outState.putString(KEY_PASSWORD, mPasswordInput.getText().toString());
+        if (!webViewLoginMethod) {
+            outState.putString(KEY_USERNAME, mUsernameInput.getText().toString().trim());
+            outState.putString(KEY_PASSWORD, mPasswordInput.getText().toString());
+        }
 
         if (mAsyncTask != null) {
             mAsyncTask.cancel(true);
@@ -714,21 +811,25 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     protected void onResume() {
         super.onResume();
 
-        // bound here to avoid spurious changes triggered by Android on device rotations
-        mHostUrlInput.setOnFocusChangeListener(this);
-        mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher);
+        AnalyticsUtils.setCurrentScreenName(this, SCREEN_NAME, TAG);
 
-        if (mNewCapturedUriFromOAuth2Redirection != null) {
-            getOAuth2AccessTokenFromCapturedRedirection();
-        }
+        if (!webViewLoginMethod) {
+            // bound here to avoid spurious changes triggered by Android on device rotations
+            mHostUrlInput.setOnFocusChangeListener(this);
+            mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher);
 
-        String dataString = getIntent().getDataString();
-        if (dataString != null) {
-            try {
-                populateLoginFields(dataString);
-            } catch (IllegalArgumentException e) {
-                Toast.makeText(this, "Illegal login data URL used", Toast.LENGTH_SHORT).show();
-                Log_OC.e(TAG, "Illegal login data URL used, no Login pre-fill!", e);
+            if (mNewCapturedUriFromOAuth2Redirection != null) {
+                getOAuth2AccessTokenFromCapturedRedirection();
+            }
+
+            String dataString = getIntent().getDataString();
+            if (dataString != null) {
+                try {
+                    populateLoginFields(dataString);
+                } catch (IllegalArgumentException e) {
+                    Toast.makeText(this, "Illegal login data URL used", Toast.LENGTH_SHORT).show();
+                    Log_OC.e(TAG, "Illegal login data URL used, no Login pre-fill!", e);
+                }
             }
         }
 
@@ -756,8 +857,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mOperationsServiceBinder.removeOperationListener(this);
         }
 
-        mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
-        mHostUrlInput.setOnFocusChangeListener(null);
+        if (!webViewLoginMethod) {
+            mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
+            mHostUrlInput.setOnFocusChangeListener(null);
+        }
 
         super.onPause();
     }
@@ -771,10 +874,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             unbindService(mOperationsServiceConnection);
             mOperationsServiceBinder = null;
         }
+
+        if (webViewLoginMethod) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+        }
+
         super.onDestroy();
     }
 
-
     /**
      * Parses the redirection with the response to the GET AUTHORIZATION request to the
      * oAuth server and requests for the access token (GET ACCESS TOKEN)
@@ -847,15 +954,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
     private void checkOcServer() {
-        String uri = mHostUrlInput.getText().toString().trim();
+        String uri;
+        if (mHostUrlInput != null) {
+            uri = mHostUrlInput.getText().toString().trim();
+            mOkButton.setEnabled(false);
+            showRefreshButton(false);
+        } else {
+            uri = mServerInfo.mBaseUrl;
+        }
+
         mServerIsValid = false;
         mServerIsChecked = false;
-        mOkButton.setEnabled(false);
         mServerInfo = new GetServerInfoOperation.ServerInfo();
-        showRefreshButton(false);
 
         if (uri.length() != 0) {
-            uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            if (mHostUrlInput != null) {
+                uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            }
 
             // Handle internationalized domain names
             try {
@@ -864,9 +979,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // Let Owncloud library check the error of the malformed URI
             }
 
-            mServerStatusText = R.string.auth_testing_connection;
-            mServerStatusIcon = R.drawable.progress_small;
-            showServerStatus();
+            if (mHostUrlInput != null) {
+                mServerStatusText = R.string.auth_testing_connection;
+                mServerStatusIcon = R.drawable.progress_small;
+                showServerStatus();
+            }
 
             Intent getServerInfoIntent = new Intent();
             getServerInfoIntent.setAction(OperationsService.ACTION_GET_SERVER_INFO);
@@ -874,6 +991,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     OperationsService.EXTRA_SERVER_URL,
                     normalizeUrlSuffix(uri)
             );
+
             if (mOperationsServiceBinder != null) {
                 mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent);
             } else {
@@ -883,7 +1001,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {
             mServerStatusText = 0;
             mServerStatusIcon = 0;
-            showServerStatus();
+            if (!webViewLoginMethod) {
+                showServerStatus();
+            }
         }
     }
 
@@ -897,6 +1017,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      *
      * @param hasFocus 'True' if focus is received, 'false' if is lost
      */
+
     private void onPasswordFocusChanged(boolean hasFocus) {
         if (hasFocus) {
             showViewPasswordButton();
@@ -976,7 +1097,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
             startSamlBasedFederatedSingleSignOnAuthorization();
         } else {
-            checkBasicAuthorization();
+            checkBasicAuthorization(null, null);
         }
 
     }
@@ -986,10 +1107,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * Tests the credentials entered by the user performing a check of existence on
      * the root folder of the ownCloud server.
      */
-    private void checkBasicAuthorization() {
+    private void checkBasicAuthorization(@Nullable String webViewUsername, @Nullable String webViewPassword) {
         /// get basic credentials entered by user
-        String username = mUsernameInput.getText().toString().trim();
-        String password = mPasswordInput.getText().toString();
+        String username;
+        String password;
+        if (!webViewLoginMethod) {
+            username = mUsernameInput.getText().toString().trim();
+            password = mPasswordInput.getText().toString();
+        } else {
+            username = webViewUsername;
+            password = webViewPassword;
+        }
 
         /// be gentle with the user
         IndeterminateProgressDialog dialog =
@@ -1093,11 +1221,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
 
             if (mAction == ACTION_CREATE) {
-                mUsernameInput.setText(username);
+                if (!webViewLoginMethod) {
+                    mUsernameInput.setText(username);
+                }
                 success = createAccount(result);
             } else {
 
-                if (!mUsernameInput.getText().toString().trim().equals(username)) {
+                if (!webViewLoginMethod && !mUsernameInput.getText().toString().trim().equals(username)) {
                     // fail - not a new account, but an existing one; disallow
                     result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);
                     mAuthToken = "";
@@ -1122,8 +1252,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 finish();
             }
         } else {
-            updateStatusIconFailUserName();
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                int statusText = result.getCode() == ResultCode.MAINTENANCE_MODE ? R.string.maintenance_mode : R.string.auth_fail_get_user_name;
+                updateStatusIconFailUserName(statusText);
+                showAuthStatus();
+            }
             Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());
         }
 
@@ -1141,7 +1274,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         mWaitingForOpId = Long.MAX_VALUE;
 
         // update server status, but don't show it yet
-        updateServerStatusIconAndText(result);
+        if (!webViewLoginMethod) {
+            updateServerStatusIconAndText(result);
+        }
 
         if (result.isSuccess()) {
             /// SUCCESS means:
@@ -1151,9 +1286,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             //      4. we got the authentication method required by the server 
             mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0));
 
+            if (webViewLoginMethod) {
+                checkBasicAuthorization(webViewUser, webViewPassword);
+            }
+
             if (!authSupported(mServerInfo.mAuthMethod)) {
 
-                updateServerStatusIconNoRegularAuth();  // overrides updateServerStatusIconAndText()  
+                if (!webViewLoginMethod) {
+                    // overrides updateServerStatusIconAndText()
+                    updateServerStatusIconNoRegularAuth();
+                }
                 mServerIsValid = false;
 
             } else {
@@ -1165,9 +1307,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
 
         // refresh UI
-        showRefreshButton(!mServerIsValid);
-        showServerStatus();
-        mOkButton.setEnabled(mServerIsValid);
+        if (!webViewLoginMethod) {
+            showRefreshButton(!mServerIsValid);
+            showServerStatus();
+            mOkButton.setEnabled(mServerIsValid);
+        }
 
         /// very special case (TODO: move to a common place for all the remote operations)
         if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
@@ -1310,6 +1454,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 mServerStatusIcon = R.drawable.ic_lock_open_white;
                 mServerStatusText = R.string.auth_redirect_non_secure_connection_title;
                 break;
+            case MAINTENANCE_MODE:
+                mServerStatusText = R.string.maintenance_mode;
+                break;
             default:
                 mServerStatusText = 0;
                 mServerStatusIcon = 0;
@@ -1399,10 +1546,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
     }
 
-
-    private void updateStatusIconFailUserName() {
+    private void updateStatusIconFailUserName(int failedStatusText){
         mAuthStatusIcon = R.drawable.ic_alert;
-        mAuthStatusText = R.string.auth_fail_get_user_name;
+        mAuthStatusText = failedStatusText;
     }
 
     private void updateServerStatusIconNoRegularAuth() {
@@ -1495,11 +1641,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             showServerStatus();
             mAuthStatusIcon = 0;
             mAuthStatusText = 0;
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                showAuthStatus();
 
-            // update input controls state
-            showRefreshButton(true);
-            mOkButton.setEnabled(false);
+                // update input controls state
+                showRefreshButton(true);
+                mOkButton.setEnabled(false);
+            }
 
             // very special case (TODO: move to a common place for all the remote operations)
             if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
@@ -1507,8 +1655,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
 
         } else {    // authorization fail due to client side - probably wrong credentials
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
         }
     }
@@ -1546,8 +1696,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
 
         } else {
-            response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
-            mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
+            if (!webViewLoginMethod) {
+                response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
+                mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
+            } else {
+                response.putString(AccountManager.KEY_AUTHTOKEN, webViewPassword);
+                mAccountMgr.setPassword(mAccount, webViewPassword);
+            }
         }
 
         // remove managed clients for this account to enforce creation with fresh credentials
@@ -1583,7 +1738,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
 
         Uri uri = Uri.parse(mServerInfo.mBaseUrl);
-        String username = mUsernameInput.getText().toString().trim();
+        String username;
+        if (!webViewLoginMethod) {
+            username = mUsernameInput.getText().toString().trim();
+        } else {
+            username = webViewUser;
+        }
         if (isOAuth) {
             username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
         }
@@ -1593,8 +1753,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         if (AccountUtils.exists(newAccount, getApplicationContext())) {
             // fail - not a new account, but an existing one; disallow
             RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW);
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, result.getLogMessage());
             return false;
 
@@ -1605,9 +1767,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // with external authorizations, the password is never input in the app
                 mAccountMgr.addAccountExplicitly(mAccount, "", null);
             } else {
-                mAccountMgr.addAccountExplicitly(
-                        mAccount, mPasswordInput.getText().toString(), null
-                );
+                if (!webViewLoginMethod) {
+                    mAccountMgr.addAccountExplicitly(
+                            mAccount, mPasswordInput.getText().toString(), null
+                    );
+                } else {
+                    mAccountMgr.addAccountExplicitly(
+                            mAccount, webViewPassword, null
+                    );
+                }
             }
 
             // include account version with the new account
@@ -1623,7 +1791,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 SharedPreferences.Editor editor = PreferenceManager
                         .getDefaultSharedPreferences(this).edit();
                 editor.putString("select_oc_account", accountName);
-                editor.commit();
+                editor.apply();
             }
 
             /// prepare result to return to the Authenticator
@@ -1690,15 +1858,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * to the last check on the ownCloud server.
      */
     private void showServerStatus() {
-        if (mServerStatusIcon == 0 && mServerStatusText == 0) {
-            mServerStatusView.setVisibility(View.INVISIBLE);
+        if (!webViewLoginMethod) {
+            if (mServerStatusIcon == 0 && mServerStatusText == 0) {
+                mServerStatusView.setVisibility(View.INVISIBLE);
 
-        } else {
-            mServerStatusView.setText(mServerStatusText);
-            mServerStatusView.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
-            mServerStatusView.setVisibility(View.VISIBLE);
+            } else {
+                mServerStatusView.setText(mServerStatusText);
+                mServerStatusView.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
+                mServerStatusView.setVisibility(View.VISIBLE);
+            }
         }
-
     }
 
 
@@ -1707,22 +1876,26 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * to the interactions with the OAuth authorization server.
      */
     private void showAuthStatus() {
-        if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {
-            mAuthStatusView.setVisibility(View.INVISIBLE);
+        if (!webViewLoginMethod) {
+            if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {
+                mAuthStatusView.setVisibility(View.INVISIBLE);
 
-        } else {
-            mAuthStatusView.setText(mAuthStatusText);
-            mAuthStatusView.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
-            mAuthStatusView.setVisibility(View.VISIBLE);
+            } else {
+                mAuthStatusView.setText(mAuthStatusText);
+                mAuthStatusView.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
+                mAuthStatusView.setVisibility(View.VISIBLE);
+            }
         }
     }
 
 
     private void showRefreshButton(boolean show) {
-        if (show) {
-            mRefreshButton.setVisibility(View.VISIBLE);
-        } else {
-            mRefreshButton.setVisibility(View.GONE);
+        if (webViewLoginMethod) {
+            if (show) {
+                mRefreshButton.setVisibility(View.VISIBLE);
+            } else {
+                mRefreshButton.setVisibility(View.GONE);
+            }
         }
     }
 
@@ -1779,7 +1952,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 inputField.equals(mHostUrlInput) &&
                 AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()).
                         equals(mAuthTokenType)) {
-                checkOcServer();
+            checkOcServer();
         }
         return false;   // always return false to grant that the software keyboard is hidden anyway
     }
@@ -1935,7 +2108,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mOperationsServiceBinder.dispatchResultIfFinished((int) mWaitingForOpId, this);
         }
 
-        if (mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0 && !mServerIsChecked) {
+        if (!webViewLoginMethod && mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0
+                && !mServerIsChecked) {
             checkOcServer();
         }
     }

+ 0 - 0
src/com/owncloud/android/authentication/AuthenticatorAsyncTask.java → src/main/java/com/owncloud/android/authentication/AuthenticatorAsyncTask.java


+ 0 - 0
src/com/owncloud/android/authentication/LoginUrlInfo.java → src/main/java/com/owncloud/android/authentication/LoginUrlInfo.java


+ 0 - 0
src/com/owncloud/android/authentication/OAuth2Constants.java → src/main/java/com/owncloud/android/authentication/OAuth2Constants.java


+ 30 - 6
src/com/owncloud/android/authentication/PassCodeManager.java → src/main/java/com/owncloud/android/authentication/PassCodeManager.java

@@ -28,6 +28,7 @@ import android.preference.PreferenceManager;
 import android.view.WindowManager;
 
 import com.owncloud.android.MainApp;
+import com.owncloud.android.ui.activity.FingerprintActivity;
 import com.owncloud.android.ui.activity.PassCodeActivity;
 
 import java.util.HashSet;
@@ -40,6 +41,7 @@ public class PassCodeManager {
     static {
         sExemptOfPasscodeActivites = new HashSet<Class>();
         sExemptOfPasscodeActivites.add(PassCodeActivity.class);
+        sExemptOfPasscodeActivites.add(FingerprintActivity.class);
         // other activities may be exempted, if needed
     }
 
@@ -48,6 +50,9 @@ public class PassCodeManager {
 
     public static PassCodeManager mPassCodeManagerInstance = null;
 
+    private Long mTimestamp = 0l;
+    private int mVisibleActivitiesCounter = 0;
+
     public static PassCodeManager getPassCodeManager() {
         if (mPassCodeManagerInstance == null) {
             mPassCodeManagerInstance = new PassCodeManager();
@@ -55,13 +60,10 @@ public class PassCodeManager {
         return mPassCodeManagerInstance;
     }
 
-    private Long mTimestamp = 0l;
-    private int mVisibleActivitiesCounter = 0;
-
-    protected PassCodeManager() {};
+    protected PassCodeManager() {}
 
     public void onActivityCreated(Activity activity) {
-        if (passCodeIsEnabled()) {
+        if (passCodeIsEnabled() || fingerprintIsEnabled()) {
             activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
         } else {
             activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
@@ -80,6 +82,17 @@ public class PassCodeManager {
 
         }
 
+        if (!sExemptOfPasscodeActivites.contains(activity.getClass()) &&
+                fingerprintShouldBeRequested() && FingerprintActivity.isFingerprintReady(MainApp.getAppContext())
+                ){
+
+            Intent i = new Intent(MainApp.getAppContext(), FingerprintActivity.class);
+            i.setAction(PassCodeActivity.ACTION_CHECK);
+            i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+            activity.startActivity(i);
+
+        }
+
         mVisibleActivitiesCounter++;    // keep it AFTER passCodeShouldBeRequested was checked
     }
 
@@ -89,7 +102,7 @@ public class PassCodeManager {
         }
         setUnlockTimestamp();
         PowerManager powerMgr = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
-        if (passCodeIsEnabled() && powerMgr != null && !powerMgr.isScreenOn()) {
+        if ((passCodeIsEnabled() || fingerprintIsEnabled())&& powerMgr != null && !powerMgr.isScreenOn()) {
             activity.moveTaskToBack(true);
         }
     }
@@ -112,4 +125,15 @@ public class PassCodeManager {
         return (appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false));
     }
 
+    private boolean fingerprintShouldBeRequested() {
+        if ((System.currentTimeMillis() - mTimestamp) > PASS_CODE_TIMEOUT && mVisibleActivitiesCounter <= 0) {
+            return fingerprintIsEnabled();
+        }
+        return false;
+    }
+
+    private boolean fingerprintIsEnabled() {
+        SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(MainApp.getAppContext());
+        return (appPrefs.getBoolean(FingerprintActivity.PREFERENCE_USE_FINGERPRINT, false));
+    }
 }

+ 5 - 7
src/com/owncloud/android/authentication/SsoWebViewClient.java → src/main/java/com/owncloud/android/authentication/SsoWebViewClient.java

@@ -55,18 +55,17 @@ import java.security.cert.X509Certificate;
 public class SsoWebViewClient extends WebViewClient {
         
     private static final String TAG = SsoWebViewClient.class.getSimpleName();
-    
-    public interface SsoWebViewClientListener {
-        public void onSsoFinished(String sessionCookie);
-    }
-    
+
     private Context mContext;
     private Handler mListenerHandler;
     private WeakReference<SsoWebViewClientListener> mListenerRef;
     private String mTargetUrl;
     private String mLastReloadedUrlAtError;
-
     
+    public interface SsoWebViewClientListener {
+        public void onSsoFinished(String sessionCookie);
+    }
+
     public SsoWebViewClient (Context context, Handler listenerHandler, SsoWebViewClientListener listener) {
         mContext = context;
         mListenerHandler = listenerHandler;
@@ -191,5 +190,4 @@ public class SsoWebViewClient extends WebViewClient {
 
         ((AuthenticatorActivity)mContext).createAuthenticationDialog(view, handler);
     }
-
 }

+ 244 - 0
src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java

@@ -0,0 +1,244 @@
+/**
+ * Nextcloud Android client application
+ *
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Mario Danic
+ * 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.
+ * <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.datamodel;
+
+import android.accounts.Account;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
+import java.util.ArrayList;
+
+/**
+ * Database provider for handling the persistence aspects of arbitrary data table.
+ */
+
+public class ArbitraryDataProvider {
+    static private final String TAG = ArbitraryDataProvider.class.getSimpleName();
+
+    private ContentResolver contentResolver;
+
+    public ArbitraryDataProvider(ContentResolver contentResolver) {
+        if (contentResolver == null) {
+            throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
+        }
+        this.contentResolver = contentResolver;
+    }
+
+    public int deleteKeyForAccount(Account account, String key) {
+        int result = contentResolver.delete(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
+                ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? AND " +
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + "= ?",
+                new String[]{account.name, key}
+        );
+
+        return result;
+    }
+
+
+    public void storeOrUpdateKeyValue(Account account, String key, String newValue) {
+        ArbitraryDataSet data = getArbitraryDataSet(account, key);
+        if (data == null) {
+            Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + account.name + " key: " + key
+                    + " value: " + newValue);
+            ContentValues cv = new ContentValues();
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, account.name);
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, key);
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, newValue);
+
+            Uri result = contentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA, cv);
+
+            if (result == null) {
+                Log_OC.v(TAG, "Failed to store arbitrary data with cloud id: " + account.name + " key: " + key
+                        + " value: " + newValue);
+            }
+        } else {
+            Log_OC.v(TAG, "Updating arbitrary data with cloud id: " + account.name + " key: " + key
+                    + " value: " + newValue);
+            ContentValues cv = new ContentValues();
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID, data.getCloudId());
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY, data.getKey());
+            cv.put(ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE, newValue);
+
+            int result = contentResolver.update(
+                    ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
+                    cv,
+                    ProviderMeta.ProviderTableMeta._ID + "=?",
+                    new String[]{String.valueOf(data.getId())}
+            );
+
+            if (result == 0) {
+                Log_OC.v(TAG, "Failed to update arbitrary data with cloud id: " + account.name + " key: " + key
+                        + " value: " + newValue);
+            }
+        }
+    }
+
+
+    public Long getLongValue(Account account, String key) {
+        String value = getValue(account, key);
+
+        if (value.isEmpty()) {
+            return -1l;
+        } else {
+            return Long.valueOf(value);
+        }
+    }
+
+    public boolean getBooleanValue(Account account, String key) {
+        String value = getValue(account, key);
+
+        return !value.isEmpty() && value.equalsIgnoreCase("true");
+    }
+
+    private ArrayList<String> getValues(Account account, String key) {
+        Cursor cursor = contentResolver.query(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
+                null,
+                ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
+                new String[]{account.name, key},
+                null
+        );
+
+        if (cursor != null) {
+            ArrayList<String> list = new ArrayList<>();
+            if (cursor.moveToFirst()) {
+                do {
+                    String value = cursor.getString(cursor.getColumnIndex(
+                            ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
+                    if (value == null) {
+                        Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
+                    } else {
+                        list.add(value);
+                    }
+                } while (cursor.moveToNext());
+            }
+            cursor.close();
+            return list;
+        } else {
+            Log_OC.e(TAG, "DB error restoring arbitrary values.");
+        }
+
+        return new ArrayList<>();
+    }
+
+    public String getValue(Account account, String key) {
+        Cursor cursor = contentResolver.query(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
+                null,
+                ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
+                new String[]{account.name, key},
+                null
+        );
+
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                String value = cursor.getString(cursor.getColumnIndex(
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
+                if (value == null) {
+                    Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
+                } else {
+                    return value;
+                }
+            }
+            cursor.close();
+            return "";
+        } else {
+            Log_OC.e(TAG, "DB error restoring arbitrary values.");
+        }
+
+        return "";
+    }
+
+    private ArbitraryDataSet getArbitraryDataSet(Account account, String key) {
+        Cursor cursor = contentResolver.query(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
+                null,
+                ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? and " +
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + " = ?",
+                new String[]{account.name, key},
+                null
+        );
+
+        ArbitraryDataSet dataSet = null;
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                int id = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
+                String dbAccount = cursor.getString(cursor.getColumnIndex(
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID));
+                String dbKey = cursor.getString(cursor.getColumnIndex(
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY));
+                String dbValue = cursor.getString(cursor.getColumnIndex(
+                        ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_VALUE));
+
+                if (id == -1) {
+                    Log_OC.e(TAG, "Arbitrary value could not be created from cursor");
+                } else {
+                    dataSet = new ArbitraryDataSet(id, dbAccount, dbKey, dbValue);
+                }
+            }
+            cursor.close();
+        } else {
+            Log_OC.e(TAG, "DB error restoring arbitrary values.");
+        }
+
+        return dataSet;
+    }
+
+
+    public class ArbitraryDataSet {
+        private int id;
+        private String cloudId;
+        private String key;
+        private String value;
+
+        public ArbitraryDataSet(int id, String cloudId, String key, String value) {
+            this.id = id;
+            this.cloudId = cloudId;
+            this.key = key;
+            this.value = value;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public String getCloudId() {
+            return cloudId;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+}

+ 175 - 0
src/main/java/com/owncloud/android/datamodel/ExternalLinksProvider.java

@@ -0,0 +1,175 @@
+/**
+ * Nextcloud Android client application
+ * <p>
+ * Copyright (C) 2017 Tobias Kaminsky
+ * Copyright (C) 2017 Nextcloud.
+ * <p>
+ * 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.
+ * <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.datamodel;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+import com.owncloud.android.db.ProviderMeta;
+import com.owncloud.android.lib.common.ExternalLink;
+import com.owncloud.android.lib.common.ExternalLinkType;
+import com.owncloud.android.lib.common.utils.Log_OC;
+
+import java.util.ArrayList;
+
+/**
+ * Database provider for handling the persistence aspects of {@link com.owncloud.android.lib.common.ExternalLink}s.
+ */
+
+public class ExternalLinksProvider {
+    static private final String TAG = ExternalLinksProvider.class.getSimpleName();
+
+    private ContentResolver mContentResolver;
+
+    public ExternalLinksProvider(ContentResolver contentResolver) {
+        if (contentResolver == null) {
+            throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver");
+        }
+        mContentResolver = contentResolver;
+    }
+
+    /**
+     * Stores an external link in database.
+     *
+     * @param externalLink object to store
+     * @return external link id, -1 if the insert process fails.
+     */
+    public long storeExternalLink(ExternalLink externalLink) {
+        Log_OC.v(TAG, "Adding " + externalLink.name);
+
+        ContentValues cv = createContentValuesFromExternalLink(externalLink);
+
+        Uri result = mContentResolver.insert(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, cv);
+
+        if (result != null) {
+            return Long.parseLong(result.getPathSegments().get(1));
+        } else {
+            Log_OC.e(TAG, "Failed to insert item " + externalLink.name + " into external link db.");
+            return -1;
+        }
+    }
+
+    /**
+     * Delete all external links from the db
+     * @return numbers of rows deleted
+     */
+    public int deleteAllExternalLinks() {
+        return mContentResolver.delete(ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS, " 1 = 1 ", null);
+    }
+
+    /**
+     * get by type external links.
+     *
+     * @return external links, empty if none exists
+     */
+    public ArrayList<ExternalLink> getExternalLink(ExternalLinkType type) {
+        Cursor cursor = mContentResolver.query(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_EXTERNAL_LINKS,
+                null,
+                "type = ?",
+                new String[]{type.toString()},
+                null
+        );
+
+        if (cursor != null) {
+            ArrayList<ExternalLink> list = new ArrayList<>();
+            if (cursor.moveToFirst()) {
+                do {
+                    ExternalLink externalLink = createExternalLinkFromCursor(cursor);
+                    if (externalLink == null) {
+                        Log_OC.e(TAG, "ExternalLink could not be created from cursor");
+                    } else {
+                        list.add(externalLink);
+                    }
+                } while (cursor.moveToNext());
+
+            }
+            cursor.close();
+            return list;
+        } else {
+            Log_OC.e(TAG, "DB error restoring externalLinks.");
+        }
+
+        return new ArrayList<>();
+    }
+
+    /**
+     * create ContentValues object based on given externalLink.
+     *
+     * @param externalLink the external Link
+     * @return the corresponding ContentValues object
+     */
+    @NonNull
+    private ContentValues createContentValuesFromExternalLink(ExternalLink externalLink) {
+        ContentValues cv = new ContentValues();
+        cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL, externalLink.iconUrl);
+        cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE, externalLink.language);
+        cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE, externalLink.type.toString());
+        cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME, externalLink.name);
+        cv.put(ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL, externalLink.url);
+        return cv;
+    }
+
+    /**
+     * cursor to externalLink
+     *
+     * @param cursor db cursor
+     * @return externalLink, null if cursor is null
+     */
+    private ExternalLink createExternalLinkFromCursor(Cursor cursor) {
+        ExternalLink externalLink = null;
+        if (cursor != null) {
+            int id = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
+            String iconUrl = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_ICON_URL));
+            String language = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_LANGUAGE));
+            ExternalLinkType type;
+            switch (cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_TYPE))) {
+                case "link":
+                    type = ExternalLinkType.LINK;
+                    break;
+                case "settings":
+                    type = ExternalLinkType.SETTINGS;
+                    break;
+                case "quota":
+                    type = ExternalLinkType.QUOTA;
+                    break;
+                default:
+                    type = ExternalLinkType.UNKNOWN;
+                    break;
+            }
+            String name = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_NAME));
+            String url = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.EXTERNAL_LINKS_URL));
+
+            externalLink = new ExternalLink(id, iconUrl, language, type, name, url);
+        }
+        return externalLink;
+    }
+
+
+
+}

+ 16 - 10
src/com/owncloud/android/datamodel/FileDataStorageManager.java → src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java

@@ -190,7 +190,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
+        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
@@ -303,7 +303,7 @@ public class FileDataStorageManager {
             cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, file.getLastSyncDateForProperties());
             cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, file.getLastSyncDateForData());
-            cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
+            cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
             cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
             cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
             cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
@@ -313,6 +313,7 @@ public class FileDataStorageManager {
             cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail());
             cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading());
             cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict());
+            cv.put(ProviderTableMeta.FILE_FAVORITE, file.getIsFavorite());
 
             boolean existsByPath = fileExists(file.getRemotePath());
             if (existsByPath || fileExists(file.getFileId())) {
@@ -380,13 +381,14 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.FILE_ACCOUNT_OWNER, mAccount.name);
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE, folder.getLastSyncDateForProperties());
         cv.put(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA, folder.getLastSyncDateForData());
-        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isFavorite() ? 1 : 0);
+        cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, folder.isAvailableOffline() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_ETAG, folder.getEtag());
         cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, folder.isSharedViaLink() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, folder.isSharedWithSharee() ? 1 : 0);
         cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, folder.getPublicLink());
         cv.put(ProviderTableMeta.FILE_PERMISSIONS, folder.getPermissions());
         cv.put(ProviderTableMeta.FILE_REMOTE_ID, folder.getRemoteId());
+        cv.put(ProviderTableMeta.FILE_FAVORITE, folder.getIsFavorite());
 
         operations.add(ContentProviderOperation.newUpdate(ProviderTableMeta.CONTENT_URI).
                 withValues(cv).
@@ -895,20 +897,21 @@ public class FileDataStorageManager {
                     c.getColumnIndex(ProviderTableMeta.FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)));
             file.setLastSyncDateForProperties(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE)));
             file.setLastSyncDateForData(c.getLong(c.getColumnIndex(ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA)));
-            file.setFavorite(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1 ? true : false);
+            file.setAvailableOffline(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_KEEP_IN_SYNC)) == 1);
             file.setEtag(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG)));
             file.setShareViaLink(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1 ? true : false);
+                    c.getColumnIndex(ProviderTableMeta.FILE_SHARED_VIA_LINK)) == 1);
             file.setShareWithSharee(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1 ? true : false);
+                    c.getColumnIndex(ProviderTableMeta.FILE_SHARED_WITH_SHAREE)) == 1);
             file.setPublicLink(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PUBLIC_LINK)));
             file.setPermissions(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_PERMISSIONS)));
             file.setRemoteId(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_REMOTE_ID)));
             file.setNeedsUpdateThumbnail(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false);
+                    c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1);
             file.setDownloading(c.getInt(
-                    c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false);
+                    c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1);
             file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT)));
+            file.setFavorite(c.getInt(c.getColumnIndex(ProviderTableMeta.FILE_FAVORITE)) == 1);
 
         }
         return file;
@@ -1315,13 +1318,14 @@ public class FileDataStorageManager {
                         ProviderTableMeta.FILE_LAST_SYNC_DATE_FOR_DATA,
                         file.getLastSyncDateForData()
                 );
-                cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isFavorite() ? 1 : 0);
+                cv.put(ProviderTableMeta.FILE_KEEP_IN_SYNC, file.isAvailableOffline() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_ETAG, file.getEtag());
                 cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, file.isSharedViaLink() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, file.isSharedWithSharee() ? 1 : 0);
                 cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, file.getPublicLink());
                 cv.put(ProviderTableMeta.FILE_PERMISSIONS, file.getPermissions());
                 cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId());
+                cv.put(ProviderTableMeta.FILE_FAVORITE, file.getIsFavorite());
                 cv.put(
                         ProviderTableMeta.FILE_UPDATE_THUMBNAIL,
                         file.needsUpdateThumbnail() ? 1 : 0
@@ -1858,6 +1862,7 @@ public class FileDataStorageManager {
         cv.put(ProviderTableMeta.CAPABILITIES_FILES_UNDELETE, capability.getFilesUndelete().getValue());
         cv.put(ProviderTableMeta.CAPABILITIES_FILES_VERSIONING, capability.getFilesVersioning().getValue());
         cv.put(ProviderTableMeta.CAPABILITIES_FILES_DROP, capability.getFilesFileDrop().getValue());
+        cv.put(ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS, capability.getExternalLinks().getValue());
 
         if (capabilityExists(mAccount.name)) {
             if (getContentResolver() != null) {
@@ -1994,7 +1999,8 @@ public class FileDataStorageManager {
                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_FILES_VERSIONING))));
             capability.setFilesFileDrop(CapabilityBooleanType.fromValue(c.getInt(c
                     .getColumnIndex(ProviderTableMeta.CAPABILITIES_FILES_DROP))));
-
+            capability.setExternalLinks(CapabilityBooleanType.fromValue(c.getInt(c
+                    .getColumnIndex(ProviderTableMeta.CAPABILITIES_EXTERNAL_LINKS))));
         }
         return capability;
     }

+ 0 - 0
src/com/owncloud/android/datamodel/MediaFolder.java → src/main/java/com/owncloud/android/datamodel/MediaFolder.java


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

@@ -86,8 +86,7 @@ public class MediaProvider {
                         filePath = cursorImages.getString(cursorImages.getColumnIndexOrThrow(
                                 MediaStore.MediaColumns.DATA));
                         mediaFolder.filePaths.add(filePath);
-                        mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf(folderName)
-                                + folderName.length());
+                        mediaFolder.absolutePath = filePath.substring(0, filePath.lastIndexOf("/"));
                     }
                     cursorImages.close();
 

+ 24 - 12
src/com/owncloud/android/datamodel/OCFile.java → src/main/java/com/owncloud/android/datamodel/OCFile.java

@@ -74,7 +74,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     private boolean mNeedsUpdating;
     private long mLastSyncDateForProperties;
     private long mLastSyncDateForData;
-    private boolean mFavorite;
+    private boolean mAvailableOffline;
 
     private String mEtag;
 
@@ -92,6 +92,8 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
 
     private boolean mShareWithSharee;
 
+    private boolean mIsFavorite;
+
     /**
      * URI to the local path of the file contents, if stored in the device; cached after first call
      * to {@link #getStorageUri()}
@@ -139,7 +141,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mLocalPath = source.readString();
         mMimeType = source.readString();
         mNeedsUpdating = source.readInt() == 0;
-        mFavorite = source.readInt() == 1;
+        mAvailableOffline = source.readInt() == 1;
         mLastSyncDateForProperties = source.readLong();
         mLastSyncDateForData = source.readLong();
         mEtag = source.readString();
@@ -151,7 +153,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mIsDownloading = source.readInt() == 1;
         mEtagInConflict = source.readString();
         mShareWithSharee = source.readInt() == 1;
-
+        mIsFavorite = source.readInt() == 1;
     }
 
     @Override
@@ -166,7 +168,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         dest.writeString(mLocalPath);
         dest.writeString(mMimeType);
         dest.writeInt(mNeedsUpdating ? 1 : 0);
-        dest.writeInt(mFavorite ? 1 : 0);
+        dest.writeInt(mAvailableOffline ? 1 : 0);
         dest.writeLong(mLastSyncDateForProperties);
         dest.writeLong(mLastSyncDateForData);
         dest.writeString(mEtag);
@@ -178,6 +180,15 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         dest.writeInt(mIsDownloading ? 1 : 0);
         dest.writeString(mEtagInConflict);
         dest.writeInt(mShareWithSharee ? 1 : 0);
+        dest.writeInt(mIsFavorite ? 1 : 0);
+    }
+
+    public boolean getIsFavorite() {
+        return mIsFavorite;
+    }
+
+    public void setFavorite(boolean mIsFavorite) {
+        this.mIsFavorite = mIsFavorite;
     }
 
     /**
@@ -412,7 +423,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mModifiedTimestampAtLastSyncForData = 0;
         mLastSyncDateForProperties = 0;
         mLastSyncDateForData = 0;
-        mFavorite = false;
+        mAvailableOffline = false;
         mNeedsUpdating = false;
         mEtag = null;
         mShareByLink = false;
@@ -423,6 +434,7 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mIsDownloading = false;
         mEtagInConflict = null;
         mShareWithSharee = false;
+        mIsFavorite = false;
     }
 
     /**
@@ -521,12 +533,12 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
         mLastSyncDateForData = lastSyncDate;
     }
 
-    public void setFavorite(boolean favorite) {
-        mFavorite = favorite;
+    public void setAvailableOffline(boolean availableOffline) {
+        mAvailableOffline = availableOffline;
     }
 
-    public boolean isFavorite() {
-        return mFavorite;
+    public boolean isAvailableOffline() {
+        return mAvailableOffline;
     }
 
     @Override
@@ -570,10 +582,10 @@ public class OCFile implements Parcelable, Comparable<OCFile> {
     @Override
     public String toString() {
         String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, " +
-                "parentId=%s, favorite=%s etag=%s]";
+                "parentId=%s, availableOffline=%s etag=%s favourite=%s]";
         asString = String.format(asString, mId, getFileName(), mMimeType, isDown(),
-                mLocalPath, mRemotePath, mParentId, mFavorite,
-                mEtag);
+                mLocalPath, mRemotePath, mParentId, mAvailableOffline,
+                mEtag, mIsFavorite);
         return asString;
     }
 

+ 81 - 0
src/main/java/com/owncloud/android/datamodel/PushConfigurationState.java

@@ -0,0 +1,81 @@
+/**
+ * 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.datamodel;
+
+public class PushConfigurationState {
+    public String pushToken;
+    public String deviceIdentifier;
+    public String deviceIdentifierSignature;
+    public String userPublicKey;
+    public boolean shouldBeDeleted;
+
+    public PushConfigurationState() {
+    }
+
+    public PushConfigurationState(String pushToken, String deviceIdentifier, String deviceIdentifierSignature,
+                             String userPublicKey, boolean shouldBeDeleted) {
+        this.pushToken = pushToken;
+        this.deviceIdentifier = deviceIdentifier;
+        this.deviceIdentifierSignature = deviceIdentifierSignature;
+        this.userPublicKey = userPublicKey;
+        this.shouldBeDeleted = shouldBeDeleted;
+    }
+
+    public String getPushToken() {
+        return pushToken;
+    }
+
+    public void setPushToken(String pushToken) {
+        this.pushToken = pushToken;
+    }
+
+    public String getDeviceIdentifier() {
+        return deviceIdentifier;
+    }
+
+    public void setDeviceIdentifier(String deviceIdentifier) {
+        this.deviceIdentifier = deviceIdentifier;
+    }
+
+    public String getDeviceIdentifierSignature() {
+        return deviceIdentifierSignature;
+    }
+
+    public void setDeviceIdentifierSignature(String deviceIdentifierSignature) {
+        this.deviceIdentifierSignature = deviceIdentifierSignature;
+    }
+
+    public String getUserPublicKey() {
+        return userPublicKey;
+    }
+
+    public void setUserPublicKey(String userPublicKey) {
+        this.userPublicKey = userPublicKey;
+    }
+
+    public boolean isShouldBeDeleted() {
+        return shouldBeDeleted;
+    }
+
+    public void setShouldBeDeleted(boolean shouldBeDeleted) {
+        this.shouldBeDeleted = shouldBeDeleted;
+    }
+}

+ 4 - 1
src/com/owncloud/android/datamodel/SyncedFolder.java → src/main/java/com/owncloud/android/datamodel/SyncedFolder.java

@@ -21,11 +21,14 @@
 
 package com.owncloud.android.datamodel;
 
+import java.io.Serializable;
+
 /**
  * Synced folder entity containing all information per synced folder.
  */
-public class SyncedFolder {
+public class SyncedFolder implements Serializable {
     public static final long UNPERSISTED_ID = Long.MIN_VALUE;
+    private static final long serialVersionUID = -793476118299906429L;
     private long id = UNPERSISTED_ID;
     private String localPath;
     private String remotePath;

+ 8 - 0
src/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java → src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java

@@ -57,6 +57,14 @@ public class SyncedFolderDisplayItem extends SyncedFolder {
         this.numberOfFiles = numberOfFiles;
     }
 
+    public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly,
+                                   Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled,
+                                   String folderName) {
+        super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled);
+        this.folderName = folderName;
+    }
+
+
     public List<String> getFilePaths() {
         return filePaths;
     }

+ 112 - 12
src/com/owncloud/android/datamodel/SyncedFolderProvider.java → src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java

@@ -19,16 +19,20 @@
  */
 package com.owncloud.android.datamodel;
 
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.support.annotation.NonNull;
 
 import com.owncloud.android.MainApp;
+import com.owncloud.android.db.PreferenceManager;
 import com.owncloud.android.db.ProviderMeta;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Observable;
@@ -165,7 +169,7 @@ public class SyncedFolderProvider extends Observable {
         Cursor cursor = mContentResolver.query(
                 ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
                 null,
-                ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH + "==" + localPath,
+                ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH + "== \"" + localPath + "\"",
                 null,
                 null
         );
@@ -186,6 +190,90 @@ public class SyncedFolderProvider extends Observable {
 
     }
 
+    /**
+     *  Delete all synced folders for an account
+     *
+     *  @param account whose synced folders should be deleted
+     */
+    public int deleteSyncFoldersForAccount(Account account) {
+        int result = mContentResolver.delete(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
+                ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " = ?",
+                new String[]{String.valueOf(account.name)}
+        );
+
+        return result;
+
+    }
+
+    /**
+     * Delete a synced folder from the db
+     *
+     * @param id for the synced folder.
+     */
+
+    private int deleteSyncFolderWithId(long id) {
+        int result = mContentResolver.delete(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
+                ProviderMeta.ProviderTableMeta._ID + " = ?",
+                new String[]{String.valueOf(id)}
+        );
+
+        return result;
+    }
+
+
+    /**
+     * Try to figure out if a path exists for synced folder, and if not, go one folder back
+     * Otherwise, delete the entry
+     *
+     * @param context the context.
+     */
+    public void updateAutoUploadPaths(Context context) {
+        List<SyncedFolder> syncedFolders = getSyncedFolders();
+        for (int i = 0; i < syncedFolders.size(); i++) {
+            SyncedFolder syncedFolder = syncedFolders.get(i);
+            if (!new File(syncedFolder.getLocalPath()).exists()) {
+                String localPath = syncedFolder.getLocalPath();
+                if (localPath.endsWith("/")) {
+                    localPath = localPath.substring(0, localPath.lastIndexOf("/"));
+                }
+                localPath = localPath.substring(0, localPath.lastIndexOf("/"));
+                if (new File(localPath).exists()) {
+                    syncedFolders.get(i).setLocalPath(localPath);
+                    updateSyncFolder(syncedFolder);
+                } else {
+                    deleteSyncFolderWithId(syncedFolder.getId());
+                }
+            }
+        }
+
+        if (context != null) {
+            PreferenceManager.setAutoUploadPathsUpdate(context, true);
+        }
+    }
+
+    /**
+     * delete any records of synchronized folders that are not within the given list of ids.
+     *
+     * @param context the context.
+     * @param ids     the list of ids to be excluded from deletion.
+     * @return number of deleted records.
+     */
+    public int deleteSyncedFoldersNotInList(Context context, ArrayList<Long> ids) {
+        int result = mContentResolver.delete(
+                ProviderMeta.ProviderTableMeta.CONTENT_URI_SYNCED_FOLDERS,
+                ProviderMeta.ProviderTableMeta._ID + " NOT IN (?)",
+                new String[]{String.valueOf(ids)}
+        );
+
+        if (result > 0 && context != null) {
+            PreferenceManager.setLegacyClean(context, true);
+        }
+
+        return result;
+    }
+
     /**
      * update given synced folder.
      *
@@ -214,21 +302,29 @@ public class SyncedFolderProvider extends Observable {
     /**
      * maps a cursor into a SyncedFolder object.
      *
-     * @param cursor the cursor
+     * @param cursor the db cursor
      * @return the mapped SyncedFolder, null if cursor is null
      */
     private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) {
         SyncedFolder syncedFolder = null;
         if (cursor != null) {
             long id = cursor.getLong(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta._ID));
-            String localPath = cursor.getString(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
-            String remotePath = cursor.getString(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
-            Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
-            Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
-            Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
-            String accountName = cursor.getString(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
-            Integer uploadAction = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
-            Boolean enabled = cursor.getInt(cursor.getColumnIndex(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
+            String localPath = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH));
+            String remotePath = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH));
+            Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1;
+            Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1;
+            Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1;
+            String accountName = cursor.getString(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT));
+            Integer uploadAction = cursor.getInt(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION));
+            Boolean enabled = cursor.getInt(cursor.getColumnIndex(
+                    ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1;
 
             syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate,
                     accountName, uploadAction, enabled);
@@ -258,9 +354,13 @@ public class SyncedFolderProvider extends Observable {
 
     /**
      * Inform all observers about data change.
+     *
+     * @param syncedFolder changed, synchronized folder
      */
     private void notifyFolderSyncObservers(SyncedFolder syncedFolder) {
-        MainApp.getSyncedFolderObserverService().restartObserver(syncedFolder);
-        Log_OC.d(TAG, "notifying folder sync data observers for changed/added: " + syncedFolder.getLocalPath());
+        if (syncedFolder != null) {
+            MainApp.getSyncedFolderObserverService().restartObserver(syncedFolder);
+            Log_OC.d(TAG, "notifying folder sync data observers for changed/added: " + syncedFolder.getLocalPath());
+        }
     }
 }

+ 2 - 2
src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java → src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java

@@ -189,7 +189,7 @@ public class ThumbnailsCacheManager {
 
 
         public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager,
-                                       Account account) {
+                                       Account account) throws IllegalArgumentException {
             // Use a WeakReference to ensure the ImageView can be garbage collected
             mImageViewReference = new WeakReference<ImageView>(imageView);
             if (storageManager == null) {
@@ -577,7 +577,7 @@ public class ThumbnailsCacheManager {
         }
 
         private Bitmap doAvatarInBackground() {
-            String username = (String) mUsername;
+            String username = mUsername;
 
             final String imageKey = "a_" + username;
 

+ 39 - 21
src/com/owncloud/android/datamodel/UploadsStorageManager.java → src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java

@@ -20,17 +20,18 @@
  */
 package com.owncloud.android.datamodel;
 
-import android.app.job.JobInfo;
-import android.app.job.JobScheduler;
+import android.accounts.Account;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Build;
-import android.os.PersistableBundle;
 import android.support.annotation.RequiresApi;
 
+import com.evernote.android.job.JobManager;
+import com.evernote.android.job.JobRequest;
+import com.evernote.android.job.util.support.PersistableBundleCompat;
 import com.owncloud.android.db.OCUpload;
 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
 import com.owncloud.android.db.UploadResult;
@@ -38,12 +39,14 @@ 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;
 import java.util.Collections;
 import java.util.List;
 import java.util.Observable;
+import java.util.Set;
 
 /**
  * Database helper for storing list of files to be uploaded, including status
@@ -395,31 +398,41 @@ public class UploadsStorageManager extends Observable {
 
     @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     private List<OCUpload> getPendingJobs() {
-        JobScheduler js = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        Set<JobRequest> jobRequests = JobManager.create(mContext).getAllJobRequestsForTag(AutoUploadJob.TAG);
 
         ArrayList<OCUpload> list = new ArrayList<>();
 
-        for (JobInfo ji: js.getAllPendingJobs()) {
-            PersistableBundle extras = ji.getExtras();
-            OCUpload upload  = new OCUpload(extras.getString("filePath"),
-                    extras.getString("remotePath"),
-                    extras.getString("account"));
+        for (JobRequest ji : jobRequests) {
+            PersistableBundleCompat extras = ji.getExtras();
+                OCUpload upload = new OCUpload(extras.getString("filePath", ""),
+                        extras.getString("remotePath", ""),
+                        extras.getString("account", ""));
 
-            list.add(upload);
+                list.add(upload);
         }
 
         return list;
     }
 
+    public void cancelPendingAutoUploadJobsForAccount(Account account) {
+        JobManager jobManager = JobManager.create(mContext);
+        for (JobRequest ji: jobManager.getAllJobRequestsForTag(AutoUploadJob.TAG)) {
+            if (ji.getExtras().getString(AutoUploadJob.ACCOUNT, "").equalsIgnoreCase(account.name)) {
+                jobManager.cancel(ji.getJobId());
+            }
+        }
+    }
+
     @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
     public void cancelPendingJob(String accountName, String remotePath){
-        JobScheduler js = (JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
-        for (JobInfo ji: js.getAllPendingJobs()) {
-            PersistableBundle extras = ji.getExtras();
-            if (remotePath.equalsIgnoreCase(extras.getString("remotePath")) &&
-                accountName.equalsIgnoreCase(extras.getString("account"))){
-                js.cancel(ji.getId());
+        JobManager jobManager = JobManager.create(mContext);
+        Set<JobRequest> jobRequests = jobManager.getAllJobRequests();
+
+        for (JobRequest ji : jobRequests) {
+            PersistableBundleCompat extras = ji.getExtras();
+            if (remotePath.equalsIgnoreCase(extras.getString("remotePath", "")) &&
+                    accountName.equalsIgnoreCase(extras.getString("account", ""))) {
+                jobManager.cancel(ji.getJobId());
                 break;
             }
         }
@@ -429,13 +442,16 @@ public class UploadsStorageManager extends Observable {
      * Get all failed uploads.
      */
     public OCUpload[] getFailedUploads() {
-        return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null);
+
+        return getUploads(
+                ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value, null);
     }
 
     /**
      * Get all uploads which where successfully completed.
      */
     public OCUpload[] getFinishedUploads() {
+
         return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value, null);
     }
 
@@ -444,8 +460,8 @@ public class UploadsStorageManager extends Observable {
      * @return      Array of failed uploads, except for those that were not performed due to lack of Wifi connection.
      */
     public OCUpload[] getFailedButNotDelayedUploads() {
-        return getUploads(
-            ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
+
+        return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
                 ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
                 ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
             null
@@ -459,7 +475,7 @@ public class UploadsStorageManager extends Observable {
     public long clearFailedButNotDelayedUploads() {
         long result = getDB().delete(
             ProviderTableMeta.CONTENT_URI_UPLOADS,
-            ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
+                ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + AND +
                 ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + AND +
                 ProviderTableMeta.UPLOADS_LAST_RESULT + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue(),
             null
@@ -472,6 +488,7 @@ public class UploadsStorageManager extends Observable {
     }
 
     public long clearSuccessfulUploads() {
+
         long result = getDB().delete(
                 ProviderTableMeta.CONTENT_URI_UPLOADS,
                 ProviderTableMeta.UPLOADS_STATUS + "=="+ UploadStatus.UPLOAD_SUCCEEDED.value, null
@@ -484,6 +501,7 @@ public class UploadsStorageManager extends Observable {
     }
 
     public long clearAllFinishedButNotDelayedUploads() {
+
         String[] whereArgs = new String[2];
         whereArgs[0] = String.valueOf(UploadStatus.UPLOAD_SUCCEEDED.value);
         whereArgs[1] = String.valueOf(UploadStatus.UPLOAD_FAILED.value);

+ 3 - 3
src/com/owncloud/android/datastorage/DataStorageProvider.java → src/main/java/com/owncloud/android/datastorage/DataStorageProvider.java

@@ -24,7 +24,6 @@ package com.owncloud.android.datastorage;
 import android.os.Build;
 
 import com.owncloud.android.MainApp;
-import com.owncloud.android.R;
 import com.owncloud.android.datastorage.providers.EnvironmentStoragePointProvider;
 import com.owncloud.android.datastorage.providers.HardcodedStoragePointProvider;
 import com.owncloud.android.datastorage.providers.IStoragePointProvider;
@@ -65,7 +64,7 @@ public class DataStorageProvider {
         }
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            for (File f : MainApp.getAppContext().getExternalFilesDirs(null)) {
+            for (File f : MainApp.getAppContext().getExternalMediaDirs()) {
                 if (f != null) {
                     mCachedStoragePoints.add(new StoragePoint(f.getAbsolutePath(), f.getAbsolutePath()));
                 }
@@ -87,7 +86,8 @@ public class DataStorageProvider {
                 return s.getDescription();
             }
         }
-        return MainApp.getAppContext().getString(R.string.storage_description_unknown);
+        // Fallback to just display complete path
+        return path;
     }
 
     public void addStoragePointProvider(IStoragePointProvider provider) {

Some files were not shown because too many files changed in this diff