Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/com/owncloud/android/files/FileMenuFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;
Expand Down Expand Up @@ -183,6 +184,7 @@ private void filter(List<Integer> toShow, List<Integer> toHide, boolean inSingle
filterEncrypt(toShow, toHide, endToEndEncryptionEnabled);
filterUnsetEncrypted(toShow, toHide, endToEndEncryptionEnabled);
filterSetPictureAs(toShow, toHide);
filterStream(toShow, toHide);
}

private void filterShareFile(List<Integer> toShow, List<Integer> toHide, OCCapability capability) {
Expand Down Expand Up @@ -346,6 +348,15 @@ private void filterDownload(List<Integer> toShow, List<Integer> toHide, boolean
}
}

private void filterStream(List<Integer> toShow, List<Integer> toHide) {
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) {
Expand Down Expand Up @@ -430,6 +441,11 @@ private boolean isSingleImage() {
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();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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 = respJSON.getJSONObject(NODE_OCS).getJSONObject(NODE_DATA).getString(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;
}
}
102 changes: 76 additions & 26 deletions src/main/java/com/owncloud/android/media/MediaService.java
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;
Expand All @@ -32,20 +38,28 @@
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;
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;
import com.owncloud.android.ui.notifications.NotificationUtils;
import com.owncloud.android.utils.ThemeUtils;

import java.io.IOException;
import java.lang.ref.WeakReference;


/**
Expand Down Expand Up @@ -365,7 +379,6 @@ protected void releaseResources(boolean releaseMediaPlayer) {
}
}


/**
* Fully releases the audio focus.
*/
Expand Down Expand Up @@ -443,33 +456,19 @@ protected void playMedia() {

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();
}
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();
if (mFile.isDown()) {
mPlayer.setDataSource(mFile.getStoragePath());
preparePlayer();
} else {
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, getBaseContext());
OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, getBaseContext());

// prevent the Wifi from going to sleep when streaming
/*
if (mIsStreaming) {
mWifiLock.acquire();
} else
*/
if (mWifiLock.isHeld()) {
mWifiLock.release();
new LoadStreamUrl(this, client).execute(mFile.getLocalId());
}

} 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()),
Expand All @@ -478,6 +477,13 @@ protected void playMedia() {
}
}

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) {
Expand Down Expand Up @@ -702,4 +708,48 @@ protected MediaControlView getMediaController() {
return mMediaController;
}

private static class LoadStreamUrl extends AsyncTask<String, Void, String> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there no other way but an AsyncTask? I thought we got rid of this ages ago :P


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);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -725,11 +725,13 @@ protected void refreshSecondFragment(String downloadEvent, String downloadedRemo
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.getServerVersionForAccount(getAccount(), this)
.isMediaStreamingSupported();
startMediaPreview(mWaitingToPreview, 0, true, true, streaming);
detailsFragmentChanged = true;
} else if (MimeTypeUtil.isVCard(mWaitingToPreview.getMimeType())) {
startContactListFragment(mWaitingToPreview);
Expand Down Expand Up @@ -2050,7 +2052,9 @@ private void onRenameFileOperationFinish(RenameFileOperation operation,
((PreviewMediaFragment) details).updateFile(renamedFile);
if (PreviewMediaFragment.canBePreviewed(renamedFile)) {
int position = ((PreviewMediaFragment) details).getPosition();
startMediaPreview(renamedFile, position, true, true);
boolean streaming = AccountUtils.getServerVersionForAccount(getAccount(), this)
.isMediaStreamingSupported();
startMediaPreview(renamedFile, position, true, true, streaming);
} else {
getFileOperationsHelper().openFile(renamedFile);
}
Expand Down Expand Up @@ -2323,8 +2327,9 @@ public void startImagePreview(OCFile file, VirtualFolderType type, boolean showP
* @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);
Expand Down Expand Up @@ -2459,9 +2464,11 @@ public void onMessageEvent(SyncEventFinished event) {
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.getServerVersionForAccount(getAccount(), this)
.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),
Expand Down
Loading