From: tobiasKaminsky Date: Wed, 22 Oct 2014 06:09:47 +0000 (+0200) Subject: Merge remote-tracking branch 'upstream/develop' into triggerMediaScan X-Git-Tag: oc-android-1.7.0_signed~90^2~25 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/d7b32ecc1754cc06023de2e6f2bbbf3555514d74?hp=-c Merge remote-tracking branch 'upstream/develop' into triggerMediaScan Conflicts: src/com/owncloud/android/ui/fragment/OCFileListFragment.java src/com/owncloud/android/ui/preview/PreviewImageActivity.java src/com/owncloud/android/ui/preview/PreviewImageFragment.java --- d7b32ecc1754cc06023de2e6f2bbbf3555514d74 diff --combined project.properties index c9e58408,28edd993..c149c453 --- a/project.properties +++ b/project.properties @@@ -10,3 -10,4 +10,4 @@@ # Project target. target=android-19 android.library.reference.1=actionbarsherlock/library -android.library.reference.2=owncloud-android-library ++android.library.reference.2=../android-library/android-library diff --combined src/com/owncloud/android/files/FileOperationsHelper.java index 00000000,e1ab1953..65e8d835 mode 000000,100644..100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@@ -1,0 -1,298 +1,296 @@@ + /* ownCloud Android client application + * Copyright (C) 2012-2014 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 . + * + */ + + package com.owncloud.android.files; + + import org.apache.http.protocol.HTTP; + + import android.accounts.Account; + import android.accounts.AccountManager; + import android.content.Intent; ++import android.media.MediaScannerConnection; + import android.net.Uri; + import android.support.v4.app.DialogFragment; + import android.webkit.MimeTypeMap; + import android.widget.Toast; + + import com.owncloud.android.R; + import com.owncloud.android.datamodel.OCFile; + import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; + import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; - + import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; + import com.owncloud.android.lib.common.network.WebdavUtils; + import com.owncloud.android.lib.common.utils.Log_OC; + import com.owncloud.android.lib.resources.status.OwnCloudVersion; + import com.owncloud.android.services.OperationsService; + import com.owncloud.android.ui.activity.FileActivity; + import com.owncloud.android.ui.dialog.ShareLinkToDialog; + + /** + * + * @author masensio + * @author David A. Velasco + */ + public class FileOperationsHelper { + + private static final String TAG = FileOperationsHelper.class.getName(); + + private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; + + protected FileActivity mFileActivity = null; + + /// Identifier of operation in progress which result shouldn't be lost + private long mWaitingForOpId = Long.MAX_VALUE; + + public FileOperationsHelper(FileActivity fileActivity) { + mFileActivity = fileActivity; + } + + + public void openFile(OCFile file) { + if (file != null) { + String storagePath = file.getStoragePath(); + String encodedStoragePath = WebdavUtils.encodePath(storagePath); + + Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW); + intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype()); + 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(storagePath.substring(storagePath.lastIndexOf('.') + 1)); + if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) { + intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW); + intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType); + intentForGuessedMimeType.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + } + + Intent chooserIntent = null; + if (intentForGuessedMimeType != null) { + chooserIntent = Intent.createChooser(intentForGuessedMimeType, mFileActivity.getString(R.string.actionbar_open_with)); + } else { + chooserIntent = Intent.createChooser(intentForSavedMimeType, mFileActivity.getString(R.string.actionbar_open_with)); + } + + mFileActivity.startActivity(chooserIntent); + + } else { + Log_OC.wtf(TAG, "Trying to open a NULL OCFile"); + } + } + + + public void shareFileWithLink(OCFile file) { + + if (isSharedSupported()) { + if (file != null) { + String link = "https://fake.url"; + Intent intent = createShareWithLinkIntent(link); + String[] packagesToExclude = new String[] { mFileActivity.getPackageName() }; + DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file); + chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); + + } else { + Log_OC.wtf(TAG, "Trying to share a NULL OCFile"); + } + + } else { + // Show a Message + Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG); + t.show(); + } + } + + + public void shareFileWithLinkToApp(OCFile file, Intent sendIntent) { + + if (file != null) { + mFileActivity.showLoadingDialog(); + + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_CREATE_SHARE); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + } else { + Log_OC.wtf(TAG, "Trying to open a NULL OCFile"); + } + } + + + private Intent createShareWithLinkIntent(String link) { + Intent intentToShareLink = new Intent(Intent.ACTION_SEND); + intentToShareLink.putExtra(Intent.EXTRA_TEXT, link); + intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE); + return intentToShareLink; + } + + + /** + * @return 'True' if the server supports the Share API + */ + public boolean isSharedSupported() { + if (mFileActivity.getAccount() != null) { + AccountManager accountManager = AccountManager.get(mFileActivity); + + String version = accountManager.getUserData(mFileActivity.getAccount(), Constants.KEY_OC_VERSION); + return (new OwnCloudVersion(version)).isSharedSupported(); + } + return false; + } + + + public void unshareFileWithLink(OCFile file) { + + if (isSharedSupported()) { + // Unshare the file + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_UNSHARE); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + + } else { + // Show a Message + Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG); + t.show(); + + } + } + + public void sendDownloadedFile(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("file://" + file.getStoragePath())); + 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) { + // Sync file + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_SYNC_FILE); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + service.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + } + + + public void renameFile(OCFile file, String newFilename) { + // RenameFile + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_RENAME); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + } + + + public void removeFile(OCFile file, boolean onlyLocalCopy) { + // RemoveFile + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_REMOVE); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + } + + + public void createFolder(String remotePath, boolean createFullPath) { + // Create Folder + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_CREATE_FOLDER); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath); + service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + } + + + public void cancelTransference(OCFile file) { + Account account = mFileActivity.getAccount(); + FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder(); + FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); + if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) { + // Remove etag for parent, if file is a keep_in_sync + if (file.keepInSync()) { + OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId()); + parent.setEtag(""); + mFileActivity.getStorageManager().saveFile(parent); + } + + downloaderBinder.cancel(account, file); + + } else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { + uploaderBinder.cancel(account, file); + } + } + + /** + * Start move file operation + * @param newfile File where it is going to be moved + * @param currentFile File with the previous info + */ + public void moveFile(OCFile newfile, OCFile currentFile) { + // Move files + Intent service = new Intent(mFileActivity, OperationsService.class); + service.setAction(OperationsService.ACTION_MOVE_FILE); + service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath()); + service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath()); + service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + mWaitingForOpId = mFileActivity.getOperationsServiceBinder().newOperation(service); + + mFileActivity.showLoadingDialog(); + } + + + public long getOpIdWaitingFor() { + return mWaitingForOpId; + } + + + public void setOpIdWaitingFor(long waitingForOpId) { + mWaitingForOpId = waitingForOpId; + } - - + } diff --combined src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java index 00000000,a78584ae..8158a5a2 mode 000000,100644..100644 --- a/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java +++ b/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java @@@ -1,0 -1,144 +1,158 @@@ + /* ownCloud Android client application + * Copyright (C) 2014 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 . + * + */ + + package com.owncloud.android.ui.dialog; + + /** + * Dialog requiring confirmation before removing a given OCFile. + * + * Triggers the removal according to the user response. + * + * @author David A. Velasco + */ ++import java.io.File; + import java.util.Vector; + + import com.owncloud.android.R; + import com.owncloud.android.datamodel.FileDataStorageManager; + import com.owncloud.android.datamodel.OCFile; + import com.owncloud.android.ui.activity.ComponentsGetter; + import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; + + import android.app.Dialog; ++import android.media.MediaScannerConnection; + import android.os.Bundle; + + public class RemoveFileDialogFragment extends ConfirmationDialogFragment + implements ConfirmationDialogFragmentListener { + + private static final String ARG_TARGET_FILE = "TARGET_FILE"; + + /** + * Public factory method to create new RemoveFileDialogFragment instances. + * + * @param file File to remove. + * @return Dialog ready to show. + */ + public static RemoveFileDialogFragment newInstance(OCFile file) { + RemoveFileDialogFragment frag = new RemoveFileDialogFragment(); + Bundle args = new Bundle(); + + int messageStringId = R.string.confirmation_remove_alert; + + int posBtn = R.string.confirmation_remove_remote; + int neuBtn = -1; + if (file.isFolder()) { + messageStringId = R.string.confirmation_remove_folder_alert; + posBtn = R.string.confirmation_remove_remote_and_local; + neuBtn = R.string.confirmation_remove_folder_local; + } else if (file.isDown()) { + posBtn = R.string.confirmation_remove_remote_and_local; + neuBtn = R.string.confirmation_remove_local; + } + + + args.putInt(ARG_CONF_RESOURCE_ID, messageStringId); + args.putStringArray(ARG_CONF_ARGUMENTS, new String[]{file.getFileName()}); + args.putInt(ARG_POSITIVE_BTN_RES, posBtn); + args.putInt(ARG_NEUTRAL_BTN_RES, neuBtn); + args.putInt(ARG_NEGATIVE_BTN_RES, R.string.common_cancel); + args.putParcelable(ARG_TARGET_FILE, file); + frag.setArguments(args); + + return frag; + } + + private OCFile mTargetFile; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + mTargetFile = getArguments().getParcelable(ARG_TARGET_FILE); + + setOnConfirmationListener(this); + + return dialog; + } + + /** + * Performs the removal of the target file, both locally and in the server. + */ + @Override + public void onConfirmation(String callerTag) { + ComponentsGetter cg = (ComponentsGetter)getSherlockActivity(); + FileDataStorageManager storageManager = cg.getStorageManager(); + if (storageManager.getFileById(mTargetFile.getFileId()) != null) { ++ String path = new File(mTargetFile.getStoragePath()).getParent(); + cg.getFileOperationsHelper().removeFile(mTargetFile, false); ++ triggerMediaScan(path); + } + } + + /** + * Performs the removal of the local copy of the target file + */ + @Override + public void onNeutral(String callerTag) { ++ String path = new File(mTargetFile.getStoragePath()).getParent(); + ComponentsGetter cg = (ComponentsGetter)getSherlockActivity(); + cg.getFileOperationsHelper() + .removeFile(mTargetFile, true); + + FileDataStorageManager storageManager = cg.getStorageManager(); + + boolean containsKeepInSync = false; + if (mTargetFile.isFolder()) { + Vector files = storageManager.getFolderContent(mTargetFile); + for(OCFile file: files) { + containsKeepInSync = file.keepInSync() || containsKeepInSync; + + if (containsKeepInSync) + break; + } + } + + // Remove etag for parent, if file is a keep_in_sync + // or is a folder and contains keep_in_sync + if (mTargetFile.keepInSync() || containsKeepInSync) { + OCFile folder = null; + if (mTargetFile.isFolder()) { + folder = mTargetFile; + } else { + folder = storageManager.getFileById(mTargetFile.getParentId()); + } + + folder.setEtag(""); - storageManager.saveFile(folder); ++ storageManager.saveFile(folder); + } ++ ++ // Trigger MediaScan ++ triggerMediaScan(path); + } + + @Override + public void onCancel(String callerTag) { + // nothing to do here + } + -} ++ private void triggerMediaScan(String path){ ++ MediaScannerConnection.scanFile( ++ getActivity().getApplicationContext(), ++ new String[]{path}, ++ null,null); ++ } ++} diff --combined src/com/owncloud/android/ui/fragment/OCFileListFragment.java index d484776a,3665fe0b..37991beb --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@@ -1,6 -1,6 +1,6 @@@ /* ownCloud Android client application * Copyright (C) 2011 Bartek Przybylski - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2014 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,63 -18,61 +18,70 @@@ package com.owncloud.android.ui.fragment; import java.io.File; - import java.util.ArrayList; - import java.util.List; - import com.owncloud.android.Log_OC; ++import android.accounts.Account; + import android.app.Activity; + import android.content.Intent; ++import android.media.MediaScannerConnection; + import android.os.Bundle; ++import android.os.Handler; + import android.support.v4.widget.SwipeRefreshLayout; + import android.view.ContextMenu; + import android.view.MenuInflater; + import android.view.MenuItem; + import android.view.View; + import android.widget.AdapterView; + import android.widget.AdapterView.AdapterContextMenuInfo; + import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; - import com.owncloud.android.files.FileHandler; + import com.owncloud.android.files.FileMenuFilter; +import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; +import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; - import com.owncloud.android.operations.OnRemoteOperationListener; - import com.owncloud.android.operations.RemoteOperation; + import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.operations.RemoveFileOperation; +import com.owncloud.android.operations.RenameFileOperation; - import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.ui.activity.FileDisplayActivity; - import com.owncloud.android.ui.activity.TransferServiceGetter; + import com.owncloud.android.ui.activity.MoveActivity; + import com.owncloud.android.ui.activity.OnEnforceableRefreshListener; import com.owncloud.android.ui.adapter.FileListListAdapter; - import com.owncloud.android.ui.dialog.EditNameDialog; - import com.owncloud.android.ui.dialog.EditNameDialog.EditNameDialogListener; - import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; + import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; + import com.owncloud.android.ui.dialog.RemoveFileDialogFragment; + import com.owncloud.android.ui.dialog.RenameFileDialogFragment; import com.owncloud.android.ui.preview.PreviewImageFragment; import com.owncloud.android.ui.preview.PreviewMediaFragment; - import android.accounts.Account; - import android.app.Activity; - import android.media.MediaScannerConnection; - import android.os.Bundle; - import android.os.Handler; - import android.view.ContextMenu; - import android.view.MenuInflater; - import android.view.MenuItem; - import android.view.View; - import android.widget.AdapterView; - import android.widget.AdapterView.AdapterContextMenuInfo; - /** * A Fragment that lists all files and folders in a given path. * - * @author Bartek Przybylski + * TODO refactorize to get rid of direct dependency on FileDisplayActivity * + * @author Bartek Przybylski + * @author masensio + * @author David A. Velasco */ - public class OCFileListFragment extends ExtendedListFragment implements EditNameDialogListener, ConfirmationDialogFragmentListener { + public class OCFileListFragment extends ExtendedListFragment { private static final String TAG = OCFileListFragment.class.getSimpleName(); - private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; - private static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE"; - - private OCFileListFragment.ContainerActivity mContainerActivity; - + private static final String MY_PACKAGE = OCFileListFragment.class.getPackage() != null ? + OCFileListFragment.class.getPackage().getName() : "com.owncloud.android.ui.fragment"; + + public final static String ARG_JUST_FOLDERS = MY_PACKAGE + ".JUST_FOLDERS"; + public final static String ARG_ALLOW_CONTEXTUAL_ACTIONS = MY_PACKAGE + ".ALLOW_CONTEXTUAL"; + + private static final String KEY_FILE = MY_PACKAGE + ".extra.FILE"; + + private FileFragment.ContainerActivity mContainerActivity; + private OCFile mFile = null; private FileListListAdapter mAdapter; + private Handler mHandler; private OCFile mTargetFile; + /** * {@inheritDoc} @@@ -84,13 -82,29 +91,29 @@@ super.onAttach(activity); Log_OC.e(TAG, "onAttach"); try { - mContainerActivity = (ContainerActivity) activity; + mContainerActivity = (FileFragment.ContainerActivity) activity; + + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement " + + FileFragment.ContainerActivity.class.getSimpleName()); + } + try { + setOnRefreshListener((OnEnforceableRefreshListener) activity); + } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement " + OCFileListFragment.ContainerActivity.class.getSimpleName()); + throw new ClassCastException(activity.toString() + " must implement " + + SwipeRefreshLayout.OnRefreshListener.class.getSimpleName()); } } + - + @Override + public void onDetach() { + setOnRefreshListener(null); + mContainerActivity = null; + super.onDetach(); + } + /** * {@inheritDoc} */ @@@ -98,18 -112,23 +121,23 @@@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log_OC.e(TAG, "onActivityCreated() start"); - mAdapter = new FileListListAdapter(getActivity(), mContainerActivity); + if (savedInstanceState != null) { - mFile = savedInstanceState.getParcelable(EXTRA_FILE); + mFile = savedInstanceState.getParcelable(KEY_FILE); } + + Bundle args = getArguments(); + boolean justFolders = (args == null) ? false : args.getBoolean(ARG_JUST_FOLDERS, false); + mAdapter = new FileListListAdapter( + justFolders, + getSherlockActivity(), + mContainerActivity + ); setListAdapter(mAdapter); registerForContextMenu(getListView()); - getListView().setOnCreateContextMenuListener(this); - - mHandler = new Handler(); - - } + getListView().setOnCreateContextMenuListener(this); + } /** * Saves the current listed folder. @@@ -117,15 -136,14 +145,14 @@@ @Override public void onSaveInstanceState (Bundle outState) { super.onSaveInstanceState(outState); - outState.putParcelable(EXTRA_FILE, mFile); + outState.putParcelable(KEY_FILE, mFile); } - - + /** * Call this, when the user presses the up button. * - * Tries to move up the current folder one level. If the parent folder was removed from the database, - * it continues browsing up until finding an existing folders. + * Tries to move up the current folder one level. If the parent folder was removed from the + * database, it continues browsing up until finding an existing folders. * * return Count of folder levels browsed up. */ @@@ -139,25 -157,29 +166,29 @@@ String parentPath = null; if (mFile.getParentId() != FileDataStorageManager.ROOT_PARENT_ID) { parentPath = new File(mFile.getRemotePath()).getParent(); - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : + parentPath + OCFile.PATH_SEPARATOR; parentDir = storageManager.getFileByPath(parentPath); moveCount++; } else { - parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH); // never returns null; keep the path in root folder + parentDir = storageManager.getFileByPath(OCFile.ROOT_PATH); } while (parentDir == null) { parentPath = new File(parentPath).getParent(); - parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR; + parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : + parentPath + OCFile.PATH_SEPARATOR; parentDir = storageManager.getFileByPath(parentPath); moveCount++; } // exit is granted because storageManager.getFileByPath("/") never returns null - mFile = parentDir; - } - - if (mFile != null) { + mFile = parentDir; + listDirectory(mFile); - mContainerActivity.startSyncFolderOperation(mFile); + onRefresh(false); + + // restore index and top position + restoreIndexAndTopPosition(); + } // else - should never happen now return moveCount; @@@ -170,26 -192,27 +201,27 @@@ if (file.isFolder()) { // update state and view of this fragment listDirectory(file); - // then, notify parent activity to let it update its state and view, and other fragments + // then, notify parent activity to let it update its state and view mContainerActivity.onBrowsedDownTo(file); + // save index and top position + saveIndexAndTopPosition(position); } else { /// Click on a file if (PreviewImageFragment.canBePreviewed(file)) { // preview image - it handles the download, if needed - mContainerActivity.startImagePreview(file); + ((FileDisplayActivity)mContainerActivity).startImagePreview(file); } else if (file.isDown()) { if (PreviewMediaFragment.canBePreviewed(file)) { // media preview - mContainerActivity.startMediaPreview(file, 0, true); + ((FileDisplayActivity)mContainerActivity).startMediaPreview(file, 0, true); } else { - // open with - mContainerActivity.openFile(file); + mContainerActivity.getFileOperationsHelper().openFile(file); } } else { // automatic download, preview on finish - mContainerActivity.startDownloadForPreview(file); + ((FileDisplayActivity)mContainerActivity).startDownloadForPreview(file); } } @@@ -204,73 -227,44 +236,44 @@@ * {@inheritDoc} */ @Override - public void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + public void onCreateContextMenu ( + ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.file_actions_menu, menu); - AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; - OCFile targetFile = (OCFile) mAdapter.getItem(info.position); - List toHide = new ArrayList(); - List toDisable = new ArrayList(); - - MenuItem item = null; - if (targetFile.isFolder()) { - // contextual menu for folders - toHide.add(R.id.action_open_file_with); - toHide.add(R.id.action_download_file); - toHide.add(R.id.action_cancel_download); - toHide.add(R.id.action_cancel_upload); - toHide.add(R.id.action_sync_file); - toHide.add(R.id.action_see_details); - if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) || - mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile) ) { - toDisable.add(R.id.action_rename_file); - toDisable.add(R.id.action_remove_file); - - } - - } else { - // contextual menu for regular files + Bundle args = getArguments(); + boolean allowContextualActions = + (args == null) ? true : args.getBoolean(ARG_ALLOW_CONTEXTUAL_ACTIONS, true); + if (allowContextualActions) { + MenuInflater inflater = getSherlockActivity().getMenuInflater(); + inflater.inflate(R.menu.file_actions_menu, menu); + AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo; + OCFile targetFile = (OCFile) mAdapter.getItem(info.position); - // new design: 'download' and 'open with' won't be available anymore in context menu - toHide.add(R.id.action_download_file); - toHide.add(R.id.action_open_file_with); - - if (targetFile.isDown()) { - toHide.add(R.id.action_cancel_download); - toHide.add(R.id.action_cancel_upload); - - } else { - toHide.add(R.id.action_sync_file); + if (mContainerActivity.getStorageManager() != null) { + FileMenuFilter mf = new FileMenuFilter( + targetFile, + mContainerActivity.getStorageManager().getAccount(), + mContainerActivity, + getSherlockActivity() + ); + mf.filter(menu); } - if ( mContainerActivity.getFileDownloaderBinder().isDownloading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) { - toHide.add(R.id.action_cancel_upload); - toDisable.add(R.id.action_rename_file); - toDisable.add(R.id.action_remove_file); - - } else if ( mContainerActivity.getFileUploaderBinder().isUploading(AccountUtils.getCurrentOwnCloudAccount(getActivity()), targetFile)) { - toHide.add(R.id.action_cancel_download); - toDisable.add(R.id.action_rename_file); - toDisable.add(R.id.action_remove_file); - - } else { - toHide.add(R.id.action_cancel_download); - toHide.add(R.id.action_cancel_upload); - } - } - - for (int i : toHide) { - item = menu.findItem(i); + + /// additional restrictions for this fragment + // TODO allow in the future 'open with' for previewable files + MenuItem item = menu.findItem(R.id.action_open_file_with); if (item != null) { item.setVisible(false); item.setEnabled(false); } - } - - for (int i : toDisable) { - item = menu.findItem(i); - if (item != null) { - item.setEnabled(false); + /// TODO break this direct dependency on FileDisplayActivity... if possible + FileFragment frag = ((FileDisplayActivity)getSherlockActivity()).getSecondFragment(); + if (frag != null && frag instanceof FileDetailFragment && + frag.getFile().getFileId() == targetFile.getFileId()) { + item = menu.findItem(R.id.action_see_details); + if (item != null) { + item.setVisible(false); + item.setEnabled(false); + } } } } @@@ -283,74 -277,64 +286,66 @@@ public boolean onContextItemSelected (MenuItem item) { AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); mTargetFile = (OCFile) mAdapter.getItem(info.position); - switch (item.getItemId()) { + switch (item.getItemId()) { + case R.id.action_share_file: { + mContainerActivity.getFileOperationsHelper().shareFileWithLink(mTargetFile); + return true; + } + case R.id.action_unshare_file: { + mContainerActivity.getFileOperationsHelper().unshareFileWithLink(mTargetFile); + return true; + } case R.id.action_rename_file: { - String fileName = mTargetFile.getFileName(); - int extensionStart = mTargetFile.isFolder() ? -1 : fileName.lastIndexOf("."); - int selectionEnd = (extensionStart >= 0) ? extensionStart : fileName.length(); - EditNameDialog dialog = EditNameDialog.newInstance(getString(R.string.rename_dialog_title), fileName, 0, selectionEnd, this); - dialog.show(getFragmentManager(), EditNameDialog.TAG); + RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(mTargetFile); + dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE); return true; } case R.id.action_remove_file: { - int messageStringId = R.string.confirmation_remove_alert; - int posBtnStringId = R.string.confirmation_remove_remote; - int neuBtnStringId = -1; - if (mTargetFile.isFolder()) { - messageStringId = R.string.confirmation_remove_folder_alert; - posBtnStringId = R.string.confirmation_remove_remote_and_local; - neuBtnStringId = R.string.confirmation_remove_folder_local; - } else if (mTargetFile.isDown()) { - posBtnStringId = R.string.confirmation_remove_remote_and_local; - neuBtnStringId = R.string.confirmation_remove_local; - } - ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance( - messageStringId, - new String[]{mTargetFile.getFileName()}, - posBtnStringId, - neuBtnStringId, - R.string.common_cancel); - confDialog.setOnConfirmationListener(this); - confDialog.show(getFragmentManager(), FileDetailFragment.FTAG_CONFIRMATION); + RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(mTargetFile); + dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION); return true; } + case R.id.action_download_file: case R.id.action_sync_file: { - Account account = AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()); - RemoteOperation operation = new SynchronizeFileOperation(mTargetFile, null, mContainerActivity.getStorageManager(), account, true, getSherlockActivity()); - operation.execute(account, getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity()); - ((FileDisplayActivity) getSherlockActivity()).showLoadingDialog(); + mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile); ++ triggerMediaScan(mTargetFile.getStoragePath()); return true; } - case R.id.action_cancel_download: { - FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); - Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); - if (downloaderBinder != null && downloaderBinder.isDownloading(account, mTargetFile)) { - downloaderBinder.cancel(account, mTargetFile); - listDirectory(); - mContainerActivity.onTransferStateChanged(mTargetFile, false, false); - } + case R.id.action_cancel_download: + case R.id.action_cancel_upload: { + ((FileDisplayActivity)mContainerActivity).cancelTransference(mTargetFile); return true; } - case R.id.action_cancel_upload: { - FileUploaderBinder uploaderBinder = mContainerActivity.getFileUploaderBinder(); - Account account = AccountUtils.getCurrentOwnCloudAccount(getActivity()); - if (uploaderBinder != null && uploaderBinder.isUploading(account, mTargetFile)) { - uploaderBinder.cancel(account, mTargetFile); - listDirectory(); - mContainerActivity.onTransferStateChanged(mTargetFile, false, false); + case R.id.action_see_details: { + mContainerActivity.showDetails(mTargetFile); + return true; + } + case R.id.action_send_file: { + // Obtain the file + if (!mTargetFile.isDown()) { // Download the file + Log_OC.d(TAG, mTargetFile.getRemotePath() + " : File must be downloaded"); + ((FileDisplayActivity)mContainerActivity).startDownloadForSending(mTargetFile); + + } else { + mContainerActivity.getFileOperationsHelper().sendDownloadedFile(mTargetFile); } return true; } - case R.id.action_see_details: { - ((FileFragment.ContainerActivity)getActivity()).showDetails(mTargetFile); + case R.id.action_move: { + Intent action = new Intent(getActivity(), MoveActivity.class); + + // Pass mTargetFile that contains info of selected file/folder + action.putExtra(MoveActivity.EXTRA_TARGET_FILE, mTargetFile); + getActivity().startActivityForResult(action, FileDisplayActivity.ACTION_MOVE_FILES); return true; } default: return super.onContextItemSelected(item); } } + + /** * Use this to query the {@link OCFile} that is currently * being displayed by this fragment @@@ -402,109 -386,5 +397,11 @@@ mFile = directory; } } - - - - /** - * Interface to implement by any Activity that includes some instance of FileListFragment - * - * @author David A. Velasco - */ - public interface ContainerActivity extends TransferServiceGetter, OnRemoteOperationListener, FileHandler { - - /** - * Callback method invoked when a the user browsed into a different folder through the list of files - * - * @param file - */ - public void onBrowsedDownTo(OCFile folder); - - public void startDownloadForPreview(OCFile file); - - public void startMediaPreview(OCFile file, int i, boolean b); - - public void startImagePreview(OCFile file); - - public void startSyncFolderOperation(OCFile folder); - - /** - * Getter for the current DataStorageManager in the container activity - */ - public FileDataStorageManager getStorageManager(); - - - /** - * Callback method invoked when a the 'transfer state' of a file changes. - * - * This happens when a download or upload is started or ended for a file. - * - * This method is necessary by now to update the user interface of the double-pane layout in tablets - * because methods {@link FileDownloaderBinder#isDownloading(Account, OCFile)} and {@link FileUploaderBinder#isUploading(Account, OCFile)} - * won't provide the needed response before the method where this is called finishes. - * - * TODO Remove this when the transfer state of a file is kept in the database (other thing TODO) - * - * @param file OCFile which state changed. - * @param downloading Flag signaling if the file is now downloading. - * @param uploading Flag signaling if the file is now uploading. - */ - public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading); - - } - - - @Override - public void onDismiss(EditNameDialog dialog) { - if (dialog.getResult()) { - String newFilename = dialog.getNewFilename(); - Log_OC.d(TAG, "name edit dialog dismissed with new name " + newFilename); - RemoteOperation operation = new RenameFileOperation(mTargetFile, - AccountUtils.getCurrentOwnCloudAccount(getActivity()), - newFilename, - mContainerActivity.getStorageManager()); - operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity()); - ((FileDisplayActivity) getActivity()).showLoadingDialog(); - } - } - - @Override - public void onConfirmation(String callerTag) { - if (callerTag.equals(FileDetailFragment.FTAG_CONFIRMATION)) { - if (mContainerActivity.getStorageManager().getFileById(mTargetFile.getFileId()) != null) { - String path = new File(mTargetFile.getStoragePath()).getParent(); - RemoteOperation operation = new RemoveFileOperation( mTargetFile, - true, - mContainerActivity.getStorageManager()); - operation.execute(AccountUtils.getCurrentOwnCloudAccount(getSherlockActivity()), getSherlockActivity(), mContainerActivity, mHandler, getSherlockActivity()); - - ((FileDisplayActivity) getActivity()).showLoadingDialog(); - - triggerMediaScan(path); - } - } - } - - @Override - public void onNeutral(String callerTag) { - String path = new File(mTargetFile.getStoragePath()).getParent(); - mContainerActivity.getStorageManager().removeFile(mTargetFile, false, true); // TODO perform in background task / new thread - - triggerMediaScan(path); - - listDirectory(); - mContainerActivity.onTransferStateChanged(mTargetFile, false, false); - } - - @Override - public void onCancel(String callerTag) { - Log_OC.d(TAG, "REMOVAL CANCELED"); - } - + private void triggerMediaScan(String path){ + MediaScannerConnection.scanFile( + getActivity().getApplicationContext(), + new String[]{path}, + null,null); + } } diff --combined src/com/owncloud/android/ui/preview/PreviewImageActivity.java index 5199c01f,c06f341f..09c2f587 --- a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@@ -16,41 -16,48 +16,51 @@@ */ package com.owncloud.android.ui.preview; + import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.OnScanCompletedListener; +import android.net.Uri; + import android.content.SharedPreferences; + import android.os.Build; import android.os.Bundle; + import android.os.Handler; import android.os.IBinder; - import android.support.v4.app.Fragment; - import android.support.v4.app.FragmentManager; - import android.support.v4.app.FragmentTransaction; + import android.os.Message; + import android.preference.PreferenceManager; import android.support.v4.view.ViewPager; - import android.view.MotionEvent; import android.view.View; - import android.view.View.OnTouchListener; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; - import com.owncloud.android.Log_OC; + import com.ortiz.touch.ExtendedViewPager; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; - import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; + import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; + import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; + import com.owncloud.android.lib.common.operations.RemoteOperation; + import com.owncloud.android.lib.common.operations.RemoteOperationResult; + import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; + import com.owncloud.android.lib.common.utils.Log_OC; + import com.owncloud.android.operations.CreateShareOperation; + import com.owncloud.android.operations.RemoveFileOperation; + import com.owncloud.android.operations.UnshareLinkOperation; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; - import com.owncloud.android.ui.dialog.LoadingDialog; + import com.owncloud.android.ui.activity.PinCodeActivity; import com.owncloud.android.ui.fragment.FileFragment; + import com.owncloud.android.utils.DisplayUtils; /** @@@ -58,7 -65,9 +68,9 @@@ * * @author David A. Velasco */ - public class PreviewImageActivity extends FileActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener, OnTouchListener { + public class PreviewImageActivity extends FileActivity implements + FileFragment.ContainerActivity, + ViewPager.OnPageChangeListener, OnRemoteOperationListener { public static final int DIALOG_SHORT_WAIT = 0; @@@ -66,23 -75,17 +78,17 @@@ public static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW"; private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER"; - - private static final String DIALOG_WAIT_TAG = "DIALOG_WAIT"; - - private FileDataStorageManager mStorageManager; - - private ViewPager mViewPager; + + private static final int INITIAL_HIDE_DELAY = 0; // immediate hide + + private ExtendedViewPager mViewPager; private PreviewImagePagerAdapter mPreviewImagePagerAdapter; - private FileDownloaderBinder mDownloaderBinder = null; - private ServiceConnection mDownloadConnection, mUploadConnection = null; - private FileUploaderBinder mUploaderBinder = null; - private boolean mRequestWaitingForBinder; private DownloadFinishReceiver mDownloadFinishReceiver; - - private boolean mFullScreen; + + private View mFullScreenAnchorView; @Override @@@ -93,10 -96,38 +99,38 @@@ setContentView(R.layout.preview_image_activity); ActionBar actionBar = getSupportActionBar(); + actionBar.setIcon(DisplayUtils.getSeasonalIconId()); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.hide(); - mFullScreen = true; + // PIN CODE request + if (getIntent().getExtras() != null && savedInstanceState == null && fromNotification()) { + requestPinCode(); + } + + // 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(); + } else { + actionBar.hide(); + } + } + }); + + } + if (savedInstanceState != null) { mRequestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER); } else { @@@ -108,14 -139,13 +142,13 @@@ private void initViewPager() { // get parent from path String parentPath = getFile().getRemotePath().substring(0, getFile().getRemotePath().lastIndexOf(getFile().getFileName())); - OCFile parentFolder = mStorageManager.getFileByPath(parentPath); - //OCFile parentFolder = mStorageManager.getFileById(getFile().getParentId()); + OCFile parentFolder = getStorageManager().getFileByPath(parentPath); if (parentFolder == null) { // should not be necessary - parentFolder = mStorageManager.getFileByPath(OCFile.ROOT_PATH); + parentFolder = getStorageManager().getFileByPath(OCFile.ROOT_PATH); } - mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), mStorageManager); - mViewPager = (ViewPager) findViewById(R.id.fragmentPager); + mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), parentFolder, getAccount(), getStorageManager()); + mViewPager = (ExtendedViewPager) findViewById(R.id.fragmentPager); int position = mPreviewImagePagerAdapter.getFilePosition(getFile()); position = (position >= 0) ? position : 0; mViewPager.setAdapter(mPreviewImagePagerAdapter); @@@ -128,13 -158,49 +161,49 @@@ } + 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 + // are available + delayedHide(INITIAL_HIDE_DELAY); + + } + + Handler mHideSystemUiHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (isHoneycombOrHigher()) { + hideSystemUI(mFullScreenAnchorView); + } + 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(); - mDownloadConnection = new PreviewImageServiceConnection(); - bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE); - mUploadConnection = new PreviewImageServiceConnection(); - bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE); } @Override @@@ -143,6 -209,49 +212,49 @@@ outState.putBoolean(KEY_WAITING_FOR_BINDER, mRequestWaitingForBinder); } + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + super.onRemoteOperationFinish(operation, result); + + if (operation instanceof CreateShareOperation) { + onCreateShareOperationFinish((CreateShareOperation) operation, result); + + } else if (operation instanceof UnshareLinkOperation) { + onUnshareLinkOperationFinish((UnshareLinkOperation) operation, result); + + } else if (operation instanceof RemoveFileOperation) { + finish(); + } + } + + + private void onUnshareLinkOperationFinish(UnshareLinkOperation operation, RemoteOperationResult result) { + if (result.isSuccess()) { + OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath()); + if (file != null) { + setFile(file); + } + invalidateOptionsMenu(); + } else if (result.getCode() == ResultCode.SHARE_NOT_FOUND) { + backToDisplayActivity(); + } + + } + + private void onCreateShareOperationFinish(CreateShareOperation operation, RemoteOperationResult result) { + if (result.isSuccess()) { + OCFile file = getStorageManager().getFileByPath(getFile().getRemotePath()); + if (file != null) { + setFile(file); + } + invalidateOptionsMenu(); + } + } + + @Override + protected ServiceConnection newTransferenceServiceConnection() { + return new PreviewImageServiceConnection(); + } /** Defines callbacks for service binding, passed to bindService() */ private class PreviewImageServiceConnection implements ServiceConnection { @@@ -183,14 -292,6 +295,6 @@@ @Override public void onStop() { super.onStop(); - if (mDownloadConnection != null) { - unbindService(mDownloadConnection); - mDownloadConnection = null; - } - if (mUploadConnection != null) { - unbindService(mUploadConnection); - mUploadConnection = null; - } } @@@ -199,7 -300,6 +303,6 @@@ super.onDestroy(); } - @Override public boolean onOptionsItemSelected(MenuItem item) { boolean returnValue = false; @@@ -220,7 -320,7 +323,7 @@@ @Override protected void onResume() { super.onResume(); - //Log.e(TAG, "ACTIVITY, ONRESUME"); + //Log_OC.e(TAG, "ACTIVITY, ONRESUME"); mDownloadFinishReceiver = new DownloadFinishReceiver(); IntentFilter filter = new IntentFilter(FileDownloader.getDownloadFinishMessage()); @@@ -230,15 -330,15 +333,15 @@@ @Override protected void onPostResume() { - //Log.e(TAG, "ACTIVITY, ONPOSTRESUME"); + //Log_OC.e(TAG, "ACTIVITY, ONPOSTRESUME"); super.onPostResume(); } @Override public void onPause() { - super.onPause(); unregisterReceiver(mDownloadFinishReceiver); mDownloadFinishReceiver = null; + super.onPause(); } @@@ -246,53 -346,6 +349,6 @@@ finish(); } - /** - * Show loading dialog - */ - public void showLoadingDialog() { - // Construct dialog - LoadingDialog loading = new LoadingDialog(getResources().getString(R.string.wait_a_moment)); - FragmentManager fm = getSupportFragmentManager(); - FragmentTransaction ft = fm.beginTransaction(); - loading.show(ft, DIALOG_WAIT_TAG); - - } - - /** - * Dismiss loading dialog - */ - public void dismissLoadingDialog(){ - Fragment frag = getSupportFragmentManager().findFragmentByTag(DIALOG_WAIT_TAG); - if (frag != null) { - LoadingDialog loading = (LoadingDialog) frag; - loading.dismiss(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public void onFileStateChanged() { - // nothing to do here! - } - - - /** - * {@inheritDoc} - */ - @Override - public FileDownloaderBinder getFileDownloaderBinder() { - return mDownloaderBinder; - } - - - @Override - public FileUploaderBinder getFileUploaderBinder() { - return mUploaderBinder; - } - - @Override public void showDetails(OCFile file) { Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class); @@@ -336,7 -389,11 +392,11 @@@ requestForDownload(currentFile); } } + + // Call to reset image zoom to initial state + ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom(); } + } /** @@@ -375,25 -432,14 +435,25 @@@ 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) && downloadedRemotePath != null) { - final OCFile file = mStorageManager.getFileByPath(downloadedRemotePath); + OCFile file = getStorageManager().getFileByPath(downloadedRemotePath); int position = mPreviewImagePagerAdapter.getFilePosition(file); boolean downloadWasFine = intent.getBooleanExtra(FileDownloader.EXTRA_DOWNLOAD_RESULT, false); //boolean isOffscreen = Math.abs((mViewPager.getCurrentItem() - position)) <= mViewPager.getOffscreenPageLimit(); + if (downloadWasFine){ + // Trigger Mediascan + MediaScannerConnection.scanFile( + context, + new String[]{file.getStoragePath()}, + null,null); + } + if (position >= 0 && intent.getAction().equals(FileDownloader.getDownloadFinishMessage())) { if (downloadWasFine) { mPreviewImagePagerAdapter.updateFile(position, file); @@@ -413,30 -459,41 +473,41 @@@ } + @SuppressLint("InlinedApi") + public void toggleFullScreen() { - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_UP) { - toggleFullScreen(); - } - return true; - } + if (isHoneycombOrHigher()) { + + boolean visible = (mFullScreenAnchorView.getSystemUiVisibility() + & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; + + if (visible) { + hideSystemUI(mFullScreenAnchorView); + // actionBar.hide(); // propagated through + // OnSystemUiVisibilityChangeListener() + } else { + showSystemUI(mFullScreenAnchorView); + // actionBar.show(); // propagated through + // OnSystemUiVisibilityChangeListener() + } - - private void toggleFullScreen() { - ActionBar actionBar = getSupportActionBar(); - if (mFullScreen) { - actionBar.show(); - } else { - actionBar.hide(); - + + ActionBar actionBar = getSupportActionBar(); + if (!actionBar.isShowing()) { + actionBar.show(); + + } else { + actionBar.hide(); + + } + } - mFullScreen = !mFullScreen; } @Override protected void onAccountSet(boolean stateWasRecovered) { + super.onAccountSet(stateWasRecovered); if (getAccount() != null) { OCFile file = getFile(); /// Validate handled file (first image to preview) @@@ -446,15 -503,14 +517,14 @@@ if (!file.isImage()) { throw new IllegalArgumentException("Non-image file passed as argument"); } - mStorageManager = new FileDataStorageManager(getAccount(), getContentResolver()); // Update file according to DB file, if it is possible if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) - file = mStorageManager.getFileById(file.getFileId()); + 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 mStorageManager + setFile(file); // reset after getting it fresh from storageManager getSupportActionBar().setTitle(getFile().getFileName()); //if (!stateWasRecovered) { initViewPager(); @@@ -464,11 -520,68 +534,68 @@@ // handled file not in the current Account finish(); } - - } else { - Log_OC.wtf(TAG, "onAccountChanged was called with NULL account associated!"); } } + /** + * Launch an intent to request the PIN code to the user before letting him use the app + */ + private void requestPinCode() { + boolean pinStart = false; + SharedPreferences appPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + pinStart = appPrefs.getBoolean("set_pincode", false); + if (pinStart) { + Intent i = new Intent(getApplicationContext(), PinCodeActivity.class); + i.putExtra(PinCodeActivity.EXTRA_ACTIVITY, "PreviewImageActivity"); + startActivity(i); + } + } + + @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) { + 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 + ); + } + + @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 + ); + } + + /** + * Checks if OS version is Honeycomb one or higher + * + * @return boolean + */ + private boolean isHoneycombOrHigher() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + return true; + } + return false; + } + } diff --combined src/com/owncloud/android/ui/preview/PreviewImageFragment.java index d5173489,4dd5c436..a3814c77 --- a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java @@@ -1,5 -1,5 +1,5 @@@ /* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. + * Copyright (C) 2012-2014 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, @@@ -16,52 -16,45 +16,45 @@@ */ package com.owncloud.android.ui.preview; + import java.io.BufferedInputStream; import java.io.File; + import java.io.FileInputStream; + import java.io.FilterInputStream; + import java.io.IOException; + import java.io.InputStream; import java.lang.ref.WeakReference; - import java.util.ArrayList; - import java.util.List; import android.accounts.Account; import android.annotation.SuppressLint; import android.app.Activity; - import android.content.ActivityNotFoundException; - import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Point; - import android.media.MediaScannerConnection; - import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; - import android.os.Handler; import android.support.v4.app.FragmentStatePagerAdapter; import android.view.Display; import android.view.LayoutInflater; import android.view.View; - import android.view.View.OnTouchListener; + import android.view.View.OnClickListener; import android.view.ViewGroup; - import android.webkit.MimeTypeMap; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; - import android.widget.Toast; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; - import com.owncloud.android.Log_OC; import com.owncloud.android.R; - import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; - import com.owncloud.android.operations.OnRemoteOperationListener; - import com.owncloud.android.operations.RemoteOperation; - import com.owncloud.android.operations.RemoteOperationResult; - import com.owncloud.android.operations.RemoveFileOperation; - import com.owncloud.android.ui.fragment.ConfirmationDialogFragment; + import com.owncloud.android.files.FileMenuFilter; + import com.owncloud.android.lib.common.utils.Log_OC; + import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; + import com.owncloud.android.ui.dialog.RemoveFileDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; + import com.owncloud.android.utils.TouchImageViewCustom; - import eu.alefzero.webdav.WebdavUtils; /** @@@ -73,23 -66,19 +66,19 @@@ * * @author David A. Velasco */ - public class PreviewImageFragment extends FileFragment implements OnRemoteOperationListener, - ConfirmationDialogFragment.ConfirmationDialogFragmentListener { + public class PreviewImageFragment extends FileFragment { + public static final String EXTRA_FILE = "FILE"; public static final String EXTRA_ACCOUNT = "ACCOUNT"; private View mView; private Account mAccount; - private FileDataStorageManager mStorageManager; - private ImageView mImageView; + private TouchImageViewCustom mImageView; private TextView mMessageView; private ProgressBar mProgressWheel; public Bitmap mBitmap = null; - private Handler mHandler; - private RemoteOperation mLastRemoteOperation; - private static final String TAG = PreviewImageFragment.class.getSimpleName(); private boolean mIgnoreFirstSavedState; @@@ -107,7 -96,6 +96,6 @@@ public PreviewImageFragment(OCFile fileToDetail, Account ocAccount, boolean ignoreFirstSavedState) { super(fileToDetail); mAccount = ocAccount; - mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment mIgnoreFirstSavedState = ignoreFirstSavedState; } @@@ -122,7 -110,6 +110,6 @@@ public PreviewImageFragment() { super(); mAccount = null; - mStorageManager = null; mIgnoreFirstSavedState = false; } @@@ -133,7 -120,6 +120,6 @@@ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mHandler = new Handler(); setHasOptionsMenu(true); } @@@ -146,38 -132,32 +132,32 @@@ Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); mView = inflater.inflate(R.layout.preview_image_fragment, container, false); - mImageView = (ImageView)mView.findViewById(R.id.image); + mImageView = (TouchImageViewCustom) mView.findViewById(R.id.image); mImageView.setVisibility(View.GONE); - mView.setOnTouchListener((OnTouchListener)getActivity()); // WATCH OUT THAT CAST + mImageView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + ((PreviewImageActivity) getActivity()).toggleFullScreen(); + } + + }); mMessageView = (TextView)mView.findViewById(R.id.message); mMessageView.setVisibility(View.GONE); mProgressWheel = (ProgressBar)mView.findViewById(R.id.progressWheel); mProgressWheel.setVisibility(View.VISIBLE); return mView; } - /** * {@inheritDoc} */ @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - if (!(activity instanceof FileFragment.ContainerActivity)) - throw new ClassCastException(activity.toString() + " must implement " + FileFragment.ContainerActivity.class.getSimpleName()); - } - - - /** - * {@inheritDoc} - */ - @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver()); if (savedInstanceState != null) { if (!mIgnoreFirstSavedState) { - setFile((OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE)); + OCFile file = (OCFile)savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE); + setFile(file); mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT); } else { mIgnoreFirstSavedState = false; @@@ -222,54 -202,100 +202,100 @@@ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.file_actions_menu, menu); - List toHide = new ArrayList(); + } + + /** + * {@inheritDoc} + */ + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); - MenuItem item = null; - toHide.add(R.id.action_cancel_download); - toHide.add(R.id.action_cancel_upload); - toHide.add(R.id.action_download_file); - toHide.add(R.id.action_rename_file); // by now - - for (int i : toHide) { - item = menu.findItem(i); - if (item != null) { - item.setVisible(false); - item.setEnabled(false); - } + if (mContainerActivity.getStorageManager() != null) { + // Update the file + setFile(mContainerActivity.getStorageManager().getFileById(getFile().getFileId())); + + FileMenuFilter mf = new FileMenuFilter( + getFile(), + mContainerActivity.getStorageManager().getAccount(), + mContainerActivity, + getSherlockActivity() + ); + mf.filter(menu); + } + + // additional restriction for this fragment + // TODO allow renaming in PreviewImageFragment + MenuItem item = menu.findItem(R.id.action_rename_file); + if (item != null) { + item.setVisible(false); + item.setEnabled(false); + } + + // additional restriction for this fragment + // TODO allow refresh file in PreviewImageFragment + item = menu.findItem(R.id.action_sync_file); + if (item != null) { + item.setVisible(false); + item.setEnabled(false); + } + + // additional restriction for this fragment + item = menu.findItem(R.id.action_move); + if (item != null) { + item.setVisible(false); + item.setEnabled(false); } } + /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.action_share_file: { + mContainerActivity.getFileOperationsHelper().shareFileWithLink(getFile()); + return true; + } + case R.id.action_unshare_file: { + mContainerActivity.getFileOperationsHelper().unshareFileWithLink(getFile()); + return true; + } case R.id.action_open_file_with: { openFile(); return true; } case R.id.action_remove_file: { - removeFile(); + RemoveFileDialogFragment dialog = RemoveFileDialogFragment.newInstance(getFile()); + dialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION); return true; } case R.id.action_see_details: { seeDetails(); return true; } + case R.id.action_send_file: { + mContainerActivity.getFileOperationsHelper().sendDownloadedFile(getFile()); + return true; + } + case R.id.action_sync_file: { + mContainerActivity.getFileOperationsHelper().syncFile(getFile()); + return true; + } default: return false; } } - + private void seeDetails() { - ((FileFragment.ContainerActivity)getActivity()).showDetails(getFile()); + mContainerActivity.showDetails(getFile()); } @@@ -284,145 -310,25 +310,25 @@@ super.onPause(); } - @Override public void onDestroy() { - super.onDestroy(); if (mBitmap != null) { mBitmap.recycle(); + System.gc(); } + super.onDestroy(); } /** * Opens the previewed image with an external application. - * - * TODO - improve this; instead of prioritize the actions available for the MIME type in the server, - * we should get a list of available apps for MIME tpye in the server and join it with the list of - * available apps for the MIME type known from the file extension, to let the user choose */ private void openFile() { - OCFile file = getFile(); - String storagePath = file.getStoragePath(); - String encodedStoragePath = WebdavUtils.encodePath(storagePath); - try { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype()); - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - - } catch (Throwable t) { - Log_OC.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + file.getMimetype()); - boolean toastIt = true; - String mimeType = ""; - try { - Intent i = new Intent(Intent.ACTION_VIEW); - mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(storagePath.substring(storagePath.lastIndexOf('.') + 1)); - if (mimeType == null || !mimeType.equals(file.getMimetype())) { - if (mimeType != null) { - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mimeType); - } else { - // desperate try - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), "*-/*"); - } - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - toastIt = false; - } - - } catch (IndexOutOfBoundsException e) { - Log_OC.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); - - } catch (ActivityNotFoundException e) { - Log_OC.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); - - } catch (Throwable th) { - Log_OC.e(TAG, "Unexpected problem when opening: " + storagePath, th); - - } finally { - if (toastIt) { - Toast.makeText(getActivity(), "There is no application to handle file " + file.getFileName(), Toast.LENGTH_SHORT).show(); - } - } - - } + mContainerActivity.getFileOperationsHelper().openFile(getFile()); finish(); } - - /** - * Starts a the removal of the previewed file. - * - * Shows a confirmation dialog. The action continues in {@link #onConfirmation(String)} , {@link #onNeutral(String)} or {@link #onCancel(String)}, - * depending upon the user selection in the dialog. - */ - private void removeFile() { - ConfirmationDialogFragment confDialog = ConfirmationDialogFragment.newInstance( - R.string.confirmation_remove_alert, - new String[]{getFile().getFileName()}, - R.string.confirmation_remove_remote_and_local, - R.string.confirmation_remove_local, - R.string.common_cancel); - confDialog.setOnConfirmationListener(this); - confDialog.show(getFragmentManager(), ConfirmationDialogFragment.FTAG_CONFIRMATION); - } - - - /** - * Performs the removal of the previewed file, both locally and in the server. - */ - @Override - public void onConfirmation(String callerTag) { - if (mStorageManager.getFileById(getFile().getFileId()) != null) { // check that the file is still there; - String path = new File(getFile().getStoragePath()).getParent(); - mLastRemoteOperation = new RemoveFileOperation( getFile(), // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters - true, - mStorageManager); - mLastRemoteOperation.execute(mAccount, getSherlockActivity(), this, mHandler, getSherlockActivity()); - - ((PreviewImageActivity) getActivity()).showLoadingDialog(); - - triggerMediaScan(path); - } - } - - - /** - * Removes the file from local storage - */ - @Override - public void onNeutral(String callerTag) { - // TODO this code should be made in a secondary thread, - OCFile file = getFile(); - if (file.isDown()) { // checks it is still there - File f = new File(file.getStoragePath()); - String path = f.getParent(); - f.delete(); - - triggerMediaScan(path); - - file.setStoragePath(null); - mStorageManager.saveFile(file); - finish(); - } - } - - /** - * User cancelled the removal action. - */ - @Override - public void onCancel(String callerTag) { - // nothing to do here - } - - private void triggerMediaScan(String path){ - MediaScannerConnection.scanFile( - getActivity().getApplicationContext(), - new String[]{path}, - null,null); - } -- + private class BitmapLoader extends AsyncTask { /** @@@ -430,7 -336,7 +336,7 @@@ * * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes. */ - private final WeakReference mImageViewRef; + private final WeakReference mImageViewRef; /** * Weak reference to the target {@link TextView} where error messages will be written. @@@ -459,65 -365,27 +365,27 @@@ * * @param imageView Target {@link ImageView} where the bitmap will be loaded into. */ - public BitmapLoader(ImageView imageView, TextView messageView, ProgressBar progressWheel) { - mImageViewRef = new WeakReference(imageView); + public BitmapLoader(ImageViewCustom imageView, TextView messageView, ProgressBar progressWheel) { + mImageViewRef = new WeakReference(imageView); mMessageViewRef = new WeakReference(messageView); mProgressWheelRef = new WeakReference(progressWheel); } - @SuppressWarnings("deprecation") - @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20 - @Override + @Override protected Bitmap doInBackground(String... params) { Bitmap result = null; if (params.length != 1) return result; String storagePath = params[0]; try { - // set desired options that will affect the size of the bitmap - BitmapFactory.Options options = new Options(); - options.inScaled = true; - options.inPurgeable = true; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - options.inPreferQualityOverSpeed = false; - } - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { - options.inMutable = false; - } - // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(storagePath, options); - - int width = options.outWidth; - int height = options.outHeight; - int scale = 1; - - Display display = getActivity().getWindowManager().getDefaultDisplay(); - Point size = new Point(); - int screenWidth; - int screenHeight; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { - display.getSize(size); - screenWidth = size.x; - screenHeight = size.y; - } else { - screenWidth = display.getWidth(); - screenHeight = display.getHeight(); - } - if (width > screenWidth) { - // second try to scale down the image , this time depending upon the screen size - scale = (int) Math.floor((float)width / screenWidth); - } - if (height > screenHeight) { - scale = Math.max(scale, (int) Math.floor((float)height / screenHeight)); - } - options.inSampleSize = scale; + File picture = new File(storagePath); - // really load the bitmap - options.inJustDecodeBounds = false; // the next decodeFile call will be real - result = BitmapFactory.decodeFile(storagePath, options); - //Log_OC.d(TAG, "Image loaded - width: " + options.outWidth + ", loaded height: " + options.outHeight); + if (picture != null) { + //Decode file into a bitmap in real size for being able to make zoom on the image + result = BitmapFactory.decodeStream(new FlushedInputStream + (new BufferedInputStream(new FileInputStream(picture)))); + } if (result == null) { mErrorMessageId = R.string.preview_image_error_unknown_format; @@@ -525,8 -393,15 +393,15 @@@ } } catch (OutOfMemoryError e) { - mErrorMessageId = R.string.preview_image_error_unknown_format; Log_OC.e(TAG, "Out of memory occured for file " + storagePath, e); + + // If out of memory error when loading image, try to load it scaled + result = loadScaledImage(storagePath); + + if (result == null) { + mErrorMessageId = R.string.preview_image_error_unknown_format; + Log_OC.e(TAG, "File could not be loaded as a bitmap: " + storagePath); + } } catch (NoSuchFieldError e) { mErrorMessageId = R.string.common_error_unknown; @@@ -549,11 -424,13 +424,13 @@@ showErrorMessage(); } } - + + @SuppressLint("InlinedApi") private void showLoadedImage(Bitmap result) { if (mImageViewRef != null) { - final ImageView imageView = mImageViewRef.get(); + final ImageViewCustom imageView = mImageViewRef.get(); if (imageView != null) { + imageView.setBitmap(result); imageView.setImageBitmap(result); imageView.setVisibility(View.VISIBLE); mBitmap = result; @@@ -607,39 -484,94 +484,94 @@@ /** - * {@inheritDoc} + * Finishes the preview */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) { - onRemoveFileOperationFinish((RemoveFileOperation)operation, result); - } + private void finish() { + Activity container = getActivity(); + container.finish(); } - private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { - ((PreviewImageActivity) getActivity()).dismissLoadingDialog(); - - if (result.isSuccess()) { - Toast msg = Toast.makeText(getActivity().getApplicationContext(), R.string.remove_success_msg, Toast.LENGTH_LONG); - msg.show(); - finish(); - - } else { - Toast msg = Toast.makeText(getActivity(), R.string.remove_fail_msg, Toast.LENGTH_LONG); - msg.show(); - if (result.isSslRecoverableException()) { - // TODO show the SSL warning dialog + public TouchImageViewCustom getImageView() { + return mImageView; + } + + static class FlushedInputStream extends FilterInputStream { + public FlushedInputStream(InputStream inputStream) { + super(inputStream); + } + + @Override + public long skip(long n) throws IOException { + long totalBytesSkipped = 0L; + while (totalBytesSkipped < n) { + long bytesSkipped = in.skip(n - totalBytesSkipped); + if (bytesSkipped == 0L) { + int byteValue = read(); + if (byteValue < 0) { + break; // we reached EOF + } else { + bytesSkipped = 1; // we read one byte + } + } + totalBytesSkipped += bytesSkipped; } + return totalBytesSkipped; } } /** - * Finishes the preview + * Load image scaled + * @param storagePath: path of the image + * @return Bitmap */ - private void finish() { - Activity container = getActivity(); - container.finish(); + @SuppressWarnings("deprecation") + private Bitmap loadScaledImage(String storagePath) { + + Log_OC.d(TAG, "Loading image scaled"); + + // set desired options that will affect the size of the bitmap + BitmapFactory.Options options = new Options(); + options.inScaled = true; + options.inPurgeable = true; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { + options.inPreferQualityOverSpeed = false; + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { + options.inMutable = false; + } + // make a false load of the bitmap - just to be able to read outWidth, outHeight and outMimeType + options.inJustDecodeBounds = true; + BitmapFactory.decodeFile(storagePath, options); + + int width = options.outWidth; + int height = options.outHeight; + int scale = 1; + + Display display = getActivity().getWindowManager().getDefaultDisplay(); + Point size = new Point(); + int screenWidth; + int screenHeight; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { + display.getSize(size); + screenWidth = size.x; + screenHeight = size.y; + } else { + screenWidth = display.getWidth(); + screenHeight = display.getHeight(); + } + + if (width > screenWidth) { + // second try to scale down the image , this time depending upon the screen size + scale = (int) Math.floor((float)width / screenWidth); + } + if (height > screenHeight) { + scale = Math.max(scale, (int) Math.floor((float)height / screenHeight)); + } + options.inSampleSize = scale; + + // really load the bitmap + options.inJustDecodeBounds = false; // the next decodeFile call will be real + return BitmapFactory.decodeFile(storagePath, options); + } - - }