diff --git a/build.gradle b/build.gradle
index 6e80c65cc5c8..098dc8ed7592 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,7 @@ dependencies {
/// dependencies for app building
compile name: 'touch-image-view'
- compile 'com.github.nextcloud:android-library:1.0.2'
+ compile 'com.github.nextcloud:android-library:1.0.6'
compile "com.android.support:support-v4:${supportLibraryVersion}"
compile "com.android.support:design:${supportLibraryVersion}"
compile 'com.jakewharton:disklrucache:2.0.2'
diff --git a/res/layout/drawer.xml b/res/layout/drawer.xml
index 843a1ebdf3df..cca0b25ba328 100644
--- a/res/layout/drawer.xml
+++ b/res/layout/drawer.xml
@@ -24,9 +24,43 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
+ android:layout_weight="1"
android:fitsSystemWindows="true"
- app:theme="@style/NavigationView_ItemTextAppearance"
app:headerLayout="@layout/drawer_header"
- app:menu="@menu/drawer_menu"/>
+ app:menu="@menu/drawer_menu"
+ app:theme="@style/NavigationView_ItemTextAppearance">
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/drawer_menu.xml b/res/menu/drawer_menu.xml
index 9551b98274dd..7c6462f8e8c6 100644
--- a/res/menu/drawer_menu.xml
+++ b/res/menu/drawer_menu.xml
@@ -67,4 +67,15 @@
android:icon="@drawable/ic_settings"
android:title="@string/actionbar_settings"/>
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 201060c8c6f9..3252a60f75dc 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -52,4 +52,10 @@
#201D2D44
#40162233
+
+
+ @color/color_accent
+ #fdd835
+ #e57373
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 10b3b8aa4e67..259a93884dd5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -23,6 +23,7 @@
On device
Settings
Uploads
+ %1$s of %2$s used
Close
Open
General
diff --git a/src/com/owncloud/android/ui/activity/DrawerActivity.java b/src/com/owncloud/android/ui/activity/DrawerActivity.java
index bab0cde0a382..7b81013aad2a 100644
--- a/src/com/owncloud/android/ui/activity/DrawerActivity.java
+++ b/src/com/owncloud/android/ui/activity/DrawerActivity.java
@@ -38,6 +38,8 @@
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
import android.widget.TextView;
import com.owncloud.android.MainApp;
@@ -45,7 +47,10 @@
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.OwnCloudAccount;
+import com.owncloud.android.lib.common.operations.RemoteOperation;
+import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.lib.resources.users.RemoteGetUserQuotaOperation;
import com.owncloud.android.ui.TextDrawable;
import com.owncloud.android.utils.DisplayUtils;
@@ -121,6 +126,21 @@ public abstract class DrawerActivity extends ToolbarActivity implements DisplayU
*/
private Account[] mAvatars = new Account[3];
+ /**
+ * container layout of the quota view.
+ */
+ private LinearLayout mQuotaView;
+
+ /**
+ * progress bar of the quota view.
+ */
+ private ProgressBar mQuotaProgressBar;
+
+ /**
+ * text view of the quota view.
+ */
+ private TextView mQuotaTextView;
+
/**
* 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.
@@ -141,30 +161,22 @@ protected void setupDrawer() {
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;
+ setupDrawerHeader();
- mAccountMiddleAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_middle);
- mAccountEndAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_end);
+ setupDrawerMenu(mNavigationView);
- // 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);
- }
+ setupQuotaElement();
+ }
- setupDrawerContent(mNavigationView);
+ setupDrawerToggle();
- findNavigationViewChildById(R.id.drawer_active_user)
- .setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- toggleAccountList();
- }
- });
- }
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+ /**
+ * initializes and sets up the drawer toggle.
+ */
+ private void setupDrawerToggle() {
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
/** Called when a drawer has settled in a completely closed state. */
@@ -188,7 +200,35 @@ public void onDrawerOpened(View drawerView) {
// Set the drawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);
mDrawerToggle.setDrawerIndicatorEnabled(true);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ /**
+ * initializes and sets up the drawer header.
+ */
+ private void setupDrawerHeader() {
+ 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);
+
+ findNavigationViewChildById(R.id.drawer_active_user)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ toggleAccountList();
+ }
+ });
+ }
+
+ /**
+ * setup quota elements of the drawer.
+ */
+ private void setupQuotaElement() {
+ mQuotaView = (LinearLayout) findViewById(R.id.drawer_quota);
+ mQuotaProgressBar = (ProgressBar) findViewById(R.id.drawer_quota_ProgressBar);
+ mQuotaTextView = (TextView) findViewById(R.id.drawer_quota_text);
+ DisplayUtils.colorPreLollipopHorizontalProgressBar(mQuotaProgressBar);
}
/**
@@ -196,7 +236,14 @@ public void onDrawerOpened(View drawerView) {
*
* @param navigationView the drawers navigation view
*/
- protected void setupDrawerContent(NavigationView navigationView) {
+ protected void setupDrawerMenu(NavigationView navigationView) {
+ // 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) {
+ navigationView.setItemIconTintList(null);
+ }
+
+ // setup actions for drawer menu items
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
@@ -246,9 +293,9 @@ public boolean onNavigationItemSelected(MenuItem menuItem) {
// handle correct state
if (mIsAccountChooserActive) {
- mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
+ navigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
} else {
- mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
+ navigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
}
}
@@ -449,6 +496,9 @@ protected void setAccountInDrawer(Account account) {
DisplayUtils.setAvatar(account, this,
mCurrentAccountAvatarRadiusDimension, getResources(), getStorageManager(),
findNavigationViewChildById(R.id.drawer_current_account));
+
+ // check and show quota info if available
+ getAndDisplayUserQuota();
}
}
@@ -477,6 +527,38 @@ private void showMenu() {
}
}
+ /**
+ * shows or hides the quota UI elements.
+ *
+ * @param showQuota show/hide quota information
+ */
+ private void showQuota(boolean showQuota) {
+ if (showQuota) {
+ mQuotaView.setVisibility(View.VISIBLE);
+ } else {
+ mQuotaView.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * configured the quota to be displayed.
+ *
+ * @param usedSpace the used space
+ * @param totalSpace the total space
+ * @param relative the percentage of space already used
+ */
+ private void setQuotaInformation(long usedSpace, long totalSpace, int relative) {
+ mQuotaProgressBar.setProgress(relative);
+ DisplayUtils.colorHorizontalProgressBar(mQuotaProgressBar, DisplayUtils.getRelativeInfoColor(this, relative));
+
+ mQuotaTextView.setText(String.format(
+ getString(R.string.drawer_quota),
+ DisplayUtils.bytesToHumanReadable(usedSpace),
+ DisplayUtils.bytesToHumanReadable(totalSpace)));
+
+ showQuota(true);
+ }
+
/**
* checks/highlights the provided menu item if the drawer has been initialized and the menu item exists.
*
@@ -492,6 +574,57 @@ protected void setDrawerMenuItemChecked(int menuItemId) {
}
}
+ /**
+ * Retrieves and shows the user quota if available
+ */
+ private void getAndDisplayUserQuota() {
+ // set user space information
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+
+ RemoteOperation getQuotaInfoOperation = new RemoteGetUserQuotaOperation();
+ RemoteOperationResult result = getQuotaInfoOperation.execute(
+ AccountUtils.getCurrentOwnCloudAccount(DrawerActivity.this), DrawerActivity.this);
+
+ if (result.isSuccess() && result.getData() != null) {
+ final RemoteGetUserQuotaOperation.Quota quota =
+ (RemoteGetUserQuotaOperation.Quota) result.getData().get(0);
+
+ final long used = quota.getUsed();
+ final long total = quota.getTotal();
+ final int relative = (int) Math.ceil(quota.getRelative());
+ final long quotaValue = quota.getQuota();
+
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (quotaValue > 0
+ || quotaValue == RemoteGetUserQuotaOperation.QUOTA_LIMIT_INFO_NOT_AVAILABLE) {
+ /**
+ * show quota in case
+ * it is available and calculated (> 0) or
+ * in case of legacy servers (==QUOTA_LIMIT_INFO_NOT_AVAILABLE)
+ */
+ setQuotaInformation(used, total, relative);
+ } else {
+ /**
+ * quotaValue < 0 means special cases like
+ * {@link RemoteGetUserQuotaOperation.SPACE_NOT_COMPUTED},
+ * {@link RemoteGetUserQuotaOperation.SPACE_UNKNOWN} or
+ * {@link RemoteGetUserQuotaOperation.SPACE_UNLIMITED}
+ * thus don't display any quota information.
+ */
+ showQuota(false);
+ }
+ }
+ });
+ }
+ }
+ });
+
+ t.start();
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java
index c9a81a9a614e..6ad06093fd7c 100644
--- a/src/com/owncloud/android/utils/DisplayUtils.java
+++ b/src/com/owncloud/android/utils/DisplayUtils.java
@@ -1,23 +1,25 @@
/**
- * ownCloud Android client application
+ * Nextcloud Android client application
*
+ * @author Andy Scherzinger
* @author Bartek Przybylski
* @author David A. Velasco
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2015 ownCloud Inc.
+ * Copyright (C) 2016 Andy Scherzinger
*
- * 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 free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU 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 .
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this program. If not, see .
*/
package com.owncloud.android.utils;
@@ -37,7 +39,6 @@
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.ContextCompat;
import android.text.format.DateUtils;
-import android.view.Display;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.SeekBar;
@@ -60,20 +61,21 @@
import java.util.Map;
/**
- * A helper class for some string operations.
+ * A helper class for UI/display related operations.
*/
public class DisplayUtils {
private static final String TAG = DisplayUtils.class.getSimpleName();
-
- private static final String OWNCLOUD_APP_NAME = "ownCloud";
-
+
private static final String[] sizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
private static final int[] sizeScales = { 0, 0, 1, 1, 1, 2, 2, 2, 2 };
+ public static final int RELATIVE_THRESHOLD_WARNING = 90;
+ public static final int RELATIVE_THRESHOLD_CRITICAL = 95;
+ public static final String MIME_TYPE_UNKNOWN = "Unknown type";
private static Map mimeType2HumanReadable;
static {
- mimeType2HumanReadable = new HashMap();
+ mimeType2HumanReadable = new HashMap<>();
// images
mimeType2HumanReadable.put("image/jpeg", "JPEG image");
mimeType2HumanReadable.put("image/jpg", "JPEG image");
@@ -94,19 +96,19 @@ public class DisplayUtils {
* rounds the size based on the suffix to 0,1 or 2 decimals
*
*
- * @param bytes Input file size
- * @return Like something readable like "12 MB"
+ * @param bytes Input file size
+ * @return something readable like "12 MB"
*/
public static String bytesToHumanReadable(long bytes) {
double result = bytes;
- int attachedSuff = 0;
- while (result > 1024 && attachedSuff < sizeSuffixes.length) {
+ int suffixIndex = 0;
+ while (result > 1024 && suffixIndex < sizeSuffixes.length) {
result /= 1024.;
- attachedSuff++;
+ suffixIndex++;
}
return new BigDecimal(result).setScale(
- sizeScales[attachedSuff], BigDecimal.ROUND_HALF_UP) + " " + sizeSuffixes[attachedSuff];
+ sizeScales[suffixIndex], BigDecimal.ROUND_HALF_UP) + " " + sizeSuffixes[suffixIndex];
}
/**
@@ -114,7 +116,7 @@ public static String bytesToHumanReadable(long bytes) {
* like "JPG image".
*
* @param mimetype MIME type to convert
- * @return A human friendly version of the MIME type
+ * @return A human friendly version of the MIME type, {@link #MIME_TYPE_UNKNOWN} if it can't be converted
*/
public static String convertMIMEtoPrettyPrint(String mimetype) {
if (mimeType2HumanReadable.containsKey(mimetype)) {
@@ -122,11 +124,12 @@ public static String convertMIMEtoPrettyPrint(String mimetype) {
}
if (mimetype.split("/").length >= 2)
return mimetype.split("/")[1].toUpperCase() + " file";
- return "Unknown type";
+ return MIME_TYPE_UNKNOWN;
}
/**
* Converts Unix time to human readable format
+ *
* @param milliseconds that have passed since 01/01/1970
* @return The human readable time for the users locale
*/
@@ -138,6 +141,7 @@ public static String unixTimeToHumanReadable(long milliseconds) {
/**
* Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode.
+ *
* @param url the URL where the domain name should be converted
* @param toASCII if true converts from Unicode to ASCII, if false converts from ASCII to Unicode
* @return the URL containing the converted domain name
@@ -155,9 +159,9 @@ public static String convertIdn(String url, boolean toASCII) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// Find host name after '//' or '@'
int hostStart = 0;
- if (urlNoDots.indexOf("//") != -1) {
+ if (urlNoDots.contains("//")) {
hostStart = url.indexOf("//") + "//".length();
- } else if (url.indexOf("@") != -1) {
+ } else if (url.contains("@")) {
hostStart = url.indexOf("@") + "@".length();
}
@@ -207,17 +211,33 @@ public static CharSequence getRelativeTimestamp(Context context, long modificati
DateUtils.WEEK_IN_MILLIS, 0);
}
- @SuppressWarnings("deprecation")
- public static CharSequence getRelativeDateTimeString (
- Context c, long time, long minResolution, long transitionResolution, int flags
- ){
-
+ /**
+ * determines the info level color based on certain thresholds
+ * {@link #RELATIVE_THRESHOLD_WARNING} and {@link #RELATIVE_THRESHOLD_CRITICAL}.
+ *
+ * @param context the app's context
+ * @param relative relative value for which the info level color should be looked up
+ * @return info level color
+ */
+ public static int getRelativeInfoColor(Context context, int relative) {
+ if (relative < RELATIVE_THRESHOLD_WARNING) {
+ return context.getResources().getColor(R.color.infolevel_info);
+ } else if (relative >= RELATIVE_THRESHOLD_WARNING && relative < RELATIVE_THRESHOLD_CRITICAL) {
+ return context.getResources().getColor(R.color.infolevel_warning);
+ } else {
+ return context.getResources().getColor(R.color.infolevel_critical);
+ }
+ }
+
+ public static CharSequence getRelativeDateTimeString(
+ Context c, long time, long minResolution, long transitionResolution, int flags) {
+
CharSequence dateString = "";
-
+
// in Future
- if (time > System.currentTimeMillis()){
+ if (time > System.currentTimeMillis()) {
return DisplayUtils.unixTimeToHumanReadable(time);
- }
+ }
// < 60 seconds -> seconds ago
else if ((System.currentTimeMillis() - time) < 60 * 1000) {
return c.getString(R.string.file_list_seconds_ago);
@@ -238,8 +258,9 @@ else if ((System.currentTimeMillis() - time) < 60 * 1000) {
}
/**
- * Update the passed path removing the last "/" if it is not the root folder
- * @param path
+ * Update the passed path removing the last "/" if it is not the root folder.
+ *
+ * @param path the path to be trimmed
*/
public static String getPathWithoutLastSlash(String path) {
@@ -250,22 +271,16 @@ public static String getPathWithoutLastSlash(String path) {
return path;
}
-
/**
- * Gets the screen size in pixels in a backwards compatible way
+ * Gets the screen size in pixels.
*
- * @param caller Activity calling; needed to get access to the {@link android.view.WindowManager}
- * @return Size in pixels of the screen, or default {@link Point} if caller is null
+ * @param caller Activity calling; needed to get access to the {@link android.view.WindowManager}
+ * @return Size in pixels of the screen, or default {@link Point} if caller is null
*/
public static Point getScreenSize(Activity caller) {
Point size = new Point();
if (caller != null) {
- Display display = caller.getWindowManager().getDefaultDisplay();
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) {
- display.getSize(size);
- } else {
- size.set(display.getWidth(), display.getHeight());
- }
+ caller.getWindowManager().getDefaultDisplay().getSize(size);
}
return size;
}
@@ -277,7 +292,18 @@ public static Point getScreenSize(Activity caller) {
*/
public static void colorPreLollipopHorizontalProgressBar(ProgressBar progressBar) {
if (progressBar != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- int color = progressBar.getResources().getColor(R.color.color_accent);
+ colorHorizontalProgressBar(progressBar, progressBar.getResources().getColor(R.color.color_accent));
+ }
+ }
+
+ /**
+ * sets the coloring of the given progress bar to color_accent.
+ *
+ * @param progressBar the progress bar to be colored
+ * @param color the color to be used
+ */
+ public static void colorHorizontalProgressBar(ProgressBar progressBar, @ColorInt int color) {
+ if (progressBar != null) {
progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
progressBar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
@@ -301,9 +327,9 @@ public static void colorPreLollipopHorizontalSeekBar(SeekBar seekBar) {
}
/**
- * set the owncloud standard colors for the snackbar.
+ * set the Nextcloud standard colors for the snackbar.
*
- * @param context the context relevant for setting the color according to the context's theme
+ * @param context the context relevant for setting the color according to the context's theme
* @param snackbar the snackbar to be colored
*/
public static void colorSnackbar(Context context, Snackbar snackbar) {
@@ -315,7 +341,7 @@ public static void colorSnackbar(Context context, Snackbar snackbar) {
* Sets the color of the status bar to {@code color} on devices with OS version lollipop or higher.
*
* @param fragmentActivity fragment activity
- * @param color the color
+ * @param color the color
*/
public static void colorStatusBar(FragmentActivity fragmentActivity, @ColorInt int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -326,11 +352,11 @@ public static void colorStatusBar(FragmentActivity fragmentActivity, @ColorInt i
/**
* Sets the color of the progressbar to {@code color} within the given toolbar.
*
- * @param activity the toolbar activity instance
+ * @param activity the toolbar activity instance
* @param progressBarColor the color to be used for the toolbar's progress bar
*/
public static void colorToolbarProgressBar(FragmentActivity activity, int progressBarColor) {
- if(activity instanceof ToolbarActivity) {
+ if (activity instanceof ToolbarActivity) {
((ToolbarActivity) activity).setProgressBarBackgroundColor(progressBarColor);
}
}