mirror of
https://github.com/nextcloud/android.git
synced 2024-11-24 06:05:42 +03:00
media streaming with NC14 endpoint
Signed-off-by: tobiasKaminsky <tobias@kaminsky.me>
This commit is contained in:
parent
e35124d3f6
commit
b8bceee138
11 changed files with 361 additions and 104 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
*
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"/>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue