From 876fab3ae18555035fbfacdbaeb1f07d9142d4ea Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Mon, 18 May 2015 21:01:17 +0200 Subject: [PATCH 1/9] resized image is loaded instead of full sized image. --- AndroidManifest.xml | 5 + .../datamodel/ThumbnailsCacheManager.java | 164 +++++++++---- .../android/files/FileOperationsHelper.java | 44 ++-- .../android/ui/adapter/DiskLruImageCache.java | 6 +- .../DiskLruImageCacheFileProvider.java | 121 ++++++++++ .../ui/adapter/FileListListAdapter.java | 2 +- .../ui/fragment/OCFileListFragment.java | 14 +- .../ui/preview/PreviewImageActivity.java | 219 +++++++++--------- .../ui/preview/PreviewImageFragment.java | 114 ++++++--- .../ui/preview/PreviewImagePagerAdapter.java | 65 +++--- 10 files changed, 511 insertions(+), 243 deletions(-) create mode 100644 src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 7ad2cfa6615..cd4b188675b 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -152,6 +152,11 @@ + + + { @Override @@ -88,17 +95,17 @@ protected Void doInBackground(File... params) { if (mThumbnailCache == null) { try { - // Check if media is mounted or storage is built-in, if so, + // Check if media is mounted or storage is built-in, if so, // try and use external cache dir; otherwise use internal cache dir - final String cachePath = - MainApp.getAppContext().getExternalCacheDir().getPath() + - File.separator + CACHE_FOLDER; + final String cachePath = + MainApp.getAppContext().getExternalCacheDir().getPath() + + File.separator + CACHE_FOLDER; Log_OC.d(TAG, "create dir: " + cachePath); final File diskCacheDir = new File(cachePath); mThumbnailCache = new DiskLruImageCache( - diskCacheDir, - DISK_CACHE_SIZE, - mCompressFormat, + diskCacheDir, + DISK_CACHE_SIZE, + mCompressFormat, mCompressQuality ); } catch (Exception e) { @@ -112,8 +119,8 @@ protected Void doInBackground(File... params) { return null; } } - - + + public static void addBitmapToCache(String key, Bitmap bitmap) { synchronized (mThumbnailsDiskCacheLock) { if (mThumbnailCache != null) { @@ -142,11 +149,12 @@ public static Bitmap getBitmapFromDiskCache(String key) { 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 ThumbnailGenerationTask(ImageView imageView, FileDataStorageManager stora 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); @@ -177,21 +191,24 @@ protected Bitmap doInBackground(Object... params) { } 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){ - // the app should never break due to a problem with thumbnails - Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t); - if (t instanceof OutOfMemoryError) { - System.gc(); - } + }catch(Throwable t){ + // the app should never break due to a problem with thumbnails + Log_OC.e(TAG, "Generation of thumbnail for " + mFile + " failed", t); + if (t instanceof OutOfMemoryError) { + System.gc(); } + } return thumbnail; } @@ -208,7 +225,14 @@ protected void onPostExecute(Bitmap bitmap){ 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); } } } @@ -219,12 +243,13 @@ protected void onPostExecute(Bitmap bitmap){ * @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); @@ -245,31 +270,54 @@ private int getThumbnailDimension(){ 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 bitmap = BitmapUtils.decodeSampledBitmapFromFile( + file.getStoragePath(), 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); @@ -284,18 +332,25 @@ private Bitmap doOCFileInBackground() { try { String uri = mClient.getBaseUri() + "" + "/index.php/apps/files/api/v1/thumbnail/" + - px + "/" + px + Uri.encode(file.getRemotePath(), "/"); + pxW + "/" + pxH + Uri.encode(file.getRemotePath(), "/"); Log_OC.d("Thumbnail", "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); - thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); + byte[] bytes = get.getResponseBody(); + + if (mIsThumbnail) { + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, pxW, pxH); + } else { + thumbnail = bitmap; + } // Handle PNG if (file.getMimetype().equalsIgnoreCase("image/png")) { - thumbnail = handlePNG(thumbnail, px); + thumbnail = handlePNG(thumbnail, pxW); } // Add thumbnail to cache @@ -336,24 +391,39 @@ private Bitmap handlePNG(Bitmap bitmap, int px){ return resultBitmap; } - private Bitmap doFileInBackground() { + private Bitmap doFileInBackground(Boolean mIsThumbnail) { File file = (File)mFile; - final String imageKey = String.valueOf(file.hashCode()); + // distinguish between thumbnail and resized image + String temp = String.valueOf(file.hashCode()); + if (mIsThumbnail){ + temp = "t" + temp; + } else { + temp = "r" + temp; + } + + final String imageKey = temp; // Check disk cache in background thread Bitmap 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 9c5583a3293..ab66d4e3069 100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@ -37,7 +37,6 @@ import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; @@ -49,9 +48,9 @@ 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.activity.ShareActivity; import com.owncloud.android.ui.dialog.ShareLinkToDialog; -import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import java.util.List; @@ -61,12 +60,12 @@ public class FileOperationsHelper { private static final String TAG = FileOperationsHelper.class.getSimpleName(); - + private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; protected FileActivity mFileActivity = null; - /// Identifier of operation in progress which result shouldn't be lost + /// Identifier of operation in progress which result shouldn't be lost private long mWaitingForOpId = Long.MAX_VALUE; public FileOperationsHelper(FileActivity fileActivity) { @@ -85,7 +84,7 @@ public void openFile(OCFile file) { intentForSavedMimeType.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION ); - + Intent intentForGuessedMimeType = null; if (storagePath.lastIndexOf('.') >= 0) { String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( @@ -217,7 +216,7 @@ public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareT if (file != null) { // TODO check capability? mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext(). - getString(R.string.wait_a_moment)); + getString(R.string.wait_a_moment)); Intent service = new Intent(mFileActivity, OperationsService.class); service.setAction(OperationsService.ACTION_CREATE_SHARE_WITH_SHAREE); @@ -426,6 +425,25 @@ public void sendDownloadedFile(OCFile file) { } } + 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.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); + chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); + } else { + Log_OC.wtf(TAG, "Trying to send a NULL OCFile"); + } + } + + + /** * Request the synchronization of a file or folder with the OC server, including its contents. * @@ -440,8 +458,8 @@ public void syncFile(OCFile file) { intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true); mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent); mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext(). - getString(R.string.wait_a_moment)); - + getString(R.string.wait_a_moment)); + } else { Intent intent = new Intent(mFileActivity, OperationsService.class); intent.setAction(OperationsService.ACTION_SYNC_FOLDER); @@ -469,7 +487,7 @@ public void toggleFavorite(OCFile file, boolean isFavorite) { syncFile(file); } } - + public void renameFile(OCFile file, String newFilename) { // RenameFile Intent service = new Intent(mFileActivity, OperationsService.class); @@ -478,7 +496,7 @@ public void renameFile(OCFile file, String newFilename) { service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename); mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service); - + mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext(). getString(R.string.wait_a_moment)); } @@ -492,7 +510,7 @@ public void removeFile(OCFile file, boolean onlyLocalCopy) { service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy); mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service); - + mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext(). getString(R.string.wait_a_moment)); } @@ -506,7 +524,7 @@ public void createFolder(String remotePath, boolean createFullPath) { service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath); service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath); mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service); - + mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext(). getString(R.string.wait_a_moment)); } @@ -607,7 +625,7 @@ public void checkCurrentCredentials(Account account) { mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service); mFileActivity.showLoadingDialog( - mFileActivity.getApplicationContext().getString(R.string.wait_checking_credentials) + mFileActivity.getApplicationContext().getString(R.string.wait_checking_credentials) ); } } diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java index 0f2536f5511..ab80aef2756 100644 --- a/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCache.java @@ -120,10 +120,10 @@ public Bitmap getBitmap( String key ) { } 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 00000000000..fe3f6ea293b --- /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 1aca7cd78b6..96502d754a9 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -326,7 +326,7 @@ public View getView(int position, View convertView, ViewGroup parent) { 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 c90edb49fd2..8521da4e33b 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -101,7 +101,7 @@ public class OCFileListFragment extends ExtendedListFragment private OCFile mTargetFile; private boolean miniFabClicked = false; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -143,7 +143,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return v; } - + @Override public void onDetach() { setOnRefreshListener(null); @@ -200,7 +200,7 @@ public void onActivityCreated(Bundle savedInstanceState) { removeFabLabels(); } } - } + } /** * adds labels to all mini FABs. @@ -506,10 +506,10 @@ public void onCreateContextMenu( if (mContainerActivity.getStorageManager() != null) { FileMenuFilter mf = new FileMenuFilter( - targetFile, - mContainerActivity.getStorageManager().getAccount(), - mContainerActivity, - getActivity() + targetFile, + mContainerActivity.getStorageManager().getAccount(), + mContainerActivity, + getActivity() ); mf.filter(menu); } diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java index d30d6f8e052..12cc08f900e 100644 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -65,11 +65,11 @@ public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnRemoteOperationListener { - + public static final int DIALOG_SHORT_WAIT = 0; public static final String TAG = PreviewImageActivity.class.getSimpleName(); - + public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER"; @@ -79,13 +79,13 @@ public class PreviewImageActivity extends FileActivity implements private PreviewImagePagerAdapter mPreviewImagePagerAdapter; private int mSavedPosition = 0; private boolean mHasSavedPosition = false; - + private boolean mRequestWaitingForBinder; - + private DownloadFinishReceiver mDownloadFinishReceiver; - + private View mFullScreenAnchorView; - + @Override protected void onCreate(Bundle savedInstanceState) { @@ -107,38 +107,38 @@ protected void onCreate(Bundle savedInstanceState) { // Make sure we're running on Honeycomb or higher to use FullScreen and // Immersive Mode if (isHoneycombOrHigher()) { - + mFullScreenAnchorView = getWindow().getDecorView(); // to keep our UI controls visibility in line with system bars // visibility mFullScreenAnchorView.setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() { - @SuppressLint("InlinedApi") - @Override - public void onSystemUiVisibilityChange(int flags) { - boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; - ActionBar actionBar = getSupportActionBar(); - if (visible) { - actionBar.show(); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); - } else { - actionBar.hide(); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - } - } - }); + @SuppressLint("InlinedApi") + @Override + public void onSystemUiVisibilityChange(int flags) { + boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + ActionBar actionBar = getSupportActionBar(); + if (visible) { + actionBar.show(); + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); + } else { + actionBar.hide(); + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + } + } + }); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().setStatusBarColor(getResources().getColor(R.color.owncloud_blue_dark_transparent)); } } - + if (savedInstanceState != null) { mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER); } else { mRequestWaitingForBinder = false; } - + } private void initViewPager() { @@ -159,7 +159,7 @@ private void initViewPager() { int position = mHasSavedPosition ? mSavedPosition : mPreviewImagePagerAdapter.getFilePosition(getFile()); position = (position >= 0) ? position : 0; - mViewPager.setAdapter(mPreviewImagePagerAdapter); + mViewPager.setAdapter(mPreviewImagePagerAdapter); mViewPager.setOnPageChangeListener(this); mViewPager.setCurrentItem(position); if (position == 0 && !getFile().isDown()) { @@ -168,18 +168,18 @@ private void initViewPager() { mRequestWaitingForBinder = true; } } - - + + protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - - // Trigger the initial hide() shortly after the activity has been - // created, to briefly hint to the user that UI controls + + // Trigger the initial hide() shortly after the activity has been + // created, to briefly hint to the user that UI controls // are available delayedHide(INITIAL_HIDE_DELAY); - + } - + Handler mHideSystemUiHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -189,42 +189,42 @@ public void handleMessage(Message msg) { getSupportActionBar().hide(); } }; - + private void delayedHide(int delayMillis) { mHideSystemUiHandler.removeMessages(0); mHideSystemUiHandler.sendEmptyMessageDelayed(0, delayMillis); } - - + + /// handle Window Focus changes @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - + // When the window loses focus (e.g. the action overflow is shown), // cancel any pending hide action. if (!hasFocus) { mHideSystemUiHandler.removeMessages(0); } } - - - + + + @Override public void onStart() { super.onStart(); } - + @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder); + outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder); } @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { super.onRemoteOperationFinish(operation, result); - + if (operation instanceof RemoveFileOperation) { finish(); } else if (operation instanceof SynchronizeFileOperation) { @@ -232,7 +232,7 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe } } - + private void onSynchronizeFileOperationFinish(SynchronizeFileOperation operation, RemoteOperationResult result) { if (result.isSuccess()) { @@ -251,7 +251,7 @@ private class PreviewImageServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName component, IBinder service) { - + if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) { mDownloaderBinder = (FileDownloaderBinder) service; @@ -269,7 +269,7 @@ public void onServiceConnected(ComponentName component, IBinder service) { } else { return; } - + } @Override @@ -284,37 +284,37 @@ public void onServiceDisconnected(ComponentName component) { mUploaderBinder = null; } } - }; - - + }; + + @Override public void onStop() { super.onStop(); } - - + + @Override public void onDestroy() { super.onDestroy(); } - + @Override public boolean onOptionsItemSelected(MenuItem item) { boolean returnValue = false; - + switch(item.getItemId()){ - case android.R.id.home: - if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { - mDrawerLayout.closeDrawer(GravityCompat.START); - } else { - backToDisplayActivity(); - } - returnValue = true; - break; - default: - returnValue = super.onOptionsItemSelected(item); + case android.R.id.home: + if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { + mDrawerLayout.closeDrawer(GravityCompat.START); + } else { + backToDisplayActivity(); + } + returnValue = true; + break; + default: + returnValue = super.onOptionsItemSelected(item); } - + return returnValue; } @@ -324,7 +324,7 @@ protected void onResume() { super.onResume(); mDownloadFinishReceiver = new DownloadFinishReceiver(); - + IntentFilter filter = new IntentFilter(FileDownloader.getDownloadFinishMessage()); filter.addAction(FileDownloader.getDownloadAddedMessage()); registerReceiver(mDownloadFinishReceiver, filter); @@ -334,22 +334,22 @@ protected void onResume() { protected void onPostResume() { super.onPostResume(); } - + @Override public void onPause() { if (mDownloadFinishReceiver != null){ unregisterReceiver(mDownloadFinishReceiver); mDownloadFinishReceiver = null; } - + super.onPause(); } - + private void backToDisplayActivity() { finish(); } - + @Override public void showDetails(OCFile file) { Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class); @@ -360,13 +360,13 @@ public void showDetails(OCFile file) { startActivity(showDetailsIntent); int pos = mPreviewImagePagerAdapter.getFilePosition(file); file = mPreviewImagePagerAdapter.getFileAt(pos); - + } private void requestForDownload(OCFile file) { if (mDownloaderBinder == null) { Log_OC.d(TAG, "requestForDownload called without binder to download service"); - + } else if (!mDownloaderBinder.isDownloading(getAccount(), file)) { Intent i = new Intent(this, FileDownloader.class); i.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount()); @@ -378,7 +378,7 @@ private void requestForDownload(OCFile file) { /** * This method will be invoked when a new page becomes selected. Animation is not necessarily * complete. - * + * * @param position Position index of the new selected page */ @Override @@ -387,27 +387,22 @@ public void onPageSelected(int position) { mHasSavedPosition = true; if (mDownloaderBinder == null) { mRequestWaitingForBinder = true; - + } else { - OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); + 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(); } } - + /** - * Called when the scroll state changes. Useful for discovering when the user begins dragging, + * Called when the scroll state changes. Useful for discovering when the user begins dragging, * when the pager is automatically settling to the current page. when it is fully stopped/idle. - * + * * @param state The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING */ @Override @@ -417,23 +412,23 @@ public void onPageScrollStateChanged(int state) { /** * This method will be invoked when the current page is scrolled, either as part of a * programmatically initiated smooth scroll or a user initiated touch scroll. - * - * @param position Position index of the first page currently being displayed. + * + * @param position Position index of the first page currently being displayed. * Page position+1 will be visible if positionOffset is * nonzero. - * + * * @param positionOffset Value from [0, 1) indicating the offset from the page * at position. - * @param positionOffsetPixels Value in pixels indicating the offset from position. + * @param positionOffsetPixels Value in pixels indicating the offset from position. */ @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } - + /** * Class waiting for broadcast events from the {@link FileDownloader} service. - * + * * Updates the UI when a download is started or finished, provided that it is relevant for the * folder displayed in the gallery. */ @@ -442,7 +437,7 @@ private class DownloadFinishReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); - if (getAccount().name.equals(accountName) && + if (getAccount().name.equals(accountName) && downloadedRemotePath != null) { OCFile file = getStorageManager().getFileByPath(downloadedRemotePath); @@ -451,22 +446,22 @@ public void onReceive(Context context, Intent intent) { FileDownloader.EXTRA_DOWNLOAD_RESULT, false); //boolean isOffscreen = Math.abs((mViewPager.getCurrentItem() - position)) // <= mViewPager.getOffscreenPageLimit(); - + if (position >= 0 && intent.getAction().equals(FileDownloader.getDownloadFinishMessage())) { if (downloadWasFine) { - mPreviewImagePagerAdapter.updateFile(position, file); - + mPreviewImagePagerAdapter.updateFile(position, file); + } else { mPreviewImagePagerAdapter.updateWithDownloadError(position); } mPreviewImagePagerAdapter.notifyDataSetChanged(); // will trigger the creation - // of new fragments - + // of new fragments + } else { Log_OC.d(TAG, "Download finished, but the fragment is offscreen"); } - + } removeStickyBroadcast(intent); } @@ -474,10 +469,10 @@ public void onReceive(Context context, Intent intent) { } @SuppressLint("InlinedApi") - public void toggleFullScreen() { + public void toggleFullScreen() { if (isHoneycombOrHigher()) { - + boolean visible = (mFullScreenAnchorView.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; @@ -519,17 +514,17 @@ protected void onAccountSet(boolean stateWasRecovered) { if (!file.isImage()) { throw new IllegalArgumentException("Non-image file passed as argument"); } - + // Update file according to DB file, if it is possible - if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) + if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) file = getStorageManager().getFileById(file.getFileId()); - + if (file != null) { /// Refresh the activity according to the Account and OCFile set setFile(file); // reset after getting it fresh from storageManager getSupportActionBar().setTitle(getFile().getFileName()); //if (!stateWasRecovered) { - initViewPager(); + initViewPager(); //} } else { @@ -542,34 +537,34 @@ protected void onAccountSet(boolean stateWasRecovered) { @Override public void onBrowsedDownTo(OCFile folder) { // TODO Auto-generated method stub - + } @Override public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) { // TODO Auto-generated method stub - + } - - + + @SuppressLint("InlinedApi") - private void hideSystemUI(View anchorView) { + private void hideSystemUI(View anchorView) { anchorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hides NAVIGATION BAR; Android >= 4.0 - | View.SYSTEM_UI_FLAG_FULLSCREEN // hides STATUS BAR; Android >= 4.1 - | View.SYSTEM_UI_FLAG_IMMERSIVE // stays interactive; Android >= 4.4 - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4.1 + | View.SYSTEM_UI_FLAG_FULLSCREEN // hides STATUS BAR; Android >= 4.1 + | View.SYSTEM_UI_FLAG_IMMERSIVE // stays interactive; Android >= 4.4 + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4.1 ); } - + @SuppressLint("InlinedApi") private void showSystemUI(View anchorView) { anchorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4.1 + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4.1 ); } diff --git a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java index da5be22d048..47555ac8451 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.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; @@ -59,7 +61,7 @@ * * Trying to get an instance with a NULL {@link OCFile} will produce an * {@link IllegalStateException}. - * + * * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on * instantiation too. */ @@ -69,11 +71,14 @@ public class PreviewImageFragment extends FileFragment { private static final String ARG_FILE = "FILE"; private static final String ARG_IGNORE_FIRST = "IGNORE_FIRST"; + private static final String ARG_SHOW_RESIZED_IMAGE = "SHOW_RESIZED_IMAGE"; private TouchImageViewCustom mImageView; private TextView mMessageView; private ProgressBar mProgressWheel; + private Boolean mShowResizedImage = false; + public Bitmap mBitmap = null; private static final String TAG = PreviewImageFragment.class.getSimpleName(); @@ -97,23 +102,26 @@ 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); + args.putBoolean(ARG_SHOW_RESIZED_IMAGE, showResizedImage); frag.setArguments(args); return frag; } - + /** * Creates an empty fragment for image previews. - * + * * MUST BE KEPT: the system uses it when tries to reinstantiate a fragment automatically * (for instance, when the device is turned a aside). - * + * * DO NOT CALL IT: an {@link OCFile} and {@link Account} must be provided for a successful * construction */ @@ -130,10 +138,11 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); setFile((OCFile)args.getParcelable(ARG_FILE)); - // TODO better in super, but needs to check ALL the class extending FileFragment; - // not right now + // TODO better in super, but needs to check ALL the class extending FileFragment; + // not right now mIgnoreFirstSavedState = args.getBoolean(ARG_IGNORE_FIRST); + mShowResizedImage = args.getBoolean(ARG_SHOW_RESIZED_IMAGE); setHasOptionsMenu(true); } @@ -179,9 +188,6 @@ public void onActivityCreated(Bundle savedInstanceState) { 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 +205,56 @@ public void onSaveInstanceState(Bundle outState) { 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 resizedImage = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf("r" + getFile().getRemoteId())); + + if (resizedImage != null && !getFile().needsUpdateThumbnail()){ + mProgressWheel.setVisibility(View.GONE); + mImageView.setImageBitmap(resizedImage); + mImageView.setVisibility(View.VISIBLE); + mBitmap = resizedImage; + } else { + // show thumbnail while loading resized image + Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( + String.valueOf("t" + getFile().getRemoteId())); + + if (thumbnail != null){ + mImageView.setImageBitmap(thumbnail); + mProgressWheel.setVisibility(View.VISIBLE); + mImageView.setVisibility(View.VISIBLE); + mBitmap = thumbnail; + } else { + thumbnail = ThumbnailsCacheManager.mDefaultImg; + } + + // generate new resized image + if (ThumbnailsCacheManager.cancelPotentialWork(getFile(), mImageView) && + mContainerActivity.getStorageManager() != null) { + final ThumbnailsCacheManager.ThumbnailGenerationTask task = + new ThumbnailsCacheManager.ThumbnailGenerationTask( + mImageView, mContainerActivity.getStorageManager(), + mContainerActivity.getStorageManager().getAccount(), + mProgressWheel); + if (resizedImage == null) { + resizedImage = thumbnail; + } + final ThumbnailsCacheManager.AsyncDrawable asyncDrawable = + new ThumbnailsCacheManager.AsyncDrawable( + MainApp.getAppContext().getResources(), + resizedImage, + task + ); + mImageView.setImageDrawable(asyncDrawable); + task.execute(getFile(), false); + } + } + } else { + mLoadBitmapTask = new LoadBitmapTask(mImageView, mMessageView, mProgressWheel); + mLoadBitmapTask.execute(getFile()); + } } } @@ -238,15 +290,15 @@ public void onPrepareOptionsMenu(Menu menu) { setFile(mContainerActivity.getStorageManager().getFileById(getFile().getFileId())); FileMenuFilter mf = new FileMenuFilter( - getFile(), - mContainerActivity.getStorageManager().getAccount(), - mContainerActivity, - getActivity() + getFile(), + mContainerActivity.getStorageManager().getAccount(), + mContainerActivity, + getActivity() ); mf.filter(menu); } - // additional restriction for this fragment + // additional restriction for this fragment // TODO allow renaming in PreviewImageFragment MenuItem item = menu.findItem(R.id.action_rename_file); if (item != null) { @@ -254,7 +306,7 @@ public void onPrepareOptionsMenu(Menu menu) { item.setEnabled(false); } - // additional restriction for this fragment + // additional restriction for this fragment // TODO allow refresh file in PreviewImageFragment item = menu.findItem(R.id.action_sync_file); if (item != null) { @@ -303,9 +355,15 @@ public boolean onOptionsItemSelected(MenuItem item) { 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_download_file: case R.id.action_sync_file: { mContainerActivity.getFileOperationsHelper().syncFile(getFile()); return true; @@ -344,9 +402,9 @@ public void onDestroy() { if (mBitmap != null) { mBitmap.recycle(); System.gc(); - // putting this in onStop() is just the same; the fragment is always destroyed by - // {@link FragmentStatePagerAdapter} when the fragment in swiped further than the - // valid offscreen distance, and onStop() is never called before than that + // putting this in onStop() is just the same; the fragment is always destroyed by + // {@link FragmentStatePagerAdapter} when the fragment in swiped further than the + // valid offscreen distance, and onStop() is never called before than that } super.onDestroy(); } @@ -360,7 +418,7 @@ private void openFile() { finish(); } - + private class LoadBitmapTask extends AsyncTask { /** @@ -382,7 +440,7 @@ private class LoadBitmapTask extends AsyncTask { /** * Weak reference to the target {@link ProgressBar} shown while the load is in progress. - * + * * Using a weak reference will avoid memory leaks if the target ImageView is retired from * memory before the load finishes. */ @@ -541,7 +599,7 @@ private void hideProgressWheel() { /** * Helper method to test if an {@link OCFile} can be passed to a {@link PreviewImageFragment} * to be previewed. - * + * * @param file File to test if can be previewed. * @return 'True' if the file can be handled by the fragment. */ diff --git a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java index dda7dda25a6..d97c886e12d 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.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 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; @@ -42,19 +45,19 @@ */ //public class PreviewImagePagerAdapter extends PagerAdapter { public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { - + private Vector mImageFiles; private Account mAccount; private Set mObsoleteFragments; private Set mObsoletePositions; private Set mDownloadErrors; private FileDataStorageManager mStorageManager; - + private Map mCachedFragments; /** * Constructor. - * + * * @param fragmentManager {@link FragmentManager} instance that will handle * the {@link Fragment}s provided by the adapter. * @param parentFolder Folder where images will be searched for. @@ -64,13 +67,13 @@ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFo Account account, FileDataStorageManager storageManager /*, boolean onlyOnDevice*/) { super(fragmentManager); - + if (fragmentManager == null) { throw new IllegalArgumentException("NULL FragmentManager instance"); } if (parentFolder == null) { throw new IllegalArgumentException("NULL parent folder"); - } + } if (storageManager == null) { throw new IllegalArgumentException("NULL storage manager"); } @@ -79,42 +82,40 @@ public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFo mStorageManager = storageManager; // TODO Enable when "On Device" is recovered ? mImageFiles = mStorageManager.getFolderImages(parentFolder/*, false*/); - + mImageFiles = FileStorageUtils.sortFolder(mImageFiles); - + mObsoleteFragments = new HashSet(); mObsoletePositions = new HashSet(); mDownloadErrors = new HashSet(); //mFragmentManager = fragmentManager; mCachedFragments = new HashMap(); } - + /** * Returns the image files handled by the adapter. - * + * * @return A vector with the image files handled by the adapter. */ protected OCFile getFileAt(int position) { return mImageFiles.get(position); } - + public Fragment getItem(int i) { OCFile file = mImageFiles.get(i); 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; @@ -123,7 +124,7 @@ public Fragment getItem(int i) { public int getFilePosition(OCFile file) { return mImageFiles.indexOf(file); } - + @Override public int getCount() { return mImageFiles.size(); @@ -134,7 +135,7 @@ public CharSequence getPageTitle(int position) { return mImageFiles.get(position).getFileName(); } - + public void updateFile(int position, OCFile file) { FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position)); if (fragmentToUpdate != null) { @@ -143,8 +144,8 @@ public void updateFile(int position, OCFile file) { mObsoletePositions.add(Integer.valueOf(position)); mImageFiles.set(position, file); } - - + + public void updateWithDownloadError(int position) { FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position)); if (fragmentToUpdate != null) { @@ -152,7 +153,7 @@ public void updateWithDownloadError(int position) { } mDownloadErrors.add(Integer.valueOf(position)); } - + public void clearErrorAt(int position) { FileFragment fragmentToUpdate = mCachedFragments.get(Integer.valueOf(position)); if (fragmentToUpdate != null) { @@ -160,8 +161,8 @@ public void clearErrorAt(int position) { } mDownloadErrors.remove(Integer.valueOf(position)); } - - + + @Override public int getItemPosition(Object object) { if (mObsoleteFragments.contains(object)) { @@ -178,11 +179,11 @@ public Object instantiateItem(ViewGroup container, int position) { mCachedFragments.put(Integer.valueOf(position), (FileFragment)fragment); return fragment; } - + @Override public void destroyItem(ViewGroup container, int position, Object object) { - mCachedFragments.remove(Integer.valueOf(position)); - super.destroyItem(container, position, object); + mCachedFragments.remove(Integer.valueOf(position)); + super.destroyItem(container, position, object); } @@ -196,7 +197,7 @@ public boolean pendingErrorAt(int position) { public void resetZoom() { Iterator entries = mCachedFragments.values().iterator(); while (entries.hasNext()) { - FileFragment fileFragment = (FileFragment) entries.next(); + FileFragment fileFragment = (FileFragment) entries.next(); if (fileFragment instanceof PreviewImageFragment) { ((PreviewImageFragment) fileFragment).getImageView().resetZoom(); } @@ -205,7 +206,7 @@ public void resetZoom() { /* -* * Called when a change in the shown pages is going to start being made. - * + * * @param container The containing View which is displaying this adapter's page views. *- / @Override @@ -216,7 +217,7 @@ public void startUpdate(ViewGroup container) { @Override public Object instantiateItem(ViewGroup container, int position) { Log_OC.e(TAG, "** instantiateItem " + position); - + if (mFragments.size() > position) { Fragment fragment = mFragments.get(position); if (fragment != null) { @@ -236,8 +237,8 @@ public Object instantiateItem(ViewGroup container, int position) { // TODO WATCH OUT: // * The Fragment must currently be attached to the FragmentManager. // * A new Fragment created using this saved state must be the same class type as the Fragment it was created from. - // * The saved state can not contain dependencies on other fragments -- that is it can't use putFragment(Bundle, String, Fragment) - // to store a fragment reference + // * The saved state can not contain dependencies on other fragments -- that is it can't use putFragment(Bundle, String, Fragment) + // to store a fragment reference fragment.setInitialSavedState(savedState); } } @@ -256,7 +257,7 @@ public Object instantiateItem(ViewGroup container, int position) { public void destroyItem(ViewGroup container, int position, Object object) { Log_OC.e(TAG, "** destroyItem " + position); Fragment fragment = (Fragment)object; - + if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } From f8a4776951a5e8b8b9c7803c65dfe2c4e4ce3f79 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Sat, 31 Oct 2015 08:30:13 +0100 Subject: [PATCH 2/9] fixed storing thumbnails by correct imageKey --- src/com/owncloud/android/ui/adapter/FileListListAdapter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index 96502d754a9..6f2e228472b 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -305,8 +305,7 @@ public View getView(int position, View convertView, ViewGroup parent) { if (file.isImage() && file.getRemoteId() != null){ // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.getRemoteId()) - ); + "t" + String.valueOf(file.getRemoteId())); if (thumbnail != null && !file.needsUpdateThumbnail()){ fileIcon.setImageBitmap(thumbnail); } else { From 8122ee833bf3ec150080cec2c5fed0e7127fba20 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Mon, 2 Nov 2015 10:24:01 +0100 Subject: [PATCH 3/9] exported must be set to true --- AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index cd4b188675b..2a5059fc8a1 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -154,7 +154,8 @@ + android:authorities="com.owncloud.imageCache.provider" + android:exported="true"> Date: Mon, 2 Nov 2015 22:09:29 +0100 Subject: [PATCH 4/9] updated for future use with later PRs --- .../DiskLruImageCacheFileProvider.java | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java b/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java index fe3f6ea293b..4e70cb42045 100644 --- a/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java +++ b/src/com/owncloud/android/ui/adapter/DiskLruImageCacheFileProvider.java @@ -27,9 +27,11 @@ import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; +import android.database.MatrixCursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.provider.OpenableColumns; import com.owncloud.android.MainApp; import com.owncloud.android.authentication.AccountUtils; @@ -46,16 +48,27 @@ public class DiskLruImageCacheFileProvider extends ContentProvider { private static String TAG = FileDataStorageManager.class.getSimpleName(); + private FileDataStorageManager mFileDataStorageManager; public static final String AUTHORITY = "com.owncloud.imageCache.provider"; @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + public boolean onCreate() { + return true; + } + + private OCFile getFile(Uri uri){ Account account = AccountUtils.getCurrentOwnCloudAccount(MainApp.getAppContext()); - FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(account, + mFileDataStorageManager = new FileDataStorageManager(account, MainApp.getAppContext().getContentResolver()); - OCFile ocFile = fileDataStorageManager.getFileByPath(uri.getPath()); + OCFile ocFile = mFileDataStorageManager.getFileByPath(uri.getPath()); + return ocFile; + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + OCFile ocFile = getFile(uri); Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( String.valueOf("r" + ocFile.getRemoteId())); @@ -88,20 +101,26 @@ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundEx return ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); } - @Override - public boolean onCreate() { - return true; + public String getType(Uri uri) { + OCFile ocFile = getFile(uri); + return ocFile.getMimetype(); } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - return null; - } + public Cursor query(Uri uri, String[] arg1, String arg2, String[] arg3, String arg4) { + MatrixCursor cursor = null; + + OCFile ocFile = getFile(uri); + File file = new File(MainApp.getAppContext().getCacheDir(), ocFile.getFileName()); + if (file.exists()) { + cursor = new MatrixCursor(new String[] { + OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE }); + cursor.addRow(new Object[] { uri.getLastPathSegment(), + file.length() }); + } - @Override - public String getType(Uri uri) { - return null; + return cursor; } @Override From 84976d12ecbf14bf84d5dd80ffea2dd3377c47ea Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Sun, 3 Jan 2016 13:36:29 +0100 Subject: [PATCH 5/9] fixed showing thumbnails in local file list (uploader) --- src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index 3769a9ab47d..650a5a6af7c 100644 --- a/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -148,7 +148,7 @@ public View getView(int position, View convertView, ViewGroup parent) { if (BitmapUtils.isImage(file)){ // Thumbnail in Cache? Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache( - String.valueOf(file.hashCode()) + "t" + String.valueOf(file.hashCode()) ); if (thumbnail != null){ fileIcon.setImageBitmap(thumbnail); @@ -168,7 +168,7 @@ public View getView(int position, View convertView, ViewGroup parent) { task ); fileIcon.setImageDrawable(asyncDrawable); - task.execute(file); + task.execute(file, true); Log_OC.v(TAG, "Executing task to generate a new thumbnail"); } // else, already being generated, don't restart it From 7d901b1f5acd0ce808ba12b8b039c311dcf507bd Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Sun, 10 Apr 2016 18:54:28 +0200 Subject: [PATCH 6/9] wip --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../michaelOrtiz/TouchImageViewCustom.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e9ce5369f19..6811220bae7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,4 @@ -#Thu Apr 07 22:12:15 CEST 2016 +#Sat Apr 09 22:31:31 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/src/third_parties/michaelOrtiz/TouchImageViewCustom.java b/src/third_parties/michaelOrtiz/TouchImageViewCustom.java index 9b0f5a05e60..2633443f01e 100644 --- a/src/third_parties/michaelOrtiz/TouchImageViewCustom.java +++ b/src/third_parties/michaelOrtiz/TouchImageViewCustom.java @@ -10,6 +10,7 @@ package third_parties.michaelOrtiz; +import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.preview.ImageViewCustom; import android.annotation.TargetApi; @@ -27,6 +28,7 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Parcelable; +import android.support.design.widget.Snackbar; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -99,6 +101,8 @@ private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM }; private OnTouchListener userTouchListener = null; private OnTouchImageViewListener touchImageViewListener = null; + private Boolean showDownloadSnackbar = false; + public TouchImageViewCustom(Context context) { super(context); sharedConstructing(context); @@ -894,6 +898,22 @@ public boolean onScaleBegin(ScaleGestureDetector detector) { @Override public boolean onScale(ScaleGestureDetector detector) { scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true); + Log_OC.d("TouchImageView", "zoom: " + normalizedScale); + + if (normalizedScale > 2 && !showDownloadSnackbar){ + Snackbar snackbar = Snackbar + .make(getRootView(), "Automatically download full image", Snackbar.LENGTH_LONG) + .setAction("UNDO", new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar snackbar1 = Snackbar.make(getRootView(), "Message is restored!", Snackbar.LENGTH_SHORT); + snackbar1.show(); + } + }); + + snackbar.show(); + showDownloadSnackbar = true; + } // // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user. From d18985902bea04a2ebdcd681afd4726490fe432b Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Fri, 13 May 2016 18:14:32 +0200 Subject: [PATCH 7/9] revert exporting FileContentProvider --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 2a5059fc8a1..efb5e177b7f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -131,7 +131,7 @@ android:enabled="true" android:exported="true" android:label="@string/sync_string_files" - android:syncable="true" /> + android:syncable="false" /> Date: Fri, 13 May 2016 18:17:49 +0200 Subject: [PATCH 8/9] revert wip --- .../michaelOrtiz/TouchImageViewCustom.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/src/third_parties/michaelOrtiz/TouchImageViewCustom.java b/src/third_parties/michaelOrtiz/TouchImageViewCustom.java index 2633443f01e..9b0f5a05e60 100644 --- a/src/third_parties/michaelOrtiz/TouchImageViewCustom.java +++ b/src/third_parties/michaelOrtiz/TouchImageViewCustom.java @@ -10,7 +10,6 @@ package third_parties.michaelOrtiz; -import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.preview.ImageViewCustom; import android.annotation.TargetApi; @@ -28,7 +27,6 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Parcelable; -import android.support.design.widget.Snackbar; import android.util.AttributeSet; import android.util.Log; import android.view.GestureDetector; @@ -101,8 +99,6 @@ private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM }; private OnTouchListener userTouchListener = null; private OnTouchImageViewListener touchImageViewListener = null; - private Boolean showDownloadSnackbar = false; - public TouchImageViewCustom(Context context) { super(context); sharedConstructing(context); @@ -898,22 +894,6 @@ public boolean onScaleBegin(ScaleGestureDetector detector) { @Override public boolean onScale(ScaleGestureDetector detector) { scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true); - Log_OC.d("TouchImageView", "zoom: " + normalizedScale); - - if (normalizedScale > 2 && !showDownloadSnackbar){ - Snackbar snackbar = Snackbar - .make(getRootView(), "Automatically download full image", Snackbar.LENGTH_LONG) - .setAction("UNDO", new View.OnClickListener() { - @Override - public void onClick(View view) { - Snackbar snackbar1 = Snackbar.make(getRootView(), "Message is restored!", Snackbar.LENGTH_SHORT); - snackbar1.show(); - } - }); - - snackbar.show(); - showDownloadSnackbar = true; - } // // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user. From bab67cea30f8b58e454c1b91d9439037af03cb0a Mon Sep 17 00:00:00 2001 From: jabarros Date: Wed, 22 Jun 2016 12:28:02 +0200 Subject: [PATCH 9/9] Changes after CR --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index efb5e177b7f..af5dec9748f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -129,7 +129,7 @@ android:name=".providers.FileContentProvider" android:authorities="@string/authority" android:enabled="true" - android:exported="true" + android:exported="false" android:label="@string/sync_string_files" android:syncable="false" />