media streaming with NC14 endpoint

Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
tobiasKaminsky 2018-04-17 12:04:27 +02:00 committed by AndyScherzinger
parent e35124d3f6
commit b8bceee138
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
11 changed files with 361 additions and 104 deletions

View file

@ -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;
@ -346,6 +347,16 @@ public class FileMenuFilter {
}
}
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;
if (mComponentsGetter != null && !mFiles.isEmpty() && mAccount != null) {
@ -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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
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<Object> 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;
}
}

View file

@ -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<String, Void, String> {
private OwnCloudClient client;
private WeakReference<MediaService> 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);
}
}
}
}
}

View file

@ -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),

View file

@ -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);

View file

@ -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}
*

View file

@ -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<String, Void, Uri> {
private OwnCloudClient client;
private WeakReference<PreviewMediaFragment> 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) {

View file

@ -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;
@ -58,12 +56,15 @@ 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();
private int mSavedPlaybackPosition; // in the unit time handled by MediaPlayer.getCurrentPosition()
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

View file

@ -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"/>

View file

@ -64,6 +64,13 @@
app:showAsAction="never"
android:showAsAction="never" />
<item
android:id="@+id/action_stream_media"
android:title="@string/stream"
app:showAsAction="never"
android:showAsAction="never"
android:orderInCategory="1"/>
<item
android:id="@+id/action_send_share_file"
android:title="@string/action_send_share"

View file

@ -814,4 +814,7 @@
<string name="trashbin_file_not_deleted">File %1$s could not be deleted!</string>
<string name="trashbin_file_not_restored">File %1$s could not be restored!</string>
<string name="trashbin_not_emptied">Files could not be deleted permanently!</string>
<string name="stream">Stream with…</string>
<string name="stream_not_possible_headline">Internal streaming not possible</string>
<string name="stream_not_possible_message">Please download media instead or use external app.</string>
</resources>