From: David A. Velasco Date: Tue, 26 Feb 2013 11:33:28 +0000 (+0100) Subject: Automatic preview after download in PreviewImageActivity X-Git-Tag: oc-android-1.4.3~39^2~43 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/fc2af5b03e9aeef0bcb985690dabb919984e874a preview after download in PreviewImageActivity --- diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f0dc61ba..218f20f8 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -86,7 +86,7 @@ - + . - * - */ -package com.owncloud.android.ui.activity; - -import java.util.Vector; - -import android.accounts.Account; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.IBinder; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.view.ViewPager; -import android.util.Log; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.datamodel.DataStorageManager; -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.FileDownloader.FileDownloaderBinder; -import com.owncloud.android.files.services.FileUploader; -import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.ui.fragment.FileDetailFragment; -import com.owncloud.android.ui.fragment.FileDownloadFragment; -import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.ui.fragment.FilePreviewFragment; -import com.owncloud.android.ui.fragment.PreviewImageFragment; - -import com.owncloud.android.AccountUtils; -import com.owncloud.android.R; - -/** - * Used as an utility to preview image files contained in an ownCloud account. - * - * @author David A. Velasco - */ -public class PreviewImageActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener { - - 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 OCFile mFile; - private OCFile mParentFolder; - private Account mAccount; - private DataStorageManager mStorageManager; - - private ViewPager mViewPager; - private PreviewImagePagerAdapter mPreviewImagePagerAdapter; - - private FileDownloaderBinder mDownloaderBinder = null; - private ServiceConnection mDownloadConnection, mUploadConnection = null; - private FileUploaderBinder mUploaderBinder = null; - private boolean mWaitingToPreview; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); - mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); - if (mFile == null) { - throw new IllegalStateException("Instanced with a NULL OCFile"); - } - if (mAccount == null) { - throw new IllegalStateException("Instanced with a NULL ownCloud Account"); - } - if (!mFile.isImage()) { - throw new IllegalArgumentException("Non-image file passed as argument"); - } - - setContentView(R.layout.preview_image_activity); - - ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setTitle(mFile.getFileName()); - - mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); - mParentFolder = mStorageManager.getFileById(mFile.getParentId()); - if (mParentFolder == null) { - // should not be necessary - mParentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); - } - - createViewPager(); - - if (savedInstanceState == null) { - mWaitingToPreview = false; - } else { - mWaitingToPreview = savedInstanceState.getBoolean(KEY_WAITING_TO_PREVIEW); - } - - mDownloadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileDownloader.class), mDownloadConnection, Context.BIND_AUTO_CREATE); - mUploadConnection = new DetailsServiceConnection(); - bindService(new Intent(this, FileUploader.class), mUploadConnection, Context.BIND_AUTO_CREATE); - - } - - private void createViewPager() { - mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), mParentFolder); - mViewPager = (ViewPager) findViewById(R.id.fragmentPager); - mViewPager.setAdapter(mPreviewImagePagerAdapter); - mViewPager.setOnPageChangeListener(this); - } - - - /** - * Adapter class that provides Fragment instances - * - * @author David A. Velasco - */ - public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { - - Vector mImageFiles; - - public PreviewImagePagerAdapter(FragmentManager fm, OCFile parentFolder) { - super(fm); - mImageFiles = mStorageManager.getDirectoryImages(parentFolder); - } - - @Override - public Fragment getItem(int i) { - Log.e(TAG, "GETTING PAGE " + i); - OCFile file = mImageFiles.get(i); - Fragment fragment = null; - if (file.isDown()) { - fragment = new PreviewImageFragment(file, mAccount); - mWaitingToPreview = false; - } else { - fragment = new FileDownloadFragment(file, mAccount); // TODO - //mWaitingToPreview = true; - } - return fragment; - } - - @Override - public int getCount() { - return mImageFiles.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - return mImageFiles.get(position).getFileName(); - } - } - - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(KEY_WAITING_TO_PREVIEW, mWaitingToPreview); - } - - - /** Defines callbacks for service binding, passed to bindService() */ - private class DetailsServiceConnection implements ServiceConnection { - - @Override - public void onServiceConnected(ComponentName component, IBinder service) { - - if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service connected"); - mDownloaderBinder = (FileDownloaderBinder) service; - if (mWaitingToPreview) { - requestForDownload(); - } - - } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service connected"); - mUploaderBinder = (FileUploaderBinder) service; - } else { - return; - } - - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - FileDetailFragment detailsFragment = (fragment instanceof FileDetailFragment) ? (FileDetailFragment) fragment : null; - if (detailsFragment != null) { - detailsFragment.listenForTransferProgress(); - detailsFragment.updateFileDetails(mWaitingToPreview); // let the fragment gets the mDownloadBinder through getDownloadBinder() (see FileDetailFragment#updateFileDetais()) - } - } - - @Override - public void onServiceDisconnected(ComponentName component) { - if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) { - Log.d(TAG, "Download service disconnected"); - mDownloaderBinder = null; - } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) { - Log.d(TAG, "Upload service disconnected"); - mUploaderBinder = null; - } - } - }; - - - @Override - public void onDestroy() { - super.onDestroy(); - if (mDownloadConnection != null) { - unbindService(mDownloadConnection); - mDownloadConnection = null; - } - if (mUploadConnection != null) { - unbindService(mUploadConnection); - mUploadConnection = null; - } - } - - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean returnValue = false; - - switch(item.getItemId()){ - case android.R.id.home: - backToDisplayActivity(); - returnValue = true; - break; - default: - returnValue = super.onOptionsItemSelected(item); - } - - return returnValue; - } - - - @Override - protected void onResume() { - super.onResume(); - Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileDetailFragment.FTAG); - if (fragment != null && fragment instanceof FileDetailFragment) { - ((FileDetailFragment) fragment).updateFileDetails(false); - } - } - - - private void backToDisplayActivity() { - /* - Intent intent = new Intent(this, FileDisplayActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(FileDetailFragment.EXTRA_FILE, mFile); - intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount); - startActivity(intent); - */ - finish(); - } - - - @Override - protected Dialog onCreateDialog(int id) { - Dialog dialog = null; - switch (id) { - case DIALOG_SHORT_WAIT: { - ProgressDialog working_dialog = new ProgressDialog(this); - working_dialog.setMessage(getResources().getString( - R.string.wait_a_moment)); - working_dialog.setIndeterminate(true); - working_dialog.setCancelable(false); - dialog = working_dialog; - break; - } - default: - dialog = null; - } - return dialog; - } - - - /** - * {@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 showFragmentWithDetails(OCFile file) { - Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); - showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); - showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS); - startActivity(showDetailsIntent); - } - - - private void requestForDownload() { - if (!mDownloaderBinder.isDownloading(mAccount, mFile)) { - Intent i = new Intent(this, FileDownloader.class); - i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); - i.putExtra(FileDownloader.EXTRA_FILE, mFile); - startService(i); - } - } - - @Override - public void notifySuccessfulDownload(OCFile file, Intent intent, boolean success) { - if (success) { - if (mWaitingToPreview) { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragment, new FilePreviewFragment(file, mAccount), FileDetailFragment.FTAG); - transaction.commit(); - mWaitingToPreview = false; - } - } - } - - - /** - * 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 - public void onPageSelected(int position) { - OCFile currentFile = ((FileFragment)mPreviewImagePagerAdapter.getItem(position)).getFile(); - getSupportActionBar().setTitle(currentFile.getFileName()); - } - - /** - * Called when the scroll state changes. Useful for discovering when the user begins dragging, - * when the pager is automatically settling to the current page, or when it is fully stopped/idle. - * - * @param State The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING - */ - @Override - 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. - * 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. - */ - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - -} diff --git a/src/com/owncloud/android/ui/fragment/FileDownloadFragment.java b/src/com/owncloud/android/ui/fragment/FileDownloadFragment.java index c8318fda..6734c043 100644 --- a/src/com/owncloud/android/ui/fragment/FileDownloadFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDownloadFragment.java @@ -65,6 +65,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis private DownloadFinishReceiver mDownloadFinishReceiver; public ProgressListener mProgressListener; + private boolean mListening; private static final String TAG = FileDownloadFragment.class.getSimpleName(); @@ -79,6 +80,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis mAccount = null; mStorageManager = null; mProgressListener = null; + mListening = false; } @@ -95,6 +97,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis mAccount = ocAccount; mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment mProgressListener = null; + mListening = false; } @@ -123,7 +126,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis view = inflater.inflate(R.layout.file_download_fragment, container, false); mView = view; - ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.fdProgressBar); + ProgressBar progressBar = (ProgressBar)mView.findViewById(R.id.progressBar); mProgressListener = new ProgressListener(progressBar); return view; @@ -136,6 +139,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis @Override public void onAttach(Activity activity) { super.onAttach(activity); + Log.e(TAG, "PREVIEW_DOWNLOAD_FRAGMENT ONATTACH " + ((mFile == null)?" (NULL)":mFile.getFileName())); try { mContainerActivity = (ContainerActivity) activity; @@ -151,6 +155,7 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + Log.e(TAG, "PREVIEW_DOWNLOAD_FRAGMENT ONACTIVITYCREATED " + ((mFile == null)?" (NULL)":mFile.getFileName())); if (mAccount != null) { mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());; } @@ -209,6 +214,9 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis @Override public View getView() { + if (!mListening) { + listenForTransferProgress(); + } return super.getView() == null ? mView : super.getView(); } @@ -244,11 +252,14 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis /** * Updates the view depending upon the state of the downloading file. + * + * @param transferring When true, the view must be updated assuming that the holded file is + * downloading, no matter what the downloaderBinder says. */ - public void updateView() { + public void updateView(boolean transferring) { // configure UI for depending upon local state of the file FileDownloaderBinder downloaderBinder = mContainerActivity.getFileDownloaderBinder(); - if (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) { + if (transferring || (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile))) { setButtonsForTransferring(); } else if (mFile.isDown()) { @@ -270,9 +281,9 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis downloadButton.setText(R.string.common_cancel); // show the progress bar for the transfer - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); + ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.progressBar); progressBar.setVisibility(View.VISIBLE); - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); + TextView progressText = (TextView)getView().findViewById(R.id.progressText); progressText.setText(R.string.downloader_download_in_progress_ticker); } @@ -285,11 +296,11 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis downloadButton.setVisibility(View.GONE); // hides the progress bar - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); + ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.progressBar); progressBar.setVisibility(View.GONE); // updates the text message - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); + TextView progressText = (TextView)getView().findViewById(R.id.progressText); progressText.setText(R.string.common_loading); } @@ -303,11 +314,11 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis //downloadButton.setText(R.string.filedetails_download); // hides the progress bar - ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.fdProgressBar); + ProgressBar progressBar = (ProgressBar)getView().findViewById(R.id.progressBar); progressBar.setVisibility(View.GONE); // updates the text message - TextView progressText = (TextView)getView().findViewById(R.id.fdProgressText); + TextView progressText = (TextView)getView().findViewById(R.id.progressText); progressText.setText(R.string.downloader_not_downloaded_yet); } @@ -326,9 +337,9 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis if (downloadWasFine) { mFile = mStorageManager.getFileByPath(downloadedRemotePath); } - mContainerActivity.notifySuccessfulDownload(mFile, intent, downloadWasFine); + updateView(false); getActivity().removeStickyBroadcast(intent); - updateView(); + mContainerActivity.notifySuccessfulDownload(mFile, intent, downloadWasFine); } } } @@ -336,12 +347,11 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis public void listenForTransferProgress() { - if (mProgressListener != null) { + if (mProgressListener != null && !mListening) { if (mContainerActivity.getFileDownloaderBinder() != null) { mContainerActivity.getFileDownloaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); - } - if (mContainerActivity.getFileUploaderBinder() != null) { - mContainerActivity.getFileUploaderBinder().addDatatransferProgressListener(mProgressListener, mAccount, mFile); + mListening = true; + setButtonsForTransferring(); } } } @@ -352,9 +362,6 @@ public class FileDownloadFragment extends SherlockFragment implements OnClickLis if (mContainerActivity.getFileDownloaderBinder() != null) { mContainerActivity.getFileDownloaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); } - if (mContainerActivity.getFileUploaderBinder() != null) { - mContainerActivity.getFileUploaderBinder().removeDatatransferProgressListener(mProgressListener, mAccount, mFile); - } } } diff --git a/src/com/owncloud/android/ui/fragment/PreviewImageFragment.java b/src/com/owncloud/android/ui/fragment/PreviewImageFragment.java deleted file mode 100644 index d6ba33bb..00000000 --- a/src/com/owncloud/android/ui/fragment/PreviewImageFragment.java +++ /dev/null @@ -1,600 +0,0 @@ -/* ownCloud Android client application - * Copyright (C) 2012-2013 ownCloud Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package com.owncloud.android.ui.fragment; - -import java.io.File; -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.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.util.Log; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.MimeTypeMap; -import android.widget.ImageView; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragment; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuInflater; -import com.actionbarsherlock.view.MenuItem; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.network.OwnCloudClientUtils; -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.activity.PreviewImageActivity; - -import com.owncloud.android.R; -import eu.alefzero.webdav.WebdavClient; -import eu.alefzero.webdav.WebdavUtils; - - -/** - * This fragment shows a preview of a downloaded image. - * - * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}. - * - * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too. - * - * @author David A. Velasco - */ -public class PreviewImageFragment extends SherlockFragment implements FileFragment, - OnRemoteOperationListener, - ConfirmationDialogFragment.ConfirmationDialogFragmentListener{ - public static final String EXTRA_FILE = "FILE"; - public static final String EXTRA_ACCOUNT = "ACCOUNT"; - - private View mView; - private OCFile mFile; - private Account mAccount; - private FileDataStorageManager mStorageManager; - private ImageView mImageView; - public Bitmap mBitmap = null; - - private Handler mHandler; - private RemoteOperation mLastRemoteOperation; - - private static final String TAG = PreviewImageFragment.class.getSimpleName(); - - - /** - * Creates a fragment to preview an image. - * - * When 'imageFile' or 'ocAccount' are null - * - * @param imageFile An {@link OCFile} to preview as an image in the fragment - * @param ocAccount An ownCloud account; needed to start downloads - */ - public PreviewImageFragment(OCFile fileToDetail, Account ocAccount) { - mFile = fileToDetail; - mAccount = ocAccount; - mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment - } - - - /** - * 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 - */ - public PreviewImageFragment() { - mFile = null; - mAccount = null; - mStorageManager = null; - } - - - /** - * {@inheritDoc} - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONCREATE " + ((mFile == null)? "(NULL)" : mFile.getFileName())); - mHandler = new Handler(); - setHasOptionsMenu(true); - } - - - /** - * {@inheritDoc} - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - mView = inflater.inflate(R.layout.preview_image_fragment, container, false); - mImageView = (ImageView)mView.findViewById(R.id.image); - 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) { - mFile = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE); - mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT); - - } - if (mFile == null) { - throw new IllegalStateException("Instanced with a NULL OCFile"); - } - if (mAccount == null) { - throw new IllegalStateException("Instanced with a NULL ownCloud Account"); - } - if (!mFile.isDown()) { - throw new IllegalStateException("There is no local file to preview"); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putParcelable(PreviewImageFragment.EXTRA_FILE, mFile); - outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount); - } - - - @Override - public void onStart() { - super.onStart(); - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONSTART " + mFile.getFileName()); - if (mFile != null) { - BitmapLoader bl = new BitmapLoader(mImageView); - bl.execute(new String[]{mFile.getStoragePath()}); - } - } - - - /** - * {@inheritDoc} - */ - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - - inflater.inflate(R.menu.file_actions_menu, menu); - List toHide = new ArrayList(); - - 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); - } - } - - } - - - /** - * {@inheritDoc} - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_open_file_with: { - openFile(); - return true; - } - case R.id.action_remove_file: { - removeFile(); - return true; - } - case R.id.action_see_details: { - seeDetails(); - return true; - } - - default: - return false; - } - } - - - private void seeDetails() { - ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mFile); - } - - - @Override - public void onResume() { - super.onResume(); - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONRESUME " + mFile.getFileName()); - /* - mDownloadFinishReceiver = new DownloadFinishReceiver(); - IntentFilter filter = new IntentFilter( - FileDownloader.DOWNLOAD_FINISH_MESSAGE); - getActivity().registerReceiver(mDownloadFinishReceiver, filter); - - mUploadFinishReceiver = new UploadFinishReceiver(); - filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE); - getActivity().registerReceiver(mUploadFinishReceiver, filter); - */ - - } - - - @Override - public void onPause() { - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONPAUSE " + mFile.getFileName()); - super.onPause(); - /* - if (mVideoPreview.getVisibility() == View.VISIBLE) { - mSavedPlaybackPosition = mVideoPreview.getCurrentPosition(); - }*/ - /* - getActivity().unregisterReceiver(mDownloadFinishReceiver); - mDownloadFinishReceiver = null; - - getActivity().unregisterReceiver(mUploadFinishReceiver); - mUploadFinishReceiver = null; - */ - } - - - @Override - public void onStop() { - super.onStop(); - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONSTOP " + mFile.getFileName()); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONDESTROY " + mFile.getFileName()); - if (mBitmap != null) { - mBitmap.recycle(); - } - } - - - /** - * 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() { - String storagePath = mFile.getStoragePath(); - String encodedStoragePath = WebdavUtils.encodePath(storagePath); - try { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); - i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivity(i); - - } catch (Throwable t) { - Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.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(mFile.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.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); - - } catch (ActivityNotFoundException e) { - Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); - - } catch (Throwable th) { - Log.e(TAG, "Unexpected problem when opening: " + storagePath, th); - - } finally { - if (toastIt) { - Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); - } - } - - } - 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[]{mFile.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(mFile.getFileId()) != null) { // check that the file is still there; - mLastRemoteOperation = new RemoveFileOperation( mFile, // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters - true, - mStorageManager); - WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); - mLastRemoteOperation.execute(wc, this, mHandler); - - getActivity().showDialog(PreviewImageActivity.DIALOG_SHORT_WAIT); - } - } - - - /** - * Removes the file from local storage - */ - @Override - public void onNeutral(String callerTag) { - // TODO this code should be made in a secondary thread, - if (mFile.isDown()) { // checks it is still there - File f = new File(mFile.getStoragePath()); - f.delete(); - mFile.setStoragePath(null); - mStorageManager.saveFile(mFile); - finish(); - } - } - - /** - * User cancelled the removal action. - */ - @Override - public void onCancel(String callerTag) { - // nothing to do here - } - - - /** - * {@inheritDoc} - */ - public OCFile getFile(){ - return mFile; - } - - /* - /** - * Use this method to signal this Activity that it shall update its view. - * - * @param file : An {@link OCFile} - *-/ - public void updateFileDetails(OCFile file, Account ocAccount) { - mFile = file; - if (ocAccount != null && ( - mStorageManager == null || - (mAccount != null && !mAccount.equals(ocAccount)) - )) { - mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver()); - } - mAccount = ocAccount; - updateFileDetails(false); - } - */ - - - private class BitmapLoader extends AsyncTask { - - /** - * Weak reference to the target {@link ImageView} where the bitmap will be loaded into. - * - * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes. - */ - private final WeakReference mImageViewRef; - - - /** - * Constructor. - * - * @param imageView Target {@link ImageView} where the bitmap will be loaded into. - */ - public BitmapLoader(ImageView imageView) { - mImageViewRef = new WeakReference(imageView); - } - - - @SuppressWarnings("deprecation") - @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20 - @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; - if (width >= 2048 || height >= 2048) { - // try to scale down the image to save memory - scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.))); - options.inSampleSize = scale; - } - Display display = getActivity().getWindowManager().getDefaultDisplay(); - Point size = new Point(); - int screenwidth; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { - display.getSize(size); - screenwidth = size.x; - } else { - screenwidth = display.getWidth(); - } - - Log.d(TAG, "image width: " + width + ", screen width: " + screenwidth); - - if (width > screenwidth) { - // second try to scale down the image , this time depending upon the screen size; WTF... - scale = (int) Math.ceil((float)width / screenwidth); - options.inSampleSize = scale; - } - - // really load the bitmap - options.inJustDecodeBounds = false; // the next decodeFile call will be real - result = BitmapFactory.decodeFile(storagePath, options); - Log.e(TAG, "loaded width: " + options.outWidth + ", loaded height: " + options.outHeight); - - } catch (OutOfMemoryError e) { - result = null; - Log.e(TAG, "Out of memory occured for file with size " + storagePath); - - } catch (NoSuchFieldError e) { - result = null; - Log.e(TAG, "Error from access to unexisting field despite protection " + storagePath); - - } catch (Throwable t) { - result = null; - Log.e(TAG, "Unexpected error while creating image preview " + storagePath, t); - } - return result; - } - - @Override - protected void onPostExecute(Bitmap result) { - if (result != null && mImageViewRef != null) { - final ImageView imageView = mImageViewRef.get(); - imageView.setImageBitmap(result); - mBitmap = result; - } - } - - } - - /** - * 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. - */ - public static boolean canBePreviewed(OCFile file) { - return (file != null && file.isImage()); - } - - /** - * {@inheritDoc} - */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) { - onRemoveFileOperationFinish((RemoveFileOperation)operation, result); - } - } - - private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { - getActivity().dismissDialog(PreviewImageActivity.DIALOG_SHORT_WAIT); - - 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 - } - } - } - - /** - * Finishes the preview - */ - private void finish() { - Activity container = getActivity(); - container.finish(); - } - - -} diff --git a/src/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java new file mode 100644 index 00000000..1e9d108a --- /dev/null +++ b/src/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -0,0 +1,340 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.ui.preview; + +import android.accounts.Account; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.view.ViewPager; +import android.util.Log; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; +import com.owncloud.android.datamodel.DataStorageManager; +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.FileDownloader.FileDownloaderBinder; +import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; +import com.owncloud.android.ui.activity.FileDetailActivity; +import com.owncloud.android.ui.fragment.FileDetailFragment; +import com.owncloud.android.ui.fragment.FileFragment; + +import com.owncloud.android.AccountUtils; +import com.owncloud.android.R; + +/** + * Used as an utility to preview image files contained in an ownCloud account. + * + * @author David A. Velasco + */ +public class PreviewImageActivity extends SherlockFragmentActivity implements FileFragment.ContainerActivity, ViewPager.OnPageChangeListener { + + 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 OCFile mFile; + private OCFile mParentFolder; + private Account mAccount; + private DataStorageManager mStorageManager; + + private ViewPager mViewPager; + private PreviewImagePagerAdapter mPreviewImagePagerAdapter; + + private FileDownloaderBinder mDownloaderBinder = null; + private ServiceConnection mDownloadConnection, mUploadConnection = null; + private FileUploaderBinder mUploaderBinder = null; + private OCFile mWaitingToPreview = null; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mFile = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_FILE); + mAccount = getIntent().getParcelableExtra(FileDetailFragment.EXTRA_ACCOUNT); + if (mFile == null) { + throw new IllegalStateException("Instanced with a NULL OCFile"); + } + if (mAccount == null) { + throw new IllegalStateException("Instanced with a NULL ownCloud Account"); + } + if (!mFile.isImage()) { + throw new IllegalArgumentException("Non-image file passed as argument"); + } + + setContentView(R.layout.preview_image_activity); + + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle(mFile.getFileName()); + + mStorageManager = new FileDataStorageManager(mAccount, getContentResolver()); + mParentFolder = mStorageManager.getFileById(mFile.getParentId()); + if (mParentFolder == null) { + // should not be necessary + mParentFolder = mStorageManager.getFileByPath(OCFile.PATH_SEPARATOR); + } + + createViewPager(); + + if (savedInstanceState == null) { + mWaitingToPreview = (mFile.isDown())?null:mFile; + } else { + mWaitingToPreview = (OCFile) savedInstanceState.getParcelable(KEY_WAITING_TO_PREVIEW); + } + + 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); + + } + + private void createViewPager() { + mPreviewImagePagerAdapter = new PreviewImagePagerAdapter(getSupportFragmentManager(), mParentFolder, mAccount, mStorageManager); + mViewPager = (ViewPager) findViewById(R.id.fragmentPager); + mViewPager.setAdapter(mPreviewImagePagerAdapter); + mViewPager.setOnPageChangeListener(this); + } + + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable(KEY_WAITING_TO_PREVIEW, mWaitingToPreview); + } + + + /** Defines callbacks for service binding, passed to bindService() */ + private class PreviewImageServiceConnection implements ServiceConnection { + + @Override + public void onServiceConnected(ComponentName component, IBinder service) { + + if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) { + Log.d(TAG, "Download service connected"); + mDownloaderBinder = (FileDownloaderBinder) service; + + } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) { + Log.d(TAG, "Upload service connected"); + mUploaderBinder = (FileUploaderBinder) service; + } else { + return; + } + + } + + @Override + public void onServiceDisconnected(ComponentName component) { + if (component.equals(new ComponentName(PreviewImageActivity.this, FileDownloader.class))) { + Log.d(TAG, "Download service suddenly disconnected"); + mDownloaderBinder = null; + } else if (component.equals(new ComponentName(PreviewImageActivity.this, FileUploader.class))) { + Log.d(TAG, "Upload service suddenly disconnected"); + mUploaderBinder = null; + } + } + }; + + + @Override + public void onDestroy() { + super.onDestroy(); + if (mDownloadConnection != null) { + unbindService(mDownloadConnection); + mDownloadConnection = null; + } + if (mUploadConnection != null) { + unbindService(mUploadConnection); + mUploadConnection = null; + } + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean returnValue = false; + + switch(item.getItemId()){ + case android.R.id.home: + backToDisplayActivity(); + returnValue = true; + break; + default: + returnValue = super.onOptionsItemSelected(item); + } + + return returnValue; + } + + + @Override + protected void onResume() { + super.onResume(); + } + + + private void backToDisplayActivity() { + /* + Intent intent = new Intent(this, FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(FileDetailFragment.EXTRA_FILE, mFile); + intent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, mAccount); + startActivity(intent); + */ + finish(); + } + + + @Override + protected Dialog onCreateDialog(int id) { + Dialog dialog = null; + switch (id) { + case DIALOG_SHORT_WAIT: { + ProgressDialog working_dialog = new ProgressDialog(this); + working_dialog.setMessage(getResources().getString( + R.string.wait_a_moment)); + working_dialog.setIndeterminate(true); + working_dialog.setCancelable(false); + dialog = working_dialog; + break; + } + default: + dialog = null; + } + return dialog; + } + + + /** + * {@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 showFragmentWithDetails(OCFile file) { + Intent showDetailsIntent = new Intent(this, FileDetailActivity.class); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_FILE, file); + showDetailsIntent.putExtra(FileDetailFragment.EXTRA_ACCOUNT, AccountUtils.getCurrentOwnCloudAccount(this)); + showDetailsIntent.putExtra(FileDetailActivity.EXTRA_MODE, FileDetailActivity.MODE_DETAILS); + startActivity(showDetailsIntent); + } + + + private void requestForDownload() { + Log.e(TAG, "REQUEST FOR DOWNLOAD : " + mWaitingToPreview.getFileName()); + if (!mDownloaderBinder.isDownloading(mAccount, mWaitingToPreview)) { + Intent i = new Intent(this, FileDownloader.class); + i.putExtra(FileDownloader.EXTRA_ACCOUNT, mAccount); + i.putExtra(FileDownloader.EXTRA_FILE, mWaitingToPreview); + startService(i); + } + } + + @Override + public void notifySuccessfulDownload(OCFile file, Intent intent, boolean success) { + if (success) { + if (mWaitingToPreview != null && mWaitingToPreview.equals(file)) { + mWaitingToPreview = null; + int position = mViewPager.getCurrentItem(); + mPreviewImagePagerAdapter.updateFile(position, file); + Log.e(TAG, "BEFORE NOTIFY DATA SET CHANGED"); + mPreviewImagePagerAdapter.notifyDataSetChanged(); + Log.e(TAG, "AFTER NOTIFY DATA SET CHANGED"); + //Log.e(TAG, "BEFORE INVALIDATE"); + //mViewPager.postInvalidate(); + //Log.e(TAG, "AFTER INVALIDATE"); + } + } + } + + + /** + * 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 + public void onPageSelected(int position) { + OCFile currentFile = mPreviewImagePagerAdapter.getFileAt(position); + getSupportActionBar().setTitle(currentFile.getFileName()); + if (currentFile.isDown()) { + mWaitingToPreview = null; + } else { + mWaitingToPreview = currentFile; + requestForDownload(); + mViewPager.invalidate(); + } + } + + /** + * Called when the scroll state changes. Useful for discovering when the user begins dragging, + * when the pager is automatically settling to the current page, or when it is fully stopped/idle. + * + * @param State The new scroll state (SCROLL_STATE_IDLE, _DRAGGING, _SETTLING + */ + @Override + 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. + * 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. + */ + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + +} diff --git a/src/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java new file mode 100644 index 00000000..4d6ad2d0 --- /dev/null +++ b/src/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -0,0 +1,603 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.ui.preview; + +import java.io.File; +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.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.MimeTypeMap; +import android.widget.ImageView; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragment; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; +import com.owncloud.android.datamodel.FileDataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.network.OwnCloudClientUtils; +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.ui.fragment.FileFragment; +import com.owncloud.android.ui.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; +import com.owncloud.android.ui.fragment.FileFragment.ContainerActivity; + +import com.owncloud.android.R; +import eu.alefzero.webdav.WebdavClient; +import eu.alefzero.webdav.WebdavUtils; + + +/** + * This fragment shows a preview of a downloaded image. + * + * Trying to get an instance with NULL {@link OCFile} or ownCloud {@link Account} values will produce an {@link IllegalStateException}. + * + * If the {@link OCFile} passed is not downloaded, an {@link IllegalStateException} is generated on instantiation too. + * + * @author David A. Velasco + */ +public class PreviewImageFragment extends SherlockFragment implements FileFragment, + OnRemoteOperationListener, + ConfirmationDialogFragment.ConfirmationDialogFragmentListener{ + public static final String EXTRA_FILE = "FILE"; + public static final String EXTRA_ACCOUNT = "ACCOUNT"; + + private View mView; + private OCFile mFile; + private Account mAccount; + private FileDataStorageManager mStorageManager; + private ImageView mImageView; + public Bitmap mBitmap = null; + + private Handler mHandler; + private RemoteOperation mLastRemoteOperation; + + private static final String TAG = PreviewImageFragment.class.getSimpleName(); + + + /** + * Creates a fragment to preview an image. + * + * When 'imageFile' or 'ocAccount' are null + * + * @param imageFile An {@link OCFile} to preview as an image in the fragment + * @param ocAccount An ownCloud account; needed to start downloads + */ + public PreviewImageFragment(OCFile fileToDetail, Account ocAccount) { + mFile = fileToDetail; + mAccount = ocAccount; + mStorageManager = null; // we need a context to init this; the container activity is not available yet at this moment + } + + + /** + * 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 + */ + public PreviewImageFragment() { + mFile = null; + mAccount = null; + mStorageManager = null; + } + + + /** + * {@inheritDoc} + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONCREATE " + ((mFile == null)? "(NULL)" : mFile.getFileName())); + mHandler = new Handler(); + setHasOptionsMenu(true); + } + + + /** + * {@inheritDoc} + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + mView = inflater.inflate(R.layout.preview_image_fragment, container, false); + mImageView = (ImageView)mView.findViewById(R.id.image); + 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) { + mFile = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_FILE); + mAccount = savedInstanceState.getParcelable(PreviewImageFragment.EXTRA_ACCOUNT); + + } + if (mFile == null) { + throw new IllegalStateException("Instanced with a NULL OCFile"); + } + if (mAccount == null) { + throw new IllegalStateException("Instanced with a NULL ownCloud Account"); + } + if (!mFile.isDown()) { + throw new IllegalStateException("There is no local file to preview"); + } + } + + + /** + * {@inheritDoc} + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelable(PreviewImageFragment.EXTRA_FILE, mFile); + outState.putParcelable(PreviewImageFragment.EXTRA_ACCOUNT, mAccount); + } + + + @Override + public void onStart() { + super.onStart(); + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONSTART " + mFile.getFileName()); + if (mFile != null) { + BitmapLoader bl = new BitmapLoader(mImageView); + bl.execute(new String[]{mFile.getStoragePath()}); + } + } + + + /** + * {@inheritDoc} + */ + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + + inflater.inflate(R.menu.file_actions_menu, menu); + List toHide = new ArrayList(); + + 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); + } + } + + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_open_file_with: { + openFile(); + return true; + } + case R.id.action_remove_file: { + removeFile(); + return true; + } + case R.id.action_see_details: { + seeDetails(); + return true; + } + + default: + return false; + } + } + + + private void seeDetails() { + ((FileFragment.ContainerActivity)getActivity()).showFragmentWithDetails(mFile); + } + + + @Override + public void onResume() { + super.onResume(); + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONRESUME " + mFile.getFileName()); + /* + mDownloadFinishReceiver = new DownloadFinishReceiver(); + IntentFilter filter = new IntentFilter( + FileDownloader.DOWNLOAD_FINISH_MESSAGE); + getActivity().registerReceiver(mDownloadFinishReceiver, filter); + + mUploadFinishReceiver = new UploadFinishReceiver(); + filter = new IntentFilter(FileUploader.UPLOAD_FINISH_MESSAGE); + getActivity().registerReceiver(mUploadFinishReceiver, filter); + */ + + } + + + @Override + public void onPause() { + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONPAUSE " + mFile.getFileName()); + super.onPause(); + /* + if (mVideoPreview.getVisibility() == View.VISIBLE) { + mSavedPlaybackPosition = mVideoPreview.getCurrentPosition(); + }*/ + /* + getActivity().unregisterReceiver(mDownloadFinishReceiver); + mDownloadFinishReceiver = null; + + getActivity().unregisterReceiver(mUploadFinishReceiver); + mUploadFinishReceiver = null; + */ + } + + + @Override + public void onStop() { + super.onStop(); + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONSTOP " + mFile.getFileName()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.e(TAG, "PREVIEW_IMAGE_FRAGMENT ONDESTROY " + mFile.getFileName()); + if (mBitmap != null) { + mBitmap.recycle(); + } + } + + + /** + * 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() { + String storagePath = mFile.getStoragePath(); + String encodedStoragePath = WebdavUtils.encodePath(storagePath); + try { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setDataAndType(Uri.parse("file://"+ encodedStoragePath), mFile.getMimetype()); + i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + startActivity(i); + + } catch (Throwable t) { + Log.e(TAG, "Fail when trying to open with the mimeType provided from the ownCloud server: " + mFile.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(mFile.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.e(TAG, "Trying to find out MIME type of a file without extension: " + storagePath); + + } catch (ActivityNotFoundException e) { + Log.e(TAG, "No activity found to handle: " + storagePath + " with MIME type " + mimeType + " obtained from extension"); + + } catch (Throwable th) { + Log.e(TAG, "Unexpected problem when opening: " + storagePath, th); + + } finally { + if (toastIt) { + Toast.makeText(getActivity(), "There is no application to handle file " + mFile.getFileName(), Toast.LENGTH_SHORT).show(); + } + } + + } + 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[]{mFile.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(mFile.getFileId()) != null) { // check that the file is still there; + mLastRemoteOperation = new RemoveFileOperation( mFile, // TODO we need to review the interface with RemoteOperations, and use OCFile IDs instead of OCFile objects as parameters + true, + mStorageManager); + WebdavClient wc = OwnCloudClientUtils.createOwnCloudClient(mAccount, getSherlockActivity().getApplicationContext()); + mLastRemoteOperation.execute(wc, this, mHandler); + + getActivity().showDialog(PreviewImageActivity.DIALOG_SHORT_WAIT); + } + } + + + /** + * Removes the file from local storage + */ + @Override + public void onNeutral(String callerTag) { + // TODO this code should be made in a secondary thread, + if (mFile.isDown()) { // checks it is still there + File f = new File(mFile.getStoragePath()); + f.delete(); + mFile.setStoragePath(null); + mStorageManager.saveFile(mFile); + finish(); + } + } + + /** + * User cancelled the removal action. + */ + @Override + public void onCancel(String callerTag) { + // nothing to do here + } + + + /** + * {@inheritDoc} + */ + public OCFile getFile(){ + return mFile; + } + + /* + /** + * Use this method to signal this Activity that it shall update its view. + * + * @param file : An {@link OCFile} + *-/ + public void updateFileDetails(OCFile file, Account ocAccount) { + mFile = file; + if (ocAccount != null && ( + mStorageManager == null || + (mAccount != null && !mAccount.equals(ocAccount)) + )) { + mStorageManager = new FileDataStorageManager(ocAccount, getActivity().getApplicationContext().getContentResolver()); + } + mAccount = ocAccount; + updateFileDetails(false); + } + */ + + + private class BitmapLoader extends AsyncTask { + + /** + * Weak reference to the target {@link ImageView} where the bitmap will be loaded into. + * + * Using a weak reference will avoid memory leaks if the target ImageView is retired from memory before the load finishes. + */ + private final WeakReference mImageViewRef; + + + /** + * Constructor. + * + * @param imageView Target {@link ImageView} where the bitmap will be loaded into. + */ + public BitmapLoader(ImageView imageView) { + mImageViewRef = new WeakReference(imageView); + } + + + @SuppressWarnings("deprecation") + @SuppressLint({ "NewApi", "NewApi", "NewApi" }) // to avoid Lint errors since Android SDK r20 + @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; + if (width >= 2048 || height >= 2048) { + // try to scale down the image to save memory + scale = (int) Math.ceil((Math.ceil(Math.max(height, width) / 2048.))); + options.inSampleSize = scale; + } + Display display = getActivity().getWindowManager().getDefaultDisplay(); + Point size = new Point(); + int screenwidth; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR2) { + display.getSize(size); + screenwidth = size.x; + } else { + screenwidth = display.getWidth(); + } + + Log.d(TAG, "image width: " + width + ", screen width: " + screenwidth); + + if (width > screenwidth) { + // second try to scale down the image , this time depending upon the screen size; WTF... + scale = (int) Math.ceil((float)width / screenwidth); + options.inSampleSize = scale; + } + + // really load the bitmap + options.inJustDecodeBounds = false; // the next decodeFile call will be real + result = BitmapFactory.decodeFile(storagePath, options); + Log.e(TAG, "loaded width: " + options.outWidth + ", loaded height: " + options.outHeight); + + } catch (OutOfMemoryError e) { + result = null; + Log.e(TAG, "Out of memory occured for file with size " + storagePath); + + } catch (NoSuchFieldError e) { + result = null; + Log.e(TAG, "Error from access to unexisting field despite protection " + storagePath); + + } catch (Throwable t) { + result = null; + Log.e(TAG, "Unexpected error while creating image preview " + storagePath, t); + } + return result; + } + + @Override + protected void onPostExecute(Bitmap result) { + if (result != null && mImageViewRef != null) { + final ImageView imageView = mImageViewRef.get(); + imageView.setImageBitmap(result); + mBitmap = result; + } + } + + } + + /** + * 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. + */ + public static boolean canBePreviewed(OCFile file) { + return (file != null && file.isImage()); + } + + /** + * {@inheritDoc} + */ + @Override + public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { + if (operation.equals(mLastRemoteOperation) && operation instanceof RemoveFileOperation) { + onRemoveFileOperationFinish((RemoveFileOperation)operation, result); + } + } + + private void onRemoveFileOperationFinish(RemoveFileOperation operation, RemoteOperationResult result) { + getActivity().dismissDialog(PreviewImageActivity.DIALOG_SHORT_WAIT); + + 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 + } + } + } + + /** + * Finishes the preview + */ + private void finish() { + Activity container = getActivity(); + container.finish(); + } + + +} diff --git a/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java new file mode 100644 index 00000000..22aa4953 --- /dev/null +++ b/src/com/owncloud/android/ui/preview/PreviewImagePagerAdapter.java @@ -0,0 +1,288 @@ +/* ownCloud Android client application + * Copyright (C) 2012-2013 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.owncloud.android.ui.preview; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; + +import android.accounts.Account; +import android.os.Bundle; +import android.os.Parcelable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.PagerAdapter; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.owncloud.android.datamodel.DataStorageManager; +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.ui.fragment.FileDownloadFragment; + +/** + * Adapter class that provides Fragment instances + * + * @author David A. Velasco + */ +//public class PreviewImagePagerAdapter extends PagerAdapter { +public class PreviewImagePagerAdapter extends FragmentStatePagerAdapter { + + private static final String TAG = PreviewImagePagerAdapter.class.getSimpleName(); + + private Vector mImageFiles; + private Account mAccount; + private Set mObsoleteFragments; + private DataStorageManager mStorageManager; + + /* + private final FragmentManager mFragmentManager; + private FragmentTransaction mCurTransaction = null; + private ArrayList mSavedState = new ArrayList(); + private ArrayList mFragments = new ArrayList(); + private Fragment mCurrentPrimaryItem = null; + */ + + /** + * 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. + * @param storageManager Bridge to database. + */ + public PreviewImagePagerAdapter(FragmentManager fragmentManager, OCFile parentFolder, Account account, DataStorageManager storageManager) { + 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"); + } + + mAccount = account; + mStorageManager = storageManager; + mImageFiles = mStorageManager.getDirectoryImages(parentFolder); + mObsoleteFragments = new HashSet(); + } + + + /** + * 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) { + Log.e(TAG, "GETTING PAGE " + i); + OCFile file = mImageFiles.get(i); + Fragment fragment = null; + if (file.isDown()) { + fragment = new PreviewImageFragment(file, mAccount); + } else { + fragment = new FileDownloadFragment(file, mAccount); + } + return fragment; + } + + @Override + public int getCount() { + return mImageFiles.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return mImageFiles.get(position).getFileName(); + } + + public void updateFile(int position, OCFile file) { + mImageFiles.set(position, file); + mObsoleteFragments.add(instantiateItem(null, position)); + } + + @Override + public int getItemPosition(Object object) { + Log.e(TAG, "getItemPosition "); + if (mObsoleteFragments.contains(object)) { + return POSITION_NONE; + } + return super.getItemPosition(object); + } + + /* * + * 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 + public void startUpdate(ViewGroup container) { + Log.e(TAG, "** startUpdate"); + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + Log.e(TAG, "** instantiateItem " + position); + + if (mFragments.size() > position) { + Fragment fragment = mFragments.get(position); + if (fragment != null) { + Log.e(TAG, "** \t returning cached item"); + return fragment; + } + } + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + + Fragment fragment = getItem(position); + if (mSavedState.size() > position) { + Fragment.SavedState savedState = mSavedState.get(position); + if (savedState != null) { + // 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 + fragment.setInitialSavedState(savedState); + } + } + while (mFragments.size() <= position) { + mFragments.add(null); + } + fragment.setMenuVisibility(false); + mFragments.set(position, fragment); + Log.e(TAG, "** \t adding fragment at position " + position + ", containerId " + container.getId()); + mCurTransaction.add(container.getId(), fragment); + + return fragment; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + Log.e(TAG, "** destroyItem " + position); + Fragment fragment = (Fragment)object; + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + Log.e(TAG, "** \t removing fragment at position " + position); + while (mSavedState.size() <= position) { + mSavedState.add(null); + } + mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); + mFragments.set(position, null); + + mCurTransaction.remove(fragment); + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + Fragment fragment = (Fragment)object; + if (fragment != mCurrentPrimaryItem) { + if (mCurrentPrimaryItem != null) { + mCurrentPrimaryItem.setMenuVisibility(false); + } + if (fragment != null) { + fragment.setMenuVisibility(true); + } + mCurrentPrimaryItem = fragment; + } + } + + @Override + public void finishUpdate(ViewGroup container) { + Log.e(TAG, "** finishUpdate (start)"); + if (mCurTransaction != null) { + mCurTransaction.commitAllowingStateLoss(); + mCurTransaction = null; + mFragmentManager.executePendingTransactions(); + } + Log.e(TAG, "** finishUpdate (end)"); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return ((Fragment)object).getView() == view; + } + + @Override + public Parcelable saveState() { + Bundle state = null; + if (mSavedState.size() > 0) { + state = new Bundle(); + Fragment.SavedState[] savedStates = new Fragment.SavedState[mSavedState.size()]; + mSavedState.toArray(savedStates); + state.putParcelableArray("states", savedStates); + } + for (int i=0; i keys = bundle.keySet(); + for (String key: keys) { + if (key.startsWith("f")) { + int index = Integer.parseInt(key.substring(1)); + Fragment f = mFragmentManager.getFragment(bundle, key); + if (f != null) { + while (mFragments.size() <= index) { + mFragments.add(null); + } + f.setMenuVisibility(false); + mFragments.set(index, f); + } else { + Log.w(TAG, "Bad fragment at key " + key); + } + } + } + } + } */ + +}