Selaa lähdekoodia

Merge pull request #44 from nextcloud/1556_toolbar

Modern Toolbar / Drawer / Account Switcher
Andy Scherzinger 8 vuotta sitten
vanhempi
commit
ce402d844e
76 muutettua tiedostoa jossa 2679 lisäystä ja 1474 poistoa
  1. 6 4
      AndroidManifest.xml
  2. BIN
      res/drawable-hdpi/ic_account_plus.png
  3. BIN
      res/drawable-hdpi/ic_down.png
  4. BIN
      res/drawable-hdpi/ic_key.png
  5. BIN
      res/drawable-hdpi/ic_up.png
  6. BIN
      res/drawable-mdpi/ic_account_plus.png
  7. BIN
      res/drawable-mdpi/ic_down.png
  8. BIN
      res/drawable-mdpi/ic_key.png
  9. BIN
      res/drawable-mdpi/ic_up.png
  10. BIN
      res/drawable-xhdpi/ic_account_plus.png
  11. BIN
      res/drawable-xhdpi/ic_down.png
  12. BIN
      res/drawable-xhdpi/ic_key.png
  13. BIN
      res/drawable-xhdpi/ic_up.png
  14. BIN
      res/drawable-xxhdpi/ic_account_plus.png
  15. BIN
      res/drawable-xxhdpi/ic_down.png
  16. BIN
      res/drawable-xxhdpi/ic_key.png
  17. BIN
      res/drawable-xxhdpi/ic_up.png
  18. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_1.png
  19. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_2.png
  20. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_3.png
  21. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_4.png
  22. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_5.png
  23. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_6.png
  24. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_7.png
  25. BIN
      res/drawable-xxhdpi/owncloud_progressbar_indeterminate_8.png
  26. 3 3
      res/drawable/actionbar_progress_horizontal.xml
  27. 47 0
      res/layout/account_action.xml
  28. 68 0
      res/layout/account_item.xml
  29. 23 12
      res/layout/accounts_layout.xml
  30. 9 80
      res/layout/drawer.xml
  31. 0 22
      res/layout/drawer_account_group.xml
  32. 123 0
      res/layout/drawer_header.xml
  33. 0 52
      res/layout/drawer_list_item.xml
  34. 0 1
      res/layout/file_preview.xml
  35. 4 11
      res/layout/files.xml
  36. 2 12
      res/layout/files_folder_picker.xml
  37. 3 0
      res/layout/log_send_file.xml
  38. 48 0
      res/layout/toolbar_standard.xml
  39. 4 1
      res/layout/upload_files_layout.xml
  40. 30 6
      res/layout/upload_list_layout.xml
  41. 31 20
      res/layout/uploader_layout.xml
  42. 72 0
      res/menu/drawer_menu.xml
  43. 8 6
      res/values-sw360dp/dims.xml
  44. 12 5
      res/values-v21/styles.xml
  45. 14 0
      res/values/dims.xml
  46. 0 41
      res/values/drawer_resources.xml
  47. 1 0
      res/values/strings.xml
  48. 12 3
      res/values/styles.xml
  49. 0 2
      res/xml/preferences.xml
  50. 14 0
      src/com/owncloud/android/authentication/AccountUtils.java
  51. 250 28
      src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java
  52. 1 0
      src/com/owncloud/android/services/OperationsService.java
  53. 94 20
      src/com/owncloud/android/ui/TextDrawable.java
  54. 250 0
      src/com/owncloud/android/ui/activity/BaseActivity.java
  55. 662 0
      src/com/owncloud/android/ui/activity/DrawerActivity.java
  56. 5 502
      src/com/owncloud/android/ui/activity/FileActivity.java
  57. 21 26
      src/com/owncloud/android/ui/activity/FileDisplayActivity.java
  58. 6 13
      src/com/owncloud/android/ui/activity/FolderPickerActivity.java
  59. 3 1
      src/com/owncloud/android/ui/activity/LogHistoryActivity.java
  60. 346 0
      src/com/owncloud/android/ui/activity/ManageAccountsActivity.java
  61. 3 342
      src/com/owncloud/android/ui/activity/Preferences.java
  62. 2 2
      src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java
  63. 121 0
      src/com/owncloud/android/ui/activity/ToolbarActivity.java
  64. 5 3
      src/com/owncloud/android/ui/activity/UploadFilesActivity.java
  65. 11 15
      src/com/owncloud/android/ui/activity/UploadListActivity.java
  66. 172 0
      src/com/owncloud/android/ui/adapter/AccountListAdapter.java
  67. 58 0
      src/com/owncloud/android/ui/adapter/AccountListItem.java
  68. 5 5
      src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java
  69. 3 3
      src/com/owncloud/android/ui/adapter/FileListListAdapter.java
  70. 8 8
      src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java
  71. 0 209
      src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java
  72. 3 3
      src/com/owncloud/android/ui/adapter/UploaderAdapter.java
  73. 3 3
      src/com/owncloud/android/ui/fragment/FileDetailFragment.java
  74. 8 8
      src/com/owncloud/android/ui/preview/PreviewImageActivity.java
  75. 45 2
      src/com/owncloud/android/utils/BitmapUtils.java
  76. 60 0
      src/com/owncloud/android/utils/DisplayUtils.java

+ 6 - 4
AndroidManifest.xml

@@ -59,22 +59,24 @@
         android:name=".MainApp"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
-        android:theme="@style/Theme.ownCloud"
+        android:theme="@style/Theme.ownCloud.Toolbar"
         android:manageSpaceActivity=".ui.activity.ManageSpaceActivity">
         <activity
             android:name=".ui.activity.FileDisplayActivity"
-            android:label="@string/app_name" >
+            android:label="@string/app_name"
+            android:theme="@style/Theme.ownCloud.Toolbar.Drawer">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".ui.activity.ManageAccountsActivity" />
         <activity android:name=".ui.activity.UploadFilesActivity" />
         <activity android:name=".ui.activity.ReceiveExternalFilesActivity"
                   android:taskAffinity=""
                   android:excludeFromRecents="true"
-                  android:theme="@style/Theme.ownCloud">
+                  android:theme="@style/Theme.ownCloud.NoActionBar">
             <intent-filter>
                 <action android:name="android.intent.action.SEND" />
 
@@ -156,7 +158,7 @@
             android:name=".authentication.AuthenticatorActivity"
             android:exported="true"
             android:launchMode="singleTask"
-            android:theme="@style/Theme.ownCloud.noActionBar" >
+            android:theme="@style/Theme.ownCloud.Toolbar" >
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
 

BIN
res/drawable-hdpi/ic_account_plus.png


BIN
res/drawable-hdpi/ic_down.png


BIN
res/drawable-hdpi/ic_key.png


BIN
res/drawable-hdpi/ic_up.png


BIN
res/drawable-mdpi/ic_account_plus.png


BIN
res/drawable-mdpi/ic_down.png


BIN
res/drawable-mdpi/ic_key.png


BIN
res/drawable-mdpi/ic_up.png


BIN
res/drawable-xhdpi/ic_account_plus.png


BIN
res/drawable-xhdpi/ic_down.png


BIN
res/drawable-xhdpi/ic_key.png


BIN
res/drawable-xhdpi/ic_up.png


BIN
res/drawable-xxhdpi/ic_account_plus.png


BIN
res/drawable-xxhdpi/ic_down.png


BIN
res/drawable-xxhdpi/ic_key.png


BIN
res/drawable-xxhdpi/ic_up.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_1.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_2.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_3.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_4.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_5.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_6.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_7.png


BIN
res/drawable-xxhdpi/owncloud_progressbar_indeterminate_8.png


+ 3 - 3
res/drawable/actionbar_progress_horizontal.xml

@@ -4,15 +4,15 @@
 
     <item
         android:id="@android:id/background"
-        android:drawable="@color/owncloud_blue" />
+        android:drawable="@color/primary" />
     <item android:id="@android:id/secondaryProgress">
         <scale
-            android:drawable="@color/owncloud_blue"
+            android:drawable="@color/primary"
             android:scaleWidth="100%" />
     </item>
     <item android:id="@android:id/progress">
         <scale
-            android:drawable="@color/owncloud_blue"
+            android:drawable="@color/primary"
             android:scaleWidth="100%" />
     </item>
 

+ 47 - 0
res/layout/account_action.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 2016 ownCloud Inc.
+  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/>.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="72dp"
+    android:orientation="horizontal"
+    android:weightSum="1">
+
+    <ImageView
+        android:id="@+id/user_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="@dimen/list_item_avatar_icon_margin"
+        android:src="@drawable/ic_account_plus"/>
+
+    <TextView
+        android:id="@+id/user_name"
+        android:layout_width="0dp"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="@dimen/list_item_avatar_text_margin"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:paddingRight="@dimen/standard_padding"
+        android:text="@string/placeholder_sentence"
+        android:textColor="@color/primary"
+        android:textSize="@dimen/two_line_primary_text_size"/>
+
+</LinearLayout>         

+ 68 - 0
res/layout/account_item.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 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/>.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="72dp"
+    android:orientation="horizontal"
+    android:weightSum="1">
+
+    <ImageView
+        android:id="@+id/user_icon"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_gravity="center_vertical"
+        android:layout_marginLeft="@dimen/standard_margin"
+        android:src="@drawable/ic_menu_archive"/>
+
+    <TextView
+        android:id="@+id/user_name"
+        android:layout_width="0dp"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="@dimen/standard_margin"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:paddingRight="@dimen/standard_padding"
+        android:text="@string/placeholder_filename"
+        android:textColor="@color/textColor"
+        android:textSize="@dimen/two_line_primary_text_size"/>
+
+    <ImageView
+        android:id="@+id/passwordButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingLeft="@dimen/standard_half_padding"
+        android:paddingTop="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_padding"
+        android:paddingRight="@dimen/standard_half_padding"
+        android:src="@drawable/ic_key"/>
+
+    <ImageView
+        android:id="@+id/removeButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingLeft="@dimen/standard_half_padding"
+        android:paddingTop="@dimen/standard_padding"
+        android:paddingBottom="@dimen/standard_padding"
+        android:paddingRight="@dimen/standard_padding"
+        android:src="@drawable/ic_close"/>
+
+</LinearLayout>         

+ 23 - 12
res/layout/drawer_radiobutton.xml → res/layout/accounts_layout.xml

@@ -1,8 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
+<!-- 
   ownCloud Android client application
 
-  Copyright (C) 2015 ownCloud Inc.
+  Copyright (C) 2016 ownCloud Inc.
+  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,
@@ -15,13 +16,23 @@
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
--->
-<RadioButton xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drawer_radiobutton"
-    android:layout_width="fill_parent"
-    android:layout_height="56dp"
-    android:gravity="center_vertical"
-    android:paddingLeft="@dimen/standard_padding"
-    android:paddingRight="@dimen/standard_padding"
-    android:textColor="@color/black"
-    android:textSize="18dp" />
+ -->
+<LinearLayout
+	android:id="@+id/upload_files_layout"
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="fill_parent"
+	android:layout_height="fill_parent"
+	android:orientation="vertical">
+
+	<include
+		layout="@layout/toolbar_standard"/>
+
+	<ListView
+		android:id="@+id/account_list"
+		android:layout_width="fill_parent"
+		android:layout_height="fill_parent"
+		android:divider="@color/list_divider_background"
+		android:dividerHeight="1dip">
+	</ListView>
+
+</LinearLayout>

+ 9 - 80
res/layout/drawer.xml

@@ -16,88 +16,17 @@
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <RelativeLayout
-        android:id="@+id/left_drawer"
-        android:layout_width="240dp"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+       xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <android.support.design.widget.NavigationView
+        android:id="@+id/nav_view"
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:layout_gravity="start"
-        android:gravity="center"
-        android:orientation="vertical"
         android:fitsSystemWindows="true"
-        android:background="@color/background_color"
-        >
-
-        <!--TODO re-enable when "Accounts" is available in Navigation Drawer-->
-        <!--<LinearLayout-->
-            <!--android:layout_width="match_parent"-->
-            <!--android:layout_height="wrap_content"-->
-            <!--android:layout_margin="5dp">-->
-
-            <!--<ImageView-->
-                <!--android:id="@+id/drawer_userIcon"-->
-                <!--android:layout_width="40dp"-->
-                <!--android:layout_height="40dp"-->
-                <!--android:src="@drawable/abc_ab_bottom_solid_dark_holo" />-->
-
-            <!--<TextView-->
-                <!--android:id="@+id/drawer_username"-->
-                <!--android:layout_width="wrap_content"-->
-                <!--android:layout_height="wrap_content"-->
-                <!--android:layout_gravity="center_vertical"-->
-                <!--android:layout_marginLeft="5dp"-->
-                <!--android:layout_marginStart="5dp"-->
-                <!--android:textAppearance="?android:attr/textAppearanceLarge" />-->
-
-        <!--</LinearLayout>-->
-
-        <!--<TextView-->
-            <!--android:layout_width="fill_parent"-->
-            <!--android:layout_height="2dip"-->
-            <!--android:background="@color/list_item_lastmod_and_filesize_text" />-->
-
-        <ListView
-            android:id="@+id/drawer_list"
-            android:choiceMode="singleChoice"
-            android:layout_width="fill_parent"
-            android:layout_height="match_parent"
-            android:background="@color/background_color"
-            android:divider="@null"
-            android:dividerHeight="0dp"
-            android:paddingTop="100dp" />
-
-        <LinearLayout
-            android:layout_width="fill_parent"
-            android:layout_height="100dp"
-            android:paddingTop="@dimen/standard_padding"
-            android:paddingBottom="@dimen/standard_padding"
-            android:background="@color/drawer_header_color">
-
-            <ImageView
-                android:id="@+id/itemIcon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_marginLeft="@dimen/standard_padding"
-                android:layout_marginBottom="3dp"
-                android:layout_gravity="bottom"
-                android:src="@drawable/ic_account_circle"
-                />
+        app:theme="@style/NavigationView_ItemTextAppearance"
+        app:headerLayout="@layout/drawer_header"
+        app:menu="@menu/drawer_menu"/>
 
-            <TextView
-                android:id="@+id/drawer_username"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="bottom"
-                android:gravity="bottom"
-                android:orientation="vertical"
-                android:text="@string/app_name"
-                android:textColor="#FFF"
-                android:paddingLeft="22dp"
-                android:paddingRight="@dimen/standard_padding"
-                android:textSize="24sp"
-                android:ellipsize="end"
-                android:singleLine="true"
-                />
-        </LinearLayout>
-    </RelativeLayout>
 </merge>

+ 0 - 22
res/layout/drawer_account_group.xml

@@ -1,22 +0,0 @@
-<!--
-  Copyright 2013 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
-  -->
-  <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/drawer_radio_group"
-        android:layout_width="fill_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:orientation="vertical" >
-    </RadioGroup>

+ 123 - 0
res/layout/drawer_header.xml

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 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/>.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="@dimen/nav_drawer_header_height"
+              android:background="@color/drawer_header_color">
+
+    <RelativeLayout
+        android:id="@+id/drawer_active_user"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:padding="@dimen/standard_padding"
+        >
+
+        <FrameLayout
+            android:id="@+id/drawer_user_avatars"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            android:layout_marginBottom="@dimen/standard_half_margin">
+
+            <ImageView
+                android:id="@+id/drawer_current_account"
+                android:layout_width="@dimen/nav_drawer_header_avatar"
+                android:layout_height="@dimen/nav_drawer_header_avatar"
+                android:src="@drawable/ic_account_circle"/>
+
+            <ImageView
+                android:id="@+id/drawer_account_middle"
+                android:layout_width="@dimen/nav_drawer_header_avatar_other_accounts_size"
+                android:layout_height="@dimen/nav_drawer_header_avatar_other_accounts_size"
+                android:layout_gravity="right"
+                android:layout_marginEnd="@dimen/nav_drawer_header_avatar_second_account_margin"
+                android:layout_marginRight="@dimen/nav_drawer_header_avatar_second_account_margin"
+                android:src="@drawable/ic_account_circle"
+                android:onClick="onAccountDrawerClick"/>
+
+            <ImageView
+                android:id="@+id/drawer_account_end"
+                android:layout_width="@dimen/nav_drawer_header_avatar_other_accounts_size"
+                android:layout_height="@dimen/nav_drawer_header_avatar_other_accounts_size"
+                android:layout_gravity="right"
+                android:src="@drawable/ic_account_circle"
+                android:onClick="onAccountDrawerClick"/>
+
+        </FrameLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/drawer_user_avatars"
+            >
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:layout_weight="1"
+                android:orientation="vertical"
+                android:paddingRight="@dimen/standard_half_padding">
+
+                <TextView
+                    android:id="@+id/drawer_username"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:shadowColor="@color/black"
+                    android:shadowDx="0.5"
+                    android:shadowDy="0"
+                    android:shadowRadius="2"
+                    android:singleLine="true"
+                    android:text="@string/app_name"
+                    android:textColor="@android:color/white"
+                    android:textSize="14sp"
+                    android:textStyle="bold"/>
+
+                <TextView
+                    android:id="@+id/drawer_username_full"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:lines="1"
+                    android:maxLines="1"
+                    android:shadowColor="@color/black"
+                    android:shadowDx="0.5"
+                    android:shadowDy="0"
+                    android:shadowRadius="2"
+                    android:text="@string/app_name"
+                    android:textColor="@android:color/white"
+                    android:textSize="12sp"/>
+
+            </LinearLayout>
+
+            <ImageView
+                android:id="@+id/drawer_account_chooser_toogle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom"
+                android:contentDescription="@string/drawer_manage_accounts"
+                android:src="@drawable/ic_down"/>
+
+        </LinearLayout>
+
+    </RelativeLayout>
+
+</LinearLayout>

+ 0 - 52
res/layout/drawer_list_item.xml

@@ -1,52 +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/>.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/itemLayout"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
-    android:layout_alignParentLeft="true"
-    android:orientation="horizontal"
-    android:background="@color/background_color"
-    android:layout_marginTop="@dimen/standard_margin"
-    android:layout_marginBottom="@dimen/standard_margin"
-    android:minHeight="?android:attr/listPreferredItemHeight">
-
-    <ImageView
-        android:id="@+id/itemIcon"
-        android:layout_width="24sp"
-        android:layout_height="24sp"
-        android:layout_marginLeft="@dimen/standard_margin"
-        android:layout_gravity="center_vertical"
-        />
-
-    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/itemTitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="22dp"
-        android:paddingRight="@dimen/standard_margin"
-        android:textColor="@color/drawerMenuTextColor"
-        android:text="@string/app_name"
-        android:textStyle="normal"
-        android:layout_gravity="center_vertical"
-        android:textSize="14sp"
-        />
-</LinearLayout>
-

+ 0 - 1
res/layout/file_preview.xml

@@ -23,7 +23,6 @@
     android:id="@+id/top"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/background_color"
     android:gravity="center"
     tools:context=".ui.fragment.FilePreviewFragment">
 

+ 4 - 11
res/layout/files.xml

@@ -18,9 +18,11 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
   -->
 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                                        xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
     android:clickable="true" >
 
     <!-- The main content view -->
@@ -29,17 +31,8 @@
         android:layout_height="match_parent"
         android:orientation="vertical">
 
-        <ProgressBar android:id="@+id/progressBar"
-            android:layout_width="match_parent"
-            android:layout_height="4dp"
-            android:padding="0dp"
-            android:layout_margin="0dp"
-            style="@style/Widget.ownCloud.TopProgressBar"
-            android:indeterminate="false"
-            android:indeterminateOnly="false"
-            android:background="@color/background_color"
-            android:visibility="visible"
-            />
+        <include
+            layout="@layout/toolbar_standard" />
 
         <LinearLayout
             xmlns:android="http://schemas.android.com/apk/res/android"

+ 2 - 12
res/layout/files_folder_picker.xml

@@ -19,20 +19,10 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/background_color"
     android:orientation="vertical" >
 
-    <ProgressBar android:id="@+id/progressBar"
-        android:layout_width="match_parent"
-        android:layout_height="4dp"
-        android:padding="0dp"
-        android:layout_margin="0dp"
-        style="@style/Widget.ownCloud.TopProgressBar"
-        android:indeterminate="false"
-        android:indeterminateOnly="false"
-        android:background="@color/background_color"
-        android:visibility="visible"
-        />
+    <include
+        layout="@layout/toolbar_standard" />
 
 	<FrameLayout 
 		android:layout_width="match_parent"

+ 3 - 0
res/layout/log_send_file.xml

@@ -22,6 +22,9 @@
     android:orientation="vertical"
     android:weightSum="1" >
 
+    <include
+        layout="@layout/toolbar_standard" />
+
     <ScrollView
         android:id="@+id/scrollView1"
         android:layout_width="match_parent"

+ 48 - 0
res/layout/toolbar_standard.xml

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 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/>.
+-->
+<android.support.design.widget.AppBarLayout
+    android:id="@+id/appbar"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/actionBarSize"
+        android:background="?attr/colorPrimary"
+        app:layout_scrollFlags="scroll|enterAlways|snap"
+        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>
+
+    <ProgressBar
+        android:id="@+id/progressBar"
+        style="@style/Widget.ownCloud.TopProgressBar"
+        android:layout_width="match_parent"
+        android:layout_height="4dp"
+        android:layout_margin="0dp"
+        android:background="@color/primary"
+        android:indeterminate="false"
+        android:indeterminateOnly="false"
+        android:padding="0dp"
+        android:visibility="visible"
+        />
+
+</android.support.design.widget.AppBarLayout>

+ 4 - 1
res/layout/upload_files_layout.xml

@@ -17,12 +17,15 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/upload_files_layout"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
-    android:background="@color/background_color"
     android:orientation="vertical" >
 
+    <include
+        layout="@layout/toolbar_standard" />
+
     <fragment
         android:id="@+id/local_files_list"
         android:layout_width="match_parent"

+ 30 - 6
res/layout/upload_list_layout.xml

@@ -1,18 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 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/>.
+  -->
 <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/drawer_layout"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:clickable="true" >
+    android:id="@+id/drawer_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clickable="true"
+    android:fitsSystemWindows="true">
 
+    <!-- The main content view -->
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="@color/background_color">
+        android:orientation="vertical">
+
+        <include
+            layout="@layout/toolbar_standard"/>
 
         <FrameLayout
             android:id="@+id/upload_list_fragment"
             android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
+            android:layout_height="match_parent"
+            android:background="@color/background_color"/>
     </LinearLayout>
 
     <include

+ 31 - 20
res/layout/uploader_layout.xml

@@ -17,21 +17,21 @@
   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"
-	android:layout_height="wrap_content"
-	android:orientation="vertical"
-	android:layout_width="wrap_content"
-	android:background="@color/white"
-	android:gravity="center">
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/upload_files_layout"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
 
-	<FrameLayout
-		android:layout_height="match_parent"
-		android:layout_width="match_parent"
-		android:id="@+id/upload_list"
-		android:layout_above="@+id/upload_actions">
+    <include
+        layout="@layout/toolbar_standard" />
+
+	<FrameLayout android:layout_height="0dp"
+		android:layout_width="fill_parent"
+		android:id="@+id/frameLayout1"
+		android:layout_weight="1">
 
-		<ListView
-			android:id="@android:id/list"
+		<ListView android:id="@android:id/list"
 			android:layout_width="fill_parent"
 			android:layout_height="fill_parent"
 			android:divider="@color/list_divider_background"
@@ -41,12 +41,23 @@
 	</FrameLayout>
 
 	<LinearLayout
-	    android:id="@+id/upload_actions"
-	    android:layout_width="fill_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_alignParentBottom="true"
-	    android:orientation="horizontal"
-	    android:padding="@dimen/standard_padding">
+		android:layout_width="match_parent"
+		android:layout_height="wrap_content"
+		android:orientation="horizontal">
+
+		<ImageView
+			android:layout_width="match_parent"
+			android:layout_height="1dp"
+			android:src="@drawable/uploader_list_separator"/>
+
+	</LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:orientation="horizontal"
+        android:padding="@dimen/standard_padding">
 
 		<android.support.v7.widget.AppCompatButton
 			android:theme="@style/Button"
@@ -68,4 +79,4 @@
 		    android:text="@string/uploader_btn_upload_text" />
 
 	</LinearLayout>
-</RelativeLayout>
+</LinearLayout>

+ 72 - 0
res/menu/drawer_menu.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ownCloud Android client application
+
+  Copyright (C) 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/>.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <!--
+      standard menu
+      all items in this group MUST have orderInCategory="0" set
+    -->
+    <group android:id="@+id/drawer_menu_standard" android:checkableBehavior="single">
+        <item
+            android:orderInCategory="0"
+            android:id="@+id/nav_all_files"
+            android:icon="@drawable/ic_folder_open"
+            android:title="@string/drawer_item_all_files"/>
+        <!-- TODO activate when On Device branch is merged
+        <item
+            android:orderInCategory="0"
+            android:id="@+id/nav_on_device"
+            android:icon="@drawable/ic_action_download_grey"
+            android:title="@string/drawer_item_on_device"/>
+         -->
+        <item
+            android:orderInCategory="0"
+            android:id="@+id/nav_uploads"
+            android:icon="@drawable/ic_uploads"
+            android:title="Uploads"/>
+    </group>
+
+    <!--
+      account list placeholder
+      all items in this group MUST have orderInCategory="2" set
+      all accounts are dynamically added with orderInCategory="1" set
+    -->
+    <group android:id="@+id/drawer_menu_accounts">
+        <item
+            android:orderInCategory="2"
+            android:id="@+id/drawer_menu_account_add"
+            android:icon="@drawable/ic_account_plus"
+            android:title="@string/prefs_add_account"/>
+        <item
+            android:orderInCategory="2"
+            android:id="@+id/drawer_menu_account_manage"
+            android:icon="@drawable/ic_settings"
+            android:title="@string/drawer_manage_accounts"/>
+    </group>
+
+    <!--
+      all items in this group MUST have orderInCategory="3" set
+    -->
+    <group>
+        <item
+            android:orderInCategory="3"
+            android:id="@+id/nav_settings"
+            android:icon="@drawable/ic_settings"
+            android:title="@string/actionbar_settings"/>
+    </group>
+</menu>

+ 8 - 6
res/menu/account_picker_long_click.xml → res/values-sw360dp/dims.xml

@@ -2,8 +2,7 @@
 <!--
   ownCloud Android client application
 
-  Copyright (C) 2012  Bartek Przybylski
-  Copyright (C) 2015 ownCloud Inc.
+  Copyright (C) 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,
@@ -17,7 +16,10 @@
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-    <item android:id="@+id/change_password" android:title="@string/change_password"></item>
-    <item android:id="@+id/delete_account" android:title="@string/delete_account"></item>
-</menu>
+<resources>
+  <!-- Default screen margins, per the Android Design guidelines. -->
+  <dimen name="nav_drawer_header_height">164dp</dimen>
+  <dimen name="nav_drawer_header_avatar">64dp</dimen>
+  <!-- avatar radius needs to 1/2 of the avatar dp value -->
+  <dimen name="nav_drawer_header_avatar_radius">32dp</dimen>
+</resources>

+ 12 - 5
res/values-v21/styles.xml

@@ -17,12 +17,19 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
-	<style name="ProgressDialogTheme" parent="ownCloud.Dialog">
-	</style>
-    <style name="Theme.ownCloud.NoActionBar">
-        <item name="windowActionBar">false</item>
-        <item name="windowNoTitle">true</item>
+    <style name="Theme.ownCloud.Toolbar">
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="windowNoTitle">true</item>
+        <item name="windowActionBar">false</item>
+        <item name="colorPrimary">@color/primary</item>
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <item name="colorAccent">@color/color_accent</item>
+        <item name="android:alertDialogTheme">@style/Theme.ownCloud.Dialog</item>
+        <item name="alertDialogTheme">@style/ownCloud.AlertDialog</item>
+        <item name="searchViewStyle">@style/ownCloud.SearchView</item>
+    </style>
+
+    <style name="Theme.ownCloud.Toolbar.Drawer">
         <item name="android:statusBarColor">@android:color/transparent</item>
     </style>
 </resources>

+ 14 - 0
res/values/dims.xml

@@ -21,9 +21,20 @@
     <dimen name="activity_horizontal_margin">@dimen/standard_padding</dimen>
     <dimen name="activity_vertical_margin">@dimen/standard_padding</dimen>
     <dimen name="nav_drawer_width">260dp</dimen>
+    <dimen name="nav_drawer_header_height">140dp</dimen>
+    <dimen name="nav_drawer_header_avatar">56dp</dimen>
+    <!-- avatar radius needs to 1/2 of the avatar dp value -->
+    <dimen name="nav_drawer_header_avatar_radius">28dp</dimen>
+    <dimen name="nav_drawer_header_avatar_other_accounts_size">40dp</dimen>
+    <dimen name="nav_drawer_header_avatar_other_accounts_radius">20dp</dimen>
+    <!-- margin must be nav_drawer_header_avatar_other_accounts_size+16dp -->
+    <dimen name="nav_drawer_header_avatar_second_account_margin">56dp</dimen>
+    <dimen name="nav_drawer_menu_avatar_radius">12sp</dimen>
+    <dimen name="list_item_avatar_icon_radius">20dp</dimen>
 
     <dimen name="file_icon_size">32dp</dimen>
     <dimen name="file_icon_size_grid">128dp</dimen>
+    <dimen name="file_avatar_size">128dp</dimen>
     <dimen name="standard_padding">16dp</dimen>
     <dimen name="standard_half_padding">8dp</dimen>
     <dimen name="standard_margin">16dp</dimen>
@@ -33,4 +44,7 @@
 
     <dimen name="two_line_primary_text_size">16sp</dimen>
     <dimen name="two_line_secondary_text_size">14sp</dimen>
+
+    <dimen name="list_item_avatar_icon_margin">20dp</dimen>
+    <dimen name="list_item_avatar_text_margin">28dp</dimen>
 </resources>

+ 0 - 41
res/values/drawer_resources.xml

@@ -1,41 +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/>.
--->
-
-<resources>
-    <!-- Nav Drawer Menu Items -->
-    <string-array name="drawer_items">
-        <!--TODO re-enable when "Accounts" is available in Navigation Drawer-->
-        <!--<item>@string/prefs_accounts</item>-->
-        <item>@string/drawer_item_all_files</item>
-        <!--<item>@string/drawer_item_on_device</item>-->
-        <item>@string/drawer_item_uploads_list</item>
-        <item>@string/actionbar_settings</item>
-    </string-array>
-
-    <!-- Nav Drawer Content Descriptions -->
-    <string-array name="drawer_content_descriptions">
-        <!-- TODO re-enable when "Accounts" is available in Navigation Drawer-->
-        <!--<item>@string/drawer_item_accounts</item>-->
-        <item>@string/drawer_item_all_files</item>
-        <!--<item>@string/drawer_item_on_device</item>-->
-        <item>@string/drawer_item_uploads_list</item>
-        <item>@string/drawer_item_settings</item>
-    </string-array>
-
-</resources>

+ 1 - 0
res/values/strings.xml

@@ -347,6 +347,7 @@
 
     <string name="prefs_category_accounts">Accounts</string>
     <string name="prefs_add_account">Add account</string>
+    <string name="drawer_manage_accounts">Manage accounts</string>
     <string name="auth_redirect_non_secure_connection_title">Secure connection is redirected through an unsecured route.</string>
 
 	<string name="actionbar_logger">Logs</string>

+ 12 - 3
res/values/styles.xml

@@ -34,7 +34,7 @@
 	</style>
 
 	<!-- seperate action bar style for activities without an action bar -->
-	<style name="Theme.ownCloud.noActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
+	<style name="Theme.ownCloud.Toolbar" parent="Theme.AppCompat.Light.NoActionBar">
 		<item name="windowNoTitle">true</item>
 		<item name="windowActionBar">false</item>
 		<item name="colorPrimary">@color/primary</item>
@@ -46,8 +46,12 @@
 		<item name="searchViewStyle">@style/ownCloud.SearchView</item>
     </style>
 
-	<style name="Theme.ownCloud.noActionBar.Login" parent="Theme.ownCloud.noActionBar">
-		<item name="colorAccent">@color/white</item>
+	<!-- separate style for Drawer activities needed for v21+ theming -->
+	<style name="Theme.ownCloud.Toolbar.Drawer" parent="Theme.ownCloud.Toolbar">
+	</style>
+
+	<style name="Theme.ownCloud.noActionBar.Login" parent="Theme.ownCloud.Toolbar">
+
 	</style>
 
 	<style name="ownCloud.AlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
@@ -160,6 +164,11 @@
 		<item name="android:textColor">@color/fab_white</item>
 	</style>
 
+	<style name="NavigationView_ItemTextAppearance">
+		<item name="android:ellipsize">end</item>
+		<item name="android:listDivider">@color/transparent</item>
+	</style>
+
 	<!-- Button Bar hack due to Lollipop bug:
 		https://code.google.com/p/android/issues/detail?id=78302
 	fix see:

+ 0 - 2
res/xml/preferences.xml

@@ -18,8 +18,6 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -->
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
-    <PreferenceCategory android:title="@string/prefs_category_accounts" android:key="accounts_category">
-    </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/prefs_category_instant_uploading" android:key="instant_uploading_category">
 		<com.owncloud.android.ui.CheckBoxPreferenceWithLongTitle android:key="instant_uploading"

+ 14 - 0
src/com/owncloud/android/authentication/AccountUtils.java

@@ -110,6 +110,20 @@ public class AccountUtils {
         }
         return false;
     }
+
+    /**
+     * returns the user's name based on the account name.
+     *
+     * @param accountName the account name
+     * @return the user's name
+     */
+    public static String getAccountUsername(String accountName) {
+        if (accountName != null) {
+            return accountName.substring(0, accountName.lastIndexOf("@"));
+        } else {
+            return null;
+        }
+    }
     
     /**
      * Returns owncloud account identified by accountName or null if it does not exist.

+ 250 - 28
src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java

@@ -21,13 +21,6 @@
 
 package com.owncloud.android.datamodel;
 
-import java.io.File;
-import java.io.InputStream;
-import java.lang.ref.WeakReference;
-
-import org.apache.commons.httpclient.HttpStatus;
-import org.apache.commons.httpclient.methods.GetMethod;
-
 import android.accounts.Account;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -35,11 +28,12 @@ import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.media.Image;
 import android.media.ThumbnailUtils;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.view.MenuItem;
 import android.widget.ImageView;
 
 import com.owncloud.android.MainApp;
@@ -53,6 +47,14 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion;
 import com.owncloud.android.ui.adapter.DiskLruImageCache;
 import com.owncloud.android.utils.BitmapUtils;
 import com.owncloud.android.utils.DisplayUtils;
+import com.owncloud.android.utils.DisplayUtils.AvatarGenerationListener;
+
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.GetMethod;
+
+import java.io.File;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
 
 /**
  * Manager for concurrent access to thumbnails cache.
@@ -323,19 +325,6 @@ public class ThumbnailsCacheManager {
 
         }
 
-        private Bitmap handlePNG(Bitmap bitmap, int px){
-            Bitmap resultBitmap = Bitmap.createBitmap(px,
-                    px,
-                    Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(resultBitmap);
-
-            c.drawColor(MainApp.getAppContext().getResources().
-                    getColor(R.color.background_color));
-            c.drawBitmap(bitmap, 0, 0, null);
-
-            return resultBitmap;
-        }
-
         private Bitmap doFileInBackground() {
             File file = (File)mFile;
 
@@ -361,7 +350,147 @@ public class ThumbnailsCacheManager {
 
     }
 
-    public static boolean cancelPotentialWork(Object file, ImageView imageView) {
+    public static class AvatarGenerationTask extends AsyncTask<String, Void, Bitmap> {
+        private final WeakReference<AvatarGenerationListener> mAvatarGenerationListener;
+        private final Object mCallContext;
+        private Account mAccount;
+        private String mUsername;
+
+
+        public AvatarGenerationTask(AvatarGenerationListener avatarGenerationListener, Object callContext,
+                                    FileDataStorageManager storageManager, Account account) {
+            mAvatarGenerationListener = new WeakReference<>(avatarGenerationListener);
+            mCallContext = callContext;
+            if (storageManager == null)
+                throw new IllegalArgumentException("storageManager must not be NULL");
+            mAccount = account;
+        }
+
+        @Override
+        protected Bitmap doInBackground(String... params) {
+            Bitmap thumbnail = null;
+
+            try {
+                if (mAccount != null) {
+                    OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount,
+                            MainApp.getAppContext());
+                    mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
+                            getClientFor(ocAccount, MainApp.getAppContext());
+                }
+
+                mUsername = params[0];
+                thumbnail = doAvatarInBackground();
+
+            } catch(Throwable t){
+                // the app should never break due to a problem with avatars
+                Log_OC.e(TAG, "Generation of avatar for " + mUsername + " failed", t);
+                if (t instanceof OutOfMemoryError) {
+                    System.gc();
+                }
+            }
+
+            return thumbnail;
+        }
+
+        protected void onPostExecute(Bitmap bitmap) {
+            if (bitmap != null) {
+                if (mAvatarGenerationListener != null) {
+                    AvatarGenerationListener listener = mAvatarGenerationListener.get();
+                    AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(mCallContext);
+                    if (this == avatarWorkerTask) {
+                        if (listener.shouldCallGeneratedCallback(mUsername, mCallContext)) {
+                            listener.avatarGenerated(new BitmapDrawable(bitmap), mCallContext);
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Add thumbnail to cache
+         * @param imageKey: thumb key
+         * @param bitmap:   image for extracting thumbnail
+         * @param path:     image path
+         * @param px:       thumbnail dp
+         * @return Bitmap
+         */
+        private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){
+
+            Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+
+            // Rotate image, obeying exif tag
+            thumbnail = BitmapUtils.rotateImage(thumbnail,path);
+
+            // Add thumbnail to cache
+            addBitmapToCache(imageKey, thumbnail);
+
+            return thumbnail;
+        }
+
+        /**
+         * Converts size of file icon from dp to pixel
+         * @return int
+         */
+        private int getAvatarDimension(){
+            // Converts dp to pixel
+            Resources r = MainApp.getAppContext().getResources();
+            return Math.round(r.getDimension(R.dimen.file_avatar_size));
+        }
+
+        private Bitmap doAvatarInBackground() {
+            String username = (String) mUsername;
+
+            final String imageKey = "a_" + username;
+
+            // Check disk cache in background thread
+            Bitmap avatar = getBitmapFromDiskCache(imageKey);
+
+            // Not found in disk cache
+            if (avatar == null) {
+
+                int px = getAvatarDimension();
+
+                // Download avatar from server
+                OwnCloudVersion serverOCVersion = AccountUtils.getServerVersion(mAccount);
+                if (mClient != null && serverOCVersion != null) {
+                    if (serverOCVersion.supportsRemoteThumbnails()) {
+                        GetMethod get = null;
+                        try {
+                            String uri = mClient.getBaseUri() + "" +
+                                    "/index.php/avatar/" + AccountUtils.getAccountUsername(username) + "/" + px;
+                            Log_OC.d("Avatar", "URI: " + uri);
+                            get = new GetMethod(uri);
+                            int status = mClient.executeMethod(get);
+                            if (status == HttpStatus.SC_OK) {
+                                InputStream inputStream = get.getResponseBodyAsStream();
+                                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
+                                avatar = ThumbnailUtils.extractThumbnail(bitmap, px, px);
+
+                                // Add avatar to cache
+                                if (avatar != null) {
+                                    avatar = handlePNG(avatar, px);
+                                    addBitmapToCache(imageKey, avatar);
+                                }
+                            } else {
+                                mClient.exhaustResponse(get.getResponseBodyAsStream());
+                            }
+                        } catch (Exception e) {
+                            Log_OC.e(TAG, "Error downloading avatar", e);
+                        } finally {
+                            if (get != null) {
+                                get.releaseConnection();
+                            }
+                        }
+                    } else {
+                        Log_OC.d(TAG, "Server too old");
+                    }
+                }
+            }
+            return avatar;
+        }
+    }
+
+    public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) {
         final ThumbnailGenerationTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 
         if (bitmapWorkerTask != null) {
@@ -380,31 +509,124 @@ public class ThumbnailsCacheManager {
         return true;
     }
 
+    public static boolean cancelPotentialAvatarWork(Object file, Object callContext) {
+        if (callContext instanceof ImageView)
+            return cancelPotentialAvatarWork(file, (ImageView)callContext);
+        else if (callContext instanceof MenuItem)
+            return cancelPotentialAvatarWork(file, (MenuItem)callContext);
+
+        return false;
+    }
+
+    public static boolean cancelPotentialAvatarWork(Object file, ImageView imageView) {
+        final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(imageView);
+
+        if (avatarWorkerTask != null) {
+            final Object usernameData = avatarWorkerTask.mUsername;
+            // If usernameData is not yet set or it differs from the new data
+            if (usernameData == null || usernameData != file) {
+                // Cancel previous task
+                avatarWorkerTask.cancel(true);
+                Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView");
+            } else {
+                // The same work is already in progress
+                return false;
+            }
+        }
+        // No task associated with the ImageView, or an existing task was cancelled
+        return true;
+    }
+
+    public static boolean cancelPotentialAvatarWork(Object file, MenuItem menuItem) {
+        final AvatarGenerationTask avatarWorkerTask = getAvatarWorkerTask(menuItem);
+
+        if (avatarWorkerTask != null) {
+            final Object usernameData = avatarWorkerTask.mUsername;
+            // If usernameData is not yet set or it differs from the new data
+            if (usernameData == null || usernameData != file) {
+                // Cancel previous task
+                avatarWorkerTask.cancel(true);
+                Log_OC.v(TAG, "Cancelled generation of avatar for a reused imageView");
+            } else {
+                // The same work is already in progress
+                return false;
+            }
+        }
+        // No task associated with the ImageView, or an existing task was cancelled
+        return true;
+    }
+
     public static ThumbnailGenerationTask getBitmapWorkerTask(ImageView imageView) {
         if (imageView != null) {
             final Drawable drawable = imageView.getDrawable();
-            if (drawable instanceof AsyncDrawable) {
-                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+            if (drawable instanceof AsyncThumbnailDrawable) {
+                final AsyncThumbnailDrawable asyncDrawable = (AsyncThumbnailDrawable) drawable;
                 return asyncDrawable.getBitmapWorkerTask();
             }
         }
         return null;
     }
 
-    public static class AsyncDrawable extends BitmapDrawable {
+    public static AvatarGenerationTask getAvatarWorkerTask(Object callContext) {
+        if (callContext instanceof ImageView)
+            return getAvatarWorkerTask(((ImageView)callContext).getDrawable());
+        else if (callContext instanceof MenuItem)
+            return getAvatarWorkerTask(((MenuItem)callContext).getIcon());
+
+        return null;
+    }
+
+    private static AvatarGenerationTask getAvatarWorkerTask(Drawable drawable) {
+        if (drawable instanceof AsyncAvatarDrawable) {
+            final AsyncAvatarDrawable asyncDrawable = (AsyncAvatarDrawable) drawable;
+            return asyncDrawable.getAvatarWorkerTask();
+        }
+        return null;
+    }
+
+    public static class AsyncThumbnailDrawable extends BitmapDrawable {
         private final WeakReference<ThumbnailGenerationTask> bitmapWorkerTaskReference;
 
-        public AsyncDrawable(
+        public AsyncThumbnailDrawable(
                 Resources res, Bitmap bitmap, ThumbnailGenerationTask bitmapWorkerTask
         ) {
 
             super(res, bitmap);
-            bitmapWorkerTaskReference =
-                    new WeakReference<ThumbnailGenerationTask>(bitmapWorkerTask);
+            bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
         }
 
         public ThumbnailGenerationTask getBitmapWorkerTask() {
             return bitmapWorkerTaskReference.get();
         }
     }
+
+    public static class AsyncAvatarDrawable extends BitmapDrawable {
+        private final WeakReference<AvatarGenerationTask> avatarWorkerTaskReference;
+
+        public AsyncAvatarDrawable(
+                Resources res, Bitmap bitmap, AvatarGenerationTask avatarWorkerTask
+        ) {
+
+            super(res, bitmap);
+            avatarWorkerTaskReference =
+                    new WeakReference<AvatarGenerationTask>(avatarWorkerTask);
+        }
+
+        public AvatarGenerationTask getAvatarWorkerTask() {
+            return avatarWorkerTaskReference.get();
+        }
+    }
+
+    private static Bitmap handlePNG(Bitmap bitmap, int px){
+        Bitmap resultBitmap = Bitmap.createBitmap(px,
+                px,
+                Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(resultBitmap);
+
+        c.drawColor(MainApp.getAppContext().getResources().
+                getColor(R.color.background_color));
+        c.drawBitmap(bitmap, 0, 0, null);
+
+        return resultBitmap;
+    }
 }

+ 1 - 0
src/com/owncloud/android/services/OperationsService.java

@@ -107,6 +107,7 @@ public class OperationsService extends Service {
     public static final String ACTION_GET_SERVER_INFO = "GET_SERVER_INFO";
     public static final String ACTION_OAUTH2_GET_ACCESS_TOKEN = "OAUTH2_GET_ACCESS_TOKEN";
     public static final String ACTION_GET_USER_NAME = "GET_USER_NAME";
+    public static final String ACTION_GET_USER_AVATAR = "GET_USER_AVATAR";
     public static final String ACTION_RENAME = "RENAME";
     public static final String ACTION_REMOVE = "REMOVE";
     public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER";

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

@@ -1,3 +1,23 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Andy Scherzinger
+ * @author Tobias Kaminsiky
+ * Copyright (C) 2016 ownCloud Inc.
+ * <p/>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ * <p/>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p/>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
 package com.owncloud.android.ui;
 
 import android.graphics.Canvas;
@@ -5,48 +25,102 @@ import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+
+import com.owncloud.android.utils.BitmapUtils;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
 
 /**
- * Created by tobi on 24.05.15.
+ * A Drawable object that draws text (1 character) on top of a circular/filled background.
  */
 public class TextDrawable extends Drawable {
+    /**
+     * the text to be rendered.
+     */
+    private String mText;
+
+    /**
+     * the text paint to be rendered.
+     */
+    private Paint mTextPaint;
 
-    private final String text;
-    private final Paint paint;
-    private final Paint bg;
+    /**
+     * the background to be rendered.
+     */
+    private Paint mBackground;
 
-    public TextDrawable(String text, int r, int g, int b) {
+    /**
+     * the radius of the circular background to be rendered.
+     */
+    private float mRadius;
 
-        this.text = text;
-        Integer color = Color.rgb(r, g, b);
+    /**
+     * Create a TextDrawable with the given radius.
+     *
+     * @param text   the text to be rendered
+     * @param r      rgb red value
+     * @param g      rgb green value
+     * @param b      rgb blue value
+     * @param radius circle radius
+     */
+    public TextDrawable(String text, int r, int g, int b, float radius) {
+        mRadius = radius;
+        mText = text;
 
-        bg = new Paint();
-        bg.setStyle(Paint.Style.FILL);
-        bg.setColor(color);
+        mBackground = new Paint();
+        mBackground.setStyle(Paint.Style.FILL);
+        mBackground.setAntiAlias(true);
+        mBackground.setColor(Color.rgb(r, g, b));
+
+        mTextPaint = new Paint();
+        mTextPaint.setColor(Color.WHITE);
+        mTextPaint.setTextSize(radius);
+        mTextPaint.setAntiAlias(true);
+        mTextPaint.setTextAlign(Paint.Align.CENTER);
+    }
 
-        paint = new Paint();
-        paint.setColor(Color.WHITE);
-        paint.setTextSize(20);
-        paint.setAntiAlias(true);
-        paint.setFakeBoldText(true);
+    /**
+     * creates an avatar in form of a TextDrawable with the first letter of the account name in a circle with the
+     * given radius.
+     *
+     * @param accountName the account name
+     * @param radiusInDp  the circle's radius
+     * @return the avatar as a TextDrawable
+     * @throws UnsupportedEncodingException if the charset is not supported when calculating the color values
+     * @throws NoSuchAlgorithmException if the specified algorithm is not available when calculating the color values
+     */
+    @NonNull
+    public static TextDrawable createAvatar(String accountName, float radiusInDp) throws
+            UnsupportedEncodingException, NoSuchAlgorithmException {
+        int[] rgb = BitmapUtils.calculateRGB(accountName);
+        TextDrawable avatar = new TextDrawable(
+                accountName.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInDp);
+        return avatar;
     }
 
+    /**
+     * Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color
+     * filter (set via setColorFilter) a circular background with a user's first character.
+     *
+     * @param canvas The canvas to draw into
+     */
     @Override
     public void draw(Canvas canvas) {
-        canvas.drawRect(0,-20,20,40,bg);
-        canvas.drawText(text, 4, 6, paint);
+        canvas.drawCircle(mRadius, mRadius, mRadius, mBackground);
+        canvas.drawText(mText, mRadius, mRadius - ((mTextPaint.descent() + mTextPaint.ascent()) / 2), mTextPaint);
     }
 
     @Override
     public void setAlpha(int alpha) {
-        paint.setAlpha(alpha);
+        mTextPaint.setAlpha(alpha);
     }
 
     @Override
     public void setColorFilter(ColorFilter cf) {
-        paint.setColorFilter(cf);
+        mTextPaint.setColorFilter(cf);
     }
 
     @Override

+ 250 - 0
src/com/owncloud/android/ui/activity/BaseActivity.java

@@ -0,0 +1,250 @@
+package com.owncloud.android.ui.activity;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.OperationCanceledException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v7.app.AppCompatActivity;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.status.OCCapability;
+
+/**
+ * Base activity with common behaviour for activities dealing with ownCloud {@link Account}s .
+ */
+public abstract class BaseActivity extends AppCompatActivity {
+    private static final String TAG = BaseActivity.class.getSimpleName();
+
+    /**
+     * ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located.
+     */
+    private Account mCurrentAccount;
+
+    /**
+     * Capabilities of the server where {@link #mCurrentAccount} lives.
+     */
+    private OCCapability mCapabilities;
+
+    /**
+     * Flag to signal that the activity will is finishing to enforce the creation of an ownCloud {@link Account}.
+     */
+    private boolean mRedirectingToSetupAccount = false;
+
+    /**
+     * Flag to signal when the value of mAccount was set.
+     */
+    protected boolean mAccountWasSet;
+
+    /**
+     * Flag to signal when the value of mAccount was restored from a saved state.
+     */
+    protected boolean mAccountWasRestored;
+
+    /**
+     * Access point to the cached database for the current ownCloud {@link Account}.
+     */
+    private FileDataStorageManager mStorageManager = null;
+
+    @Override
+    protected void onNewIntent (Intent intent) {
+        Log_OC.v(TAG, "onNewIntent() start");
+        Account current = AccountUtils.getCurrentOwnCloudAccount(this);
+        if (current != null && mCurrentAccount != null && !mCurrentAccount.name.equals(current.name)) {
+            mCurrentAccount = current;
+        }
+        Log_OC.v(TAG, "onNewIntent() stop");
+    }
+
+    /**
+     *  Since ownCloud {@link Account}s can be managed from the system setting menu, the existence of the {@link
+     *  Account} associated to the instance must be checked every time it is restarted.
+     */
+    @Override
+    protected void onRestart() {
+        Log_OC.v(TAG, "onRestart() start");
+        super.onRestart();
+        boolean validAccount = (mCurrentAccount != null && AccountUtils.exists(mCurrentAccount, this));
+        if (!validAccount) {
+            swapToDefaultAccount();
+        }
+        Log_OC.v(TAG, "onRestart() end");
+    }
+
+    /**
+     * Sets and validates the ownCloud {@link Account} associated to the Activity.
+     *
+     * If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
+     *
+     * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
+     *
+     * @param account      New {@link Account} to set.
+     * @param savedAccount When 'true', account was retrieved from a saved instance state.
+     */
+    protected void setAccount(Account account, boolean savedAccount) {
+        Account oldAccount = mCurrentAccount;
+        boolean validAccount =
+                (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(),
+                        account.name));
+        if (validAccount) {
+            mCurrentAccount = account;
+            mAccountWasSet = true;
+            mAccountWasRestored = (savedAccount || mCurrentAccount.equals(oldAccount));
+
+        } else {
+            swapToDefaultAccount();
+        }
+    }
+
+    /**
+     * Tries to swap the current ownCloud {@link Account} for other valid and existing.
+     *
+     * If no valid ownCloud {@link Account} exists, the the user is requested
+     * to create a new ownCloud {@link Account}.
+     *
+     * POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
+     */
+    protected void swapToDefaultAccount() {
+        // default to the most recently used account
+        Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
+        if (newAccount == null) {
+            /// no account available: force account creation
+            createAccount();
+            mRedirectingToSetupAccount = true;
+            mAccountWasSet = false;
+            mAccountWasRestored = false;
+
+        } else {
+            mAccountWasSet = true;
+            mAccountWasRestored = (newAccount.equals(mCurrentAccount));
+            mCurrentAccount = newAccount;
+        }
+    }
+
+    /**
+     * Launches the account creation activity. To use when no ownCloud account is available.
+     */
+    protected void createAccount() {
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.addAccount(MainApp.getAccountType(),
+                null,
+                null,
+                null,
+                this,
+                new AccountCreationCallback(),
+                new Handler());
+    }
+
+    /**
+     * Called when the ownCloud {@link Account} associated to the Activity was just updated.
+     *
+     * Child classes must grant that state depending on the {@link Account} is updated.
+     */
+    protected void onAccountSet(boolean stateWasRecovered) {
+        if (getAccount() != null) {
+            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
+            mCapabilities = mStorageManager.getCapability(mCurrentAccount.name);
+        } else {
+            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
+        }
+    }
+
+    protected void setAccount(Account account) {
+        mCurrentAccount = account;
+    }
+
+    /**
+     * Getter for the capabilities of the server where the current OC account lives.
+     *
+     * @return Capabilities of the server where the current OC account lives. Null if the account is not
+     * set yet.
+     */
+    public OCCapability getCapabilities() {
+        return mCapabilities;
+    }
+
+    /**
+     * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity
+     * is located.
+     *
+     * @return OwnCloud {@link Account} where the main {@link OCFile} handled by the activity
+     * is located.
+     */
+    public Account getAccount() {
+        return mCurrentAccount;
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        if (mAccountWasSet) {
+            onAccountSet(mAccountWasRestored);
+        }
+    }
+
+    /**
+     * @return 'True' when the Activity is finishing to enforce the setup of a new account.
+     */
+    protected boolean isRedirectingToSetupAccount() {
+        return mRedirectingToSetupAccount;
+    }
+
+    public FileDataStorageManager getStorageManager() {
+        return mStorageManager;
+    }
+
+    /**
+     * Method that gets called when a new account has been successfully created.
+     *
+     * @param future
+     */
+    protected void onAccountCreationSuccessful(AccountManagerFuture<Bundle> future) {
+        // no special handling in base activity
+    }
+
+    /**
+     * Helper class handling a callback from the {@link AccountManager} after the creation of
+     * a new ownCloud {@link Account} finished, successfully or not.
+     */
+    public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
+
+        @Override
+        public void run(AccountManagerFuture<Bundle> future) {
+            BaseActivity.this.mRedirectingToSetupAccount = false;
+            boolean accountWasSet = false;
+            if (future != null) {
+                try {
+                    Bundle result;
+                    result = future.getResult();
+                    String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
+                    String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
+                    if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
+                        setAccount(new Account(name, type), false);
+                        accountWasSet = true;
+                    }
+
+                    onAccountCreationSuccessful(future);
+                } catch (OperationCanceledException e) {
+                    Log_OC.d(TAG, "Account creation canceled");
+
+                } catch (Exception e) {
+                    Log_OC.e(TAG, "Account creation finished in exception: ", e);
+                }
+
+            } else {
+                Log_OC.e(TAG, "Account creation callback with null bundle");
+            }
+            if (!accountWasSet) {
+                moveTaskToBack(true);
+            }
+        }
+    }
+}

+ 662 - 0
src/com/owncloud/android/ui/activity/DrawerActivity.java

@@ -0,0 +1,662 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author Andy Scherzinger
+ *   Copyright (C) 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.activity;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.media.Image;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.TextDrawable;
+import com.owncloud.android.utils.BitmapUtils;
+import com.owncloud.android.utils.DisplayUtils;
+
+/**
+ * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback
+ * generation.
+ */
+public abstract class DrawerActivity extends ToolbarActivity implements DisplayUtils.AvatarGenerationListener {
+    private static final String TAG = DrawerActivity.class.getSimpleName();
+    private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE";
+    private static final String KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM";
+    private static final int ACTION_MANAGE_ACCOUNTS = 101;
+    private static final int MENU_ORDER_ACCOUNT = 1;
+    private static final int MENU_ORDER_ACCOUNT_FUNCTION = 2;
+
+    /**
+     * menu account avatar radius.
+     */
+    private float mMenuAccountAvatarRadiusDimension;
+
+    /**
+     * current account avatar radius.
+     */
+    private float mCurrentAccountAvatarRadiusDimension;
+
+    /**
+     * other accounts avatar radius.
+     */
+    private float mOtherAccountAvatarRadiusDimension;
+
+    /**
+     * Reference to the drawer layout.
+     */
+    private DrawerLayout mDrawerLayout;
+
+    /**
+     * Reference to the drawer toggle.
+     */
+    private ActionBarDrawerToggle mDrawerToggle;
+
+    /**
+     * Reference to the navigation view.
+     */
+    private NavigationView mNavigationView;
+
+    /**
+     * Reference to the account chooser toggle.
+     */
+    private ImageView mAccountChooserToggle;
+
+    /**
+     * Reference to the middle account avatar.
+     */
+    private ImageView mAccountMiddleAccountAvatar;
+
+    /**
+     * Reference to the end account avatar.
+     */
+    private ImageView mAccountEndAccountAvatar;
+
+    /**
+     * Flag to signal if the account chooser is active.
+     */
+    private boolean mIsAccountChooserActive;
+
+    /**
+     * Id of the checked menu item.
+     */
+    private int mCheckedMenuItem = Menu.NONE;
+
+    /**
+     * accounts for the (max) three displayed accounts in the drawer header.
+     */
+    private Account[] mAvatars = new Account[3];
+
+    /**
+     * Initializes the drawer, its content and highlights the menu item with the given id.
+     * This method needs to be called after the content view has been set.
+     *
+     * @param menuItemId the menu item to be checked/highlighted
+     */
+    protected void setupDrawer(int menuItemId) {
+        setupDrawer();
+        setDrawerMenuItemChecked(menuItemId);
+    }
+
+    /**
+     * Initializes the drawer and its content.
+     * This method needs to be called after the content view has been set.
+     */
+    protected void setupDrawer() {
+        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+
+        mNavigationView = (NavigationView) findViewById(R.id.nav_view);
+        if (mNavigationView != null) {
+            mAccountChooserToggle = (ImageView) findNavigationViewChildById(R.id.drawer_account_chooser_toogle);
+            mAccountChooserToggle.setImageResource(R.drawable.ic_down);
+            mIsAccountChooserActive = false;
+
+            mAccountMiddleAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_middle);
+            mAccountEndAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_end);
+
+            // on pre lollipop the light theme adds a black tint to icons with white coloring
+            // ruining the generic avatars, so tinting for icons is deactivated pre lollipop
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+                mNavigationView.setItemIconTintList(null);
+            }
+
+            setupDrawerContent(mNavigationView);
+
+            findNavigationViewChildById(R.id.drawer_active_user)
+                    .setOnClickListener(new View.OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            toggleAccountList();
+                        }
+                    });
+        }
+
+        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
+
+            /** Called when a drawer has settled in a completely closed state. */
+            public void onDrawerClosed(View view) {
+                super.onDrawerClosed(view);
+                // standard behavior of drawer is to switch to the standard menu on closing
+                if (mIsAccountChooserActive) {
+                    toggleAccountList();
+                }
+                invalidateOptionsMenu();
+            }
+
+            /** Called when a drawer has settled in a completely open state. */
+            public void onDrawerOpened(View drawerView) {
+                super.onDrawerOpened(drawerView);
+                mDrawerToggle.setDrawerIndicatorEnabled(true);
+                invalidateOptionsMenu();
+            }
+        };
+
+        // Set the drawer toggle as the DrawerListener
+        mDrawerLayout.setDrawerListener(mDrawerToggle);
+        mDrawerToggle.setDrawerIndicatorEnabled(true);
+        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+    }
+
+    /**
+     * setup drawer content, basically setting the item selected listener.
+     *
+     * @param navigationView the drawers navigation view
+     */
+    protected void setupDrawerContent(NavigationView navigationView) {
+        navigationView.setNavigationItemSelectedListener(
+                new NavigationView.OnNavigationItemSelectedListener() {
+                    @Override
+                    public boolean onNavigationItemSelected(MenuItem menuItem) {
+                        mDrawerLayout.closeDrawers();
+
+                        switch (menuItem.getItemId()) {
+                            case R.id.nav_all_files:
+                                menuItem.setChecked(true);
+                                mCheckedMenuItem = menuItem.getItemId();
+
+                                allFilesOption();
+                                // TODO activate when On Device branch is merged
+                                // MainApp.showOnlyFilesOnDevice(false);
+                                // refreshDirectory();
+                                break;
+                            // TODO activate when On Device branch is merged
+                            // case R.id.nav_on_device:
+                            //     menuItem.setChecked(true);
+                            //     mCheckedMenuItem = menuItem.getItemId();
+                            //     MainApp.showOnlyFilesOnDevice(true);
+                            //     refreshDirectory();
+                            //     break;
+                            case R.id.nav_uploads:
+                                Intent uploadListIntent = new Intent(getApplicationContext(),
+                                        UploadListActivity.class);
+                                uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                                startActivity(uploadListIntent);
+                                break;
+                            case R.id.nav_settings:
+                                Intent settingsIntent = new Intent(getApplicationContext(),
+                                        Preferences.class);
+                                startActivity(settingsIntent);
+                                break;
+                            case R.id.drawer_menu_account_add:
+                                createAccount();
+                                break;
+                            case R.id.drawer_menu_account_manage:
+                                Intent manageAccountsIntent = new Intent(getApplicationContext(),
+                                        ManageAccountsActivity.class);
+                                startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS);
+                                break;
+                            case Menu.NONE:
+                                // account clicked
+                                accountClicked(menuItem.getTitle().toString());
+                            default:
+                                Log_OC.i(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle());
+                        }
+
+                        return true;
+                    }
+                });
+
+        // handle correct state
+        if (mIsAccountChooserActive) {
+            mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
+        } else {
+            mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
+        }
+    }
+
+    /**
+     * sets the new/current account and restarts. In case the given account equals the actual/current account the
+     * call will be ignored.
+     *
+     * @param accountName The account name to be set
+     */
+    private void accountClicked(String accountName) {
+        if (!AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()).name.equals(accountName)) {
+            AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), accountName);
+            restart();
+        }
+    }
+
+    /**
+     * click method for mini avatars in drawer header.
+     *
+     * @param view the clicked ImageView
+     */
+    public void onAccountDrawerClick(View view) {
+        accountClicked(view.getContentDescription().toString());
+    }
+
+    /**
+     * checks if the drawer exists and is opened.
+     *
+     * @return <code>true</code> if the drawer is open, else <code>false</code>
+     */
+    public boolean isDrawerOpen() {
+        return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
+    }
+
+    /**
+     * closes the drawer.
+     */
+    public void closeDrawer() {
+        if (mDrawerLayout != null) {
+            mDrawerLayout.closeDrawer(GravityCompat.START);
+        }
+    }
+
+    /**
+     * opens the drawer.
+     */
+    public void openDrawer() {
+        if (mDrawerLayout != null) {
+            mDrawerLayout.openDrawer(GravityCompat.START);
+        }
+    }
+
+    /**
+     * Enable or disable interaction with all drawers.
+     *
+     * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED},
+     *                 {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
+     */
+    public void setDrawerLockMode(int lockMode) {
+        if (mDrawerLayout != null) {
+            mDrawerLayout.setDrawerLockMode(lockMode);
+        }
+    }
+
+    /**
+     * Enable or disable the drawer indicator.
+     *
+     * @param enable <code>true</code> to enable, <code>false</code> to disable
+     */
+    public void setDrawerIndicatorEnabled(boolean enable) {
+        if (mDrawerToggle != null) {
+            mDrawerToggle.setDrawerIndicatorEnabled(enable);
+        }
+    }
+
+    /**
+     * updates the account list in the drawer.
+     */
+    public void updateAccountList() {
+        Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
+        if (mNavigationView != null && mDrawerLayout != null) {
+            if (accounts.length > 0) {
+                repopulateAccountList(accounts);
+                setAccountInDrawer(AccountUtils.getCurrentOwnCloudAccount(this));
+                populateDrawerOwnCloudAccounts();
+
+                // activate second/end account avatar
+                if (mAvatars[1] != null) {
+                    DisplayUtils.setAvatar(mAvatars[1], this,
+                            mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(),
+                            findNavigationViewChildById(R.id.drawer_account_end));
+                    mAccountEndAccountAvatar.setVisibility(View.VISIBLE);
+                } else {
+                    mAccountEndAccountAvatar.setVisibility(View.GONE);
+                }
+
+                // activate third/middle account avatar
+                if (mAvatars[2] != null) {
+                    DisplayUtils.setAvatar(mAvatars[2], this,
+                            mOtherAccountAvatarRadiusDimension, getResources(), getStorageManager(),
+                            findNavigationViewChildById(R.id.drawer_account_middle));
+                    mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE);
+                } else {
+                    mAccountMiddleAccountAvatar.setVisibility(View.GONE);
+                }
+            } else {
+                mAccountEndAccountAvatar.setVisibility(View.GONE);
+                mAccountMiddleAccountAvatar.setVisibility(View.GONE);
+            }
+        }
+    }
+
+    /**
+     * re-populates the account list.
+     *
+     * @param accounts list of accounts
+     */
+    private void repopulateAccountList(Account[] accounts) {
+        // remove all accounts from list
+        mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts);
+
+        // add all accounts to list
+        for (int i = 0; i < accounts.length; i++) {
+            try {
+                MenuItem accountMenuItem = mNavigationView.getMenu().add(
+                        R.id.drawer_menu_accounts,
+                        Menu.NONE,
+                        MENU_ORDER_ACCOUNT,
+                        accounts[i].name)
+                        .setIcon(TextDrawable.createAvatar(
+                                accounts[i].name,
+                                mMenuAccountAvatarRadiusDimension)
+                        );
+                DisplayUtils.setAvatar(accounts[i], this, mMenuAccountAvatarRadiusDimension, getResources(), getStorageManager(), accountMenuItem);
+            } catch (Exception e) {
+                Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e);
+                mNavigationView.getMenu().add(
+                        R.id.drawer_menu_accounts,
+                        Menu.NONE,
+                        MENU_ORDER_ACCOUNT,
+                        accounts[i].name)
+                        .setIcon(R.drawable.ic_user);
+            }
+        }
+
+        // re-add add-account and manage-accounts
+        mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add,
+                MENU_ORDER_ACCOUNT_FUNCTION,
+                getResources().getString(R.string.prefs_add_account)).setIcon(R.drawable.ic_account_plus);
+        mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage,
+                MENU_ORDER_ACCOUNT_FUNCTION,
+                getResources().getString(R.string.drawer_manage_accounts)).setIcon(R.drawable.ic_settings);
+
+        // adding sets menu group back to visible, so safety check and setting invisible
+        showMenu();
+    }
+
+    /**
+     * Method that gets called on drawer menu click for 'All Files'.
+     */
+    public abstract void allFilesOption();
+
+    /**
+     * Updates title bar and home buttons (state and icon).
+     * <p/>
+     * Assumes that navigation drawer is NOT visible.
+     */
+    protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
+        super.updateActionBarTitleAndHomeButton(chosenFile);
+
+        /// set home button properties
+        if (mDrawerToggle != null) {
+            mDrawerToggle.setDrawerIndicatorEnabled(isRoot(chosenFile));
+        }
+    }
+
+    /**
+     * sets the given account name in the drawer in case the drawer is available. The account name is shortened
+     * beginning from the @-sign in the username.
+     *
+     * @param account the account to be set in the drawer
+     */
+    protected void setAccountInDrawer(Account account) {
+        if (mDrawerLayout != null && account != null) {
+            TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username);
+            TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full);
+            usernameFull.setText(account.name);
+            try {
+                OwnCloudAccount oca = new OwnCloudAccount(account, this);
+                username.setText(oca.getDisplayName());
+            } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
+                Log_OC.w(TAG, "Couldn't read display name of account fallback to account name");
+                username.setText(AccountUtils.getAccountUsername(account.name));
+            }
+
+            DisplayUtils.setAvatar(account, this,
+                    mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(),
+                    findNavigationViewChildById(R.id.drawer_current_account));
+        }
+    }
+
+    /**
+     * Toggle between standard menu and account list including saving the state.
+     */
+    private void toggleAccountList() {
+        mIsAccountChooserActive = !mIsAccountChooserActive;
+        showMenu();
+    }
+
+    /**
+     * depending on the #mIsAccountChooserActive flag shows the account chooser or the standard menu.
+     */
+    private void showMenu() {
+        if (mNavigationView != null) {
+            if (mIsAccountChooserActive) {
+                mAccountChooserToggle.setImageResource(R.drawable.ic_up);
+                mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
+                mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false);
+            } else {
+                mAccountChooserToggle.setImageResource(R.drawable.ic_down);
+                mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
+                mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true);
+            }
+        }
+    }
+
+    /**
+     * checks/highlights the provided menu item if the drawer has been initialized and the menu item exists.
+     *
+     * @param menuItemId the menu item to be highlighted
+     */
+    protected void setDrawerMenuItemChecked(int menuItemId) {
+        if (mNavigationView != null && mNavigationView.getMenu() != null && mNavigationView.getMenu().findItem
+                (menuItemId) != null) {
+            mNavigationView.getMenu().findItem(menuItemId).setChecked(true);
+            mCheckedMenuItem = menuItemId;
+        } else {
+            Log_OC.w(TAG, "setDrawerMenuItemChecked has been called with invalid menu-item-ID");
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (savedInstanceState != null) {
+            mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
+            mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
+        }
+
+        mCurrentAccountAvatarRadiusDimension = getResources()
+                .getDimension(R.dimen.nav_drawer_header_avatar_radius);
+        mOtherAccountAvatarRadiusDimension = getResources()
+                .getDimension(R.dimen.nav_drawer_header_avatar_other_accounts_radius);
+        mMenuAccountAvatarRadiusDimension = getResources()
+                .getDimension(R.dimen.nav_drawer_menu_avatar_radius);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive);
+        outState.putInt(KEY_CHECKED_MENU_ITEM, mCheckedMenuItem);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+
+        mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
+        mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
+
+        // (re-)setup drawer state
+        showMenu();
+
+        // check/highlight the menu item if present
+        if (mCheckedMenuItem > Menu.NONE || mCheckedMenuItem < Menu.NONE) {
+            setDrawerMenuItemChecked(mCheckedMenuItem);
+        }
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        // Sync the toggle state after onRestoreInstanceState has occurred.
+        if (mDrawerToggle != null) {
+            mDrawerToggle.syncState();
+            if (isDrawerOpen()) {
+                mDrawerToggle.setDrawerIndicatorEnabled(true);
+            }
+        }
+        updateAccountList();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mDrawerToggle != null) {
+            mDrawerToggle.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (isDrawerOpen()) {
+            closeDrawer();
+            return;
+        }
+        super.onBackPressed();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        setDrawerMenuItemChecked(mCheckedMenuItem);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+
+        // update Account list and active account if Manage Account activity replies with
+        // - ACCOUNT_LIST_CHANGED = true
+        // - RESULT_OK
+        if (requestCode == ACTION_MANAGE_ACCOUNTS
+                && resultCode == RESULT_OK
+                && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) {
+
+            // current account has changed
+            if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) {
+                setAccount(AccountUtils.getCurrentOwnCloudAccount(this));
+                restart();
+            } else {
+                updateAccountList();
+            }
+        }
+    }
+
+    /**
+     * Finds a view that was identified by the id attribute from the drawer header.
+     *
+     * @param id the view's id
+     * @return The view if found or <code>null</code> otherwise.
+     */
+    private View findNavigationViewChildById(int id) {
+        return ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id);
+    }
+
+    /**
+     * restart helper method which is called after a changing the current account.
+     */
+    protected abstract void restart();
+
+    @Override
+    protected void onAccountCreationSuccessful(AccountManagerFuture<Bundle> future) {
+        super.onAccountCreationSuccessful(future);
+        updateAccountList();
+        restart();
+    }
+
+    /**
+     * populates the avatar drawer array with the first three ownCloud {@link Account}s while the first element is
+     * always the current account.
+     */
+    private void populateDrawerOwnCloudAccounts() {
+        mAvatars = new Account[3];
+        Account[] accountsAll = AccountManager.get(this).getAccountsByType
+                (MainApp.getAccountType());
+        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(this);
+
+        mAvatars[0] = currentAccount;
+        int j = 0;
+        for (int i = 1; i <= 2 && i < accountsAll.length && j < accountsAll.length; j++) {
+            if (!currentAccount.equals(accountsAll[j])) {
+                mAvatars[i] = accountsAll[j];
+                i++;
+            }
+        }
+    }
+
+    @Override
+    public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
+        if (callContext instanceof MenuItem) {
+            MenuItem mi = (MenuItem)callContext;
+            mi.setIcon(avatarDrawable);
+        } else if (callContext instanceof ImageView) {
+            ImageView iv = (ImageView)callContext;
+            iv.setImageDrawable(avatarDrawable);
+        }
+    }
+
+    @Override
+    public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
+        if (callContext instanceof MenuItem) {
+            MenuItem mi = (MenuItem)callContext;
+            return String.valueOf(mi.getTitle()).equals(tag);
+        } else if (callContext instanceof ImageView) {
+            ImageView iv = (ImageView)callContext;
+            return String.valueOf(iv.getTag()).equals(tag);
+        }
+        return false;
+    }
+}

+ 5 - 502
src/com/owncloud/android/ui/activity/FileActivity.java

@@ -23,39 +23,25 @@ package com.owncloud.android.ui.activity;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
-import android.support.v4.view.GravityCompat;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ListView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
 import android.widget.Toast;
 
-import com.owncloud.android.BuildConfig;
-import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.authentication.AuthenticatorActivity;
-import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.files.FileOperationsHelper;
 import com.owncloud.android.files.services.FileDownloader;
@@ -72,7 +58,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperation;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult;
 import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.lib.resources.status.OCCapability;
 import com.owncloud.android.operations.CreateShareWithShareeOperation;
 import com.owncloud.android.operations.GetSharesForFileOperation;
 import com.owncloud.android.operations.SynchronizeFileOperation;
@@ -82,21 +67,16 @@ import com.owncloud.android.operations.UpdateSharePermissionsOperation;
 import com.owncloud.android.operations.UpdateShareViaLinkOperation;
 import com.owncloud.android.services.OperationsService;
 import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
-import com.owncloud.android.ui.NavigationDrawerItem;
-import com.owncloud.android.ui.adapter.NavigationDrawerListAdapter;
 import com.owncloud.android.ui.dialog.ConfirmationDialogFragment;
 import com.owncloud.android.ui.dialog.LoadingDialog;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 import com.owncloud.android.utils.ErrorMessageAdapter;
 
-import java.util.ArrayList;
-
 
 /**
- * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud
- * {@link Account}s .
+ * Activity with common behaviour for activities handling {@link OCFile}s in ownCloud {@link Account}s .
  */
-public class FileActivity extends AppCompatActivity
+public class FileActivity extends DrawerActivity
         implements OnRemoteOperationListener, ComponentsGetter, SslUntrustedCertDialog.OnSslUntrustedCertListener {
 
     public static final String EXTRA_FILE = "com.owncloud.android.ui.activity.FILE";
@@ -120,35 +100,15 @@ public class FileActivity extends AppCompatActivity
     private static final String DIALOG_UNTRUSTED_CERT = "DIALOG_UNTRUSTED_CERT";
     private static final String DIALOG_CERT_NOT_SAVED = "DIALOG_CERT_NOT_SAVED";
 
-    /** OwnCloud {@link Account} where the main {@link OCFile} handled by the activity is located.*/
-    private Account mAccount;
-
-    /** Capabilites of the server where {@link #mAccount} lives */
-     private OCCapability mCapabilities;
-
      /** Main {@link OCFile} handled by the activity.*/
     private OCFile mFile;
 
-
-    /** Flag to signal that the activity will is finishing to enforce the creation of an ownCloud
-     * {@link Account} */
-    private boolean mRedirectingToSetupAccount = false;
-
-    /** Flag to signal when the value of mAccount was set */
-    protected boolean mAccountWasSet;
-
-    /** Flag to signal when the value of mAccount was restored from a saved state */
-    protected boolean mAccountWasRestored;
-
     /** Flag to signal if the activity is launched by a notification */
     private boolean mFromNotification;
 
     /** Messages handler associated to the main thread and the life cycle of the activity */
     private Handler mHandler;
 
-    /** Access point to the cached database for the current ownCloud {@link Account} */
-    private FileDataStorageManager mStorageManager = null;
-
     private FileOperationsHelper mFileOperationsHelper;
 
     private ServiceConnection mOperationsServiceConnection = null;
@@ -161,24 +121,6 @@ public class FileActivity extends AppCompatActivity
     protected FileUploaderBinder mUploaderBinder = null;
     private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
 
-    // Navigation Drawer
-    protected DrawerLayout mDrawerLayout;
-    protected ActionBarDrawerToggle mDrawerToggle;
-    protected ListView mDrawerList;
-
-    // Slide menu items
-    protected String[] mDrawerTitles;
-    protected String[] mDrawerContentDescriptions;
-
-    protected ArrayList<NavigationDrawerItem> mDrawerItems;
-
-    protected NavigationDrawerListAdapter mNavigationDrawerAdapter = null;
-
-
-
-    // TODO re-enable when "Accounts" is available in Navigation Drawer
-//    protected boolean mShowAccounts = false;
-
     /**
      * Loads the ownCloud {@link Account} and main {@link OCFile} to be handled by the instance of
      * the {@link FileActivity}.
@@ -230,40 +172,9 @@ public class FileActivity extends AppCompatActivity
 
     }
 
-    @Override
-    protected void onNewIntent (Intent intent) {
-        Log_OC.v(TAG, "onNewIntent() start");
-        Account current = AccountUtils.getCurrentOwnCloudAccount(this);
-        if (current != null && mAccount != null && !mAccount.name.equals(current.name)) {
-            mAccount = current;
-        }
-        Log_OC.v(TAG, "onNewIntent() stop");
-    }
-
-    /**
-     *  Since ownCloud {@link Account}s can be managed from the system setting menu,
-     *  the existence of the {@link Account} associated to the instance must be checked
-     *  every time it is restarted.
-     */
-    @Override
-    protected void onRestart() {
-        Log_OC.v(TAG, "onRestart() start");
-        super.onRestart();
-        boolean validAccount = (mAccount != null && AccountUtils.exists(mAccount, this));
-        if (!validAccount) {
-            swapToDefaultAccount();
-        }
-        Log_OC.v(TAG, "onRestart() end");
-    }
-
-
     @Override
     protected void onStart() {
         super.onStart();
-
-        if (mAccountWasSet) {
-            onAccountSet(mAccountWasRestored);
-        }
     }
 
     @Override
@@ -284,6 +195,7 @@ public class FileActivity extends AppCompatActivity
         super.onPause();
     }
 
+
     @Override
     protected void onDestroy() {
         if (mOperationsServiceConnection != null) {
@@ -302,275 +214,6 @@ public class FileActivity extends AppCompatActivity
         super.onDestroy();
     }
 
-    @Override
-    protected void onPostCreate(Bundle savedInstanceState) {
-        super.onPostCreate(savedInstanceState);
-        // Sync the toggle state after onRestoreInstanceState has occurred.
-        if (mDrawerToggle != null) {
-            mDrawerToggle.syncState();
-            if (isDrawerOpen()) {
-                getSupportActionBar().setTitle(R.string.app_name);
-                mDrawerToggle.setDrawerIndicatorEnabled(true);
-            }
-        }
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        if (mDrawerToggle != null) {
-            mDrawerToggle.onConfigurationChanged(newConfig);
-        }
-    }
-
-    @Override
-    public void onBackPressed() {
-        if (isDrawerOpen()) {
-            closeNavDrawer();
-            return;
-        }
-        super.onBackPressed();
-    }
-
-    /**
-     * checks if the drawer exists and is opened.
-     *
-     * @return <code>true</code> if the drawer is open, else <code>false</code>
-     */
-    public boolean isDrawerOpen() {
-        if(mDrawerLayout != null) {
-            return mDrawerLayout.isDrawerOpen(GravityCompat.START);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * closes the navigation drawer.
-     */
-    public void closeNavDrawer() {
-        if(mDrawerLayout != null) {
-            mDrawerLayout.closeDrawer(GravityCompat.START);
-        }
-    }
-
-    protected void initDrawer(){
-        // constant settings for action bar when navigation drawer is inited
-        getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
-
-
-        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
-        // Notification Drawer
-        RelativeLayout navigationDrawerLayout = (RelativeLayout) findViewById(R.id.left_drawer);
-        mDrawerList = (ListView) navigationDrawerLayout.findViewById(R.id.drawer_list);
-
-        // TODO re-enable when "Accounts" is available in Navigation Drawer
-//        // load Account in the Drawer Title
-//        // User-Icon
-//        ImageView userIcon = (ImageView) navigationDrawerLayout.findViewById(R.id.drawer_userIcon);
-//        userIcon.setImageResource(DisplayUtils.getSeasonalIconId());
-//
-//        // Username
-//        TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
-//        Account account = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
-//
-//        if (account != null) {
-//            int lastAtPos = account.name.lastIndexOf("@");
-//            username.setText(account.name.substring(0, lastAtPos));
-//        }
-
-        // Display username in drawer
-        setUsernameInDrawer(navigationDrawerLayout, AccountUtils.getCurrentOwnCloudAccount(getApplicationContext()));
-
-        // load slide menu items
-        mDrawerTitles = getResources().getStringArray(R.array.drawer_items);
-
-        // nav drawer content description from resources
-        mDrawerContentDescriptions = getResources().
-                getStringArray(R.array.drawer_content_descriptions);
-
-        // nav drawer items
-        mDrawerItems = new ArrayList<NavigationDrawerItem>();
-        // adding nav drawer items to array
-        // TODO re-enable when "Accounts" is available in Navigation Drawer
-        // Accounts
-        // mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0],
-        // mDrawerContentDescriptions[0]));
-        // All Files
-        mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[0], mDrawerContentDescriptions[0],
-                R.drawable.ic_folder_open));
-
-        // TODO Enable when "On Device" is recovered
-        // On Device
-        //mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2],
-        //        mDrawerContentDescriptions[2]));
-
-        // Uploads
-        mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[1], mDrawerContentDescriptions[2],
-                R.drawable.ic_uploads));
-
-        // Settings
-        mDrawerItems.add(new NavigationDrawerItem(mDrawerTitles[2], mDrawerContentDescriptions[1],
-                R.drawable.ic_settings));
-
-        // setting the nav drawer list adapter
-        mNavigationDrawerAdapter = new NavigationDrawerListAdapter(getApplicationContext(), this,
-                mDrawerItems);
-        mDrawerList.setAdapter(mNavigationDrawerAdapter);
-
-
-        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,R.string.drawer_open,R.string.drawer_close) {
-
-
-            /** Called when a drawer has settled in a completely closed state. */
-            public void onDrawerClosed(View view) {
-                super.onDrawerClosed(view);
-                updateActionBarTitleAndHomeButton(null);
-                invalidateOptionsMenu();
-            }
-
-            /** Called when a drawer has settled in a completely open state. */
-            public void onDrawerOpened(View drawerView) {
-                super.onDrawerOpened(drawerView);
-                getSupportActionBar().setTitle(R.string.app_name);
-                mDrawerToggle.setDrawerIndicatorEnabled(true);
-                invalidateOptionsMenu();
-            }
-        };
-        
-        // Set the list's click listener
-        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
-
-        // Set the drawer toggle as the DrawerListener
-        mDrawerLayout.setDrawerListener(mDrawerToggle);
-        mDrawerToggle.setDrawerIndicatorEnabled(false);
-    }
-
-    /**
-     * sets the given account name in the drawer in case the drawer is available. The account name
-     * is shortened beginning from the @-sign in the username.
-     *
-     * @param navigationDrawerLayout the drawer layout to be used
-     * @param account                the account to be set in the drawer
-     */
-    protected void setUsernameInDrawer(View navigationDrawerLayout, Account account) {
-        if (navigationDrawerLayout != null && account != null) {
-            TextView username = (TextView) navigationDrawerLayout.findViewById(R.id.drawer_username);
-            int lastAtPos = account.name.lastIndexOf("@");
-            username.setText(account.name.substring(0, lastAtPos));
-        }
-    }
-
-    /**
-     * Updates title bar and home buttons (state and icon).
-     *
-     * Assumes that navigation drawer is NOT visible.
-     */
-    protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
-        String title = getDefaultTitle();    // default
-        boolean inRoot;
-
-        /// choose the appropiate title
-        if (chosenFile == null) {
-            chosenFile = mFile;     // if no file is passed, current file decides
-        }
-        inRoot = (
-                chosenFile == null ||
-                (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID)
-        );
-        if (!inRoot) {
-            title = chosenFile.getFileName();
-        }
-
-        /// set the chosen title
-        ActionBar actionBar = getSupportActionBar();
-        actionBar.setTitle(title);
-        /// also as content description
-        View actionBarTitleView = getWindow().getDecorView().findViewById(
-                getResources().getIdentifier("action_bar_title", "id", "android")
-        );
-        if (actionBarTitleView != null) {    // it's null in Android 2.x
-            actionBarTitleView.setContentDescription(title);
-        }
-
-        /// set home button properties
-        mDrawerToggle.setDrawerIndicatorEnabled(inRoot);
-        actionBar.setDisplayHomeAsUpEnabled(true);
-        actionBar.setDisplayShowTitleEnabled(true);
-
-    }
-
-    protected String getDefaultTitle() {
-        return getString(R.string.default_display_name_for_root_folder);
-    }
-
-    /**
-     *  Sets and validates the ownCloud {@link Account} associated to the Activity.
-     *
-     *  If not valid, tries to swap it for other valid and existing ownCloud {@link Account}.
-     *
-     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
-     *
-     *  @param account          New {@link Account} to set.
-     *  @param savedAccount     When 'true', account was retrieved from a saved instance state.
-     */
-    protected void setAccount(Account account, boolean savedAccount) {
-        Account oldAccount = mAccount;
-        boolean validAccount =
-                (account != null && AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(),
-                        account.name));
-        if (validAccount) {
-            mAccount = account;
-            mAccountWasSet = true;
-            mAccountWasRestored = (savedAccount || mAccount.equals(oldAccount));
-
-        } else {
-            swapToDefaultAccount();
-        }
-    }
-
-
-    /**
-     *  Tries to swap the current ownCloud {@link Account} for other valid and existing.
-     *
-     *  If no valid ownCloud {@link Account} exists, the the user is requested
-     *  to create a new ownCloud {@link Account}.
-     *
-     *  POSTCONDITION: updates {@link #mAccountWasSet} and {@link #mAccountWasRestored}.
-     */
-    private void swapToDefaultAccount() {
-        // default to the most recently used account
-        Account newAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
-        if (newAccount == null) {
-            /// no account available: force account creation
-            createFirstAccount();
-            mRedirectingToSetupAccount = true;
-            mAccountWasSet = false;
-            mAccountWasRestored = false;
-
-        } else {
-            mAccountWasSet = true;
-            mAccountWasRestored = (newAccount.equals(mAccount));
-            mAccount = newAccount;
-        }
-    }
-
-
-    /**
-     * Launches the account creation activity. To use when no ownCloud account is available
-     */
-    private void createFirstAccount() {
-        AccountManager am = AccountManager.get(getApplicationContext());
-        am.addAccount(MainApp.getAccountType(),
-                null,
-                null,
-                null,
-                this,
-                new AccountCreationCallback(),
-                null);
-    }
-
-
     /**
      * {@inheritDoc}
      */
@@ -607,34 +250,6 @@ public class FileActivity extends AppCompatActivity
         mFile = file;
     }
 
-
-    /**
-     * Getter for the ownCloud {@link Account} where the main {@link OCFile} handled by the activity
-     * is located.
-     *
-     * @return  OwnCloud {@link Account} where the main {@link OCFile} handled by the activity
-     *          is located.
-     */
-    public Account getAccount() {
-        return mAccount;
-    }
-
-    protected void setAccount(Account account) {
-        mAccount = account;
-    }
-
-
-    /**
-     * Getter for the capabilities of the server where the current OC account lives.
-     *
-     * @return  Capabilities of the server where the current OC account lives. Null if the account is not
-     *          set yet.
-     */
-    public OCCapability getCapabilities() {
-        return mCapabilities;
-    }
-
-
     /**
      * @return Value of mFromNotification: True if the Activity is launched by a notification
      */
@@ -642,13 +257,6 @@ public class FileActivity extends AppCompatActivity
         return mFromNotification;
     }
 
-    /**
-     * @return 'True' when the Activity is finishing to enforce the setup of a new account.
-     */
-    protected boolean isRedirectingToSetupAccount() {
-        return mRedirectingToSetupAccount;
-    }
-
     public OperationsServiceBinder getOperationsServiceBinder() {
         return mOperationsServiceBinder;
     }
@@ -657,67 +265,6 @@ public class FileActivity extends AppCompatActivity
         return null;
     }
 
-    /**
-     * Helper class handling a callback from the {@link AccountManager} after the creation of
-     * a new ownCloud {@link Account} finished, successfully or not.
-     *
-     * At this moment, only called after the creation of the first account.
-     */
-    public class AccountCreationCallback implements AccountManagerCallback<Bundle> {
-
-        @Override
-        public void run(AccountManagerFuture<Bundle> future) {
-            FileActivity.this.mRedirectingToSetupAccount = false;
-            boolean accountWasSet = false;
-            if (future != null) {
-                try {
-                    Bundle result;
-                    result = future.getResult();
-                    String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
-                    String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
-                    if (AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name)) {
-                        setAccount(new Account(name, type), false);
-                        accountWasSet = true;
-                    }
-                } catch (OperationCanceledException e) {
-                    Log_OC.d(TAG, "Account creation canceled");
-
-                } catch (Exception e) {
-                    Log_OC.e(TAG, "Account creation finished in exception: ", e);
-                }
-
-            } else {
-                Log_OC.e(TAG, "Account creation callback with null bundle");
-            }
-            if (!accountWasSet) {
-                moveTaskToBack(true);
-            }
-        }
-
-    }
-
-
-    /**
-     *  Called when the ownCloud {@link Account} associated to the Activity was just updated.
-     *
-     *  Child classes must grant that state depending on the {@link Account} is updated.
-     */
-    protected void onAccountSet(boolean stateWasRecovered) {
-        if (getAccount() != null) {
-            mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver());
-            mCapabilities = mStorageManager.getCapability(mAccount.name);
-
-        } else {
-            Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!");
-        }
-    }
-
-
-    public FileDataStorageManager getStorageManager() {
-        return mStorageManager;
-    }
-
-
     public OnRemoteOperationListener getRemoteOperationListener() {
         return this;
     }
@@ -967,7 +514,6 @@ public class FileActivity extends AppCompatActivity
             }
         }
 
-
         @Override
         public void onServiceDisconnected(ComponentName component) {
             if (component.equals(new ComponentName(FileActivity.this, OperationsService.class))) {
@@ -978,26 +524,24 @@ public class FileActivity extends AppCompatActivity
         }
     }
 
-
     @Override
     public FileDownloaderBinder getFileDownloaderBinder() {
         return mDownloaderBinder;
     }
 
-
     @Override
     public FileUploaderBinder getFileUploaderBinder() {
         return mUploaderBinder;
     }
 
-
+    @Override
     public void restart(){
         Intent i = new Intent(this, FileDisplayActivity.class);
         i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
         startActivity(i);
     }
 
+    @Override
     public void allFilesOption(){
         restart();
     }
@@ -1035,45 +579,4 @@ public class FileActivity extends AppCompatActivity
         // nothing to do
     }
 
-    private class DrawerItemClickListener implements ListView.OnItemClickListener {
-        @Override
-        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            // TODO re-enable when "Accounts" is available in Navigation Drawer
-//            if (mShowAccounts && position > 0){
-//                position = position - 1;
-//            }
-            switch (position){
-                // TODO re-enable when "Accounts" is available in Navigation Drawer
-//                case 0: // Accounts
-//                    mShowAccounts = !mShowAccounts;
-//                    mNavigationDrawerAdapter.setShowAccounts(mShowAccounts);
-//                    mNavigationDrawerAdapter.notifyDataSetChanged();
-//                    break;
-
-                case 0: // All Files
-                    allFilesOption();
-                    break;
-
-                // TODO Enable when "On Device" is recovered ?
-//                case 2:
-//                    MainApp.showOnlyFilesOnDevice(true);
-//                    break;
-
-                case 1: // Uploads
-                    Intent uploadListIntent = new Intent(getApplicationContext(),
-                            UploadListActivity.class);
-                    uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                    startActivity(uploadListIntent);
-                    break;
-
-                case 2: // Settings
-                    Intent settingsIntent = new Intent(getApplicationContext(),
-                            Preferences.class);
-                    startActivity(settingsIntent);
-                    break;
-            }
-            mDrawerLayout.closeDrawers();
-        }
-    }
-
 }

+ 21 - 26
src/com/owncloud/android/ui/activity/FileDisplayActivity.java

@@ -40,6 +40,7 @@ import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcelable;
+import android.support.design.widget.NavigationView;
 import android.support.design.widget.Snackbar;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
@@ -47,6 +48,7 @@ import android.support.v4.app.FragmentTransaction;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.Toolbar;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -110,7 +112,6 @@ public class FileDisplayActivity extends HookActivity
     private boolean mDualPane;
     private View mLeftFragmentContainer;
     private View mRightFragmentContainer;
-    private ProgressBar mProgressBar;
 
     private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
     private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
@@ -166,25 +167,17 @@ public class FileDisplayActivity extends HookActivity
         // Inflate and set the layout view
         setContentView(R.layout.files);
 
-        // Navigation Drawer
-        initDrawer();
+        // setup toolbar
+        setupToolbar();
 
-        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
-        mProgressBar.setIndeterminateDrawable(
-                ContextCompat.getDrawable(this,
-                        R.drawable.actionbar_progress_indeterminate_horizontal));
+        // setup drawer
+        setupDrawer(R.id.nav_all_files);
 
         mDualPane = getResources().getBoolean(R.bool.large_land_layout);
         mLeftFragmentContainer = findViewById(R.id.left_fragment_container);
         mRightFragmentContainer = findViewById(R.id.right_fragment_container);
 
         // Action bar setup
-        getSupportActionBar().setHomeButtonEnabled(true);       // mandatory since Android ICS,
-        // according to the official
-        // documentation
-
-        // enable ActionBar app icon to behave as action to toggle nav drawer
-        //getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         getSupportActionBar().setHomeButtonEnabled(true);
 
         // Init Fragment without UI to retain AsyncTask across configuration changes
@@ -231,7 +224,9 @@ public class FileDisplayActivity extends HookActivity
             createMinFragments();
         }
 
-        mProgressBar.setIndeterminate(mSyncInProgress);
+        refreshList(true);
+
+        setIndeterminate(mSyncInProgress);
         // always AFTER setContentView(...) in onCreate(); to work around bug in its implementation
 
         setBackgroundText();
@@ -309,7 +304,7 @@ public class FileDisplayActivity extends HookActivity
             setFile(file);
 
             if (mAccountWasSet) {
-                setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount());
+                setAccountInDrawer(getAccount());
             }
 
             if (!stateWasRecovered) {
@@ -522,7 +517,7 @@ public class FileDisplayActivity extends HookActivity
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
-        boolean drawerOpen = mDrawerLayout.isDrawerOpen(GravityCompat.START);
+        boolean drawerOpen = isDrawerOpen();
         menu.findItem(R.id.action_sort).setVisible(!drawerOpen);
         menu.findItem(R.id.action_sync_account).setVisible(!drawerOpen);
         menu.findItem(R.id.action_switch_view).setVisible(!drawerOpen);
@@ -550,14 +545,14 @@ public class FileDisplayActivity extends HookActivity
             case android.R.id.home: {
                 FileFragment second = getSecondFragment();
                 OCFile currentDir = getCurrentDir();
-                if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
-                    mDrawerLayout.closeDrawer(GravityCompat.START);
+                if (isDrawerOpen()) {
+                    closeDrawer();
                 } else if ((currentDir != null && currentDir.getParentId() != 0) ||
                         (second != null && second.getFile() != null)) {
                     onBackPressed();
 
                 } else {
-                    mDrawerLayout.openDrawer(GravityCompat.START);
+                    openDrawer();
                 }
                 break;
             }
@@ -839,8 +834,6 @@ public class FileDisplayActivity extends HookActivity
     protected void onResume() {
         Log_OC.v(TAG, "onResume() start");
         super.onResume();
-        // refresh Navigation Drawer account list
-        mNavigationDrawerAdapter.updateAccountList();
 
         // refresh list of files
         refreshListOfFilesFragment();
@@ -996,10 +989,9 @@ public class FileDisplayActivity extends HookActivity
                     }
                     removeStickyBroadcast(intent);
                     Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
-                    mProgressBar.setIndeterminate(mSyncInProgress);
+                    setIndeterminate(mSyncInProgress);
 
                     setBackgroundText();
-
                 }
 
                 if (synchResult != null) {
@@ -1107,7 +1099,7 @@ public class FileDisplayActivity extends HookActivity
                     }
                 }
 
-                mProgressBar.setIndeterminate(false);
+                setIndeterminate(false);
 
             } finally {
                 if (intent != null) {
@@ -1241,6 +1233,9 @@ public class FileDisplayActivity extends HookActivity
 
     @Override
     protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
+        if (chosenFile == null) {
+            chosenFile = getFile();     // if no file is passed, current file decides
+        }
         if (mDualPane) {
             // in dual pane mode, keep the focus of title an action bar in the current folder
             super.updateActionBarTitleAndHomeButton(getCurrentDir());
@@ -1625,7 +1620,7 @@ public class FileDisplayActivity extends HookActivity
                                     null
                             );
 
-                            mProgressBar.setIndeterminate(true);
+                            setIndeterminate(true);
 
                             setBackgroundText();
 
@@ -1712,7 +1707,7 @@ public class FileDisplayActivity extends HookActivity
                 PreviewTextFragment.class.getName(), args);
         setSecondFragment(textPreviewFragment);
         updateFragmentsVisibility(true);
-        //updateNavigationElementsInActionBar(file);
+        updateActionBarTitleAndHomeButton(file);
         setFile(file);
     }
 

+ 6 - 13
src/com/owncloud/android/ui/activity/FolderPickerActivity.java

@@ -74,8 +74,6 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
 
     protected Button mCancelBtn;
     protected Button mChooseBtn;
-    private ProgressBar mProgressBar;
-
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -93,15 +91,10 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
         initControls();
 
         // Action bar setup
-        ActionBar actionBar = getSupportActionBar();
-        actionBar.setDisplayShowTitleEnabled(true);
-        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
-
-        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
-        mProgressBar.setIndeterminateDrawable(
-                getResources().getDrawable(
-                        R.drawable.actionbar_progress_indeterminate_horizontal));
-        mProgressBar.setIndeterminate(mSyncInProgress);
+        setupToolbar();
+        getSupportActionBar().setDisplayShowTitleEnabled(true);
+
+        setIndeterminate(mSyncInProgress);
         // always AFTER setContentView(...) ; to work around bug in its implementation
         
         // sets message for empty list of folders
@@ -219,7 +212,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
                                                                       );
         synchFolderOp.execute(getAccount(), this, null, null);
 
-        mProgressBar.setIndeterminate(true);
+        setIndeterminate(true);
 
         setBackgroundText();
     }
@@ -502,7 +495,7 @@ public class FolderPickerActivity extends FileActivity implements FileFragment.C
                     removeStickyBroadcast(intent);
                     Log_OC.d(TAG, "Setting progress visibility to " + mSyncInProgress);
 
-                    mProgressBar.setIndeterminate(mSyncInProgress);
+                    setIndeterminate(mSyncInProgress);
 
                     setBackgroundText();
                 }

+ 3 - 1
src/com/owncloud/android/ui/activity/LogHistoryActivity.java

@@ -50,7 +50,7 @@ import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.FileStorageUtils;
 
 
-public class LogHistoryActivity extends AppCompatActivity {
+public class LogHistoryActivity extends ToolbarActivity {
 
     private static final String MAIL_ATTACHMENT_TYPE = "text/plain";
 
@@ -70,6 +70,8 @@ public class LogHistoryActivity extends AppCompatActivity {
         super.onCreate(savedInstanceState);
 
         setContentView(R.layout.log_send_file);
+        setupToolbar();
+
         setTitle(getText(R.string.actionbar_logger));
         getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         Button deleteHistoryButton = (Button) findViewById(R.id.deleteLogHistoryButton);

+ 346 - 0
src/com/owncloud/android/ui/activity/ManageAccountsActivity.java

@@ -0,0 +1,346 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Andy Scherzinger
+ * Copyright (C) 2016 ownCloud Inc.
+ * <p/>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ * <p/>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p/>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.activity;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
+import android.accounts.AccountManagerFuture;
+import android.accounts.OperationCanceledException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.view.MenuItem;
+import android.widget.ListView;
+
+import com.owncloud.android.MainApp;
+import com.owncloud.android.R;
+import com.owncloud.android.authentication.AccountUtils;
+import com.owncloud.android.authentication.AuthenticatorActivity;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.files.FileOperationsHelper;
+import com.owncloud.android.files.services.FileDownloader;
+import com.owncloud.android.files.services.FileUploader;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.services.OperationsService;
+import com.owncloud.android.ui.adapter.AccountListAdapter;
+import com.owncloud.android.ui.adapter.AccountListItem;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * An Activity that allows the user to manage accounts.
+ */
+public class ManageAccountsActivity extends FileActivity
+        implements AccountListAdapter.AccountListAdapterListener, AccountManagerCallback<Boolean>, ComponentsGetter {
+    private static final String TAG = ManageAccountsActivity.class.getSimpleName();
+    public static final String KEY_ACCOUNT_LIST_CHANGED = "ACCOUNT_LIST_CHANGED";
+    public static final String KEY_CURRENT_ACCOUNT_CHANGED = "CURRENT_ACCOUNT_CHANGED";
+
+    private ListView mListView;
+    private final Handler mHandler = new Handler();
+    private String mAccountName;
+    private AccountListAdapter mAccountListAdapter;
+    protected FileUploader.FileUploaderBinder mUploaderBinder = null;
+    protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
+    private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
+    Set<String> mOriginalAccounts;
+    String mOriginalCurrentAccount;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.accounts_layout);
+
+        mListView = (ListView) findViewById(R.id.account_list);
+
+        setupToolbar();
+        updateActionBarTitleAndHomeButtonByString(getResources().getString(R.string.prefs_manage_accounts));
+
+        Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
+        mOriginalAccounts = toAccountNameSet(accountList);
+        mOriginalCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name;
+
+        setAccount(AccountUtils.getCurrentOwnCloudAccount(this));
+        onAccountSet(false);
+
+        mAccountListAdapter = new AccountListAdapter(this, getAccountListItems());
+
+        mListView.setAdapter(mAccountListAdapter);
+
+        initializeComponentGetters();
+    }
+
+    /**
+     * converts an array of accounts into a set of account names.
+     *
+     * @param accountList the account array
+     * @return set of account names
+     */
+    private Set<String> toAccountNameSet(Account[] accountList) {
+        Set<String> actualAccounts = new HashSet<String>(accountList.length);
+        for (Account account : accountList) {
+            actualAccounts.add(account.name);
+        }
+        return actualAccounts;
+    }
+
+    @Override
+    public void onBackPressed() {
+        Intent resultIntent = new Intent();
+        resultIntent.putExtra(KEY_ACCOUNT_LIST_CHANGED, hasAccountListChanged());
+        resultIntent.putExtra(KEY_CURRENT_ACCOUNT_CHANGED, hasCurrentAccountChanged());
+        setResult(RESULT_OK, resultIntent);
+
+        finish();
+        super.onBackPressed();
+    }
+
+    /**
+     * checks the set of actual accounts against the set of original accounts when the activity has been started.
+     *
+     * @return <code>true</code> if aacount list has changed, <code>false</code> if not
+     */
+    private boolean hasAccountListChanged() {
+        Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
+        Set<String> actualAccounts = toAccountNameSet(accountList);
+        return !mOriginalAccounts.equals(actualAccounts);
+    }
+
+    /**
+     * checks actual current account against current accounts when the activity has been started.
+     *
+     * @return <code>true</code> if aacount list has changed, <code>false</code> if not
+     */
+    private boolean hasCurrentAccountChanged() {
+        String currentAccount = AccountUtils.getCurrentOwnCloudAccount(this).name;
+        return !mOriginalCurrentAccount.equals(currentAccount);
+    }
+
+    /**
+     * Initialize ComponentsGetters.
+     */
+    private void initializeComponentGetters() {
+        mDownloadServiceConnection = newTransferenceServiceConnection();
+        if (mDownloadServiceConnection != null) {
+            bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
+                    Context.BIND_AUTO_CREATE);
+        }
+        mUploadServiceConnection = newTransferenceServiceConnection();
+        if (mUploadServiceConnection != null) {
+            bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
+                    Context.BIND_AUTO_CREATE);
+        }
+    }
+
+    /**
+     * creates the account list items list including the add-account action in case multiaccount_support is enabled.
+     *
+     * @return list of account list items
+     */
+    private ArrayList<AccountListItem> getAccountListItems() {
+        Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
+        ArrayList<AccountListItem> adapterAccountList = new ArrayList<AccountListItem>(accountList.length);
+        for (Account account : accountList) {
+            adapterAccountList.add(new AccountListItem(account));
+        }
+
+        // Add Create Account item at the end of account list if multi-account is enabled
+        if (getResources().getBoolean(R.bool.multiaccount_support)) {
+            adapterAccountList.add(new AccountListItem());
+        }
+
+        return adapterAccountList;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        boolean retval = true;
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                onBackPressed();
+                break;
+            default:
+                retval = super.onOptionsItemSelected(item);
+        }
+        return retval;
+    }
+
+    @Override
+    public void removeAccount(Account account) {
+        AccountManager am = (AccountManager) this.getSystemService(ACCOUNT_SERVICE);
+        mAccountName = account.name;
+        am.removeAccount(account, ManageAccountsActivity.this, mHandler);
+        Log_OC.d(TAG, "Remove an account " + account.name);
+    }
+
+    @Override
+    public void changePasswordOfAccount(Account account) {
+        Intent updateAccountCredentials = new Intent(ManageAccountsActivity.this, AuthenticatorActivity.class);
+        updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
+        updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION,
+                AuthenticatorActivity.ACTION_UPDATE_TOKEN);
+        startActivity(updateAccountCredentials);
+    }
+
+    @Override
+    public void createAccount() {
+        AccountManager am = AccountManager.get(getApplicationContext());
+        am.addAccount(MainApp.getAccountType(),
+                null,
+                null,
+                null,
+                this,
+                new AccountManagerCallback<Bundle>() {
+                    @Override
+                    public void run(AccountManagerFuture<Bundle> future) {
+                        if (future != null) {
+                            try {
+                                Bundle result = future.getResult();
+                                String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
+                                AccountUtils.setCurrentOwnCloudAccount(getApplicationContext(), name);
+                                mAccountListAdapter = new AccountListAdapter(ManageAccountsActivity
+                                        .this, getAccountListItems());
+                                mListView.setAdapter(mAccountListAdapter);
+                                runOnUiThread(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        mAccountListAdapter.notifyDataSetChanged();
+                                    }
+                                });
+                            } catch (OperationCanceledException e) {
+                                Log_OC.d(TAG, "Account creation canceled");
+                            } catch (Exception e) {
+                                Log_OC.e(TAG, "Account creation finished in exception: ", e);
+                            }
+                        }
+                    }
+                }, mHandler);
+    }
+
+    @Override
+    public void run(AccountManagerFuture<Boolean> future) {
+        if (future.isDone()) {
+            // after remove account
+            Account account = new Account(mAccountName, MainApp.getAccountType());
+            if (!AccountUtils.exists(account, MainApp.getAppContext())) {
+                // Cancel transfers of the removed account
+                if (mUploaderBinder != null) {
+                    mUploaderBinder.cancel(account);
+                }
+                if (mDownloaderBinder != null) {
+                    mDownloaderBinder.cancel(account);
+                }
+            }
+            
+            if (AccountUtils.getCurrentOwnCloudAccount(this) == null) {
+                String accountName = "";
+                Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType());
+                if (accounts.length != 0) {
+                    accountName = accounts[0].name;
+                }
+                AccountUtils.setCurrentOwnCloudAccount(this, accountName);
+            }
+
+            mAccountListAdapter = new AccountListAdapter(this, getAccountListItems());
+            mAccountListAdapter.notifyDataSetChanged();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mDownloadServiceConnection != null) {
+            unbindService(mDownloadServiceConnection);
+            mDownloadServiceConnection = null;
+        }
+        if (mUploadServiceConnection != null) {
+            unbindService(mUploadServiceConnection);
+            mUploadServiceConnection = null;
+        }
+
+        super.onDestroy();
+    }
+
+    // Methods for ComponentsGetter
+    @Override
+    public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
+        return mDownloaderBinder;
+    }
+
+    @Override
+    public FileUploader.FileUploaderBinder getFileUploaderBinder() {
+        return mUploaderBinder;
+    }
+
+    @Override
+    public OperationsService.OperationsServiceBinder getOperationsServiceBinder() {
+        return null;
+    }
+
+    @Override
+    public FileDataStorageManager getStorageManager() {
+        return super.getStorageManager();
+    }
+
+    @Override
+    public FileOperationsHelper getFileOperationsHelper() {
+        return null;
+    }
+
+    protected ServiceConnection newTransferenceServiceConnection() {
+        return new ManageAccountsServiceConnection();
+    }
+
+    /**
+     * Defines callbacks for service binding, passed to bindService()
+     */
+    private class ManageAccountsServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName component, IBinder service) {
+
+            if (component.equals(new ComponentName(ManageAccountsActivity.this, FileDownloader.class))) {
+                mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
+
+            } else if (component.equals(new ComponentName(ManageAccountsActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service connected");
+                mUploaderBinder = (FileUploader.FileUploaderBinder) service;
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName component) {
+            if (component.equals(new ComponentName(ManageAccountsActivity.this, FileDownloader.class))) {
+                Log_OC.d(TAG, "Download service suddenly disconnected");
+                mDownloaderBinder = null;
+            } else if (component.equals(new ComponentName(ManageAccountsActivity.this, FileUploader.class))) {
+                Log_OC.d(TAG, "Upload service suddenly disconnected");
+                mUploaderBinder = null;
+            }
+        }
+    }
+}

+ 3 - 342
src/com/owncloud/android/ui/activity/Preferences.java

@@ -22,21 +22,13 @@
 package com.owncloud.android.ui.activity;
 
 import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AccountManagerCallback;
-import android.accounts.AccountManagerFuture;
-import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
 import android.preference.CheckBoxPreference;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
@@ -47,36 +39,20 @@ import android.preference.PreferenceManager;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
 import android.support.v7.app.ActionBar;
-import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.widget.Toolbar;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.ListAdapter;
-import android.widget.ListView;
 import android.widget.Toast;
 
 import com.owncloud.android.BuildConfig;
-import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
 import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.authentication.AuthenticatorActivity;
-import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
-import com.owncloud.android.files.FileOperationsHelper;
-import com.owncloud.android.files.services.FileDownloader;
-import com.owncloud.android.files.services.FileUploader;
 import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.services.OperationsService;
-import com.owncloud.android.ui.RadioButtonPreference;
 import com.owncloud.android.utils.DisplayUtils;
 
 
@@ -86,12 +62,10 @@ import com.owncloud.android.utils.DisplayUtils;
  * It proxies the necessary calls via {@link android.support.v7.app.AppCompatDelegate} to be used
  * with AppCompat.
  */
-public class Preferences extends PreferenceActivity
-        implements AccountManagerCallback<Boolean>, ComponentsGetter {
+public class Preferences extends PreferenceActivity {
     
     private static final String TAG = Preferences.class.getSimpleName();
 
-
     private static final int ACTION_SELECT_UPLOAD_PATH = 1;
     private static final int ACTION_SELECT_UPLOAD_VIDEO_PATH = 2;
     private static final int ACTION_REQUEST_PASSCODE = 5;
@@ -102,9 +76,6 @@ public class Preferences extends PreferenceActivity
     private AppCompatDelegate mDelegate;
 
     private PreferenceCategory mAccountsPrefCategory = null;
-    private final Handler mHandler = new Handler();
-    private String mAccountName;
-    private boolean mShowContextMenu = false;
     private String mUploadPath;
     private PreferenceCategory mPrefInstantUploadCategory;
     private Preference mPrefInstantUpload;
@@ -116,11 +87,6 @@ public class Preferences extends PreferenceActivity
     private Preference mPrefInstantVideoUploadPathWiFi;
     private String mUploadVideoPath;
 
-    protected FileDownloader.FileDownloaderBinder mDownloaderBinder = null;
-    protected FileUploader.FileUploaderBinder mUploaderBinder = null;
-    private ServiceConnection mDownloadServiceConnection, mUploadServiceConnection = null;
-
-
     @SuppressWarnings("deprecation")
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -140,72 +106,6 @@ public class Preferences extends PreferenceActivity
             getWindow().getDecorView().findViewById(actionBarTitleId).
                     setContentDescription(getString(R.string.actionbar_settings));
         }
-
-        // Load the accounts category for adding the list of accounts
-        mAccountsPrefCategory = (PreferenceCategory) findPreference("accounts_category");
-
-        ListView listView = getListView();
-        listView.setOnItemLongClickListener(new OnItemLongClickListener() {
-            @Override
-            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-                ListView listView = (ListView) parent;
-                ListAdapter listAdapter = listView.getAdapter();
-                Object obj = listAdapter.getItem(position);
-
-                if (obj != null && obj instanceof RadioButtonPreference) {
-                    mShowContextMenu = true;
-                    mAccountName = ((RadioButtonPreference) obj).getKey();
-
-                    String[] items = {
-                            getResources().getString(R.string.change_password),
-                            getResources().getString(R.string.delete_account)
-                    };
-                    final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(Preferences.this);
-                    View convertView = getLayoutInflater().inflate(R.layout.alert_dialog_list_view, null);
-                    alertDialogBuilder.setView(convertView);
-                    ListView lv = (ListView) convertView.findViewById(R.id.list);
-                    ArrayAdapter<String> adapter = new ArrayAdapter<String>(
-                            Preferences.this,R.layout.simple_dialog_list_item,items);
-                    lv.setAdapter(adapter);
-
-                    //Setup proper inline listener
-                    final AlertDialog alertDialog = alertDialogBuilder.create();
-                    lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
-                        @Override
-                        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                            AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-                            Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
-                            for (Account a : accounts) {
-                                if (a.name.equals(mAccountName)) {
-                                    if (position==0) {
-
-                                        // Change account password
-                                        Intent updateAccountCredentials = new Intent(Preferences.this, AuthenticatorActivity.class);
-                                        updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, a);
-                                        updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACTION,
-                                                AuthenticatorActivity.ACTION_UPDATE_TOKEN);
-                                        startActivity(updateAccountCredentials);
-                                        alertDialog.cancel();
-                                        
-                                    } else if (position==1) {
-
-                                        // Remove account
-                                        am.removeAccount(a, Preferences.this, mHandler);
-                                        Log_OC.d(TAG, "Remove an account " + a.name);
-                                        alertDialog.cancel();
-                                    }
-                                }
-                            }
-                        }
-                    });
-                    alertDialog.show();
-
-                    View.OnLongClickListener longListener = (View.OnLongClickListener) obj;
-                    return longListener.onLongClick(view);
-                }
-                return false;
-            }
-        });
         
         // Load package info
         String temp;
@@ -230,22 +130,19 @@ public class Preferences extends PreferenceActivity
                     Boolean incoming = (Boolean) newValue;
 
                     i.setAction(
-                            incoming.booleanValue() ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT :
+                            incoming ? PassCodeActivity.ACTION_REQUEST_WITH_RESULT :
                                     PassCodeActivity.ACTION_CHECK_WITH_RESULT
                     );
 
-                    startActivityForResult(i, incoming.booleanValue() ? ACTION_REQUEST_PASSCODE :
+                    startActivityForResult(i, incoming ? ACTION_REQUEST_PASSCODE :
                             ACTION_CONFIRM_PASSCODE);
 
                     // Don't update just yet, we will decide on it in onActivityResult
                     return false;
                 }
             });
-            
         }
 
-
-
         PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference("more");
         
         boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
@@ -267,7 +164,6 @@ public class Preferences extends PreferenceActivity
             } else {
                 preferenceCategory.removePreference(pHelp);
             }
-            
         }
         
        boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
@@ -303,7 +199,6 @@ public class Preferences extends PreferenceActivity
             } else {
                 preferenceCategory.removePreference(pRecommend);
             }
-            
         }
         
         boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
@@ -458,19 +353,6 @@ public class Preferences extends PreferenceActivity
 
        loadInstantUploadPath();
        loadInstantUploadVideoPath();
-
-        /* ComponentsGetter */
-        mDownloadServiceConnection = newTransferenceServiceConnection();
-        if (mDownloadServiceConnection != null) {
-            bindService(new Intent(this, FileDownloader.class), mDownloadServiceConnection,
-                    Context.BIND_AUTO_CREATE);
-        }
-        mUploadServiceConnection = newTransferenceServiceConnection();
-        if (mUploadServiceConnection != null) {
-            bindService(new Intent(this, FileUploader.class), mUploadServiceConnection,
-                    Context.BIND_AUTO_CREATE);
-        }
-
     }
     
     private void toggleInstantPictureOptions(Boolean value){
@@ -501,46 +383,6 @@ public class Preferences extends PreferenceActivity
         }
     }
 
-    @Override
-    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
-
-        // Filter for only showing contextual menu when long press on the
-        // accounts
-        if (mShowContextMenu) {
-            getMenuInflater().inflate(R.menu.account_picker_long_click, menu);
-            mShowContextMenu = false;
-        }
-        super.onCreateContextMenu(menu, v, menuInfo);
-    }
-
-    @Override
-    public void run(AccountManagerFuture<Boolean> future) {
-        if (future.isDone()) {
-            // after remove account
-            Account account = new Account(mAccountName, MainApp.getAccountType());
-            if (!AccountUtils.exists(account, MainApp.getAppContext())) {
-                // Cancel tranfers
-                if (mUploaderBinder != null) {
-                    mUploaderBinder.cancel(account);
-                }
-                if (mDownloaderBinder != null) {
-                    mDownloaderBinder.cancel(account);
-                }
-            }
-
-            Account a = AccountUtils.getCurrentOwnCloudAccount(this);
-            String accountName = "";
-            if (a == null) {
-                Account[] accounts = AccountManager.get(this)
-                        .getAccountsByType(MainApp.getAccountType());
-                if (accounts.length != 0)
-                    accountName = accounts[0].name;
-                AccountUtils.setCurrentOwnCloudAccount(this, accountName);
-            }
-            addAccountsCheckboxPreferences();
-        }
-    }
-
     @Override
     protected void onResume() {
         super.onResume();
@@ -548,9 +390,6 @@ public class Preferences extends PreferenceActivity
                 PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
         boolean state = appPrefs.getBoolean(PassCodeActivity.PREFERENCE_SET_PASSCODE, false);
         pCode.setChecked(state);
-
-        // Populate the accounts category with the list of accounts
-        addAccountsCheckboxPreferences();
     }
 
     @Override
@@ -691,15 +530,6 @@ public class Preferences extends PreferenceActivity
 
     @Override
     protected void onDestroy() {
-        if (mDownloadServiceConnection != null) {
-            unbindService(mDownloadServiceConnection);
-            mDownloadServiceConnection = null;
-        }
-        if (mUploadServiceConnection != null) {
-            unbindService(mUploadServiceConnection);
-            mUploadServiceConnection = null;
-        }
-
         super.onDestroy();
         getDelegate().onDestroy();
     }
@@ -721,112 +551,6 @@ public class Preferences extends PreferenceActivity
         return mDelegate;
     }
 
-    /**
-     * Create the list of accounts that has been added into the app
-     */
-    @SuppressWarnings("deprecation")
-    private void addAccountsCheckboxPreferences() {
-
-        // Remove accounts in case list is refreshing for avoiding to have
-        // duplicate items
-        if (mAccountsPrefCategory.getPreferenceCount() > 0) {
-            mAccountsPrefCategory.removeAll();
-        }
-
-        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-        Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
-        Account currentAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
-
-        if (am.getAccountsByType(MainApp.getAccountType()).length == 0) {
-            // Show create account screen if there isn't any account
-            am.addAccount(MainApp.getAccountType(), null, null, null, this,
-                    null,
-                    null);
-        }
-        else {
-
-            for (Account a : accounts) {
-                RadioButtonPreference accountPreference = new RadioButtonPreference(this);
-                accountPreference.setKey(a.name);
-                // Handle internationalized domain names
-                accountPreference.setTitle(DisplayUtils.convertIdn(a.name, false));
-                mAccountsPrefCategory.addPreference(accountPreference);
-
-                // Check the current account that is being used
-                if (a.name.equals(currentAccount.name)) {
-                    accountPreference.setChecked(true);
-                } else {
-                    accountPreference.setChecked(false);
-                }
-
-                accountPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-                    @Override
-                    public boolean onPreferenceChange(Preference preference, Object newValue) {
-                        String key = preference.getKey();
-                        AccountManager am = (AccountManager) getSystemService(ACCOUNT_SERVICE);
-                        Account accounts[] = am.getAccountsByType(MainApp.getAccountType());
-                        for (Account a : accounts) {
-                            RadioButtonPreference p =
-                                    (RadioButtonPreference) findPreference(a.name);
-                            if (key.equals(a.name)) {
-                                boolean accountChanged = !p.isChecked(); 
-                                p.setChecked(true);
-                                AccountUtils.setCurrentOwnCloudAccount(
-                                        getApplicationContext(),
-                                        a.name
-                                );
-                                if (accountChanged) {
-                                    // restart the main activity
-                                    Intent i = new Intent(
-                                            Preferences.this, 
-                                            FileDisplayActivity.class
-                                    );
-                                    i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-                                    i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-                                    startActivity(i);
-                                } else {
-                                    finish();
-                                }
-                            } else {
-                                p.setChecked(false);
-                            }
-                        }
-                        return (Boolean) newValue;
-                    }
-                });
-
-            }
-
-            // Add Create Account preference at the end of account list if
-            // Multiaccount is enabled
-            if (getResources().getBoolean(R.bool.multiaccount_support)) {
-                createAddAccountPreference();
-            }
-
-        }
-    }
-
-    /**
-     * Create the preference for allow adding new accounts
-     */
-    private void createAddAccountPreference() {
-        Preference addAccountPref = new Preference(this);
-        addAccountPref.setKey("add_account");
-        addAccountPref.setTitle(getString(R.string.prefs_add_account));
-        mAccountsPrefCategory.addPreference(addAccountPref);
-
-        addAccountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
-            @Override
-            public boolean onPreferenceClick(Preference preference) {
-                AccountManager am = AccountManager.get(getApplicationContext());
-                am.addAccount(MainApp.getAccountType(), null, null, null, Preferences.this,
-                        null, null);
-                return true;
-            }
-        });
-
-    }
-
     /**
      * Load upload path set on preferences
      */
@@ -868,67 +592,4 @@ public class Preferences extends PreferenceActivity
         editor.putString("instant_video_upload_path", mUploadVideoPath);
         editor.commit();
     }
-
-    // Methods for ComponetsGetter
-    @Override
-    public FileDownloader.FileDownloaderBinder getFileDownloaderBinder() {
-        return mDownloaderBinder;
-    }
-
-
-    @Override
-    public FileUploader.FileUploaderBinder getFileUploaderBinder() {
-        return mUploaderBinder;
-    }
-
-    @Override
-    public OperationsService.OperationsServiceBinder getOperationsServiceBinder() {
-        return null;
-    }
-
-    @Override
-    public FileDataStorageManager getStorageManager() {
-        return null;
-    }
-
-    @Override
-    public FileOperationsHelper getFileOperationsHelper() {
-        return null;
-    }
-
-    protected ServiceConnection newTransferenceServiceConnection() {
-        return new PreferencesServiceConnection();
-    }
-
-    /** Defines callbacks for service binding, passed to bindService() */
-    private class PreferencesServiceConnection implements ServiceConnection {
-
-        @Override
-        public void onServiceConnected(ComponentName component, IBinder service) {
-
-            if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
-                mDownloaderBinder = (FileDownloader.FileDownloaderBinder) service;
-
-            } else if (component.equals(new ComponentName(Preferences.this,
-                    FileUploader.class))) {
-                Log_OC.d(TAG, "Upload service connected");
-                mUploaderBinder = (FileUploader.FileUploaderBinder) service;
-            } else {
-                return;
-            }
-
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName component) {
-            if (component.equals(new ComponentName(Preferences.this, FileDownloader.class))) {
-                Log_OC.d(TAG, "Download service suddenly disconnected");
-                mDownloaderBinder = null;
-            } else if (component.equals(new ComponentName(Preferences.this,
-                    FileUploader.class))) {
-                Log_OC.d(TAG, "Upload service suddenly disconnected");
-                mUploaderBinder = null;
-            }
-        }
-    };
 }

+ 2 - 2
src/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java

@@ -156,7 +156,6 @@ public class ReceiveExternalFilesActivity extends FileActivity
             fm.beginTransaction()
                     .add(taskRetainerFragment, TaskRetainerFragment.FTAG_TASK_RETAINER_FRAGMENT).commit();
         }   // else, Fragment already created and retained across configuration change
-
     }
 
     @Override
@@ -380,9 +379,10 @@ public class ReceiveExternalFilesActivity extends FileActivity
 
     private void populateDirectoryList() {
         setContentView(R.layout.uploader_layout);
+        setupToolbar();
+        ActionBar actionBar = getSupportActionBar();
 
         ListView mListView = (ListView) findViewById(android.R.id.list);
-        ActionBar actionBar = getSupportActionBar();
 
         String current_dir = mParents.peek();
         if (current_dir.equals("")) {

+ 121 - 0
src/com/owncloud/android/ui/activity/ToolbarActivity.java

@@ -0,0 +1,121 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author Andy Scherzinger
+ *   Copyright (C) 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.activity;
+
+import android.accounts.AccountManagerFuture;
+import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
+import com.owncloud.android.datamodel.OCFile;
+
+/**
+ * Base class providing toolbar registration functionality, see {@link #setupToolbar()}.
+ */
+public abstract class ToolbarActivity extends BaseActivity {
+    private ProgressBar mProgressBar;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+
+    /**
+     * Toolbar setup that must be called in implementer's {@link #onCreate} after {@link #setContentView} if they
+     * want to use the toolbar.
+     */
+    protected void setupToolbar() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+
+        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
+        mProgressBar.setIndeterminateDrawable(
+                ContextCompat.getDrawable(this, R.drawable.actionbar_progress_indeterminate_horizontal));
+    }
+
+    /**
+     * Updates title bar and home buttons (state and icon).
+     */
+    protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
+        String title = getString(R.string.default_display_name_for_root_folder);    // default
+        boolean inRoot;
+
+        // choose the appropriate title
+        inRoot = (
+                chosenFile == null ||
+                        (chosenFile.isFolder() && chosenFile.getParentId() == FileDataStorageManager.ROOT_PARENT_ID)
+        );
+        if (!inRoot) {
+            title = chosenFile.getFileName();
+        }
+
+        updateActionBarTitleAndHomeButtonByString(title);
+    }
+
+    /**
+     * Updates title bar and home buttons (state and icon).
+     */
+    protected void updateActionBarTitleAndHomeButtonByString(String title) {
+        String titleToSet = getString(R.string.app_name);    // default
+
+        if (title != null) {
+            titleToSet = title;
+        }
+
+        // set the chosen title
+        ActionBar actionBar = getSupportActionBar();
+        actionBar.setTitle(titleToSet);
+
+        // also as content description
+        View actionBarTitleView = getWindow().getDecorView().findViewById(
+                getResources().getIdentifier("action_bar_title", "id", "android")
+        );
+
+        // set home button properties
+        actionBar.setDisplayHomeAsUpEnabled(true);
+        actionBar.setDisplayShowTitleEnabled(true);
+    }
+
+    /**
+     * checks if the given file is the root folder.
+     *
+     * @param file file to be checked if it is the root folder
+     * @return <code>true</code> if it is <code>null</code> or the root folder, else returns <code>false</code>
+     */
+    public boolean isRoot(OCFile file) {
+        return file == null ||
+                (file.isFolder() && file.getParentId() == FileDataStorageManager.ROOT_PARENT_ID);
+    }
+
+    /**
+     * Change the indeterminate mode for the toolbar's progress bar.
+     *
+     * @param indeterminate <code>true</code> to enable the indeterminate mode
+     */
+    public void setIndeterminate(boolean indeterminate) {
+        mProgressBar.setIndeterminate(indeterminate);
+    }
+}

+ 5 - 3
src/com/owncloud/android/ui/activity/UploadFilesActivity.java

@@ -54,9 +54,8 @@ import java.io.File;
 
 /**
  * Displays local files and let the user choose what of them wants to upload
- * to the current ownCloud account
+ * to the current ownCloud account.
  */
-
 public class UploadFilesActivity extends FileActivity implements
     LocalFileListFragment.ContainerActivity, ActionBar.OnNavigationListener,
         OnClickListener, ConfirmationDialogFragmentListener {
@@ -119,6 +118,7 @@ public class UploadFilesActivity extends FileActivity implements
 
         // Inflate and set the layout view
         setContentView(R.layout.upload_files_layout);
+
         mFileListFragment = (LocalFileListFragment)
                 getSupportFragmentManager().findFragmentById(R.id.local_files_list);
         
@@ -143,7 +143,9 @@ public class UploadFilesActivity extends FileActivity implements
         if (localBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY){
             mRadioBtnCopyFiles.setChecked(true);
         }
-        
+
+        // setup the toolbar
+        setupToolbar();
             
         // Action bar setup
         ActionBar actionBar = getSupportActionBar();

+ 11 - 15
src/com/owncloud/android/ui/activity/UploadListActivity.java

@@ -82,20 +82,18 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         // but that's other story
         setFile(null);
 
-        // Navigation Drawer
-        initDrawer();
+        // setup toolbar
+        setupToolbar();
+
+        // setup drawer
+        setupDrawer(R.id.nav_uploads);
 
         // Add fragment with a transaction for setting a tag
         if(savedInstanceState == null) {
             createUploadListFragment();
         } // else, the Fragment Manager makes the job on configuration changes
 
-        // enable ActionBar app icon to behave as action to toggle nav drawer
-        getSupportActionBar().setHomeButtonEnabled(true);
-        mDrawerToggle.setDrawerIndicatorEnabled(true);
-        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-        getSupportActionBar().setTitle(R.string.uploads_view_title);
-
+        getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
     }
 
     private void createUploadListFragment(){
@@ -186,12 +184,11 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
                 (UploadListFragment) getSupportFragmentManager().findFragmentByTag(TAG_UPLOAD_LIST_FRAGMENT);
         switch (item.getItemId()) {
             case android.R.id.home:
-                if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
-                    mDrawerLayout.closeDrawer(GravityCompat.START);
+                if (isDrawerOpen()) {
+                    closeDrawer();
                 } else {
-                    mDrawerLayout.openDrawer(GravityCompat.START);
+                    openDrawer();
                 }
-                break;
             case R.id.action_retry_uploads:
                 FileUploader.UploadRequester requester = new FileUploader.UploadRequester();
                 requester.retryFailedUploads(this, null, null);
@@ -340,7 +337,6 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
         }
     }
 
-    @Override
     protected String getDefaultTitle() {
         return getString(R.string.uploads_view_title);
     }
@@ -352,9 +348,9 @@ public class UploadListActivity extends FileActivity implements UploadListFragme
     @Override
     protected void onAccountSet(boolean stateWasRecovered) {
         super.onAccountSet(stateWasRecovered);
-        updateActionBarTitleAndHomeButton(null);
+        getSupportActionBar().setTitle(getString(R.string.uploads_view_title));
         if (mAccountWasSet) {
-            setUsernameInDrawer(findViewById(R.id.left_drawer), getAccount());
+            setAccountInDrawer(getAccount());
         }
     }
 

+ 172 - 0
src/com/owncloud/android/ui/adapter/AccountListAdapter.java

@@ -0,0 +1,172 @@
+/**
+ * ownCloud Android client application
+ *
+ * @author Andy Scherzinger
+ * Copyright (C) 2016 ownCloud Inc.
+ * <p/>
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2,
+ * as published by the Free Software Foundation.
+ * <p/>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * <p/>
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.owncloud.android.ui.adapter;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.owncloud.android.R;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.TextDrawable;
+import com.owncloud.android.ui.activity.BaseActivity;
+import com.owncloud.android.ui.activity.ManageAccountsActivity;
+import com.owncloud.android.utils.DisplayUtils;
+
+import java.util.List;
+
+/**
+ * This Adapter populates a ListView with all accounts within the app.
+ */
+public class AccountListAdapter extends ArrayAdapter<AccountListItem> implements DisplayUtils.AvatarGenerationListener {
+    private static final String TAG = AccountListAdapter.class.getSimpleName();
+    private float mAccountAvatarRadiusDimension;
+    private final BaseActivity mContext;
+    private List<AccountListItem> mValues;
+    private AccountListAdapterListener mListener;
+
+    public AccountListAdapter(BaseActivity context, List<AccountListItem> values) {
+        super(context, -1, values);
+        this.mContext = context;
+        this.mValues = values;
+        this.mListener = (AccountListAdapterListener) context;
+        this.mAccountAvatarRadiusDimension = context.getResources().getDimension(R.dimen.list_item_avatar_icon_radius);
+    }
+
+    public void setAccountList(List<AccountListItem> values) {
+        this.mValues = values;
+    }
+
+    @Override
+    public View getView(final int position, View convertView, ViewGroup parent) {
+        AccountViewHolderItem viewHolder;
+
+        if (convertView == null) {
+            LayoutInflater inflater = mContext.getLayoutInflater();
+            convertView = inflater.inflate(R.layout.account_item, parent, false);
+
+            viewHolder = new AccountViewHolderItem();
+            viewHolder.textViewItem = (TextView) convertView.findViewById(R.id.user_name);
+            viewHolder.imageViewItem = (ImageView) convertView.findViewById(R.id.user_icon);
+            viewHolder.passwordButtonItem = (ImageView) convertView.findViewById(R.id.passwordButton);
+            viewHolder.removeButtonItem = (ImageView) convertView.findViewById(R.id.removeButton);
+
+            convertView.setTag(viewHolder);
+        } else {
+            viewHolder = (AccountViewHolderItem) convertView.getTag();
+        }
+
+        AccountListItem accountListItem = mValues.get(position);
+
+
+        if (accountListItem != null) {
+            // create account item
+            if (AccountListItem.TYPE_ACCOUNT == accountListItem.getType()) {
+                Account account = accountListItem.getAccount();
+                viewHolder.textViewItem.setText(account.name);
+                viewHolder.textViewItem.setTag(account.name);
+
+                try {
+                    DisplayUtils.setAvatar(account, this, mAccountAvatarRadiusDimension,
+                            mContext.getResources(), mContext.getStorageManager(), viewHolder.imageViewItem);
+                } catch (Exception e) {
+                    Log_OC.e(TAG, "Error calculating RGB value for account list item.", e);
+                    // use user icon as a fallback
+                    viewHolder.imageViewItem.setImageResource(R.drawable.ic_user);
+                }
+
+                /// bind listener to change password
+                viewHolder.passwordButtonItem.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mListener.changePasswordOfAccount(mValues.get(position).getAccount());
+                    }
+                });
+
+                /// bind listener to remove account
+                viewHolder.removeButtonItem.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mListener.removeAccount(mValues.get(position).getAccount());
+                        mValues.remove(position);
+                        AccountListAdapter.this.notifyDataSetChanged();
+                    }
+                });
+            } // create add account action item
+            else if (AccountListItem.TYPE_ACTION_ADD == accountListItem.getType()) {
+                LayoutInflater inflater = ((ManageAccountsActivity) mContext).getLayoutInflater();
+                View actionView = inflater.inflate(R.layout.account_action, parent, false);
+                ((TextView) actionView.findViewById(R.id.user_name)).setText(R.string.prefs_add_account);
+                ((ImageView) actionView.findViewById(R.id.user_icon)).setImageResource(R.drawable.ic_account_plus);
+
+                // bind action listener
+                actionView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        mListener.createAccount();
+                    }
+                });
+
+                return actionView;
+            }
+        }
+
+        return convertView;
+    }
+
+    @Override
+    public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
+        ((ImageView)callContext).setImageDrawable(avatarDrawable);
+    }
+
+    @Override
+    public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
+        return String.valueOf(((ImageView)callContext).getTag()).equals(tag);
+    }
+
+    /**
+     * Listener interface for Activities using the {@link AccountListAdapter}
+     */
+    public interface AccountListAdapterListener {
+        void removeAccount(Account account);
+
+        void changePasswordOfAccount(Account account);
+
+        void createAccount();
+    }
+
+    /**
+     * Account ViewHolderItem to get smooth scrolling.
+     */
+    static class AccountViewHolderItem {
+        TextView textViewItem;
+        ImageView imageViewItem;
+
+        ImageView passwordButtonItem;
+        ImageView removeButtonItem;
+    }
+}

+ 58 - 0
src/com/owncloud/android/ui/adapter/AccountListItem.java

@@ -0,0 +1,58 @@
+/**
+ *   ownCloud Android client application
+ *
+ *   @author Andy Scherzinger
+ *   Copyright (C) 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.adapter;
+
+import android.accounts.Account;
+
+/**
+ * Container implementation to add {@link Account}s and the add action to the list.
+ */
+public class AccountListItem {
+    public static final int TYPE_ACCOUNT = 0;
+    public static final int TYPE_ACTION_ADD = 1;
+
+    private Account mAccount;
+    private int mType;
+
+    /**
+     * creates an account list item containing an {@link Account}.
+     *
+     * @param account the account
+     */
+    public AccountListItem(Account account) {
+        mAccount = account;
+        mType = TYPE_ACCOUNT;
+    }
+
+    /**
+     * creates an account list item flagged as add-action.
+     */
+    public AccountListItem() {
+        mType = TYPE_ACTION_ADD;
+    }
+
+    public Account getAccount() {
+        return mAccount;
+    }
+
+    public int getType() {
+        return mType;
+    }
+}

+ 5 - 5
src/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java

@@ -390,7 +390,7 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
             fakeFileToCheatThumbnailsCacheManagerInterface.setStoragePath(upload.getLocalPath());
             fakeFileToCheatThumbnailsCacheManagerInterface.setMimetype(upload.getMimeType());
 
-            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialWork(
+            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(
                     fakeFileToCheatThumbnailsCacheManagerInterface,
                     fileIcon)
             );
@@ -415,8 +415,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                         if (thumbnail == null) {
                             thumbnail = ThumbnailsCacheManager.mDefaultImg;
                         }
-                        final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
-                                new ThumbnailsCacheManager.AsyncDrawable(
+                        final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncThumbnailDrawable(
                                         mParentActivity.getResources(),
                                         thumbnail,
                                         task
@@ -447,8 +447,8 @@ public class ExpandableUploadListAdapter extends BaseExpandableListAdapter imple
                         if (thumbnail == null) {
                             thumbnail = ThumbnailsCacheManager.mDefaultImg;
                         }
-                        final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
-                                new ThumbnailsCacheManager.AsyncDrawable(
+                        final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncThumbnailDrawable(
                                         mParentActivity.getResources(),
                                         thumbnail,
                                         task

+ 3 - 3
src/com/owncloud/android/ui/adapter/FileListListAdapter.java

@@ -311,7 +311,7 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                         fileIcon.setImageBitmap(thumbnail);
                     } else {
                         // generate new Thumbnail
-                        if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {
+                        if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
                             final ThumbnailsCacheManager.ThumbnailGenerationTask task =
                                     new ThumbnailsCacheManager.ThumbnailGenerationTask(
                                             fileIcon, mStorageManager, mAccount
@@ -319,8 +319,8 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter {
                             if (thumbnail == null) {
                                 thumbnail = ThumbnailsCacheManager.mDefaultImg;
                             }
-                            final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
-                                    new ThumbnailsCacheManager.AsyncDrawable(
+                            final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                    new ThumbnailsCacheManager.AsyncThumbnailDrawable(
                                     mContext.getResources(), 
                                     thumbnail, 
                                     task

+ 8 - 8
src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java

@@ -20,10 +20,6 @@
  */
 package com.owncloud.android.ui.adapter;
 
-import java.io.File;
-import java.util.Arrays;
-import java.util.Comparator;
-
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.view.LayoutInflater;
@@ -42,6 +38,10 @@ import com.owncloud.android.utils.BitmapUtils;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimetypeIconUtil;
 
+import java.io.File;
+import java.util.Arrays;
+import java.util.Comparator;
+
 /**
  * This Adapter populates a ListView with all files and directories contained
  * in a local directory
@@ -109,9 +109,9 @@ public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
             ImageView fileIcon = (ImageView) view.findViewById(R.id.thumbnail);
 
             /** Cancellation needs do be checked and done before changing the drawable in fileIcon, or
-             * {@link ThumbnailsCacheManager#cancelPotentialWork} will NEVER cancel any task.
+             * {@link ThumbnailsCacheManager#cancelPotentialThumbnailWork} will NEVER cancel any task.
              **/
-            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon));
+            boolean allowedToCreateNewThumbnail = (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon));
 
             if (!file.isDirectory()) {
                 fileIcon.setImageResource(R.drawable.file);
@@ -161,8 +161,8 @@ public class LocalFileListAdapter extends BaseAdapter implements ListAdapter {
                             if (thumbnail == null) {
                                 thumbnail = ThumbnailsCacheManager.mDefaultImg;
                             }
-                            final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
-                        		new ThumbnailsCacheManager.AsyncDrawable(
+                            final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                        		new ThumbnailsCacheManager.AsyncThumbnailDrawable(
                                     mContext.getResources(), 
                                     thumbnail, 
                                     task

+ 0 - 209
src/com/owncloud/android/ui/adapter/NavigationDrawerListAdapter.java

@@ -1,209 +0,0 @@
-/**
- *   ownCloud Android client application
- *
- *   @author tobiasKaminsky
- *   @author masensio
- *   Copyright (C) 2015 ownCloud Inc.
- *
- *   This program is free software: you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License version 2,
- *   as published by the Free Software Foundation.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-package com.owncloud.android.ui.adapter;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.owncloud.android.MainApp;
-import com.owncloud.android.R;
-import com.owncloud.android.authentication.AccountUtils;
-import com.owncloud.android.ui.NavigationDrawerItem;
-import com.owncloud.android.ui.activity.FileActivity;
-
-import java.util.ArrayList;
-
-public class NavigationDrawerListAdapter extends BaseAdapter {
-
-    private final static String TAG  = NavigationDrawerListAdapter.class.getSimpleName();
-
-    private Context mContext;
-
-    private ArrayList<NavigationDrawerItem> mNavigationDrawerItems;
-    private ArrayList<Object> mAll = new ArrayList<Object>();
-    private Account[] mAccounts;
-    private boolean mShowAccounts;
-    private Account mCurrentAccount;
-    private FileActivity mFileActivity;
-
-
-    public NavigationDrawerListAdapter(Context context, FileActivity fileActivity,
-                                       ArrayList<NavigationDrawerItem> navigationDrawerItems){
-        mFileActivity = fileActivity;
-        mContext = context;
-        mNavigationDrawerItems = navigationDrawerItems;
-
-        updateAccountList();
-
-        mAll.addAll(mNavigationDrawerItems);
-    }
-
-    public void updateAccountList(){
-        AccountManager am = (AccountManager) mContext.getSystemService(mContext.ACCOUNT_SERVICE);
-        mAccounts = am.getAccountsByType(MainApp.getAccountType());
-        mCurrentAccount = AccountUtils.getCurrentOwnCloudAccount(mContext);
-    }
-
-    @Override
-    public int getCount() {
-        if (mShowAccounts){
-            return mNavigationDrawerItems.size() + 1;
-        } else {
-            return mNavigationDrawerItems.size();
-        }
-    }
-
-    @Override
-    public Object getItem(int position) {
-        //return all.get(position);
-        return null;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return 0;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-
-        LayoutInflater inflator = (LayoutInflater) mContext
-                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        if (mAll.size() > position) {
-            // Normal entry
-            if (mAll.get(position) instanceof NavigationDrawerItem){
-                NavigationDrawerItem navItem = (NavigationDrawerItem) mAll.get(position);
-
-                View view = inflator.inflate(R.layout.drawer_list_item, null);
-
-                LinearLayout itemLayout = (LinearLayout) view.findViewById(R.id.itemLayout);
-                itemLayout.setContentDescription(navItem.getContentDescription());
-
-                TextView itemText = (TextView) view.findViewById(R.id.itemTitle);
-                itemText.setText(navItem.getTitle());
-
-                if(navItem.getIcon()!=0) {
-                    ImageView itemImage = (ImageView) view.findViewById(R.id.itemIcon);
-                    itemImage.setImageResource(navItem.getIcon());
-                }
-
-                return view;
-            }
-            // TODO re-enable when "Accounts" is available in Navigation Drawer
-            // Account
-//            if (mAll.get(position) instanceof Account[]){
-//                final View view = inflator.inflate(R.layout.drawer_account_group, null);
-//
-//                final RadioGroup group = (RadioGroup) view.findViewById(R.id.drawer_radio_group);
-//
-//                for (Account account : mAccounts) {
-//                    RadioButton rb = new RadioButton(mContext);
-//
-//                    rb.setText(account.name);
-//                    rb.setContentDescription(account.name);
-//                    rb.setTextColor(Color.BLACK);
-//                    rb.setEllipsize(TextUtils.TruncateAt.MIDDLE);
-//                    rb.setSingleLine();
-//                    rb.setCompoundDrawablePadding(30);
-//
-//
-//                    try {
-//                        // using adapted algorithm from /core/js/placeholder.js:50
-//                        int lastAtPos = account.name.lastIndexOf("@");
-//                        String username  = account.name.substring(0, lastAtPos);
-//                        byte[] seed = username.getBytes("UTF-8");
-//                        MessageDigest md = MessageDigest.getInstance("MD5");
-////                        Integer seedMd5Int = Math.abs(new String(Hex.encodeHex(seedMd5))
-////                      .hashCode());
-//                        Integer seedMd5Int = String.format(Locale.ROOT, "%032x",
-//                                new BigInteger(1, md.digest(seed))).hashCode();
-//
-//                        double maxRange = java.lang.Integer.MAX_VALUE;
-//                        float hue = (float) (seedMd5Int / maxRange * 360);
-//
-//                        int[] rgb = BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f);
-//
-//                        TextDrawable text = new TextDrawable(username.substring(0, 1).toUpperCase(),
-//                                rgb[0], rgb[1], rgb[2]);
-//                        rb.setCompoundDrawablesWithIntrinsicBounds(text, null, null, null);
-//
-//
-//                    } catch (Exception e){
-//                        Log_OC.d(TAG, e.toString());
-//                        rb.setTextColor(mContext.getResources().getColor(R.color.black));
-//                    }
-//                    RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(
-//                            LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-//                    params.weight=1.0f;
-//                    params.setMargins(15, 5, 5, 5);
-//
-//                    // Check the current account that is being used
-//                    if (account.name.equals(mCurrentAccount.name)) {
-//                        rb.setChecked(true);
-//                    } else {
-//                        rb.setChecked(false);
-//                    }
-//
-//                    group.addView(rb, params);
-//                }
-//
-//                group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener(){
-//                    public void onCheckedChanged(RadioGroup group, int checkedId) {
-//                        // checkedId is the RadioButton selected
-//                        RadioButton rb = (RadioButton) view.findViewById(checkedId);
-//
-//                        AccountUtils.setCurrentOwnCloudAccount(mContext,rb.getText().toString());
-//                        notifyDataSetChanged();
-//                        mFileActivity.closeDrawer();
-//
-//                        // restart the main activity
-//                        mFileActivity.restart();
-//                    }
-//                });
-//
-//                return view;
-//            }
-        }
-        return convertView;
-    }
-
-    //TODO re-enable when "Accounts" is available in Navigation Drawer
-    // TODO update Account List after creating a new account and on fresh installation
-//    public void setShowAccounts(boolean value){
-//        mAll.clear();
-//        mAll.addAll(mNavigationDrawerItems);
-//
-//        if (value){
-//            mAll.add(1, mAccounts);
-//        }
-//        mShowAccounts = value;
-//    }
-}

+ 3 - 3
src/com/owncloud/android/ui/adapter/UploaderAdapter.java

@@ -34,7 +34,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
 import com.owncloud.android.datamodel.ThumbnailsCacheManager;
-import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncDrawable;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable;
 import com.owncloud.android.utils.DisplayUtils;
 import com.owncloud.android.utils.MimetypeIconUtil;
 
@@ -100,14 +100,14 @@ public class UploaderAdapter extends SimpleAdapter {
                 fileIcon.setImageBitmap(thumbnail);
             } else {
                 // generate new Thumbnail
-                if (ThumbnailsCacheManager.cancelPotentialWork(file, fileIcon)) {
+                if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, fileIcon)) {
                     final ThumbnailsCacheManager.ThumbnailGenerationTask task = 
                             new ThumbnailsCacheManager.ThumbnailGenerationTask(fileIcon, mStorageManager, 
                                     mAccount);
                     if (thumbnail == null) {
                         thumbnail = ThumbnailsCacheManager.mDefaultImg;
                     }
-                    final AsyncDrawable asyncDrawable = new AsyncDrawable(
+                    final AsyncThumbnailDrawable asyncDrawable = new AsyncThumbnailDrawable(
                             mContext.getResources(), 
                             thumbnail, 
                             task

+ 3 - 3
src/com/owncloud/android/ui/fragment/FileDetailFragment.java

@@ -434,7 +434,7 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                     iv.setImageBitmap(thumbnail);
                 } else {
                     // generate new Thumbnail
-                    if (ThumbnailsCacheManager.cancelPotentialWork(file, iv)) {
+                    if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, iv)) {
                         final ThumbnailsCacheManager.ThumbnailGenerationTask task =
                                 new ThumbnailsCacheManager.ThumbnailGenerationTask(
                                         iv, mContainerActivity.getStorageManager(), mAccount
@@ -442,8 +442,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener
                         if (thumbnail == null) {
                             thumbnail = ThumbnailsCacheManager.mDefaultImg;
                         }
-                        final ThumbnailsCacheManager.AsyncDrawable asyncDrawable =
-                                new ThumbnailsCacheManager.AsyncDrawable(
+                        final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncThumbnailDrawable(
                                         MainApp.getAppContext().getResources(),
                                         thumbnail,
                                         task

+ 8 - 8
src/com/owncloud/android/ui/preview/PreviewImageActivity.java

@@ -96,7 +96,7 @@ public class PreviewImageActivity extends FileActivity implements
         setContentView(R.layout.preview_image_activity);
 
         // Navigation Drawer
-        initDrawer();
+        setupDrawer();
 
         // ActionBar
         ActionBar actionBar = getSupportActionBar();
@@ -120,10 +120,10 @@ public class PreviewImageActivity extends FileActivity implements
                     ActionBar actionBar = getSupportActionBar();
                     if (visible) {
                         actionBar.show();
-                        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+                        setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
                     } else {
                         actionBar.hide();
-                        mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+                        setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                     }
                 }
             });
@@ -304,8 +304,8 @@ public class PreviewImageActivity extends FileActivity implements
         
         switch(item.getItemId()){
         case android.R.id.home:
-            if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
-                mDrawerLayout.closeDrawer(GravityCompat.START);
+            if (isDrawerOpen()) {
+                closeDrawer();
             } else {
                 backToDisplayActivity();
             }
@@ -391,7 +391,7 @@ public class PreviewImageActivity extends FileActivity implements
         } else {
             OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); 
             getSupportActionBar().setTitle(currentFile.getFileName());
-            mDrawerToggle.setDrawerIndicatorEnabled(false);
+            setDrawerIndicatorEnabled(false);
             if (!currentFile.isDown()) {
                 if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) {
                     requestForDownload(currentFile);
@@ -496,11 +496,11 @@ public class PreviewImageActivity extends FileActivity implements
             ActionBar actionBar = getSupportActionBar();
             if (!actionBar.isShowing()) {
                 actionBar.show();
-                mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
+                setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
 
             } else {
                 actionBar.hide();
-                mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+                setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
 
             }
 

+ 45 - 2
src/com/owncloud/android/utils/BitmapUtils.java

@@ -19,17 +19,26 @@
  */
 package com.owncloud.android.utils;
 
+import com.owncloud.android.authentication.AccountUtils;
 import com.owncloud.android.lib.common.utils.Log_OC;
 
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
 import android.graphics.BitmapFactory.Options;
 import android.media.ExifInterface;
 import android.net.Uri;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
 import android.webkit.MimeTypeMap;
 
 import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Locale;
 
 /**
  * Utility class with methods for decoding Bitmaps.
@@ -70,7 +79,6 @@ public class BitmapUtils {
         // decode bitmap with inSampleSize set
         options.inJustDecodeBounds = false;
         return BitmapFactory.decodeFile(srcPath, options);
-        
     }    
 
 
@@ -266,5 +274,40 @@ public class BitmapUtils {
 
         return (mimeType != null && mimeType.startsWith("image/"));
     }
-    
+
+    /**
+     * calculates the RGB value based on a given account name.
+     *
+     * @param accountName The account name
+     * @return corresponding RGB color
+     * @throws UnsupportedEncodingException if the charset is not supported
+     * @throws NoSuchAlgorithmException if the specified algorithm is not available
+     */
+    public static int[] calculateRGB(String accountName) throws UnsupportedEncodingException, NoSuchAlgorithmException {
+        // using adapted algorithm from /core/js/placeholder.js:50
+        String username = AccountUtils.getAccountUsername(accountName);
+        byte[] seed = username.getBytes("UTF-8");
+        MessageDigest md = MessageDigest.getInstance("MD5");
+        Integer seedMd5Int = String.format(Locale.ROOT, "%032x",
+                new BigInteger(1, md.digest(seed))).hashCode();
+
+        double maxRange = Integer.MAX_VALUE;
+        float hue = (float) (seedMd5Int / maxRange * 360);
+
+        return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f);
+    }
+
+    /**
+     * Returns a new circular bitmap drawable by creating it from a bitmap, setting initial target density based on
+     * the display metrics of the resources.
+     *
+     * @param resources the resources for initial target density
+     * @param bitmap the original bitmap
+     * @return the circular bitmap
+     */
+    public static RoundedBitmapDrawable bitmapToCircularBitmapDrawable(Resources resources, Bitmap bitmap) {
+        RoundedBitmapDrawable roundedBitmap = RoundedBitmapDrawableFactory.create(resources, bitmap);
+        roundedBitmap.setCircular(true);
+        return roundedBitmap;
+    }
 }

+ 60 - 0
src/com/owncloud/android/utils/DisplayUtils.java

@@ -22,24 +22,33 @@
 
 package com.owncloud.android.utils;
 
+import android.accounts.Account;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.design.widget.Snackbar;
 import android.support.v4.content.ContextCompat;
 import android.text.format.DateUtils;
 import android.view.Display;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.datamodel.FileDataStorageManager;
 import com.owncloud.android.datamodel.OCFile;
+import com.owncloud.android.datamodel.ThumbnailsCacheManager;
+import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.TextDrawable;
 
 import java.math.BigDecimal;
 import java.net.IDN;
@@ -53,6 +62,7 @@ import java.util.Map;
  * A helper class for some string operations.
  */
 public class DisplayUtils {
+    private static final String TAG = DisplayUtils.class.getSimpleName();
     
     private static final String OWNCLOUD_APP_NAME = "ownCloud";
     
@@ -278,4 +288,54 @@ public class DisplayUtils {
         // Changing action button text color
         snackbar.setActionTextColor(ContextCompat.getColor(context, R.color.white));
     }
+
+    public interface AvatarGenerationListener {
+        void avatarGenerated(Drawable avatarDrawable, Object callContext);
+        boolean shouldCallGeneratedCallback(String tag, Object callContext);
+    }
+
+    /**
+     * fetches and sets the avatar of the current account in the drawer in case the drawer is available.
+     *
+     * @param account        the account to be set in the drawer
+     * @param avatarRadius   the avatar radius
+     * @param resources      reference for density information
+     * @param storageManager reference for caching purposes
+     */
+    public static void setAvatar(Account account, AvatarGenerationListener listener, float avatarRadius, Resources resources,
+                           FileDataStorageManager storageManager, Object callContext) {
+        if (account != null) {
+            if (callContext instanceof View)
+                ((View)callContext).setContentDescription(account.name);
+
+            // Thumbnail in Cache?
+            Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache("a_" + account.name);
+
+            if (thumbnail != null) {
+                listener.avatarGenerated(
+                        BitmapUtils.bitmapToCircularBitmapDrawable(MainApp.getAppContext().getResources(), thumbnail),
+                        callContext);
+            } else {
+                // generate new avatar
+                if (ThumbnailsCacheManager.cancelPotentialAvatarWork(account.name, callContext)) {
+                    final ThumbnailsCacheManager.AvatarGenerationTask task =
+                            new ThumbnailsCacheManager.AvatarGenerationTask(listener, callContext, storageManager, account);
+                    if (thumbnail == null) {
+                        try {
+                            listener.avatarGenerated(TextDrawable.createAvatar(account.name, avatarRadius), callContext);
+                        } catch (Exception e) {
+                            Log_OC.e(TAG, "Error calculating RGB value for active account icon.", e);
+                            listener.avatarGenerated(resources.getDrawable(R.drawable.ic_account_circle), callContext);
+                        }
+                    } else {
+                        final ThumbnailsCacheManager.AsyncAvatarDrawable asyncDrawable =
+                                new ThumbnailsCacheManager.AsyncAvatarDrawable(resources, thumbnail, task);
+                        listener.avatarGenerated(BitmapUtils.bitmapToCircularBitmapDrawable(
+                                        resources, asyncDrawable.getBitmap()), callContext);
+                    }
+                    task.execute(account.name);
+                }
+            }
+        }
+    }
 }