From: tobiasKaminsky Date: Thu, 29 Oct 2015 16:55:56 +0000 (+0100) Subject: Merge branch 'switchListVsGridMaster' into beta X-Git-Tag: beta-20151122~94 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/e8654191a721c6d5831992645f2c4f38a7ca35b7?hp=65f8034c3030948b0a348f7f654821b6226f6a1d Merge branch 'switchListVsGridMaster' into beta --- diff --git a/.gitignore b/.gitignore index 8346dbfc..9b1a0d0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # built application files -*.apk *.ap_ # files for the dex VM @@ -39,4 +38,4 @@ tests/proguard-project.txt build # Actionbarsherlock is now ignored since scripts takes care of init the sub-modules. -actionbarsherlock \ No newline at end of file +actionbarsherlock diff --git a/AndroidManifest.xml b/AndroidManifest.xml index acb5f1d8..dacb8b89 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -110,12 +110,17 @@ + + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 099a4456..6436dc01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,60 +1,5 @@ -## 1.8.0 (September 2015) -- New MATERIAL DESIGN theme -- Updated FILE TYPE ICONS -- Preview TXT files within the app -- COPY files & folders -- Preview the full file/folder name from the long press menu -- Set a file as FAVORITE (kept-in-sync) from the CONTEXT MENU -- Updated CONFLICT RESOLUTION dialog (wording) -- Updated background for images with TRANSPARENCY in GALLERY -- Hidden files will not enforce list view instead of GRID VIEW (folders from Picasa & others) -- Security: - + Updated network stack with security fixes (Jackrabbit 2.10.1) -- Bugs fixed: - + Fixed crash when ETag is lost - + Passcode creation not restarted on device rotation - + Recovered share icon shown on folders 'shared with me' - + User name added to subject when sending a share link through e-mail (fixed on SAMLed apps) - -## 1.7.2 (July 2015) -- New navigation drawer -- Improved Passcode -- Automatic grid view just for folders full of images -- More characters allowed in file names -- Support for servers in same domain, different path -- Bugs fixed: - + Frequent crashes in folder with several images - + Sync error in servers with huge quota and external storage enable - + Share by link error - + Some other crashes and minor bugs - -## 1.7.1 (April 2015) - -- Share link even with password enforced by server -- Get the app ready for oc 8.1 servers -- Added option to create new folder in uploads from external apps -- Improved management of deleted users -- Bugs fixed - + Fixed crash on Android 2.x devices - + Improvements on uploads - -## 1.7.0 (February 2015) - -- Download full folders -- Grid view for images -- Remote thumbnails (OC Server 8.0+) -- Added number of files and folders at the end of the list -- "Open with" in contextual menu -- Downloads added to Media Provider -- Uploads: - + Local thumbnails in section "Files" - + Multiple selection in "Content from other apps" (Android 4.3+) -- Gallery: - + proper handling of EXIF - + obey sorting in the list of files -- Settings view updated -- Improved subjects in e-mails -- Bugs fixed - - - +# 2015-10-26 +- start of branch +- PR [#745](https://github.com/owncloud/android/pull/745) merged +- PR [#1044](https://github.com/owncloud/android/pull/1044) merged: < 8.1: GalleryPlus app needed, >= 8.2 Gallery app needed +- PR [#1111](https://github.com/owncloud/android/pull/1111) merged \ No newline at end of file diff --git a/README.md b/README.md index e3ff5353..d8808f1b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,17 @@ -#This is the Android client for [ownCloud][0] +#This is the BETA Android client for [ownCloud][0] + +The BETA app is only intended to be used by experienced users that want to use and test the latest features. +All pull requests labeled "3 - to review" or higher will be included into the branch. +If you find a bug please comment in the corresponding pull request or create a new issue with the label "Beta". + +The compiled APKs can be found [here][2] + +The changelog is found [here][3] The app performs file synchronization with an ownCloud server. Other ownCloud features may be added in the future, but they are not a priority right now. ## Build Status on -Git master: ![Build Status](https://api.travis-ci.org/owncloud/android.svg?branch=master) - -Git stable: ![Build Status](https://api.travis-ci.org/owncloud/android.svg?branch=stable) +Git beta: ![Build Status](https://api.travis-ci.org/owncloud/android.svg?branch=beta) ## Development @@ -14,6 +20,8 @@ Make sure you read [SETUP.md][1] when you start working on this project. [0]: https://github.com/owncloud/core [1]: https://github.com/owncloud/android/blob/master/SETUP.md +[2]: https://github.com/owncloud/android/tree/beta/apks/ +[3]: https://github.com/owncloud/android/blob/beta/CHANGELOG.md ### Contributing Please see [Contribution Guidelines](https://owncloud.org/contribute/). Fork this repository and contribute back using diff --git a/android-release.apk b/android-release.apk new file mode 100644 index 00000000..61a3b3bc Binary files /dev/null and b/android-release.apk differ diff --git a/apks/owncloud-beta-2015-10-26.apk b/apks/owncloud-beta-2015-10-26.apk new file mode 100644 index 00000000..61a3b3bc Binary files /dev/null and b/apks/owncloud-beta-2015-10-26.apk differ diff --git a/build.gradle b/build.gradle index a0db0184..b9ea96ec 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,11 @@ dependencies { android { compileSdkVersion 22 buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "com.owncloud.android.beta" + } + sourceSets { main { manifest.srcFile 'AndroidManifest.xml' diff --git a/owncloud-android-library b/owncloud-android-library index ecc3415e..652cd28b 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit ecc3415e3e3c13fa8f73fdd51a88c1ab7087b199 +Subproject commit 652cd28bb15672eaedfe8c1d9a46cf293c909b89 diff --git a/res/values-tzl/strings.xml b/res/values-tzl/strings.xml deleted file mode 100644 index 37e61524..00000000 --- a/res/values-tzl/strings.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - diff --git a/res/values/setup.xml b/res/values/setup.xml index 76bdd9f8..5c775e92 100644 --- a/res/values/setup.xml +++ b/res/values/setup.xml @@ -1,12 +1,12 @@ - ownCloud + Owncloud Beta owncloud org.owncloud - owncloud.db + owncloud-beta.db ownCloud - owncloud + owncloud-beta Owncloud_ ownCloud Mozilla/5.0 (Android) ownCloud-android/%1$s diff --git a/res/values/strings.xml b/res/values/strings.xml index ad90cdf1..43cb0d96 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -263,9 +263,11 @@ 389 KB 2012/05/18 12:23 PM 12:23:45 - - Upload pictures via WiFi only - Upload videos via WiFi only + + Upload pictures via wifi only + Upload when charging only + Upload videos via wifi only + Upload when charging only /InstantUpload File conflict Which files do you want to keep? If you select both versions, the local file will have a number added to its name. @@ -277,7 +279,7 @@ This image cannot be shown %1$s could not be copied to %2$s local folder - Upload Path + Upload path Sorry, sharing is not enabled on your server. Please contact your administrator. @@ -342,7 +344,7 @@ Instant Uploads Security - Upload Video Path + Upload video path Download of %1$s folder could not be completed shared diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 4823a83f..5c853fe4 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -3,7 +3,7 @@ ownCloud Android client application Copyright (C) 2012 Bartek Przybylski - Copyright (C) 2015 ownCloud Inc. + Copyright (C) 2012-2013 ownCloud Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2, @@ -18,52 +18,82 @@ along with this program. If not, see . --> - + - + + - - - + + + + - + android:dependency="instant_uploading" + android:disableDependentsState="true" + android:title="@string/instant_upload_on_wifi" + android:key="instant_upload_on_wifi"/> + + + + android:dependency="instant_video_uploading" + android:disableDependentsState="true" + android:title="@string/prefs_instant_video_upload_path_title" + android:key="instant_video_upload_path" /> - - - - - - - - - - - + + + + + + - + diff --git a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index f0ecf767..e8496951 100644 --- a/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/src/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -29,18 +29,25 @@ import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.Canvas; +import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.AsyncTask; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; import android.widget.ImageView; +import android.widget.ProgressBar; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -142,11 +149,12 @@ public class ThumbnailsCacheManager { public static class ThumbnailGenerationTask extends AsyncTask { private final WeakReference mImageViewReference; + private WeakReference mProgressWheelRef; private static Account mAccount; private Object mFile; + private Boolean mIsThumbnail; private FileDataStorageManager mStorageManager; - public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager, Account account) { // Use a WeakReference to ensure the ImageView can be garbage collected @@ -157,6 +165,12 @@ public class ThumbnailsCacheManager { mAccount = account; } + public ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager storageManager, + Account account, ProgressBar progressWheel) { + this(imageView, storageManager, account); + mProgressWheelRef = new WeakReference(progressWheel); + } + public ThumbnailGenerationTask(ImageView imageView) { // Use a WeakReference to ensure the ImageView can be garbage collected mImageViewReference = new WeakReference(imageView); @@ -175,12 +189,15 @@ public class ThumbnailsCacheManager { } mFile = params[0]; + mIsThumbnail = (Boolean) params[1]; + if (mFile instanceof OCFile) { - thumbnail = doOCFileInBackground(); + thumbnail = doOCFileInBackground(mIsThumbnail); } else if (mFile instanceof File) { - thumbnail = doFileInBackground(); - //} else { do nothing + thumbnail = doFileInBackground(mIsThumbnail); + } else { + // do nothing } }catch(Throwable t){ @@ -206,7 +223,14 @@ public class ThumbnailsCacheManager { tagId = String.valueOf(mFile.hashCode()); } if (String.valueOf(imageView.getTag()).equals(tagId)) { + if (mProgressWheelRef != null) { + final ProgressBar progressWheel = mProgressWheelRef.get(); + if (progressWheel != null) { + progressWheel.setVisibility(View.GONE); + } + } imageView.setImageBitmap(bitmap); + imageView.setVisibility(View.VISIBLE); } } } @@ -217,12 +241,13 @@ public class ThumbnailsCacheManager { * @param imageKey: thumb key * @param bitmap: image for extracting thumbnail * @param path: image path - * @param px: thumbnail dp + * @param pxW: thumbnail width + * @param pxH: thumbnail height * @return Bitmap */ - private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int px){ + private Bitmap addThumbnailToCache(String imageKey, Bitmap bitmap, String path, int pxW, int pxH){ - Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + Bitmap thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); // Rotate image, obeying exif tag thumbnail = BitmapUtils.rotateImage(thumbnail,path); @@ -243,31 +268,56 @@ public class ThumbnailsCacheManager { return Math.round(r.getDimension(R.dimen.file_icon_size_grid)); } - private Bitmap doOCFileInBackground() { + private Point getScreenDimension(){ + WindowManager wm = (WindowManager) MainApp.getAppContext().getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + Point test = new Point(); + display.getSize(test); + return test; + } + + private Bitmap doOCFileInBackground(Boolean isThumbnail) { + Bitmap thumbnail = null; OCFile file = (OCFile)mFile; - final String imageKey = String.valueOf(file.getRemoteId()); + // distinguish between thumbnail and resized image + String temp = String.valueOf(file.getRemoteId()); + if (isThumbnail){ + temp = "t" + temp; + } else { + temp = "r" + temp; + } + + final String imageKey = temp; // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + thumbnail = getBitmapFromDiskCache(imageKey); // Not found in disk cache if (thumbnail == null || file.needsUpdateThumbnail()) { - - int px = getThumbnailDimension(); + int pxW = 0; + int pxH = 0; + if (mIsThumbnail) { + pxW = pxH = getThumbnailDimension(); + } else { + Point p = getScreenDimension(); + pxW = p.x; + pxH = p.y; + } if (file.isDown()) { - Bitmap temp = BitmapUtils.decodeSampledBitmapFromFile( - file.getStoragePath(), px, px); - Bitmap bitmap = ThumbnailUtils.extractThumbnail(temp, px, px); + Bitmap tempBitmap = BitmapUtils.decodeSampledBitmapFromFile( + file.getStoragePath(), pxW, pxH); + Bitmap bitmap = ThumbnailUtils.extractThumbnail(tempBitmap, pxW, pxH); if (bitmap != null) { // Handle PNG if (file.getMimetype().equalsIgnoreCase("image/png")) { - bitmap = handlePNG(bitmap, px); + bitmap = handlePNG(bitmap, pxW); } - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getStoragePath(), px); + thumbnail = addThumbnailToCache(imageKey, bitmap, + file.getStoragePath(), pxW, pxH); file.setNeedsUpdateThumbnail(false); mStorageManager.saveFile(file); @@ -279,27 +329,51 @@ public class ThumbnailsCacheManager { if (mClient != null && serverOCVersion != null) { if (serverOCVersion.supportsRemoteThumbnails()) { try { - String uri = mClient.getBaseUri() + "" + - "/index.php/apps/files/api/v1/thumbnail/" + - px + "/" + px + Uri.encode(file.getRemotePath(), "/"); - Log_OC.d("Thumbnail", "URI: " + uri); - GetMethod get = new GetMethod(uri); - int status = mClient.executeMethod(get); - if (status == HttpStatus.SC_OK) { - InputStream inputStream = get.getResponseBodyAsStream(); - Bitmap bitmap = BitmapFactory.decodeStream(inputStream); - thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); - - // Handle PNG - if (file.getMimetype().equalsIgnoreCase("image/png")) { - thumbnail = handlePNG(thumbnail, px); + if (mIsThumbnail) { + String uri = mClient.getBaseUri() + "" + + "/index.php/apps/files/api/v1/thumbnail/" + + pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/"); + Log_OC.d("Thumbnail", "Download URI: " + uri); + GetMethod get = new GetMethod(uri); + int status = mClient.executeMethod(get); + if (status == HttpStatus.SC_OK) { + InputStream inputStream = get.getResponseBodyAsStream(); + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); + } else { + Log_OC.d(TAG, "Status: " + status); + } + } else { + String gallery = ""; + if (serverOCVersion.supportsNativeGallery()){ + gallery = "gallery"; + } else { + gallery = "galleryplus"; } - // Add thumbnail to cache - if (thumbnail != null) { - addBitmapToCache(imageKey, thumbnail); + String uri = mClient.getBaseUri() + + "/index.php/apps/" + gallery + "/api/preview/" + Integer.parseInt(file.getRemoteId().substring(0,8)) + + "/" + pxW + "/" + pxH; + Log_OC.d("Thumbnail", "FileName: " + file.getFileName() + " Download URI: " + uri); + GetMethod get = new GetMethod(uri); + int status = mClient.executeMethod(get); + if (status == HttpStatus.SC_OK) { + InputStream inputStream = get.getResponseBodyAsStream(); + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + // Download via gallery app + thumbnail = bitmap; } } + + // Handle PNG + if (thumbnail != null && file.getMimetype().equalsIgnoreCase("image/png")) { + thumbnail = handlePNG(thumbnail, pxW); + } + + // Add thumbnail to cache + if (thumbnail != null) { + addBitmapToCache(imageKey, thumbnail); + } } catch (Exception e) { e.printStackTrace(); } @@ -327,24 +401,32 @@ public class ThumbnailsCacheManager { return resultBitmap; } - private Bitmap doFileInBackground() { + private Bitmap doFileInBackground(Boolean mIsThumbnail) { + Bitmap thumbnail = null; File file = (File)mFile; final String imageKey = String.valueOf(file.hashCode()); // Check disk cache in background thread - Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + thumbnail = getBitmapFromDiskCache(imageKey); // Not found in disk cache if (thumbnail == null) { - - int px = getThumbnailDimension(); + int pxW = 0; + int pxH = 0; + if (mIsThumbnail) { + pxW = pxH = getThumbnailDimension(); + } else { + Point p = getScreenDimension(); + pxW = p.x; + pxH = p.y; + } Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile( - file.getAbsolutePath(), px, px); + file.getAbsolutePath(), pxW, pxH); if (bitmap != null) { - thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), px); + thumbnail = addThumbnailToCache(imageKey, bitmap, file.getPath(), pxW, pxH); } } return thumbnail; diff --git a/src/com/owncloud/android/files/FileOperationsHelper.java b/src/com/owncloud/android/files/FileOperationsHelper.java index fba62091..88cc8843 100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@ -27,14 +27,17 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.graphics.Bitmap; import android.net.Uri; import android.support.v4.app.DialogFragment; import android.webkit.MimeTypeMap; import android.widget.Toast; +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.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.lib.common.network.WebdavUtils; @@ -43,12 +46,19 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.observer.FileObserverService; import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.ui.adapter.DiskLruImageCacheFileProvider; import com.owncloud.android.ui.dialog.ShareLinkToDialog; import org.apache.http.protocol.HTTP; import java.util.List; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + /** * */ @@ -236,6 +246,24 @@ public class FileOperationsHelper { } } + public void sendCachedImage(OCFile file) { + if (file != null) { + Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND); + // set MimeType + sendIntent.setType(file.getMimetype()); +// sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + "/#" + file.getRemoteId() + "#" + file.getFileName())); + sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + file.getRemotePath())); + sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action + + // Show dialog, without the own app + String[] packagesToExclude = new String[] { mFileActivity.getPackageName() }; + DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file); + chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); + } else { + Log_OC.wtf(TAG, "Trying to send a NULL OCFile"); + } + } + public void syncFile(OCFile file) { diff --git a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java index 47f7127b..33ad1cc1 100644 --- a/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java +++ b/src/com/owncloud/android/files/InstantUploadBroadcastReceiver.java @@ -29,14 +29,15 @@ import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.FileStorageUtils; - import android.accounts.Account; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo.State; +import android.os.BatteryManager; import android.preference.PreferenceManager; import android.provider.MediaStore.Images; import android.provider.MediaStore.Video; @@ -58,7 +59,7 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log_OC.d(TAG, "Received: " + intent.getAction()); - if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION)) { + if (intent.getAction().equals(android.net.ConnectivityManager.CONNECTIVITY_ACTION) || intent.getAction().equals(Intent.ACTION_POWER_CONNECTED)) { handleConnectivityAction(context, intent); }else if (intent.getAction().equals(NEW_PHOTO_ACTION_UNOFFICIAL)) { handleNewPictureAction(context, intent); @@ -103,7 +104,6 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { file_name = c.getString(c.getColumnIndex(Images.Media.DISPLAY_NAME)); mime_type = c.getString(c.getColumnIndex(Images.Media.MIME_TYPE)); c.close(); - Log_OC.d(TAG, file_path + ""); // save always temporally the picture to upload @@ -111,7 +111,10 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { db.putFileForLater(file_path, account.name, null); db.close(); - if (!isOnline(context) || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) { + if (!isOnline(context) + || (instantPictureUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context)) + || (instantUploadWhenChargingOnly(context) && !isCharging(context)) + ) { return; } @@ -155,8 +158,16 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { mime_type = c.getString(c.getColumnIndex(Video.Media.MIME_TYPE)); c.close(); Log_OC.d(TAG, file_path + ""); + + // save always temporally the picture to upload + DbHandler db = new DbHandler(context); + db.putFileForLater(file_path, account.name, null); + db.close(); - if (!isOnline(context) || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context))) { + if (!isOnline(context) + || (instantVideoUploadViaWiFiOnly(context) && !isConnectedViaWiFi(context)) + || (instantVideoUploadWhenChargingOnly(context) && !isCharging(context)) + ) { return; } @@ -172,14 +183,18 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { } private void handleConnectivityAction(Context context, Intent intent) { - if (!instantPictureUploadEnabled(context)) { + if (!instantPictureUploadEnabled(context) && !instantVideoUploadEnabled(context)) { Log_OC.d(TAG, "Instant upload disabled, don't upload anything"); return; } if (!intent.hasExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY) && isOnline(context) - && (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) == isConnectedViaWiFi(context) == true))) { + && (!instantUploadWhenChargingOnly(context) || (instantUploadWhenChargingOnly(context) && isCharging(context))) + && (!instantVideoUploadWhenChargingOnly(context) || (instantVideoUploadWhenChargingOnly(context) && isCharging(context))) + && (!instantPictureUploadViaWiFiOnly(context) || (instantPictureUploadViaWiFiOnly(context) && isConnectedViaWiFi(context))) + && (!instantVideoUploadViaWiFiOnly(context) || (instantVideoUploadViaWiFiOnly(context) && isConnectedViaWiFi(context))) + ) { DbHandler db = new DbHandler(context); Cursor c = db.getAwaitingFiles(); if (c.moveToFirst()) { @@ -217,7 +232,6 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { c.close(); db.close(); } - } public static boolean isOnline(Context context) { @@ -231,6 +245,18 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { && cm.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI && cm.getActiveNetworkInfo().getState() == State.CONNECTED; } + + public static boolean isCharging(Context context){ + IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + Intent batteryStatus = context.registerReceiver(null, ifilter); + + int status = 0; + if (batteryStatus != null) { + status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + } + return status == BatteryManager.BATTERY_STATUS_CHARGING || + status == BatteryManager.BATTERY_STATUS_FULL; + } public static boolean instantPictureUploadEnabled(Context context) { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_uploading", false); @@ -247,4 +273,10 @@ public class InstantUploadBroadcastReceiver extends BroadcastReceiver { public static boolean instantVideoUploadViaWiFiOnly(Context context) { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_wifi", false); } + public static boolean instantUploadWhenChargingOnly(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_upload_on_charging", false); + } + public static boolean instantVideoUploadWhenChargingOnly(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("instant_video_upload_on_charging", false); + } } diff --git a/src/com/owncloud/android/ui/activity/Preferences.java b/src/com/owncloud/android/ui/activity/Preferences.java index e1c5c10d..d980b977 100644 --- a/src/com/owncloud/android/ui/activity/Preferences.java +++ b/src/com/owncloud/android/ui/activity/Preferences.java @@ -457,8 +457,8 @@ public class Preferences extends PreferenceActivity mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPathWiFi); mPrefInstantUploadCategory.addPreference(mPrefInstantUploadPath); } else { - mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi); - mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath); +// mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPathWiFi); +// mPrefInstantUploadCategory.removePreference(mPrefInstantUploadPath); } } @@ -467,8 +467,8 @@ public class Preferences extends PreferenceActivity mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPathWiFi); mPrefInstantUploadCategory.addPreference(mPrefInstantVideoUploadPath); } else { - mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi); - mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath); +// mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPathWiFi); +// mPrefInstantUploadCategory.removePreference(mPrefInstantVideoUploadPath); } } @@ -805,7 +805,7 @@ public class Preferences extends PreferenceActivity SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); mUploadVideoPath = appPrefs.getString("instant_video_upload_path", getString(R.string.instant_upload_path)); - mPrefInstantVideoUploadPath.setSummary(mUploadVideoPath); +// mPrefInstantVideoUploadPath.setSummary(mUploadVideoPath); } /** diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java index 0f2536f5..ab80aef2 100644 --- a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java @@ -120,10 +120,10 @@ public class DiskLruImageCache { } final InputStream in = snapshot.getInputStream( 0 ); if ( in != null ) { - final BufferedInputStream buffIn = + final BufferedInputStream buffIn = new BufferedInputStream( in, IO_BUFFER_SIZE ); - bitmap = BitmapFactory.decodeStream( buffIn ); - } + bitmap = BitmapFactory.decodeStream( buffIn ); + } } catch ( IOException e ) { e.printStackTrace(); } finally { diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java new file mode 100644 index 00000000..fe3f6ea2 --- /dev/null +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java @@ -0,0 +1,121 @@ +/** + * ownCloud Android client application + * + * Copyright (C) 2015 Tobias Kaminsky + * 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 . + * + * adapted from: http://stephendnicholas.com/archives/974 + * + */ + +package com.owncloud.android.ui.adapter; + +import android.accounts.Account; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +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.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.lib.common.utils.Log_OC; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +public class DiskLruImageCacheFileProvider extends ContentProvider { + private static String TAG = FileDataStorageManager.class.getSimpleName(); + + public static final String AUTHORITY = "com.owncloud.imageCache.provider"; + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + Account account = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); + FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(account, + MainApp.getAppContext().getContentResolver()); + + OCFile ocFile = fileDataStorageManager.getFileByPath(uri.getPath()); + + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf("r" + ocFile.getRemoteId())); + + // create a file to write bitmap data + File f = new File(MainApp.getAppContext().getCacheDir(), ocFile.getFileName()); + try { + f.createNewFile(); + + //Convert bitmap to byte array + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, bos); + byte[] bitmapdata = bos.toByteArray(); + + //write the bytes in file + FileOutputStream fos = null; + try { + fos = new FileOutputStream(f); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + fos.write(bitmapdata); + fos.flush(); + fos.close(); + + } catch (IOException e) { + e.printStackTrace(); + } + + return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); + } + + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index dcc715f0..5d6b8192 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -319,7 +319,7 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(file, true); } } diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index 1321fa00..bfe6ec24 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -282,12 +282,8 @@ public class OCFileListFragment extends ExtendedListFragment implements FileActi } else { mContainerActivity.getFileOperationsHelper().openFile(file); } - - } else { - // automatic download, preview on finish - ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file); + } - } } else { diff --git a/src/com/owncloud/android/ui/preview/ImageViewCustom.java b/src/com/owncloud/android/ui/preview/ImageViewCustom.java index 69182d6e..5bb24e7b 100644 --- a/src/com/owncloud/android/ui/preview/ImageViewCustom.java +++ b/src/com/owncloud/android/ui/preview/ImageViewCustom.java @@ -4,12 +4,16 @@ import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Movie; import android.os.Build; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; -import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.datamodel.OCFile; + +import java.io.FileInputStream; +import java.io.InputStream; public class ImageViewCustom extends ImageView { @@ -23,7 +27,12 @@ public class ImageViewCustom extends ImageView { private int mBitmapHeight; private int mBitmapWidth; - + private Movie mGifMovie; + private int mMovieWidth, mMovieHeight; + private long mMovieDuration; + private long mMovieRunDuration; + private long mLastTick; + public ImageViewCustom(Context context) { super(context); } @@ -39,18 +48,58 @@ public class ImageViewCustom extends ImageView { @SuppressLint("NewApi") @Override protected void onDraw(Canvas canvas) { - if(IS_ICS_OR_HIGHER && checkIfMaximumBitmapExceed(canvas) || IS_VERSION_BUGGY_ON_RECYCLES ) { // Software type is set with two targets: // 1. prevent that bitmaps larger than maximum textures allowed are shown as black views in devices // with LAYER_TYPE_HARDWARE enabled by default; - // 2. grant that bitmaps are correctly dellocated from memory in versions suffering the bug fixed in + // 2. grant that bitmaps are correctly de-allocated from memory in versions suffering the bug fixed in // https://android.googlesource.com/platform/frameworks/base/+/034de6b1ec561797a2422314e6ef03e3cd3e08e0; // setLayerType(View.LAYER_TYPE_SOFTWARE, null); } - super.onDraw(canvas); + if(mGifMovie == null){ + super.onDraw(canvas); + } else { + long nowTick = android.os.SystemClock.uptimeMillis(); + if (mLastTick == 0) { + mMovieRunDuration = 0; + } else { + mMovieRunDuration += nowTick - mLastTick; + if(mMovieRunDuration > mMovieDuration){ + mMovieRunDuration = 0; + } + } + + mGifMovie.setTime((int) mMovieRunDuration); + + float scale; + if(mGifMovie.height() > getHeight() || mGifMovie.width() > getWidth()) { + scale = (1f / Math.min(canvas.getHeight() / mGifMovie.height(), + canvas.getWidth() / mGifMovie.width())) + 0.25f; + } else { + scale = Math.min(canvas.getHeight() / mGifMovie.height(), + canvas.getWidth() / mGifMovie.width()); + } + + canvas.scale(scale, scale); + canvas.translate(((float) getWidth() / scale - (float) mGifMovie.width()) / 2f, + ((float) getHeight() / scale - (float) mGifMovie.height()) /2f); + + mGifMovie.draw(canvas, 0, 0); + + mLastTick = nowTick; + invalidate(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (mGifMovie == null){ + setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); + } else { + setMeasuredDimension(mMovieWidth, mMovieHeight); + } } /** @@ -60,13 +109,9 @@ public class ImageViewCustom extends ImageView { */ @SuppressLint("NewApi") private boolean checkIfMaximumBitmapExceed(Canvas canvas) { - Log_OC.v(TAG, "Canvas maximum: " + canvas.getMaximumBitmapWidth() + " - " + canvas.getMaximumBitmapHeight()); - if (mBitmapWidth > canvas.getMaximumBitmapWidth() - || mBitmapHeight > canvas.getMaximumBitmapHeight()) { - return true; - } - - return false; + return mBitmapWidth > canvas.getMaximumBitmapWidth() + || mBitmapHeight > canvas.getMaximumBitmapHeight(); + } @Override @@ -74,10 +119,25 @@ public class ImageViewCustom extends ImageView { * Keeps the size of the bitmap cached in member variables for faster access in {@link #onDraw(Canvas)} , * but without keeping another reference to the {@link Bitmap} */ - public void setImageBitmap (Bitmap bm) { + public void setImageBitmap(Bitmap bm) { mBitmapWidth = bm.getWidth(); mBitmapHeight = bm.getHeight(); super.setImageBitmap(bm); } + public void setGifImage(OCFile file){ + try { + InputStream gifInputStream = new FileInputStream(file.getStoragePath()); + setLayerType(View.LAYER_TYPE_SOFTWARE, null); + setFocusable(true); + + mGifMovie = Movie.decodeStream(gifInputStream); + mMovieWidth = mGifMovie.width(); + mMovieHeight = mGifMovie.height(); + mMovieDuration = mGifMovie.duration(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java index 5cbacfcf..4b622cbf 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -430,12 +430,7 @@ public class PreviewImageActivity extends FileActivity implements OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); getSupportActionBar().setTitle(currentFile.getFileName()); mDrawerToggle.setDrawerIndicatorEnabled(false); - if (!currentFile.isDown()) { - if (!mPreviewImagePagerAdapter.pendingErrorAt(position)) { - requestForDownload(currentFile); - } - } - + // Call to reset image zoom to initial state ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom(); } diff --git a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java index e61e3355..c45f76cf 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -41,8 +41,10 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.files.FileMenuFilter; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; @@ -74,6 +76,8 @@ public class PreviewImageFragment extends FileFragment { private TextView mMessageView; private ProgressBar mProgressWheel; + private Boolean mShowResizedImage = false; + public Bitmap mBitmap = null; private static final String TAG = PreviewImageFragment.class.getSimpleName(); @@ -97,8 +101,10 @@ public class PreviewImageFragment extends FileFragment { * {@link FragmentStatePagerAdapter} * ; TODO better solution */ - public static PreviewImageFragment newInstance(OCFile imageFile, boolean ignoreFirstSavedState){ + public static PreviewImageFragment newInstance(OCFile imageFile, boolean ignoreFirstSavedState, + boolean showResizedImage){ PreviewImageFragment frag = new PreviewImageFragment(); + frag.mShowResizedImage = showResizedImage; Bundle args = new Bundle(); args.putParcelable(ARG_FILE, imageFile); args.putBoolean(ARG_IGNORE_FIRST, ignoreFirstSavedState); @@ -179,9 +185,6 @@ public class PreviewImageFragment extends FileFragment { if (getFile() == null) { throw new IllegalStateException("Instanced with a NULL OCFile"); } - if (!getFile().isDown()) { - throw new IllegalStateException("There is no local file to preview"); - } } @@ -199,10 +202,43 @@ public class PreviewImageFragment extends FileFragment { public void onStart() { super.onStart(); if (getFile() != null) { - mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel); - //mLoadBitmapTask.execute(new String[]{getFile().getStoragePath()}); -// mLoadBitmapTask.execute(getFile().getStoragePath()); - mLoadBitmapTask.execute(getFile()); + mImageView.setTag(getFile().getFileId()); + + if (mShowResizedImage){ + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf("r" + getFile().getRemoteId()) + ); + + if (thumbnail != null && !getFile().needsUpdateThumbnail()){ + mProgressWheel.setVisibility(View.GONE); + mImageView.setImageBitmap(thumbnail); + mImageView.setVisibility(View.VISIBLE); + mBitmap = thumbnail; + } else { + // generate new Thumbnail + if (ThumbnailsCacheManager.cancelPotentialWork(getFile(), mImageView)) { + final ThumbnailsCacheManager.ThumbnailGenerationTask task = + new ThumbnailsCacheManager.ThumbnailGenerationTask( + mImageView, mContainerActivity.getStorageManager(), + mContainerActivity.getStorageManager().getAccount(), + mProgressWheel); + if (thumbnail == null) { + thumbnail = ThumbnailsCacheManager.mDefaultImg; + } + final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncDrawable( + MainApp.getAppContext().getResources(), + thumbnail, + task + ); + mImageView.setImageDrawable(asyncDrawable); + task.execute(getFile(), false); + } + } + } else { + mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel); + mLoadBitmapTask.execute(getFile()); + } } } @@ -307,8 +343,13 @@ public class PreviewImageFragment extends FileFragment { return true; } case R.id.action_send_file: { - mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); - return true; + if (getFile().isImage() && !getFile().isDown()){ + mContainerActivity.getFileOperationsHelper().sendCachedImage(getFile()); + return true; + } else { + mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); + return true; + } } case R.id.action_sync_file: { mContainerActivity.getFileOperationsHelper().syncFile(getFile()); @@ -509,7 +550,12 @@ public class PreviewImageFragment extends FileFragment { imageView.setBackground(backrepeat); } - imageView.setImageBitmap(bitmap); + if (result.ocFile.getMimetype().equalsIgnoreCase("image/gif")){ + imageView.setGifImage(result.ocFile); + } else { + imageView.setImageBitmap(bitmap); + } + imageView.setVisibility(View.VISIBLE); mBitmap = bitmap; // needs to be kept for recycling when not useful } diff --git a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index dda7dda2..d6ea3479 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java +++ b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -27,6 +27,7 @@ import java.util.Set; import java.util.Vector; import android.accounts.Account; +import android.graphics.Bitmap; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; @@ -34,6 +35,8 @@ import android.view.ViewGroup; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.ThumbnailsCacheManager; +import com.owncloud.android.ui.adapter.FileListListAdapter; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.utils.FileStorageUtils; @@ -104,17 +107,15 @@ public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { Fragment fragment = null; if (file.isDown()) { fragment = PreviewImageFragment.newInstance(file, - mObsoletePositions.contains(Integer.valueOf(i))); + mObsoletePositions.contains(Integer.valueOf(i)), false); } else if (mDownloadErrors.contains(Integer.valueOf(i))) { fragment = FileDownloadFragment.newInstance(file, mAccount, true); ((FileDownloadFragment)fragment).setError(true); mDownloadErrors.remove(Integer.valueOf(i)); - } else { - fragment = FileDownloadFragment.newInstance( - file, mAccount, mObsoletePositions.contains(Integer.valueOf(i)) - ); + fragment = PreviewImageFragment.newInstance(file, + mObsoletePositions.contains(Integer.valueOf(i)), true); } mObsoletePositions.remove(Integer.valueOf(i)); return fragment;