diff --git a/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/src/main/java/com/owncloud/android/files/FileMenuFilter.java index 2b0be52c48..6a91d37efc 100644 --- a/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -27,6 +27,7 @@ import android.view.Menu; import android.view.MenuItem; import com.owncloud.android.R; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; @@ -345,6 +346,16 @@ public class FileMenuFilter { toShow.add(R.id.action_download_file); } } + + private void filterStream() { + // STREAM + if (mFiles.isEmpty() || !isSingleFile() || !isSingleMedia() || + !AccountUtils.getServerVersion(mAccount).isMediaStreamingSupported()) { + toHide.add(R.id.action_stream_media); + } else { + toShow.add(R.id.action_stream_media); + } + } private boolean anyFileSynchronizing() { boolean synchronizing = false; @@ -430,6 +441,11 @@ public class FileMenuFilter { return isSingleSelection() && MimeTypeUtil.isImage(mFiles.iterator().next()); } + private boolean isSingleMedia() { + OCFile file = mFiles.iterator().next(); + return isSingleSelection() && (MimeTypeUtil.isVideo(file) || MimeTypeUtil.isAudio(file)); + } + private boolean allFiles() { return mFiles != null && !containsFolder(); } diff --git a/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java b/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java new file mode 100644 index 0000000000..6d573ef272 --- /dev/null +++ b/src/main/java/com/owncloud/android/files/StreamMediaFileOperation.java @@ -0,0 +1,90 @@ +/* + * Nextcloud Android client application + * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.files; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.methods.PostMethod; +import org.json.JSONObject; + +import java.util.ArrayList; + +public class StreamMediaFileOperation extends RemoteOperation { + private static final String TAG = StreamMediaFileOperation.class.getSimpleName(); + private static final int SYNC_READ_TIMEOUT = 40000; + private static final int SYNC_CONNECTION_TIMEOUT = 5000; + private static final String STREAM_MEDIA_URL = "/ocs/v2.php/apps/dav/api/v1/direct"; + + private String fileID; + + // JSON node names + private static final String NODE_OCS = "ocs"; + private static final String NODE_DATA = "data"; + private static final String NODE_URL = "url"; + private static final String JSON_FORMAT = "?format=json"; + + public StreamMediaFileOperation(String fileID) { + this.fileID = fileID; + } + + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result; + PostMethod postMethod = null; + + try { + postMethod = new PostMethod(client.getBaseUri() + STREAM_MEDIA_URL + JSON_FORMAT); + postMethod.setParameter("fileId", fileID); + + // remote request + postMethod.addRequestHeader(OCS_API_HEADER, OCS_API_HEADER_VALUE); + + int status = client.executeMethod(postMethod, SYNC_READ_TIMEOUT, SYNC_CONNECTION_TIMEOUT); + + if (status == HttpStatus.SC_OK) { + String response = postMethod.getResponseBodyAsString(); + + // Parse the response + JSONObject respJSON = new JSONObject(response); + String url = (String) respJSON.getJSONObject(NODE_OCS).getJSONObject(NODE_DATA).get(NODE_URL); + + result = new RemoteOperationResult(true, postMethod); + ArrayList urlArray = new ArrayList<>(); + urlArray.add(url); + result.setData(urlArray); + } else { + result = new RemoteOperationResult(false, postMethod); + client.exhaustResponse(postMethod.getResponseBodyAsStream()); + } + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Get stream url for file with id " + fileID + " failed: " + result.getLogMessage(), + result.getException()); + } finally { + if (postMethod != null) + postMethod.releaseConnection(); + } + return result; + } +} diff --git a/src/main/java/com/owncloud/android/media/MediaService.java b/src/main/java/com/owncloud/android/media/MediaService.java index 1fdcee567c..e0bea1e6a0 100644 --- a/src/main/java/com/owncloud/android/media/MediaService.java +++ b/src/main/java/com/owncloud/android/media/MediaService.java @@ -1,9 +1,13 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco * Copyright (C) 2016 ownCloud Inc. * + * @author Tobias Kaminsky + * Copyright (C) 2018 Tobias Kaminsky + * Copyright (C) 2018 Nextcloud GmbH. + * * 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. @@ -20,6 +24,8 @@ package com.owncloud.android.media; import android.accounts.Account; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; @@ -32,6 +38,7 @@ import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; +import android.os.AsyncTask; import android.os.IBinder; import android.os.PowerManager; import android.support.v4.app.NotificationCompat; @@ -39,6 +46,12 @@ import android.widget.Toast; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.files.StreamMediaFileOperation; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; +import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; @@ -46,6 +59,7 @@ import com.owncloud.android.ui.notifications.NotificationUtils; import com.owncloud.android.utils.ThemeUtils; import java.io.IOException; +import java.lang.ref.WeakReference; /** @@ -365,7 +379,6 @@ public class MediaService extends Service implements OnCompletionListener, OnPre } } - /** * Fully releases the audio focus. */ @@ -443,33 +456,19 @@ public class MediaService extends Service implements OnCompletionListener, OnPre createMediaPlayerIfNeeded(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - String url = mFile.getStoragePath(); - /* Streaming is not possible right now - if (url == null || url.length() <= 0) { - url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath(); + if (mFile.isDown()) { + mPlayer.setDataSource(mFile.getStoragePath()); + preparePlayer(); + } else { + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, getBaseContext()); + OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, getBaseContext()); + + new LoadStreamUrl(this, client).execute(mFile.getLocalId()); } - mIsStreaming = url.startsWith("http:") || url.startsWith("https:"); - */ - //mIsStreaming = false; - mPlayer.setDataSource(url); - - mState = State.PREPARING; - setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName())); - - // starts preparing the media player in background - mPlayer.prepareAsync(); - - // prevent the Wifi from going to sleep when streaming - /* - if (mIsStreaming) { - mWifiLock.acquire(); - } else - */ - if (mWifiLock.isHeld()) { - mWifiLock.release(); - } - + } catch (AccountUtils.AccountNotFoundException | OperationCanceledException | AuthenticatorException e) { + Log_OC.e(TAG, "Loading stream url not possible: " + e.getMessage()); } catch (SecurityException | IOException | IllegalStateException | IllegalArgumentException e) { Log_OC.e(TAG, e.getClass().getSimpleName() + " playing " + mAccount.name + mFile.getRemotePath(), e); Toast.makeText(this, String.format(getString(R.string.media_err_playing), mFile.getFileName()), @@ -478,6 +477,13 @@ public class MediaService extends Service implements OnCompletionListener, OnPre } } + private void preparePlayer() { + mState = State.PREPARING; + setUpAsForeground(String.format(getString(R.string.media_state_loading), mFile.getFileName())); + + // starts preparing the media player in background + mPlayer.prepareAsync(); + } /** Called when media player is done playing current song. */ public void onCompletion(MediaPlayer player) { @@ -702,4 +708,48 @@ public class MediaService extends Service implements OnCompletionListener, OnPre return mMediaController; } + private static class LoadStreamUrl extends AsyncTask { + + private OwnCloudClient client; + private WeakReference mediaServiceWeakReference; + + public LoadStreamUrl(MediaService mediaService, OwnCloudClient client) { + this.client = client; + this.mediaServiceWeakReference = new WeakReference<>(mediaService); + } + + @Override + protected String doInBackground(String... fileId) { + StreamMediaFileOperation sfo = new StreamMediaFileOperation(fileId[0]); + RemoteOperationResult result = sfo.execute(client); + + if (!result.isSuccess()) { + return null; + } + + return (String) result.getData().get(0); + } + + @Override + protected void onPostExecute(String url) { + MediaService mediaService = mediaServiceWeakReference.get(); + + if (mediaService != null) { + if (url != null) { + try { + mediaService.mPlayer.setDataSource(url); + + // prevent the Wifi from going to sleep when streaming + mediaService.mWifiLock.acquire(); + mediaService.preparePlayer(); + } catch (IOException e) { + Log_OC.e(TAG, "Streaming not possible: " + e.getMessage()); + } + } else { + // we already show a toast with error from media player + mediaService.processStopRequest(true); + } + } + } + } } diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index fef7482823..a4532d808e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -70,6 +70,7 @@ import android.widget.ImageView; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; +import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.VirtualFolderType; @@ -725,11 +726,12 @@ public class FileDisplayActivity extends HookActivity boolean detailsFragmentChanged = false; if (waitedPreview) { if (success) { - mWaitingToPreview = getStorageManager().getFileById( - mWaitingToPreview.getFileId()); // update the file from database, - // for the local storage path + // update the file from database, for the local storage path + mWaitingToPreview = getStorageManager().getFileById(mWaitingToPreview.getFileId()); + if (PreviewMediaFragment.canBePreviewed(mWaitingToPreview)) { - startMediaPreview(mWaitingToPreview, 0, true, true); + boolean streaming = AccountUtils.getServerVersion(getAccount()).isMediaStreamingSupported(); + startMediaPreview(mWaitingToPreview, 0, true, true, streaming); detailsFragmentChanged = true; } else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimeType())) { startContactListFragment(mWaitingToPreview); @@ -2050,7 +2052,8 @@ public class FileDisplayActivity extends HookActivity ((PreviewMediaFragment) details).updateFile(renamedFile); if (PreviewMediaFragment.canBePreviewed(renamedFile)) { int position = ((PreviewMediaFragment) details).getPosition(); - startMediaPreview(renamedFile, position, true, true); + boolean streaming = AccountUtils.getServerVersion(getAccount()).isMediaStreamingSupported(); + startMediaPreview(renamedFile, position, true, true, streaming); } else { getFileOperationsHelper().openFile(renamedFile); } @@ -2323,8 +2326,9 @@ public class FileDisplayActivity extends HookActivity * @param autoplay When 'true', the playback will start without user * interactions. */ - public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay, boolean showPreview) { - if (showPreview && file.isDown() && !file.isDownloading()) { + public void startMediaPreview(OCFile file, int startPlaybackPosition, boolean autoplay, boolean showPreview, + boolean streamMedia) { + if ((showPreview && file.isDown() && !file.isDownloading()) || streamMedia) { Fragment mediaFragment = PreviewMediaFragment.newInstance(file, getAccount(), startPlaybackPosition, autoplay); setSecondFragment(mediaFragment); updateFragmentsVisibility(true); @@ -2459,9 +2463,10 @@ public class FileDisplayActivity extends HookActivity if (event.getIntent().getBooleanExtra(TEXT_PREVIEW, false)) { startTextPreview((OCFile) bundle.get(EXTRA_FILE), true); } else if (bundle.containsKey(PreviewVideoActivity.EXTRA_START_POSITION)) { + boolean streaming = AccountUtils.getServerVersion(getAccount()).isMediaStreamingSupported(); startMediaPreview((OCFile)bundle.get(EXTRA_FILE), (int)bundle.get(PreviewVideoActivity.EXTRA_START_POSITION), - (boolean)bundle.get(PreviewVideoActivity.EXTRA_AUTOPLAY), true); + (boolean) bundle.get(PreviewVideoActivity.EXTRA_AUTOPLAY), true, streaming); } else if (bundle.containsKey(PreviewImageActivity.EXTRA_VIRTUAL_TYPE)) { startImagePreview((OCFile)bundle.get(EXTRA_FILE), (VirtualFolderType)bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE), diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 3e001bd4bf..af8a566894 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -813,48 +813,51 @@ public class OCFileListFragment extends ExtendedListFragment implements saveIndexAndTopPosition(position); } - } else if (!mOnlyFoldersClickable){ // Click on a file - if (PreviewImageFragment.canBePreviewed(file)) { - // preview image - it handles the download, if needed - if (searchFragment) { - VirtualFolderType type; - switch (currentSearchType) { - case FAVORITE_SEARCH: - type = VirtualFolderType.FAVORITE; - break; - case PHOTO_SEARCH: - type = VirtualFolderType.PHOTOS; - break; - default: - type = VirtualFolderType.NONE; - break; + } else if (!mOnlyFoldersClickable) { // Click on a file + if (PreviewImageFragment.canBePreviewed(file)) { + // preview image - it handles the download, if needed + if (searchFragment) { + VirtualFolderType type; + switch (currentSearchType) { + case FAVORITE_SEARCH: + type = VirtualFolderType.FAVORITE; + break; + case PHOTO_SEARCH: + type = VirtualFolderType.PHOTOS; + break; + default: + type = VirtualFolderType.NONE; + break; + } + ((FileDisplayActivity) mContainerActivity).startImagePreview(file, type, !file.isDown()); + } else { + ((FileDisplayActivity) mContainerActivity).startImagePreview(file, !file.isDown()); + } + } else if (file.isDown() && MimeTypeUtil.isVCard(file)) { + ((FileDisplayActivity) mContainerActivity).startContactListFragment(file); + } else if (PreviewTextFragment.canBePreviewed(file)) { + ((FileDisplayActivity) mContainerActivity).startTextPreview(file, false); + } else if (file.isDown()) { + if (PreviewMediaFragment.canBePreviewed(file)) { + // media preview + ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, false); + } else { + mContainerActivity.getFileOperationsHelper().openFile(file); } - ((FileDisplayActivity) mContainerActivity).startImagePreview(file, type, !file.isDown()); } else { - ((FileDisplayActivity) mContainerActivity).startImagePreview(file, !file.isDown()); + if (PreviewMediaFragment.canBePreviewed(file) && AccountUtils.getServerVersion( + AccountUtils.getCurrentOwnCloudAccount(getContext())).isMediaStreamingSupported()) { + // stream media preview on >= NC14 + ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true); + } else { + // automatic download, preview on finish + ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file); + } } - } else if (file.isDown() && MimeTypeUtil.isVCard(file)) { - ((FileDisplayActivity) mContainerActivity).startContactListFragment(file); - } else if (PreviewTextFragment.canBePreviewed(file)) { - ((FileDisplayActivity) mContainerActivity).startTextPreview(file, false); - } else if (file.isDown()) { - if (PreviewMediaFragment.canBePreviewed(file)) { - // media preview - ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, - true, false); - } else { - mContainerActivity.getFileOperationsHelper().openFile(file); - } - - } else { - // automatic download, preview on finish - ((FileDisplayActivity) mContainerActivity).startDownloadForPreview(file); - - - } -} + } } else { - Log_OC.d(TAG, "Null object in ListAdapter!!");} + Log_OC.d(TAG, "Null object in ListAdapter!"); + } } } @@ -903,6 +906,10 @@ public class OCFileListFragment extends ExtendedListFragment implements mContainerActivity.getFileOperationsHelper().openFile(singleFile); return true; } + case R.id.action_stream_media: { + mContainerActivity.getFileOperationsHelper().streamMediaFile(singleFile); + return true; + } case R.id.action_rename_file: { RenameFileDialogFragment dialog = RenameFileDialogFragment.newInstance(singleFile); dialog.show(getFragmentManager(), FileDetailFragment.FTAG_RENAME_FILE); diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 740df96da5..afa9a02b17 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -48,6 +48,7 @@ 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.StreamMediaFileOperation; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -348,6 +349,34 @@ public class FileOperationsHelper { } } + public void streamMediaFile(OCFile file) { + mFileActivity.showLoadingDialog(mFileActivity.getString(R.string.wait_a_moment)); + + new Thread(new Runnable() { + @Override + public void run() { + Account account = AccountUtils.getCurrentOwnCloudAccount(mFileActivity); + StreamMediaFileOperation sfo = new StreamMediaFileOperation(file.getLocalId()); + RemoteOperationResult result = sfo.execute(account, mFileActivity); + + mFileActivity.dismissLoadingDialog(); + + if (!result.isSuccess()) { + DisplayUtils.showSnackMessage(mFileActivity, R.string.stream_not_possible_headline); + return; + } + + Intent openFileWithIntent = new Intent(Intent.ACTION_VIEW); + Uri uri = Uri.parse((String) result.getData().get(0)); + + openFileWithIntent.setDataAndType(uri, file.getMimetype()); + + mFileActivity.startActivity(Intent.createChooser(openFileWithIntent, + mFileActivity.getString(R.string.stream))); + } + }).start(); + } + /** * Helper method to share a file via a public link. Starts a request to do it in {@link OperationsService} * diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java index 41e497e634..33cae84fc7 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -34,6 +34,8 @@ import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; +import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.DrawableRes; @@ -58,6 +60,11 @@ import android.widget.VideoView; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.FileMenuFilter; +import com.owncloud.android.files.StreamMediaFileOperation; +import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.media.MediaControlView; import com.owncloud.android.media.MediaService; @@ -69,6 +76,8 @@ import com.owncloud.android.ui.dialog.RemoveFilesDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.utils.MimeTypeUtil; +import java.lang.ref.WeakReference; + /** * This fragment shows a preview of a downloaded media file (audio or video). @@ -108,6 +117,8 @@ public class PreviewMediaFragment extends FileFragment implements private static boolean mOnResume = false; public boolean mPrepared; + private Uri mVideoUri; + private static final String TAG = PreviewMediaFragment.class.getSimpleName(); private static final String FILE = "FILE"; @@ -221,6 +232,7 @@ public class PreviewMediaFragment extends FileFragment implements mMultiListMessage.setText(message); mMultiListIcon.setImageResource(icon); + mMultiListMessage.setVisibility(View.VISIBLE); mMultiListIcon.setVisibility(View.VISIBLE); mMultiListProgress.setVisibility(View.GONE); } @@ -244,19 +256,15 @@ public class PreviewMediaFragment extends FileFragment implements if (mAccount == null) { throw new IllegalStateException("Instanced with a NULL ownCloud Account"); } - if (!file.isDown()) { - throw new IllegalStateException("There is no local file to preview"); - } } else { file = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_FILE); setFile(file); mAccount = savedInstanceState.getParcelable(PreviewMediaFragment.EXTRA_ACCOUNT); mSavedPlaybackPosition = savedInstanceState.getInt(PreviewMediaFragment.EXTRA_PLAY_POSITION); mAutoplay = savedInstanceState.getBoolean(PreviewMediaFragment.EXTRA_PLAYING); - } - if (file != null && file.isDown()) { + if (file != null) { if (MimeTypeUtil.isVideo(file)) { mVideoPreview.setVisibility(View.VISIBLE); mImagePreview.setVisibility(View.GONE); @@ -327,16 +335,12 @@ public class PreviewMediaFragment extends FileFragment implements Log_OC.v(TAG, "onStart"); OCFile file = getFile(); - if (file != null && file.isDown()) { + if (file != null) { if (MimeTypeUtil.isAudio(file)) { bindMediaService(); - - } - else { - if (MimeTypeUtil.isVideo(file)) { - stopAudio(); - playVideo(); - } + } else if (MimeTypeUtil.isVideo(file)) { + stopAudio(); + playVideo(); } } } @@ -492,16 +496,68 @@ public class PreviewMediaFragment extends FileFragment implements mVideoPreview.setOnErrorListener(videoHelper); } - @SuppressWarnings("static-access") private void playVideo() { // create and prepare control panel for the user mMediaController.setMediaPlayer(mVideoPreview); - // load the video file in the video player ; + // load the video file in the video player // when done, VideoHelper#onPrepared() will be called - mVideoPreview.setVideoURI(getFile().getStorageUri()); + if (getFile().isDown()) { + mVideoPreview.setVideoURI(getFile().getStorageUri()); + } else { + try { + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, getContext()); + OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, getContext()); + + new LoadStreamUrl(this, client).execute(getFile().getLocalId()); + } catch (Exception e) { + Log_OC.e(TAG, "Loading stream url not possible: " + e.getMessage()); + } + } } + private static class LoadStreamUrl extends AsyncTask { + + private OwnCloudClient client; + private WeakReference previewMediaFragmentWeakReference; + + public LoadStreamUrl(PreviewMediaFragment previewMediaFragment, OwnCloudClient client) { + this.client = client; + this.previewMediaFragmentWeakReference = new WeakReference<>(previewMediaFragment); + } + + @Override + protected Uri doInBackground(String... fileId) { + StreamMediaFileOperation sfo = new StreamMediaFileOperation(fileId[0]); + RemoteOperationResult result = sfo.execute(client); + + if (!result.isSuccess()) { + return null; + } + + return Uri.parse((String) result.getData().get(0)); + } + + @Override + protected void onPostExecute(Uri uri) { + PreviewMediaFragment previewMediaFragment = previewMediaFragmentWeakReference.get(); + + if (previewMediaFragment != null) { + if (uri != null) { + previewMediaFragment.mVideoUri = uri; + previewMediaFragment.mVideoPreview.setVideoURI(uri); + } else { + previewMediaFragment.mMultiView.setVisibility(View.VISIBLE); + previewMediaFragment.setMessageForMultiList( + previewMediaFragment.getString(R.string.stream_not_possible_headline), + R.string.stream_not_possible_message, R.drawable.file_movie); + } + } else { + Log_OC.e(TAG, "Error streaming file: no previewMediaFragment!"); + } + } + } private class VideoHelper implements OnCompletionListener, OnPreparedListener, OnErrorListener { @@ -624,6 +680,7 @@ public class PreviewMediaFragment extends FileFragment implements i.putExtra(FileActivity.EXTRA_ACCOUNT, mAccount); i.putExtra(FileActivity.EXTRA_FILE, getFile()); i.putExtra(PreviewVideoActivity.EXTRA_AUTOPLAY, mVideoPreview.isPlaying()); + i.putExtra(PreviewVideoActivity.EXTRA_STREAM_URL, mVideoUri); mVideoPreview.pause(); i.putExtra(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPreview.getCurrentPosition()); startActivityForResult(i, FileActivity.REQUEST_CODE__LAST_SHARED + 1); @@ -652,7 +709,6 @@ public class PreviewMediaFragment extends FileFragment implements if (!mMediaServiceBinder.isPlaying(file) && !mOnResume) { Log_OC.d(TAG, "starting playback of " + file.getStoragePath()); mMediaServiceBinder.start(mAccount, file, mAutoplay, mSavedPlaybackPosition); - } else { if (!mMediaServiceBinder.isPlaying() && mAutoplay) { diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.java b/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.java index ec2adee05d..31962310ef 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewVideoActivity.java @@ -35,8 +35,6 @@ import android.widget.VideoView; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.media.MediaService; import com.owncloud.android.ui.activity.FileActivity; @@ -57,6 +55,8 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi /** Key to receive the position of the playback where the video should be put at start */ public static final String EXTRA_START_POSITION = "START_POSITION"; + + public static final String EXTRA_STREAM_URL = "STREAM_URL"; private static final String TAG = PreviewVideoActivity.class.getSimpleName(); @@ -64,6 +64,7 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi private boolean mAutoplay; // when 'true', the playback starts immediately with the activity private VideoView mVideoPlayer; // view to play the file; both performs and show the playback private MediaController mMediaController; // panel control used by the user to control the playback + private Uri mStreamUri; /** * Called when the activity is first created. @@ -86,15 +87,16 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi Bundle extras = getIntent().getExtras(); mSavedPlaybackPosition = extras.getInt(EXTRA_START_POSITION); mAutoplay = extras.getBoolean(EXTRA_AUTOPLAY); - + mStreamUri = (Uri) extras.get(EXTRA_STREAM_URL); } else { mSavedPlaybackPosition = savedInstanceState.getInt(EXTRA_START_POSITION); mAutoplay = savedInstanceState.getBoolean(EXTRA_AUTOPLAY); + mStreamUri = (Uri) savedInstanceState.get(EXTRA_STREAM_URL); } mVideoPlayer = (VideoView) findViewById(R.id.videoPlayer); - // set listeners to get more contol on the playback + // set listeners to get more control on the playback mVideoPlayer.setOnPreparedListener(this); mVideoPlayer.setOnCompletionListener(this); mVideoPlayer.setOnErrorListener(this); @@ -115,6 +117,7 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi super.onSaveInstanceState(outState); outState.putInt(PreviewVideoActivity.EXTRA_START_POSITION, mVideoPlayer.getCurrentPosition()); outState.putBoolean(PreviewVideoActivity.EXTRA_AUTOPLAY , mVideoPlayer.isPlaying()); + outState.putParcelable(PreviewVideoActivity.EXTRA_STREAM_URL, mStreamUri); } @@ -207,16 +210,8 @@ public class PreviewVideoActivity extends FileActivity implements OnCompletionLi if (file != null) { if (file.isDown()) { mVideoPlayer.setVideoURI(file.getStorageUri()); - } else { - // not working yet - String url; - try { - url = AccountUtils.constructFullURLForAccount(this, getAccount()) + file.getRemotePath(); - mVideoPlayer.setVideoURI(Uri.parse(url)); - } catch (AccountNotFoundException e) { - onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_no_account); - } + mVideoPlayer.setVideoURI(mStreamUri); } // create and prepare control panel for the user diff --git a/src/main/res/layout/empty_list.xml b/src/main/res/layout/empty_list.xml index 103af88ca7..2951f2ae12 100644 --- a/src/main/res/layout/empty_list.xml +++ b/src/main/res/layout/empty_list.xml @@ -62,7 +62,6 @@ android:layout_gravity="center_horizontal" android:ellipsize="end" android:gravity="center" - android:maxLines="3" android:text="@string/file_list_empty" android:visibility="gone"/> diff --git a/src/main/res/menu/file_actions_menu.xml b/src/main/res/menu/file_actions_menu.xml index f8a980d68f..d3e82fa4ea 100644 --- a/src/main/res/menu/file_actions_menu.xml +++ b/src/main/res/menu/file_actions_menu.xml @@ -64,6 +64,13 @@ app:showAsAction="never" android:showAsAction="never" /> + + File %1$s could not be deleted! File %1$s could not be restored! Files could not be deleted permanently! + Stream with… + Internal streaming not possible + Please download media instead or use external app.