mirror of
https://github.com/videolan/vlc-android
synced 2024-11-23 18:05:36 +08:00
Stubbed Media and LibVLC classes
Used Abstract Factory Manager design pattern for LibVLC components Tests done: StreamsModel, SubtitlesModel, HistoryModel, FilePickerModel, BrowserModel, StorageModel, FileBrowserModel, NetworkModel, VideosViewModel Used CoroutineContextProvider to replace context on-demand. Added extension function for the child of SingletonHolder used in ExternalSubRepository. Replaced OpenSubtitleRepository.getInstance to use lazy value, so it can be replaced in tests. Added Dependency Provider for BrowserProvider Updated StubDataSource to configure data set to provide LibVLC: Refactored interfaces Signed-off-by: Shivansh Saini <shivanshs9@gmail.com>
This commit is contained in:
parent
3374f11ae6
commit
a2896d2b4b
@ -58,6 +58,7 @@ ext {
|
||||
espressoVersion = '3.1.1'
|
||||
livedataTest = '1.1.0'
|
||||
robolectric = '4.2.1'
|
||||
mockk = '1.9.3'
|
||||
supportTest = '1.1.0'
|
||||
// versionCode scheme is T M NN RR AA
|
||||
// T: Target/Flavour (1 for Android, 2 for Chrome?)
|
||||
|
@ -31,6 +31,8 @@ import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
|
||||
import org.videolan.libvlc.interfaces.IVLCVout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
|
@ -24,11 +24,13 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.annotation.MainThread;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public abstract class Dialog {
|
||||
|
||||
/**
|
||||
* Dialog Callback, see {@link Dialog#setCallbacks(LibVLC, Callbacks)}
|
||||
* Dialog Callback, see {@link Dialog#setCallbacks(ILibVLC, Callbacks)}
|
||||
*/
|
||||
public interface Callbacks {
|
||||
/**
|
||||
@ -164,15 +166,15 @@ public abstract class Dialog {
|
||||
/**
|
||||
* Register callbacks in order to handle VLC dialogs
|
||||
*
|
||||
* @param libVLC valid LibVLC object
|
||||
* @param ILibVLC valid LibVLC object
|
||||
* @param callbacks dialog callbacks or null to unregister
|
||||
*/
|
||||
@MainThread
|
||||
public static void setCallbacks(LibVLC libVLC, Callbacks callbacks) {
|
||||
public static void setCallbacks(ILibVLC ILibVLC, Callbacks callbacks) {
|
||||
if (callbacks != null && sHandler == null)
|
||||
sHandler = new Handler(Looper.getMainLooper());
|
||||
sCallbacks = callbacks;
|
||||
nativeSetCallbacks(libVLC, callbacks != null);
|
||||
nativeSetCallbacks(ILibVLC, callbacks != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -476,5 +478,5 @@ public abstract class Dialog {
|
||||
});
|
||||
}
|
||||
|
||||
private static native void nativeSetCallbacks(LibVLC libVLC, boolean enabled);
|
||||
private static native void nativeSetCallbacks(ILibVLC ILibVLC, boolean enabled);
|
||||
}
|
18
libvlc/src/org/videolan/libvlc/FactoryManager.java
Normal file
18
libvlc/src/org/videolan/libvlc/FactoryManager.java
Normal file
@ -0,0 +1,18 @@
|
||||
package org.videolan.libvlc;
|
||||
|
||||
import org.videolan.libvlc.interfaces.IComponentFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FactoryManager {
|
||||
private static Map<String, IComponentFactory> factories = new HashMap<>();
|
||||
|
||||
public static void registerFactory(String factoryId, IComponentFactory factory) {
|
||||
factories.put(factoryId, factory);
|
||||
}
|
||||
|
||||
public static IComponentFactory getFactory(String factoryId) {
|
||||
return factories.get(factoryId);
|
||||
}
|
||||
}
|
@ -23,18 +23,22 @@ package org.videolan.libvlc;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.util.HWDecoderUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class LibVLC extends VLCObject<LibVLC.Event> {
|
||||
public class LibVLC extends VLCObject<ILibVLC.Event> implements ILibVLC {
|
||||
private static final String TAG = "VLC/LibVLC";
|
||||
|
||||
final Context mAppContext;
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static class Event extends AbstractVLCEvent {
|
||||
protected Event(int type) {
|
||||
super(type);
|
||||
}
|
||||
@ -45,12 +49,12 @@ public class LibVLC extends VLCObject<LibVLC.Event> {
|
||||
*
|
||||
* @param options
|
||||
*/
|
||||
public LibVLC(Context context, ArrayList<String> options) {
|
||||
public LibVLC(Context context, List<String> options) {
|
||||
mAppContext = context.getApplicationContext();
|
||||
loadLibraries();
|
||||
|
||||
if (options == null)
|
||||
options = new ArrayList<String>();
|
||||
options = new ArrayList<>();
|
||||
boolean setAout = true, setChroma = true;
|
||||
// check if aout/vout options are already set
|
||||
for (String option : options) {
|
||||
@ -88,27 +92,35 @@ public class LibVLC extends VLCObject<LibVLC.Event> {
|
||||
|
||||
/**
|
||||
* Get the libVLC version
|
||||
*
|
||||
* @return the libVLC version string
|
||||
*/
|
||||
public native String version();
|
||||
|
||||
/**
|
||||
* Get the libVLC compiler
|
||||
*
|
||||
* @return the libVLC compiler string
|
||||
*/
|
||||
public native String compiler();
|
||||
|
||||
/**
|
||||
* Get the libVLC changeset
|
||||
*
|
||||
* @return the libVLC changeset string
|
||||
*/
|
||||
public native String changeset();
|
||||
|
||||
@Override
|
||||
protected Event onEventNative(int eventType, long arg1, long arg2, float argf1, @Nullable String args1) {
|
||||
protected ILibVLC.Event onEventNative(int eventType, long arg1, long arg2, float argf1, @Nullable String args1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getAppContext() {
|
||||
return mAppContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReleaseNative() {
|
||||
nativeRelease();
|
||||
@ -121,13 +133,15 @@ public class LibVLC extends VLCObject<LibVLC.Event> {
|
||||
* @param name human-readable application name, e.g. "FooBar player 1.2.3"
|
||||
* @param http HTTP User Agent, e.g. "FooBar/1.2.3 Python/2.6.0"
|
||||
*/
|
||||
public void setUserAgent(String name, String http){
|
||||
public void setUserAgent(String name, String http) {
|
||||
nativeSetUserAgent(name, http);
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNew(String[] options, String homePath);
|
||||
|
||||
private native void nativeRelease();
|
||||
|
||||
private native void nativeSetUserAgent(String name, String http);
|
||||
|
||||
private static boolean sLoaded = false;
|
||||
|
24
libvlc/src/org/videolan/libvlc/LibVLCFactory.java
Normal file
24
libvlc/src/org/videolan/libvlc/LibVLCFactory.java
Normal file
@ -0,0 +1,24 @@
|
||||
package org.videolan.libvlc;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.ILibVLCFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LibVLCFactory implements ILibVLCFactory {
|
||||
static {
|
||||
FactoryManager.registerFactory(ILibVLCFactory.factoryId, new LibVLCFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getFromOptions(Context context, List<String> options) {
|
||||
return new LibVLC(context, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getFromContext(Context context) {
|
||||
return new LibVLC(context, null);
|
||||
}
|
||||
}
|
@ -23,6 +23,9 @@ package org.videolan.libvlc;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IMediaList;
|
||||
import org.videolan.libvlc.util.AndroidUtil;
|
||||
import org.videolan.libvlc.util.HWDecoderUtil;
|
||||
import org.videolan.libvlc.util.VLCUtil;
|
||||
@ -32,171 +35,9 @@ import java.io.FileDescriptor;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class Media extends VLCObject<Media.Event> {
|
||||
public class Media extends VLCObject<IMedia.Event> implements IMedia {
|
||||
private final static String TAG = "LibVLC/Media";
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static final int MetaChanged = 0;
|
||||
public static final int SubItemAdded = 1;
|
||||
public static final int DurationChanged = 2;
|
||||
public static final int ParsedChanged = 3;
|
||||
//public static final int Freed = 4;
|
||||
public static final int StateChanged = 5;
|
||||
public static final int SubItemTreeAdded = 6;
|
||||
|
||||
protected Event(int type) {
|
||||
super(type);
|
||||
}
|
||||
protected Event(int type, long arg1) {
|
||||
super(type, arg1);
|
||||
}
|
||||
|
||||
public int getMetaId() {
|
||||
return (int) arg1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ParsedStatus in case of {@link Event#ParsedChanged} event
|
||||
* @return {@link Media.ParsedStatus}
|
||||
*/
|
||||
public int getParsedStatus() {
|
||||
return (int) arg1;
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventListener extends VLCEvent.Listener<Media.Event> {}
|
||||
|
||||
/**
|
||||
* libvlc_media_type_t
|
||||
*/
|
||||
public static class Type {
|
||||
public static final int Unknown = 0;
|
||||
public static final int File = 1;
|
||||
public static final int Directory = 2;
|
||||
public static final int Disc = 3;
|
||||
public static final int Stream = 4;
|
||||
public static final int Playlist = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_meta_t
|
||||
*/
|
||||
public static class Meta {
|
||||
public static final int Title = 0;
|
||||
public static final int Artist = 1;
|
||||
public static final int Genre = 2;
|
||||
public static final int Copyright = 3;
|
||||
public static final int Album = 4;
|
||||
public static final int TrackNumber = 5;
|
||||
public static final int Description = 6;
|
||||
public static final int Rating = 7;
|
||||
public static final int Date = 8;
|
||||
public static final int Setting = 9;
|
||||
public static final int URL = 10;
|
||||
public static final int Language = 11;
|
||||
public static final int NowPlaying = 12;
|
||||
public static final int Publisher = 13;
|
||||
public static final int EncodedBy = 14;
|
||||
public static final int ArtworkURL = 15;
|
||||
public static final int TrackID = 16;
|
||||
public static final int TrackTotal = 17;
|
||||
public static final int Director = 18;
|
||||
public static final int Season = 19;
|
||||
public static final int Episode = 20;
|
||||
public static final int ShowName = 21;
|
||||
public static final int Actors = 22;
|
||||
public static final int AlbumArtist = 23;
|
||||
public static final int DiscNumber = 24;
|
||||
public static final int MAX = 25;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_state_t
|
||||
*/
|
||||
public static class State {
|
||||
public static final int NothingSpecial = 0;
|
||||
public static final int Opening = 1;
|
||||
/* deprecated public static final int Buffering = 2; */
|
||||
public static final int Playing = 3;
|
||||
public static final int Paused = 4;
|
||||
public static final int Stopped = 5;
|
||||
public static final int Ended = 6;
|
||||
public static final int Error = 7;
|
||||
public static final int MAX = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_parse_flag_t
|
||||
*/
|
||||
public static class Parse {
|
||||
public static final int ParseLocal = 0;
|
||||
public static final int ParseNetwork = 0x01;
|
||||
public static final int FetchLocal = 0x02;
|
||||
public static final int FetchNetwork = 0x04;
|
||||
public static final int DoInteract = 0x08;
|
||||
}
|
||||
|
||||
/*
|
||||
* see libvlc_media_parsed_status_t
|
||||
*/
|
||||
public static class ParsedStatus {
|
||||
public static final int Skipped = 1;
|
||||
public static final int Failed = 2;
|
||||
public static final int Timeout = 3;
|
||||
public static final int Done = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_track_t
|
||||
*/
|
||||
public static abstract class Track {
|
||||
public static class Type {
|
||||
public static final int Unknown = -1;
|
||||
public static final int Audio = 0;
|
||||
public static final int Video = 1;
|
||||
public static final int Text = 2;
|
||||
}
|
||||
|
||||
public final int type;
|
||||
public final String codec;
|
||||
public final String originalCodec;
|
||||
public final int id;
|
||||
public final int profile;
|
||||
public final int level;
|
||||
public final int bitrate;
|
||||
public final String language;
|
||||
public final String description;
|
||||
|
||||
private Track(int type, String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description) {
|
||||
this.type = type;
|
||||
this.codec = codec;
|
||||
this.originalCodec = originalCodec;
|
||||
this.id = id;
|
||||
this.profile = profile;
|
||||
this.level = level;
|
||||
this.bitrate = bitrate;
|
||||
this.language = language;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_audio_track_t
|
||||
*/
|
||||
public static class AudioTrack extends Track {
|
||||
public final int channels;
|
||||
public final int rate;
|
||||
|
||||
private AudioTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
int channels, int rate) {
|
||||
super(Type.Audio, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.channels = channels;
|
||||
this.rate = rate;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Track createAudioTrackFromNative(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
@ -206,61 +47,6 @@ public class Media extends VLCObject<Media.Event> {
|
||||
channels, rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_video_track_t
|
||||
*/
|
||||
public static class VideoTrack extends Track {
|
||||
public static final class Orientation {
|
||||
/** Top line represents top, left column left */
|
||||
public static final int TopLeft = 0;
|
||||
/** Flipped horizontally */
|
||||
public static final int TopRight = 1;
|
||||
/** Flipped vertically */
|
||||
public static final int BottomLeft = 2;
|
||||
/** Rotated 180 degrees */
|
||||
public static final int BottomRight = 3;
|
||||
/** Transposed */
|
||||
public static final int LeftTop = 4;
|
||||
/** Rotated 90 degrees clockwise (or 270 anti-clockwise) */
|
||||
public static final int LeftBottom = 5;
|
||||
/** Rotated 90 degrees anti-clockwise */
|
||||
public static final int RightTop= 6;
|
||||
/** Anti-transposed */
|
||||
public static final int RightBottom = 7;
|
||||
}
|
||||
|
||||
public static final class Projection {
|
||||
public static final int Rectangular = 0;
|
||||
/** 360 spherical */
|
||||
public static final int EquiRectangular = 1;
|
||||
public static final int CubemapLayoutStandard = 0x100;
|
||||
}
|
||||
|
||||
public final int height;
|
||||
public final int width;
|
||||
public final int sarNum;
|
||||
public final int sarDen;
|
||||
public final int frameRateNum;
|
||||
public final int frameRateDen;
|
||||
public final int orientation;
|
||||
public final int projection;
|
||||
|
||||
private VideoTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
int height, int width, int sarNum, int sarDen, int frameRateNum, int frameRateDen,
|
||||
int orientation, int projection) {
|
||||
super(Type.Video, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.sarNum = sarNum;
|
||||
this.sarDen = sarDen;
|
||||
this.frameRateNum = frameRateNum;
|
||||
this.frameRateDen = frameRateDen;
|
||||
this.orientation = orientation;
|
||||
this.projection = projection;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Track createVideoTrackFromNative(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
@ -271,20 +57,6 @@ public class Media extends VLCObject<Media.Event> {
|
||||
height, width, sarNum, sarDen, frameRateNum, frameRateDen, orientation, projection);
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_subtitle_track_t
|
||||
*/
|
||||
public static class SubtitleTrack extends Track {
|
||||
public final String encoding;
|
||||
|
||||
private SubtitleTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
String encoding) {
|
||||
super(Type.Text, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.encoding = encoding;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Track createSubtitleTrackFromNative(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
@ -294,16 +66,6 @@ public class Media extends VLCObject<Media.Event> {
|
||||
encoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_subtitle_track_t
|
||||
*/
|
||||
public static class UnknownTrack extends Track {
|
||||
private UnknownTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description) {
|
||||
super(Type.Unknown, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Track createUnknownTrackFromNative(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description) {
|
||||
@ -311,78 +73,11 @@ public class Media extends VLCObject<Media.Event> {
|
||||
level, bitrate, language, description);
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_slave_t
|
||||
*/
|
||||
public static class Slave {
|
||||
public static class Type {
|
||||
public static final int Subtitle = 0;
|
||||
public static final int Audio = 1;
|
||||
}
|
||||
|
||||
/** @see Type */
|
||||
public final int type;
|
||||
/** From 0 (low priority) to 4 (high priority) */
|
||||
public final int priority;
|
||||
public final String uri;
|
||||
|
||||
public Slave(int type, int priority, String uri) {
|
||||
this.type = type;
|
||||
this.priority = priority;
|
||||
this.uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Slave createSlaveFromNative(int type, int priority, String uri) {
|
||||
return new Slave(type, priority, uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_stats_t
|
||||
*/
|
||||
public static class Stats {
|
||||
|
||||
public final int readBytes;
|
||||
public final float inputBitrate;
|
||||
public final int demuxReadBytes;
|
||||
public final float demuxBitrate;
|
||||
public final int demuxCorrupted;
|
||||
public final int demuxDiscontinuity;
|
||||
public final int decodedVideo;
|
||||
public final int decodedAudio;
|
||||
public final int displayedPictures;
|
||||
public final int lostPictures;
|
||||
public final int playedAbuffers;
|
||||
public final int lostAbuffers;
|
||||
public final int sentPackets;
|
||||
public final int sentBytes;
|
||||
public final float sendBitrate;
|
||||
|
||||
public Stats(int readBytes, float inputBitrate, int demuxReadBytes,
|
||||
float demuxBitrate, int demuxCorrupted,
|
||||
int demuxDiscontinuity, int decodedVideo, int decodedAudio,
|
||||
int displayedPictures, int lostPictures, int playedAbuffers,
|
||||
int lostAbuffers, int sentPackets, int sentBytes,
|
||||
float sendBitrate) {
|
||||
this.readBytes = readBytes;
|
||||
this.inputBitrate = inputBitrate;
|
||||
this.demuxReadBytes = demuxReadBytes;
|
||||
this.demuxBitrate = demuxBitrate;
|
||||
this.demuxCorrupted = demuxCorrupted;
|
||||
this.demuxDiscontinuity = demuxDiscontinuity;
|
||||
this.decodedVideo = decodedVideo;
|
||||
this.decodedAudio = decodedAudio;
|
||||
this.displayedPictures = displayedPictures;
|
||||
this.lostPictures = lostPictures;
|
||||
this.playedAbuffers = playedAbuffers;
|
||||
this.lostAbuffers = lostAbuffers;
|
||||
this.sentPackets = sentPackets;
|
||||
this.sentBytes = sentBytes;
|
||||
this.sendBitrate = sendBitrate;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private static Stats createStatsFromNative(int readBytes,
|
||||
float inputBitrate,
|
||||
@ -426,50 +121,50 @@ public class Media extends VLCObject<Media.Event> {
|
||||
/**
|
||||
* Create a Media from libVLC and a local path starting with '/'.
|
||||
*
|
||||
* @param libVLC a valid libVLC
|
||||
* @param ILibVLC a valid libVLC
|
||||
* @param path an absolute local path
|
||||
*/
|
||||
public Media(LibVLC libVLC, String path) {
|
||||
super(libVLC);
|
||||
nativeNewFromPath(libVLC, path);
|
||||
public Media(ILibVLC ILibVLC, String path) {
|
||||
super(ILibVLC);
|
||||
nativeNewFromPath(ILibVLC, path);
|
||||
mUri = VLCUtil.UriFromMrl(nativeGetMrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Media from libVLC and a Uri
|
||||
*
|
||||
* @param libVLC a valid libVLC
|
||||
* @param ILibVLC a valid libVLC
|
||||
* @param uri a valid RFC 2396 Uri
|
||||
*/
|
||||
public Media(LibVLC libVLC, Uri uri) {
|
||||
super(libVLC);
|
||||
nativeNewFromLocation(libVLC, VLCUtil.encodeVLCUri(uri));
|
||||
public Media(ILibVLC ILibVLC, Uri uri) {
|
||||
super(ILibVLC);
|
||||
nativeNewFromLocation(ILibVLC, VLCUtil.encodeVLCUri(uri));
|
||||
mUri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Media from libVLC and a FileDescriptor
|
||||
*
|
||||
* @param libVLC a valid LibVLC
|
||||
* @param ILibVLC a valid LibVLC
|
||||
* @param fd file descriptor object
|
||||
*/
|
||||
public Media(LibVLC libVLC, FileDescriptor fd) {
|
||||
super(libVLC);
|
||||
nativeNewFromFd(libVLC, fd);
|
||||
public Media(ILibVLC ILibVLC, FileDescriptor fd) {
|
||||
super(ILibVLC);
|
||||
nativeNewFromFd(ILibVLC, fd);
|
||||
mUri = VLCUtil.UriFromMrl(nativeGetMrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Media from libVLC and an AssetFileDescriptor
|
||||
*
|
||||
* @param libVLC a valid LibVLC
|
||||
* @param ILibVLC a valid LibVLC
|
||||
* @param afd asset file descriptor object
|
||||
*/
|
||||
public Media(LibVLC libVLC, AssetFileDescriptor afd) {
|
||||
super(libVLC);
|
||||
public Media(ILibVLC ILibVLC, AssetFileDescriptor afd) {
|
||||
super(ILibVLC);
|
||||
long offset = afd.getStartOffset();
|
||||
long length = afd.getLength();
|
||||
nativeNewFromFdWithOffsetLength(libVLC, afd.getFileDescriptor(), offset, length);
|
||||
nativeNewFromFdWithOffsetLength(ILibVLC, afd.getFileDescriptor(), offset, length);
|
||||
mUri = VLCUtil.UriFromMrl(nativeGetMrl());
|
||||
}
|
||||
|
||||
@ -478,7 +173,7 @@ public class Media extends VLCObject<Media.Event> {
|
||||
* @param ml Should not be released and locked
|
||||
* @param index index of the Media from the MediaList
|
||||
*/
|
||||
protected Media(MediaList ml, int index) {
|
||||
protected Media(IMediaList ml, int index) {
|
||||
super(ml);
|
||||
if (ml == null || ml.isReleased())
|
||||
throw new IllegalArgumentException("MediaList is null or released");
|
||||
@ -794,7 +489,7 @@ public class Media extends VLCObject<Media.Event> {
|
||||
/**
|
||||
* Enable HWDecoder options if not already set
|
||||
*/
|
||||
protected void setDefaultMediaPlayerOptions() {
|
||||
public void setDefaultMediaPlayerOptions() {
|
||||
boolean codecOptionSet;
|
||||
synchronized (this) {
|
||||
codecOptionSet = mCodecOptionSet;
|
||||
@ -875,11 +570,11 @@ public class Media extends VLCObject<Media.Event> {
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNewFromPath(LibVLC libVLC, String path);
|
||||
private native void nativeNewFromLocation(LibVLC libVLC, String location);
|
||||
private native void nativeNewFromFd(LibVLC libVLC, FileDescriptor fd);
|
||||
private native void nativeNewFromFdWithOffsetLength(LibVLC libVLC, FileDescriptor fd, long offset, long length);
|
||||
private native void nativeNewFromMediaList(MediaList ml, int index);
|
||||
private native void nativeNewFromPath(ILibVLC ILibVLC, String path);
|
||||
private native void nativeNewFromLocation(ILibVLC ILibVLC, String location);
|
||||
private native void nativeNewFromFd(ILibVLC ILibVLC, FileDescriptor fd);
|
||||
private native void nativeNewFromFdWithOffsetLength(ILibVLC ILibVLC, FileDescriptor fd, long offset, long length);
|
||||
private native void nativeNewFromMediaList(IMediaList ml, int index);
|
||||
private native void nativeRelease();
|
||||
private native boolean nativeParseAsync(int flags, int timeout);
|
||||
private native boolean nativeParse(int flags);
|
||||
|
@ -22,11 +22,14 @@ package org.videolan.libvlc;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class MediaDiscoverer extends VLCObject<MediaDiscoverer.Event> {
|
||||
private final static String TAG = "LibVLC/MediaDiscoverer";
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static class Event extends AbstractVLCEvent {
|
||||
|
||||
public static final int Started = 0x500;
|
||||
public static final int Ended = 0x501;
|
||||
@ -65,19 +68,19 @@ public class MediaDiscoverer extends VLCObject<MediaDiscoverer.Event> {
|
||||
return new Description(name, longName, category);
|
||||
}
|
||||
|
||||
public interface EventListener extends VLCEvent.Listener<MediaDiscoverer.Event> {}
|
||||
public interface EventListener extends AbstractVLCEvent.Listener<MediaDiscoverer.Event> {}
|
||||
|
||||
private MediaList mMediaList = null;
|
||||
|
||||
/**
|
||||
* Create a MediaDiscover.
|
||||
*
|
||||
* @param libVLC a valid LibVLC
|
||||
* @param ILibVLC a valid LibVLC
|
||||
* @param name Name of the vlc service discovery ("dsm", "upnp", "bonjour"...).
|
||||
*/
|
||||
public MediaDiscoverer(LibVLC libVLC, String name) {
|
||||
super(libVLC);
|
||||
nativeNew(libVLC, name);
|
||||
public MediaDiscoverer(ILibVLC ILibVLC, String name) {
|
||||
super(ILibVLC);
|
||||
nativeNew(ILibVLC, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,14 +151,14 @@ public class MediaDiscoverer extends VLCObject<MediaDiscoverer.Event> {
|
||||
* @param category see {@link Description.Category}
|
||||
*/
|
||||
@Nullable
|
||||
public static Description[] list(LibVLC libVLC, int category) {
|
||||
return nativeList(libVLC, category);
|
||||
public static Description[] list(ILibVLC ILibVLC, int category) {
|
||||
return nativeList(ILibVLC, category);
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNew(LibVLC libVLC, String name);
|
||||
private native void nativeNew(ILibVLC ILibVLC, String name);
|
||||
private native void nativeRelease();
|
||||
private native boolean nativeStart();
|
||||
private native void nativeStop();
|
||||
private static native Description[] nativeList(LibVLC libVLC, int category);
|
||||
private static native Description[] nativeList(ILibVLC ILibVLC, int category);
|
||||
}
|
||||
|
36
libvlc/src/org/videolan/libvlc/MediaFactory.java
Normal file
36
libvlc/src/org/videolan/libvlc/MediaFactory.java
Normal file
@ -0,0 +1,36 @@
|
||||
package org.videolan.libvlc;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
public class MediaFactory implements IMediaFactory {
|
||||
static {
|
||||
FactoryManager.registerFactory(IMediaFactory.factoryId, new MediaFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromLocalPath(ILibVLC ILibVLC, String path) {
|
||||
return new Media(ILibVLC, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromUri(ILibVLC ILibVLC, Uri uri) {
|
||||
return new Media(ILibVLC, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromFileDescriptor(ILibVLC ILibVLC, FileDescriptor fd) {
|
||||
return new Media(ILibVLC, fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromAssetFileDescriptor(ILibVLC ILibVLC, AssetFileDescriptor assetFileDescriptor) {
|
||||
return new Media(ILibVLC, assetFileDescriptor);
|
||||
}
|
||||
}
|
@ -25,46 +25,16 @@ import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IMediaList;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class MediaList extends VLCObject<MediaList.Event> {
|
||||
public class MediaList extends VLCObject<IMediaList.Event> implements IMediaList {
|
||||
private final static String TAG = "LibVLC/MediaList";
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
|
||||
public static final int ItemAdded = 0x200;
|
||||
//public static final int WillAddItem = 0x201;
|
||||
public static final int ItemDeleted = 0x202;
|
||||
//public static final int WillDeleteItem = 0x203;
|
||||
public static final int EndReached = 0x204;
|
||||
|
||||
/**
|
||||
* In case of ItemDeleted, the media will be already released. If it's released, cached
|
||||
* attributes are still available (like {@link Media#getUri()}}).
|
||||
*/
|
||||
public final Media media;
|
||||
private final boolean retain;
|
||||
public final int index;
|
||||
|
||||
protected Event(int type, Media media, boolean retain, int index) {
|
||||
super(type);
|
||||
if (retain && (media == null || !media.retain()))
|
||||
throw new IllegalStateException("invalid media reference");
|
||||
this.media = media;
|
||||
this.retain = retain;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
void release() {
|
||||
if (retain)
|
||||
media.release();
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventListener extends VLCEvent.Listener<MediaList.Event> {}
|
||||
|
||||
private int mCount = 0;
|
||||
private final SparseArray<Media> mMediaArray = new SparseArray<Media>();
|
||||
private final SparseArray<IMedia> mMediaArray = new SparseArray<IMedia>();
|
||||
private boolean mLocked = false;
|
||||
|
||||
private void init() {
|
||||
@ -77,16 +47,16 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
|
||||
/**
|
||||
* Create a MediaList from libVLC
|
||||
* @param libVLC a valid libVLC
|
||||
*
|
||||
* @param ILibVLC a valid libVLC
|
||||
*/
|
||||
public MediaList(LibVLC libVLC) {
|
||||
super(libVLC);
|
||||
nativeNewFromLibVlc(libVLC);
|
||||
public MediaList(ILibVLC ILibVLC) {
|
||||
super(ILibVLC);
|
||||
nativeNewFromLibVlc(ILibVLC);
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param md Should not be released
|
||||
*/
|
||||
protected MediaList(MediaDiscoverer md) {
|
||||
@ -96,27 +66,26 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param m Should not be released
|
||||
*/
|
||||
protected MediaList(Media m) {
|
||||
protected MediaList(IMedia m) {
|
||||
super(m);
|
||||
nativeNewFromMedia(m);
|
||||
init();
|
||||
}
|
||||
|
||||
private synchronized Media insertMediaFromEvent(int index) {
|
||||
private synchronized IMedia insertMediaFromEvent(int index) {
|
||||
for (int i = mCount - 1; i >= index; --i)
|
||||
mMediaArray.put(i + 1, mMediaArray.valueAt(i));
|
||||
mCount++;
|
||||
final Media media = new Media(this, index);
|
||||
final IMedia media = new Media(this, index);
|
||||
mMediaArray.put(index, media);
|
||||
return media;
|
||||
}
|
||||
|
||||
private synchronized Media removeMediaFromEvent(int index) {
|
||||
private synchronized IMedia removeMediaFromEvent(int index) {
|
||||
mCount--;
|
||||
final Media media = mMediaArray.get(index);
|
||||
final IMedia media = mMediaArray.get(index);
|
||||
if (media != null)
|
||||
media.release();
|
||||
for (int i = index; i < mCount; ++i) {
|
||||
@ -130,7 +99,7 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized Event onEventNative(int eventType, long arg1, long arg2, float argf1, @Nullable String args1) {
|
||||
protected synchronized Event onEventNative(int eventType, long arg1, long arg2, float argf1, @Nullable String args1) {
|
||||
if (mLocked)
|
||||
throw new IllegalStateException("already locked from event callback");
|
||||
mLocked = true;
|
||||
@ -138,23 +107,23 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
int index;
|
||||
|
||||
switch (eventType) {
|
||||
case Event.ItemAdded:
|
||||
index = (int) arg1;
|
||||
if (index != -1) {
|
||||
final Media media = insertMediaFromEvent(index);
|
||||
event = new Event(eventType, media, true, index);
|
||||
}
|
||||
break;
|
||||
case Event.ItemDeleted:
|
||||
index = (int) arg1;
|
||||
if (index != -1) {
|
||||
final Media media = removeMediaFromEvent(index);
|
||||
event = new Event(eventType, media, false, index);
|
||||
}
|
||||
break;
|
||||
case Event.EndReached:
|
||||
event = new Event(eventType, null, false, -1);
|
||||
break;
|
||||
case Event.ItemAdded:
|
||||
index = (int) arg1;
|
||||
if (index != -1) {
|
||||
final IMedia media = insertMediaFromEvent(index);
|
||||
event = new Event(eventType, media, true, index);
|
||||
}
|
||||
break;
|
||||
case Event.ItemDeleted:
|
||||
index = (int) arg1;
|
||||
if (index != -1) {
|
||||
final IMedia media = removeMediaFromEvent(index);
|
||||
event = new Event(eventType, media, false, index);
|
||||
}
|
||||
break;
|
||||
case Event.EndReached:
|
||||
event = new Event(eventType, null, false, -1);
|
||||
break;
|
||||
}
|
||||
mLocked = false;
|
||||
return event;
|
||||
@ -173,10 +142,10 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
* @param index index of the media
|
||||
* @return Media hold by MediaList. This Media should be released with {@link #release()}.
|
||||
*/
|
||||
public synchronized Media getMediaAt(int index) {
|
||||
public synchronized IMedia getMediaAt(int index) {
|
||||
if (index < 0 || index >= getCount())
|
||||
throw new IndexOutOfBoundsException();
|
||||
final Media media = mMediaArray.get(index);
|
||||
final IMedia media = mMediaArray.get(index);
|
||||
media.retain();
|
||||
return media;
|
||||
}
|
||||
@ -184,7 +153,7 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
@Override
|
||||
public void onReleaseNative() {
|
||||
for (int i = 0; i < mMediaArray.size(); ++i) {
|
||||
final Media media = mMediaArray.get(i);
|
||||
final IMedia media = mMediaArray.get(i);
|
||||
if (media != null)
|
||||
media.release();
|
||||
}
|
||||
@ -206,16 +175,22 @@ public class MediaList extends VLCObject<MediaList.Event> {
|
||||
nativeUnlock();
|
||||
}
|
||||
|
||||
protected synchronized boolean isLocked() {
|
||||
public synchronized boolean isLocked() {
|
||||
return mLocked;
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNewFromLibVlc(LibVLC libvlc);
|
||||
private native void nativeNewFromLibVlc(ILibVLC libvlc);
|
||||
|
||||
private native void nativeNewFromMediaDiscoverer(MediaDiscoverer md);
|
||||
private native void nativeNewFromMedia(Media m);
|
||||
|
||||
private native void nativeNewFromMedia(IMedia m);
|
||||
|
||||
private native void nativeRelease();
|
||||
|
||||
private native int nativeGetCount();
|
||||
|
||||
private native void nativeLock();
|
||||
|
||||
private native void nativeUnlock();
|
||||
}
|
||||
|
@ -40,6 +40,10 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IVLCVout;
|
||||
import org.videolan.libvlc.util.AndroidUtil;
|
||||
import org.videolan.libvlc.util.DisplayManager;
|
||||
import org.videolan.libvlc.util.VLCUtil;
|
||||
@ -51,7 +55,7 @@ import java.io.IOException;
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static class Event extends AbstractVLCEvent {
|
||||
public static final int MediaChanged = 0x100;
|
||||
//public static final int NothingSpecial = 0x101;
|
||||
public static final int Opening = 0x102;
|
||||
@ -141,7 +145,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventListener extends VLCEvent.Listener<MediaPlayer.Event> {}
|
||||
public interface EventListener extends AbstractVLCEvent.Listener<MediaPlayer.Event> {}
|
||||
|
||||
public static class Position {
|
||||
public static final int Disable = -1;
|
||||
@ -396,7 +400,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
}
|
||||
public static final int SURFACE_SCALES_COUNT = ScaleType.values().length;
|
||||
|
||||
private Media mMedia = null;
|
||||
private IMedia mMedia = null;
|
||||
private RendererItem mRenderer = null;
|
||||
private AssetFileDescriptor mAfd = null;
|
||||
private boolean mPlaying = false;
|
||||
@ -503,11 +507,11 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
private void registerAudioPlugV21(boolean register) {
|
||||
if (register) {
|
||||
final IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
|
||||
final Intent stickyIntent = mLibVLC.mAppContext.registerReceiver(mAudioPlugReceiver, intentFilter);
|
||||
final Intent stickyIntent = mILibVLC.getAppContext().registerReceiver(mAudioPlugReceiver, intentFilter);
|
||||
if (stickyIntent != null)
|
||||
mAudioPlugReceiver.onReceive(mLibVLC.mAppContext, stickyIntent);
|
||||
mAudioPlugReceiver.onReceive(mILibVLC.getAppContext(), stickyIntent);
|
||||
} else {
|
||||
mLibVLC.mAppContext.unregisterReceiver(mAudioPlugReceiver);
|
||||
mILibVLC.getAppContext().unregisterReceiver(mAudioPlugReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,7 +564,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
private void registerAudioPlugV23(boolean register) {
|
||||
AudioManager am = (AudioManager) mLibVLC.mAppContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
AudioManager am = (AudioManager) mILibVLC.getAppContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (register) {
|
||||
mAudioDeviceCallback.onAudioDevicesAdded(am.getDevices(AudioManager.GET_DEVICES_OUTPUTS));
|
||||
am.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
@ -582,11 +586,11 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
/**
|
||||
* Create an empty MediaPlayer
|
||||
*
|
||||
* @param libVLC a valid libVLC
|
||||
* @param ILibVLC a valid libVLC
|
||||
*/
|
||||
public MediaPlayer(LibVLC libVLC) {
|
||||
super(libVLC);
|
||||
nativeNewFromLibVlc(libVLC, mWindow);
|
||||
public MediaPlayer(ILibVLC ILibVLC) {
|
||||
super(ILibVLC);
|
||||
nativeNewFromLibVlc(ILibVLC, mWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -594,7 +598,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
*
|
||||
* @param media a valid Media object
|
||||
*/
|
||||
public MediaPlayer(@NonNull Media media) {
|
||||
public MediaPlayer(@NonNull IMedia media) {
|
||||
super(media);
|
||||
if (media == null || media.isReleased())
|
||||
throw new IllegalArgumentException("Media is null or released");
|
||||
@ -663,7 +667,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
*
|
||||
* @param media a valid Media object
|
||||
*/
|
||||
public void setMedia(@Nullable Media media) {
|
||||
public void setMedia(@Nullable IMedia media) {
|
||||
if (media != null) {
|
||||
if (media.isReleased())
|
||||
throw new IllegalArgumentException("Media is released");
|
||||
@ -703,7 +707,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
* Get the Media used by this MediaPlayer. This Media should be released with {@link #release()}.
|
||||
*/
|
||||
@Nullable
|
||||
public synchronized Media getMedia() {
|
||||
public synchronized IMedia getMedia() {
|
||||
if (mMedia != null)
|
||||
mMedia.retain();
|
||||
return mMedia;
|
||||
@ -751,7 +755,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
* @param afd The {@link AssetFileDescriptor} to play
|
||||
*/
|
||||
public void play(@NonNull AssetFileDescriptor afd) {
|
||||
final Media media = new Media(mLibVLC, afd);
|
||||
final IMedia media = new Media(mILibVLC, afd);
|
||||
play(media);
|
||||
}
|
||||
|
||||
@ -760,7 +764,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
* @param path Path of the media file to play
|
||||
*/
|
||||
public void play(@NonNull String path) {
|
||||
final Media media = new Media(mLibVLC, path);
|
||||
final IMedia media = new Media(mILibVLC, path);
|
||||
play(media);
|
||||
}
|
||||
|
||||
@ -769,15 +773,15 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
* @param uri {@link Uri} of the media to play
|
||||
*/
|
||||
public void play(@NonNull Uri uri) {
|
||||
final Media media = new Media(mLibVLC, uri);
|
||||
final IMedia media = new Media(mILibVLC, uri);
|
||||
play(media);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts playback from an already prepared Media
|
||||
* @param media The {@link Media} to play
|
||||
* @param media The {@link IMedia} to play
|
||||
*/
|
||||
public void play(@NonNull Media media) {
|
||||
public void play(@NonNull IMedia media) {
|
||||
setMedia(media);
|
||||
media.release();
|
||||
play();
|
||||
@ -1066,14 +1070,14 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
/**
|
||||
* Get the current video track
|
||||
*/
|
||||
public Media.VideoTrack getCurrentVideoTrack() {
|
||||
public IMedia.VideoTrack getCurrentVideoTrack() {
|
||||
if (getVideoTrack() == -1)
|
||||
return null;
|
||||
final int trackCount = mMedia.getTrackCount();
|
||||
for (int i = 0; i < trackCount; ++i) {
|
||||
final Media.Track track = mMedia.getTrack(i);
|
||||
if (track.type == Media.Track.Type.Video)
|
||||
return (Media.VideoTrack) track;
|
||||
final IMedia.Track track = mMedia.getTrack(i);
|
||||
if (track.type == IMedia.Track.Type.Video)
|
||||
return (IMedia.VideoTrack) track;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -1208,7 +1212,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
/**
|
||||
* Add a slave (or subtitle) to the current media player.
|
||||
*
|
||||
* @param type see {@link org.videolan.libvlc.Media.Slave.Type}
|
||||
* @param type see {@link IMedia.Slave.Type}
|
||||
* @param uri a valid RFC 2396 Uri
|
||||
* @return true on success.
|
||||
*/
|
||||
@ -1230,7 +1234,7 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
/**
|
||||
* Add a slave (or subtitle) to the current media player.
|
||||
*
|
||||
* @param type see {@link org.videolan.libvlc.Media.Slave.Type}
|
||||
* @param type see {@link IMedia.Slave.Type}
|
||||
* @param path a local path
|
||||
* @return true on success.
|
||||
*/
|
||||
@ -1381,10 +1385,10 @@ public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNewFromLibVlc(LibVLC libVLC, AWindow window);
|
||||
private native void nativeNewFromMedia(Media media, AWindow window);
|
||||
private native void nativeNewFromLibVlc(ILibVLC ILibVLC, AWindow window);
|
||||
private native void nativeNewFromMedia(IMedia media, AWindow window);
|
||||
private native void nativeRelease();
|
||||
private native void nativeSetMedia(Media media);
|
||||
private native void nativeSetMedia(IMedia media);
|
||||
private native void nativePlay();
|
||||
private native void nativeStop();
|
||||
private native int nativeSetRenderer(RendererItem item);
|
||||
|
@ -26,12 +26,15 @@ import java.util.List;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.LongSparseArray;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
|
||||
public class RendererDiscoverer extends VLCObject<RendererDiscoverer.Event> {
|
||||
private final static String TAG = "LibVLC/RendererDiscoverer";
|
||||
|
||||
final List<RendererItem> mRenderers = new ArrayList<>();
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static class Event extends AbstractVLCEvent {
|
||||
|
||||
public static final int ItemAdded = 0x502;
|
||||
public static final int ItemDeleted = 0x503;
|
||||
@ -49,7 +52,7 @@ public class RendererDiscoverer extends VLCObject<RendererDiscoverer.Event> {
|
||||
}
|
||||
|
||||
@Override
|
||||
void release() {
|
||||
public void release() {
|
||||
item.release();
|
||||
super.release();
|
||||
}
|
||||
@ -60,17 +63,17 @@ public class RendererDiscoverer extends VLCObject<RendererDiscoverer.Event> {
|
||||
return new RendererItem(name, type, iconUrl, flags, ref);
|
||||
}
|
||||
|
||||
public interface EventListener extends VLCEvent.Listener<RendererDiscoverer.Event> {}
|
||||
public interface EventListener extends AbstractVLCEvent.Listener<RendererDiscoverer.Event> {}
|
||||
|
||||
/**
|
||||
* Create a MediaDiscover.
|
||||
*
|
||||
* @param libVLC a valid LibVLC
|
||||
* @param ILibVLC a valid LibVLC
|
||||
* @param name Name of the vlc service discovery.
|
||||
*/
|
||||
public RendererDiscoverer(LibVLC libVLC, String name) {
|
||||
super(libVLC);
|
||||
nativeNew(libVLC, name);
|
||||
public RendererDiscoverer(ILibVLC ILibVLC, String name) {
|
||||
super(ILibVLC);
|
||||
nativeNew(ILibVLC, name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,8 +101,8 @@ public class RendererDiscoverer extends VLCObject<RendererDiscoverer.Event> {
|
||||
super.setEventListener(listener);
|
||||
}
|
||||
|
||||
public static Description[] list(LibVLC libVlc) {
|
||||
return nativeList(libVlc);
|
||||
public static Description[] list(ILibVLC ILibVlc) {
|
||||
return nativeList(ILibVlc);
|
||||
}
|
||||
|
||||
public static class Description {
|
||||
@ -154,10 +157,10 @@ public class RendererDiscoverer extends VLCObject<RendererDiscoverer.Event> {
|
||||
}
|
||||
|
||||
/* JNI */
|
||||
private native void nativeNew(LibVLC libVLC, String name);
|
||||
private native void nativeNew(ILibVLC ILibVLC, String name);
|
||||
private native void nativeRelease();
|
||||
private native boolean nativeStart();
|
||||
private native void nativeStop();
|
||||
private static native Description[] nativeList(LibVLC libVLC);
|
||||
private static native Description[] nativeList(ILibVLC ILibVLC);
|
||||
private native RendererItem nativeNewItem(long ref);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package org.videolan.libvlc;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
|
||||
@SuppressWarnings("unused, JniMissingFunction")
|
||||
public class RendererItem extends VLCObject<RendererItem.Event> {
|
||||
|
||||
@ -42,7 +44,7 @@ public class RendererItem extends VLCObject<RendererItem.Event> {
|
||||
nativeReleaseItem();
|
||||
}
|
||||
|
||||
public static class Event extends VLCEvent {
|
||||
public static class Event extends AbstractVLCEvent {
|
||||
protected Event(int type) {
|
||||
super(type);
|
||||
}
|
||||
|
@ -27,23 +27,27 @@ import java.lang.ref.WeakReference;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IVLCObject;
|
||||
|
||||
@SuppressWarnings("JniMissingFunction")
|
||||
abstract class VLCObject<T extends VLCEvent> {
|
||||
private VLCEvent.Listener<T> mEventListener = null;
|
||||
abstract class VLCObject<T extends AbstractVLCEvent> implements IVLCObject<T> {
|
||||
private AbstractVLCEvent.Listener<T> mEventListener = null;
|
||||
private Handler mHandler = null;
|
||||
final LibVLC mLibVLC;
|
||||
final ILibVLC mILibVLC;
|
||||
private int mNativeRefCount = 1;
|
||||
|
||||
protected VLCObject(LibVLC libvlc) {
|
||||
mLibVLC = libvlc;
|
||||
protected VLCObject(ILibVLC libvlc) {
|
||||
mILibVLC = libvlc;
|
||||
}
|
||||
|
||||
protected VLCObject(VLCObject parent) {
|
||||
mLibVLC = parent.mLibVLC;
|
||||
protected VLCObject(IVLCObject parent) {
|
||||
mILibVLC = parent.getLibVLC();
|
||||
}
|
||||
|
||||
protected VLCObject() {
|
||||
mLibVLC = null;
|
||||
mILibVLC = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,22 +103,27 @@ abstract class VLCObject<T extends VLCEvent> {
|
||||
throw new AssertionError("VLCObject (" + getClass().getName() + ") finalized but not natively released (" + mNativeRefCount + " refs)");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getLibVLC() {
|
||||
return mILibVLC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an event listener.
|
||||
* Events are sent via the android main thread.
|
||||
*
|
||||
* @param listener see {@link VLCEvent.Listener}
|
||||
* @param listener see {@link AbstractVLCEvent.Listener}
|
||||
*/
|
||||
protected synchronized void setEventListener(VLCEvent.Listener<T> listener) {
|
||||
protected synchronized void setEventListener(AbstractVLCEvent.Listener<T> listener) {
|
||||
setEventListener(listener, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an event listener and an executor Handler
|
||||
* @param listener see {@link VLCEvent.Listener}
|
||||
* @param listener see {@link AbstractVLCEvent.Listener}
|
||||
* @param handler Handler in which events are sent. If null, a handler will be created running on the main thread
|
||||
*/
|
||||
protected synchronized void setEventListener(VLCEvent.Listener<T> listener, Handler handler) {
|
||||
protected synchronized void setEventListener(AbstractVLCEvent.Listener<T> listener, Handler handler) {
|
||||
if (mHandler != null)
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
mEventListener = listener;
|
||||
@ -151,10 +160,10 @@ abstract class VLCObject<T extends VLCEvent> {
|
||||
final T event = onEventNative(eventType, arg1, arg2, argf1, args1);
|
||||
|
||||
class EventRunnable implements Runnable {
|
||||
private final VLCEvent.Listener<T> listener;
|
||||
private final AbstractVLCEvent.Listener<T> listener;
|
||||
private final T event;
|
||||
|
||||
private EventRunnable(VLCEvent.Listener<T> listener, T event) {
|
||||
private EventRunnable(AbstractVLCEvent.Listener<T> listener, T event) {
|
||||
this.listener = listener;
|
||||
this.event = event;
|
||||
}
|
||||
@ -173,7 +182,7 @@ abstract class VLCObject<T extends VLCEvent> {
|
||||
/* used only before API 7: substitute for NewWeakGlobalRef */
|
||||
@SuppressWarnings("unused") /* Used from JNI */
|
||||
private Object getWeakReference() {
|
||||
return new WeakReference<VLCObject>(this);
|
||||
return new WeakReference<IVLCObject>(this);
|
||||
}
|
||||
@SuppressWarnings("unchecked,unused") /* Used from JNI */
|
||||
private static void dispatchEventFromWeakNative(Object weak, int eventType, long arg1, long arg2,
|
||||
|
@ -15,6 +15,8 @@ import android.view.ViewStub;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import org.videolan.R;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IVLCVout;
|
||||
import org.videolan.libvlc.util.AndroidUtil;
|
||||
import org.videolan.libvlc.util.DisplayManager;
|
||||
import org.videolan.libvlc.util.VLCVideoLayout;
|
||||
@ -135,11 +137,11 @@ class VideoHelper implements IVLCVout.OnNewVideoLayoutListener {
|
||||
break;
|
||||
case SURFACE_FIT_SCREEN:
|
||||
case SURFACE_FILL: {
|
||||
Media.VideoTrack vtrack = mMediaPlayer.getCurrentVideoTrack();
|
||||
IMedia.VideoTrack vtrack = mMediaPlayer.getCurrentVideoTrack();
|
||||
if (vtrack == null)
|
||||
return;
|
||||
final boolean videoSwapped = vtrack.orientation == Media.VideoTrack.Orientation.LeftBottom
|
||||
|| vtrack.orientation == Media.VideoTrack.Orientation.RightTop;
|
||||
final boolean videoSwapped = vtrack.orientation == IMedia.VideoTrack.Orientation.LeftBottom
|
||||
|| vtrack.orientation == IMedia.VideoTrack.Orientation.RightTop;
|
||||
if (mCurrentScaleType == MediaPlayer.ScaleType.SURFACE_FIT_SCREEN) {
|
||||
int videoW = vtrack.width;
|
||||
int videoH = vtrack.height;
|
||||
|
@ -18,44 +18,44 @@
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
package org.videolan.libvlc;
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public abstract class VLCEvent {
|
||||
public abstract class AbstractVLCEvent {
|
||||
public final int type;
|
||||
protected final long arg1;
|
||||
protected final long arg2;
|
||||
protected final float argf1;
|
||||
protected final String args1;
|
||||
|
||||
VLCEvent(int type) {
|
||||
public AbstractVLCEvent(int type) {
|
||||
this.type = type;
|
||||
this.arg1 = this.arg2 = 0;
|
||||
this.argf1 = 0.0f;
|
||||
this.args1 = null;
|
||||
}
|
||||
VLCEvent(int type, long arg1) {
|
||||
public AbstractVLCEvent(int type, long arg1) {
|
||||
this.type = type;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = 0;
|
||||
this.argf1 = 0.0f;
|
||||
this.args1 = null;
|
||||
}
|
||||
VLCEvent(int type, long arg1, long arg2) {
|
||||
public AbstractVLCEvent(int type, long arg1, long arg2) {
|
||||
this.type = type;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
this.argf1 = 0.0f;
|
||||
this.args1 = null;
|
||||
}
|
||||
VLCEvent(int type, float argf) {
|
||||
public AbstractVLCEvent(int type, float argf) {
|
||||
this.type = type;
|
||||
this.arg1 = this.arg2 = 0;
|
||||
this.argf1 = argf;
|
||||
this.args1 = null;
|
||||
}
|
||||
VLCEvent(int type, long arg1, @Nullable String args1) {
|
||||
public AbstractVLCEvent(int type, long arg1, @Nullable String args1) {
|
||||
this.type = type;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = 0;
|
||||
@ -63,16 +63,16 @@ public abstract class VLCEvent {
|
||||
this.args1 = args1;
|
||||
}
|
||||
|
||||
void release() {
|
||||
public void release() {
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener for libvlc events
|
||||
*
|
||||
* @see VLCEvent
|
||||
* @see AbstractVLCEvent
|
||||
*/
|
||||
public interface Listener<T extends VLCEvent> {
|
||||
public interface Listener<T extends AbstractVLCEvent> {
|
||||
void onEvent(T event);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
public interface IComponentFactory {
|
||||
|
||||
}
|
13
libvlc/src/org/videolan/libvlc/interfaces/ILibVLC.java
Normal file
13
libvlc/src/org/videolan/libvlc/interfaces/ILibVLC.java
Normal file
@ -0,0 +1,13 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface ILibVLC extends IVLCObject<ILibVLC.Event> {
|
||||
class Event extends AbstractVLCEvent {
|
||||
protected Event(int type) {
|
||||
super(type);
|
||||
}
|
||||
}
|
||||
|
||||
Context getAppContext();
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ILibVLCFactory extends IComponentFactory {
|
||||
String factoryId = ILibVLCFactory.class.getName();
|
||||
|
||||
ILibVLC getFromOptions(Context context, List<String> options);
|
||||
|
||||
ILibVLC getFromContext(Context context);
|
||||
}
|
385
libvlc/src/org/videolan/libvlc/interfaces/IMedia.java
Normal file
385
libvlc/src/org/videolan/libvlc/interfaces/IMedia.java
Normal file
@ -0,0 +1,385 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
public interface IMedia extends IVLCObject<IMedia.Event> {
|
||||
class Event extends AbstractVLCEvent {
|
||||
public static final int MetaChanged = 0;
|
||||
public static final int SubItemAdded = 1;
|
||||
public static final int DurationChanged = 2;
|
||||
public static final int ParsedChanged = 3;
|
||||
//public static final int Freed = 4;
|
||||
public static final int StateChanged = 5;
|
||||
public static final int SubItemTreeAdded = 6;
|
||||
|
||||
public Event(int type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
public Event(int type, long arg1) {
|
||||
super(type, arg1);
|
||||
}
|
||||
|
||||
public int getMetaId() {
|
||||
return (int) arg1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ParsedStatus in case of {@link Event#ParsedChanged} event
|
||||
*
|
||||
* @return {@link ParsedStatus}
|
||||
*/
|
||||
public int getParsedStatus() {
|
||||
return (int) arg1;
|
||||
}
|
||||
}
|
||||
|
||||
interface EventListener extends AbstractVLCEvent.Listener<Event> {
|
||||
}
|
||||
|
||||
/**
|
||||
* libvlc_media_type_t
|
||||
*/
|
||||
class Type {
|
||||
public static final int Unknown = 0;
|
||||
public static final int File = 1;
|
||||
public static final int Directory = 2;
|
||||
public static final int Disc = 3;
|
||||
public static final int Stream = 4;
|
||||
public static final int Playlist = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_meta_t
|
||||
*/
|
||||
class Meta {
|
||||
public static final int Title = 0;
|
||||
public static final int Artist = 1;
|
||||
public static final int Genre = 2;
|
||||
public static final int Copyright = 3;
|
||||
public static final int Album = 4;
|
||||
public static final int TrackNumber = 5;
|
||||
public static final int Description = 6;
|
||||
public static final int Rating = 7;
|
||||
public static final int Date = 8;
|
||||
public static final int Setting = 9;
|
||||
public static final int URL = 10;
|
||||
public static final int Language = 11;
|
||||
public static final int NowPlaying = 12;
|
||||
public static final int Publisher = 13;
|
||||
public static final int EncodedBy = 14;
|
||||
public static final int ArtworkURL = 15;
|
||||
public static final int TrackID = 16;
|
||||
public static final int TrackTotal = 17;
|
||||
public static final int Director = 18;
|
||||
public static final int Season = 19;
|
||||
public static final int Episode = 20;
|
||||
public static final int ShowName = 21;
|
||||
public static final int Actors = 22;
|
||||
public static final int AlbumArtist = 23;
|
||||
public static final int DiscNumber = 24;
|
||||
public static final int MAX = 25;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_state_t
|
||||
*/
|
||||
class State {
|
||||
public static final int NothingSpecial = 0;
|
||||
public static final int Opening = 1;
|
||||
/* deprecated public static final int Buffering = 2; */
|
||||
public static final int Playing = 3;
|
||||
public static final int Paused = 4;
|
||||
public static final int Stopped = 5;
|
||||
public static final int Ended = 6;
|
||||
public static final int Error = 7;
|
||||
public static final int MAX = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_parse_flag_t
|
||||
*/
|
||||
class Parse {
|
||||
public static final int ParseLocal = 0;
|
||||
public static final int ParseNetwork = 0x01;
|
||||
public static final int FetchLocal = 0x02;
|
||||
public static final int FetchNetwork = 0x04;
|
||||
public static final int DoInteract = 0x08;
|
||||
}
|
||||
|
||||
/*
|
||||
* see libvlc_media_parsed_status_t
|
||||
*/
|
||||
class ParsedStatus {
|
||||
public static final int Skipped = 1;
|
||||
public static final int Failed = 2;
|
||||
public static final int Timeout = 3;
|
||||
public static final int Done = 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_track_t
|
||||
*/
|
||||
abstract class Track {
|
||||
public static class Type {
|
||||
public static final int Unknown = -1;
|
||||
public static final int Audio = 0;
|
||||
public static final int Video = 1;
|
||||
public static final int Text = 2;
|
||||
}
|
||||
|
||||
public final int type;
|
||||
public final String codec;
|
||||
public final String originalCodec;
|
||||
public final int id;
|
||||
public final int profile;
|
||||
public final int level;
|
||||
public final int bitrate;
|
||||
public final String language;
|
||||
public final String description;
|
||||
|
||||
protected Track(int type, String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description) {
|
||||
this.type = type;
|
||||
this.codec = codec;
|
||||
this.originalCodec = originalCodec;
|
||||
this.id = id;
|
||||
this.profile = profile;
|
||||
this.level = level;
|
||||
this.bitrate = bitrate;
|
||||
this.language = language;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_audio_track_t
|
||||
*/
|
||||
class AudioTrack extends Track {
|
||||
public final int channels;
|
||||
public final int rate;
|
||||
|
||||
public AudioTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
int channels, int rate) {
|
||||
super(Type.Audio, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.channels = channels;
|
||||
this.rate = rate;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_video_track_t
|
||||
*/
|
||||
class VideoTrack extends Track {
|
||||
public static final class Orientation {
|
||||
/**
|
||||
* Top line represents top, left column left
|
||||
*/
|
||||
public static final int TopLeft = 0;
|
||||
/**
|
||||
* Flipped horizontally
|
||||
*/
|
||||
public static final int TopRight = 1;
|
||||
/**
|
||||
* Flipped vertically
|
||||
*/
|
||||
public static final int BottomLeft = 2;
|
||||
/**
|
||||
* Rotated 180 degrees
|
||||
*/
|
||||
public static final int BottomRight = 3;
|
||||
/**
|
||||
* Transposed
|
||||
*/
|
||||
public static final int LeftTop = 4;
|
||||
/**
|
||||
* Rotated 90 degrees clockwise (or 270 anti-clockwise)
|
||||
*/
|
||||
public static final int LeftBottom = 5;
|
||||
/**
|
||||
* Rotated 90 degrees anti-clockwise
|
||||
*/
|
||||
public static final int RightTop = 6;
|
||||
/**
|
||||
* Anti-transposed
|
||||
*/
|
||||
public static final int RightBottom = 7;
|
||||
}
|
||||
|
||||
public static final class Projection {
|
||||
public static final int Rectangular = 0;
|
||||
/**
|
||||
* 360 spherical
|
||||
*/
|
||||
public static final int EquiRectangular = 1;
|
||||
public static final int CubemapLayoutStandard = 0x100;
|
||||
}
|
||||
|
||||
public final int height;
|
||||
public final int width;
|
||||
public final int sarNum;
|
||||
public final int sarDen;
|
||||
public final int frameRateNum;
|
||||
public final int frameRateDen;
|
||||
public final int orientation;
|
||||
public final int projection;
|
||||
|
||||
public VideoTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
int height, int width, int sarNum, int sarDen, int frameRateNum, int frameRateDen,
|
||||
int orientation, int projection) {
|
||||
super(Type.Video, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.height = height;
|
||||
this.width = width;
|
||||
this.sarNum = sarNum;
|
||||
this.sarDen = sarDen;
|
||||
this.frameRateNum = frameRateNum;
|
||||
this.frameRateDen = frameRateDen;
|
||||
this.orientation = orientation;
|
||||
this.projection = projection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_subtitle_track_t
|
||||
*/
|
||||
class SubtitleTrack extends Track {
|
||||
public final String encoding;
|
||||
|
||||
public SubtitleTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description,
|
||||
String encoding) {
|
||||
super(Type.Text, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
this.encoding = encoding;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_subtitle_track_t
|
||||
*/
|
||||
class UnknownTrack extends Track {
|
||||
public UnknownTrack(String codec, String originalCodec, int id, int profile,
|
||||
int level, int bitrate, String language, String description) {
|
||||
super(Type.Unknown, codec, originalCodec, id, profile, level, bitrate, language, description);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_slave_t
|
||||
*/
|
||||
class Slave {
|
||||
public static class Type {
|
||||
public static final int Subtitle = 0;
|
||||
public static final int Audio = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Type
|
||||
*/
|
||||
public final int type;
|
||||
/**
|
||||
* From 0 (low priority) to 4 (high priority)
|
||||
*/
|
||||
public final int priority;
|
||||
public final String uri;
|
||||
|
||||
public Slave(int type, int priority, String uri) {
|
||||
this.type = type;
|
||||
this.priority = priority;
|
||||
this.uri = uri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* see libvlc_media_stats_t
|
||||
*/
|
||||
class Stats {
|
||||
|
||||
public final int readBytes;
|
||||
public final float inputBitrate;
|
||||
public final int demuxReadBytes;
|
||||
public final float demuxBitrate;
|
||||
public final int demuxCorrupted;
|
||||
public final int demuxDiscontinuity;
|
||||
public final int decodedVideo;
|
||||
public final int decodedAudio;
|
||||
public final int displayedPictures;
|
||||
public final int lostPictures;
|
||||
public final int playedAbuffers;
|
||||
public final int lostAbuffers;
|
||||
public final int sentPackets;
|
||||
public final int sentBytes;
|
||||
public final float sendBitrate;
|
||||
|
||||
public Stats(int readBytes, float inputBitrate, int demuxReadBytes,
|
||||
float demuxBitrate, int demuxCorrupted,
|
||||
int demuxDiscontinuity, int decodedVideo, int decodedAudio,
|
||||
int displayedPictures, int lostPictures, int playedAbuffers,
|
||||
int lostAbuffers, int sentPackets, int sentBytes,
|
||||
float sendBitrate) {
|
||||
this.readBytes = readBytes;
|
||||
this.inputBitrate = inputBitrate;
|
||||
this.demuxReadBytes = demuxReadBytes;
|
||||
this.demuxBitrate = demuxBitrate;
|
||||
this.demuxCorrupted = demuxCorrupted;
|
||||
this.demuxDiscontinuity = demuxDiscontinuity;
|
||||
this.decodedVideo = decodedVideo;
|
||||
this.decodedAudio = decodedAudio;
|
||||
this.displayedPictures = displayedPictures;
|
||||
this.lostPictures = lostPictures;
|
||||
this.playedAbuffers = playedAbuffers;
|
||||
this.lostAbuffers = lostAbuffers;
|
||||
this.sentPackets = sentPackets;
|
||||
this.sentBytes = sentBytes;
|
||||
this.sendBitrate = sendBitrate;
|
||||
}
|
||||
}
|
||||
|
||||
long getDuration();
|
||||
|
||||
int getState();
|
||||
|
||||
IMediaList subItems();
|
||||
|
||||
boolean parse(int flags);
|
||||
|
||||
boolean parse();
|
||||
|
||||
boolean parseAsync(int flags, int timeout);
|
||||
|
||||
boolean parseAsync(int flags);
|
||||
|
||||
boolean parseAsync();
|
||||
|
||||
int getType();
|
||||
|
||||
int getTrackCount();
|
||||
|
||||
Track getTrack(int idx);
|
||||
|
||||
String getMeta(int id);
|
||||
|
||||
void setHWDecoderEnabled(boolean enabled, boolean force);
|
||||
|
||||
void setEventListener(EventListener listener);
|
||||
|
||||
void addOption(String option);
|
||||
|
||||
void addSlave(Slave slave);
|
||||
|
||||
void clearSlaves();
|
||||
|
||||
Slave[] getSlaves();
|
||||
|
||||
Uri getUri();
|
||||
|
||||
boolean isParsed();
|
||||
|
||||
Stats getStats();
|
||||
|
||||
/**
|
||||
* Enable HWDecoder options if not already set
|
||||
*/
|
||||
void setDefaultMediaPlayerOptions();
|
||||
}
|
15
libvlc/src/org/videolan/libvlc/interfaces/IMediaFactory.java
Normal file
15
libvlc/src/org/videolan/libvlc/interfaces/IMediaFactory.java
Normal file
@ -0,0 +1,15 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
public interface IMediaFactory extends IComponentFactory {
|
||||
String factoryId = IMediaFactory.class.getName();
|
||||
|
||||
IMedia getFromLocalPath(ILibVLC ILibVLC, String path);
|
||||
IMedia getFromUri(ILibVLC ILibVLC, Uri uri);
|
||||
IMedia getFromFileDescriptor(ILibVLC ILibVLC, FileDescriptor fd);
|
||||
IMedia getFromAssetFileDescriptor(ILibVLC ILibVLC, AssetFileDescriptor assetFileDescriptor);
|
||||
}
|
57
libvlc/src/org/videolan/libvlc/interfaces/IMediaList.java
Normal file
57
libvlc/src/org/videolan/libvlc/interfaces/IMediaList.java
Normal file
@ -0,0 +1,57 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
public interface IMediaList extends IVLCObject<IMediaList.Event> {
|
||||
class Event extends AbstractVLCEvent {
|
||||
|
||||
public static final int ItemAdded = 0x200;
|
||||
//public static final int WillAddItem = 0x201;
|
||||
public static final int ItemDeleted = 0x202;
|
||||
//public static final int WillDeleteItem = 0x203;
|
||||
public static final int EndReached = 0x204;
|
||||
|
||||
/**
|
||||
* In case of ItemDeleted, the media will be already released. If it's released, cached
|
||||
* attributes are still available (like {@link IMedia#getUri()}}).
|
||||
*/
|
||||
public final IMedia media;
|
||||
private final boolean retain;
|
||||
public final int index;
|
||||
|
||||
public Event(int type, IMedia media, boolean retain, int index) {
|
||||
super(type);
|
||||
if (retain && (media == null || !media.retain()))
|
||||
throw new IllegalStateException("invalid media reference");
|
||||
this.media = media;
|
||||
this.retain = retain;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (retain)
|
||||
media.release();
|
||||
}
|
||||
}
|
||||
|
||||
interface EventListener extends AbstractVLCEvent.Listener<IMediaList.Event> {
|
||||
}
|
||||
|
||||
void setEventListener(EventListener listener, Handler handler);
|
||||
|
||||
/**
|
||||
* Get the number of Media.
|
||||
*/
|
||||
int getCount();
|
||||
|
||||
/**
|
||||
* Get a Media at specified index.
|
||||
*
|
||||
* @param index index of the media
|
||||
* @return Media hold by MediaList. This Media should be released with {@link #release()}.
|
||||
*/
|
||||
IMedia getMediaAt(int index);
|
||||
|
||||
boolean isLocked();
|
||||
}
|
11
libvlc/src/org/videolan/libvlc/interfaces/IVLCObject.java
Normal file
11
libvlc/src/org/videolan/libvlc/interfaces/IVLCObject.java
Normal file
@ -0,0 +1,11 @@
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
public interface IVLCObject<T extends AbstractVLCEvent> {
|
||||
boolean retain();
|
||||
|
||||
void release();
|
||||
|
||||
boolean isReleased();
|
||||
|
||||
ILibVLC getLibVLC();
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
*****************************************************************************/
|
||||
|
||||
package org.videolan.libvlc;
|
||||
package org.videolan.libvlc.interfaces;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.SurfaceTexture;
|
@ -32,10 +32,11 @@ import android.os.Parcelable;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.Media;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
@ -67,13 +68,13 @@ public class MediaPlayer
|
||||
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
|
||||
public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
|
||||
|
||||
private Media mCurrentMedia = null;
|
||||
private final LibVLC mLibVLC;
|
||||
private IMedia mCurrentMedia = null;
|
||||
private final ILibVLC mILibVLC;
|
||||
private org.videolan.libvlc.MediaPlayer mMediaPlayer;
|
||||
|
||||
public MediaPlayer() {
|
||||
mLibVLC = new LibVLC(null); //FIXME, this is wrong
|
||||
mMediaPlayer = new org.videolan.libvlc.MediaPlayer(mLibVLC);
|
||||
mILibVLC = new LibVLC(null); //FIXME, this is wrong
|
||||
mMediaPlayer = new org.videolan.libvlc.MediaPlayer(mILibVLC);
|
||||
}
|
||||
|
||||
public static MediaPlayer create(Context context, Uri uri) {
|
||||
@ -108,19 +109,19 @@ public class MediaPlayer
|
||||
// FIXME, this is INCORRECT, @headers are ignored
|
||||
public void setDataSource(Context context, Uri uri, Map<String, String> headers)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
mCurrentMedia = new Media(mLibVLC, uri);
|
||||
mCurrentMedia = new Media(mILibVLC, uri);
|
||||
mMediaPlayer.setMedia(mCurrentMedia);
|
||||
}
|
||||
|
||||
public void setDataSource(String path)
|
||||
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
|
||||
mCurrentMedia = new Media(mLibVLC, path);
|
||||
mCurrentMedia = new Media(mILibVLC, path);
|
||||
mMediaPlayer.setMedia(mCurrentMedia);
|
||||
}
|
||||
|
||||
public void setDataSource(FileDescriptor fd)
|
||||
throws IOException, IllegalArgumentException, IllegalStateException {
|
||||
mCurrentMedia = new Media(mLibVLC, fd);
|
||||
mCurrentMedia = new Media(mILibVLC, fd);
|
||||
mMediaPlayer.setMedia(mCurrentMedia);
|
||||
}
|
||||
|
||||
@ -279,11 +280,11 @@ public class MediaPlayer
|
||||
public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
|
||||
|
||||
public void addTimedTextSource(String path, String mimeType) {
|
||||
mMediaPlayer.addSlave(Media.Slave.Type.Subtitle, path, false);
|
||||
mMediaPlayer.addSlave(IMedia.Slave.Type.Subtitle, path, false);
|
||||
}
|
||||
|
||||
public void addTimedTextSource(Context context, Uri uri, String mimeType) {
|
||||
mMediaPlayer.addSlave(Media.Slave.Type.Subtitle, uri, false);
|
||||
mMediaPlayer.addSlave(IMedia.Slave.Type.Subtitle, uri, false);
|
||||
}
|
||||
|
||||
public void addTimedTextSource(FileDescriptor fd, String mimeType)
|
||||
|
@ -42,7 +42,9 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.MediaController;
|
||||
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.Media;
|
||||
|
||||
import java.io.InputStream;
|
||||
@ -51,11 +53,11 @@ import java.util.Map;
|
||||
public class VideoView extends SurfaceView
|
||||
implements MediaController.MediaPlayerControl {
|
||||
|
||||
private static LibVLC sLibVLC;
|
||||
private static ILibVLC sILibVLC;
|
||||
|
||||
public VideoView(Context context) {
|
||||
super(context);
|
||||
sLibVLC = new LibVLC(context, null);
|
||||
sILibVLC = new LibVLC(context, null);
|
||||
}
|
||||
|
||||
public VideoView(Context context, AttributeSet attrs) {
|
||||
@ -88,11 +90,11 @@ public class VideoView extends SurfaceView
|
||||
}
|
||||
|
||||
public void setVideoPath(String path) {
|
||||
final Media media = new Media(sLibVLC, path);
|
||||
final IMedia media = new Media(sILibVLC, path);
|
||||
}
|
||||
|
||||
public void setVideoURI(Uri uri) {
|
||||
final Media media = new Media(sLibVLC, uri);
|
||||
final IMedia media = new Media(sILibVLC, uri);
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
|
24
libvlc/src/org/videolan/libvlc/stubs/StubLibVLC.java
Normal file
24
libvlc/src/org/videolan/libvlc/stubs/StubLibVLC.java
Normal file
@ -0,0 +1,24 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StubLibVLC extends StubVLCObject<ILibVLC.Event> implements ILibVLC {
|
||||
private final Context mContext;
|
||||
|
||||
public StubLibVLC(Context context, List<String> options) {
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
public StubLibVLC(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getAppContext() {
|
||||
return mContext;
|
||||
}
|
||||
}
|
20
libvlc/src/org/videolan/libvlc/stubs/StubLibVLCFactory.java
Normal file
20
libvlc/src/org/videolan/libvlc/stubs/StubLibVLCFactory.java
Normal file
@ -0,0 +1,20 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLCFactory;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StubLibVLCFactory implements ILibVLCFactory {
|
||||
@Override
|
||||
public ILibVLC getFromOptions(Context context, List<String> options) {
|
||||
return new StubLibVLC(context, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getFromContext(Context context) {
|
||||
return new StubLibVLC(context);
|
||||
}
|
||||
}
|
183
libvlc/src/org/videolan/libvlc/stubs/StubMedia.java
Normal file
183
libvlc/src/org/videolan/libvlc/stubs/StubMedia.java
Normal file
@ -0,0 +1,183 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IMediaList;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
public class StubMedia extends StubVLCObject<IMedia.Event> implements IMedia {
|
||||
private Uri mUri;
|
||||
private ILibVLC mILibVLC;
|
||||
|
||||
private int mType = Type.Unknown;
|
||||
|
||||
public StubMedia(ILibVLC ILibVLC, String path) {
|
||||
this(ILibVLC, Uri.parse(path));
|
||||
}
|
||||
|
||||
public StubMedia(ILibVLC ILibVLC, Uri uri) {
|
||||
mUri = uri;
|
||||
mILibVLC = ILibVLC;
|
||||
}
|
||||
|
||||
public StubMedia(ILibVLC ILibVLC, FileDescriptor fd) {
|
||||
mILibVLC = ILibVLC;
|
||||
}
|
||||
|
||||
public StubMedia(ILibVLC ILibVLC, AssetFileDescriptor assetFileDescriptor) {
|
||||
mILibVLC = ILibVLC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getState() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMediaList subItems() {
|
||||
return new StubMediaList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse(int flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parse() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parseAsync(int flags, int timeout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parseAsync(int flags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean parseAsync() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTrackCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track getTrack(int idx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMeta(int id) {
|
||||
if (mUri == null)
|
||||
return null;
|
||||
switch (id) {
|
||||
case Meta.Title:
|
||||
return getTitle();
|
||||
case Meta.URL:
|
||||
return mUri.getPath();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getTitle() {
|
||||
if ("file".equals(mUri.getScheme())) {
|
||||
return mUri.getLastPathSegment();
|
||||
}
|
||||
return mUri.getPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHWDecoderEnabled(boolean enabled, boolean force) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEventListener(EventListener listener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addOption(String option) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSlave(Slave slave) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSlaves() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slave[] getSlaves() {
|
||||
return new Slave[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParsed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stats getStats() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultMediaPlayerOptions() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retain() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReleased() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getLibVLC() {
|
||||
return mILibVLC;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.mType = type;
|
||||
}
|
||||
}
|
32
libvlc/src/org/videolan/libvlc/stubs/StubMediaFactory.java
Normal file
32
libvlc/src/org/videolan/libvlc/stubs/StubMediaFactory.java
Normal file
@ -0,0 +1,32 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
||||
public class StubMediaFactory implements IMediaFactory {
|
||||
@Override
|
||||
public IMedia getFromLocalPath(ILibVLC ILibVLC, String path) {
|
||||
return new StubMedia(ILibVLC, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromUri(ILibVLC ILibVLC, Uri uri) {
|
||||
return new StubMedia(ILibVLC, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromFileDescriptor(ILibVLC ILibVLC, FileDescriptor fd) {
|
||||
return new StubMedia(ILibVLC, fd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getFromAssetFileDescriptor(ILibVLC ILibVLC, AssetFileDescriptor assetFileDescriptor) {
|
||||
return new StubMedia(ILibVLC, assetFileDescriptor);
|
||||
}
|
||||
}
|
28
libvlc/src/org/videolan/libvlc/stubs/StubMediaList.java
Normal file
28
libvlc/src/org/videolan/libvlc/stubs/StubMediaList.java
Normal file
@ -0,0 +1,28 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.interfaces.IMediaList;
|
||||
|
||||
public class StubMediaList extends StubVLCObject<IMediaList.Event> implements IMediaList {
|
||||
@Override
|
||||
public void setEventListener(EventListener listener, Handler handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMedia getMediaAt(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
return false;
|
||||
}
|
||||
}
|
27
libvlc/src/org/videolan/libvlc/stubs/StubVLCObject.java
Normal file
27
libvlc/src/org/videolan/libvlc/stubs/StubVLCObject.java
Normal file
@ -0,0 +1,27 @@
|
||||
package org.videolan.libvlc.stubs;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.AbstractVLCEvent;
|
||||
import org.videolan.libvlc.interfaces.IVLCObject;
|
||||
|
||||
public class StubVLCObject<T extends AbstractVLCEvent> implements IVLCObject<T> {
|
||||
@Override
|
||||
public boolean retain() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReleased() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILibVLC getLibVLC() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -23,7 +23,9 @@ package org.videolan.libvlc.util;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.MainThread;
|
||||
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.MediaPlayer;
|
||||
|
||||
@ -35,7 +37,7 @@ public class Dumper {
|
||||
void onProgress(float progress);
|
||||
}
|
||||
|
||||
private final LibVLC mLibVLC;
|
||||
private final ILibVLC mILibVLC;
|
||||
private final MediaPlayer mMediaPlayer;
|
||||
private final Listener mListener;
|
||||
|
||||
@ -60,9 +62,9 @@ public class Dumper {
|
||||
options.add("--no-audio");
|
||||
options.add("--no-spu");
|
||||
options.add("-vv");
|
||||
mLibVLC = new LibVLC(null, options);
|
||||
mILibVLC = new LibVLC(null, options);
|
||||
|
||||
final Media media = new Media(mLibVLC, uri);
|
||||
final IMedia media = new Media(mILibVLC, uri);
|
||||
mMediaPlayer = new MediaPlayer(media);
|
||||
mMediaPlayer.setEventListener(new MediaPlayer.EventListener() {
|
||||
@Override
|
||||
@ -99,6 +101,6 @@ public class Dumper {
|
||||
public void cancel() {
|
||||
mMediaPlayer.stop();
|
||||
mMediaPlayer.release();
|
||||
mLibVLC.release();
|
||||
mILibVLC.release();
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,12 @@ import android.os.Handler;
|
||||
import androidx.annotation.MainThread;
|
||||
import android.util.Log;
|
||||
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.FactoryManager;
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.MediaDiscoverer;
|
||||
import org.videolan.libvlc.interfaces.IMediaList;
|
||||
import org.videolan.libvlc.MediaList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -35,14 +38,15 @@ import java.util.ArrayList;
|
||||
public class MediaBrowser {
|
||||
private static final String TAG = "MediaBrowser";
|
||||
|
||||
private final LibVLC mLibVlc;
|
||||
private final ILibVLC mILibVlc;
|
||||
private final ArrayList<MediaDiscoverer> mMediaDiscoverers = new ArrayList<MediaDiscoverer>();
|
||||
private final ArrayList<Media> mDiscovererMediaArray = new ArrayList<Media>();
|
||||
private MediaList mBrowserMediaList;
|
||||
private Media mMedia;
|
||||
private final ArrayList<IMedia> mDiscovererMediaArray = new ArrayList<IMedia>();
|
||||
private IMediaList mBrowserMediaList;
|
||||
private IMedia mMedia;
|
||||
private EventListener mEventListener;
|
||||
private Handler mHandler;
|
||||
private boolean mAlive;
|
||||
private IMediaFactory mFactory;
|
||||
|
||||
private static final String IGNORE_LIST_OPTION = ":ignore-filetypes=";
|
||||
private String mIgnoreList = "db,nfo,ini,jpg,jpeg,ljpg,gif,png,pgm,pgmyuv,pbm,pam,tga,bmp,pnm,xpm,xcf,pcx,tif,tiff,lbm,sfv,txt,sub,idx,srt,ssa,ass,smi,utf,utf-8,rt,aqt,txt,usf,jss,cdg,psb,mpsub,mpl2,pjs,dks,stl,vtt,ttml";
|
||||
@ -65,14 +69,14 @@ public class MediaBrowser {
|
||||
* @param index
|
||||
* @param media
|
||||
*/
|
||||
void onMediaAdded(int index, Media media);
|
||||
void onMediaAdded(int index, IMedia media);
|
||||
/**
|
||||
* Received when a media is removed (Happens only when you discover networks)
|
||||
* @param index
|
||||
* @param media Released media, but cached attributes are still
|
||||
* available (like media.getMrl())
|
||||
*/
|
||||
void onMediaRemoved(int index, Media media);
|
||||
void onMediaRemoved(int index, IMedia media);
|
||||
/**
|
||||
* Called when browse ended.
|
||||
* It won't be called when you discover networks
|
||||
@ -87,9 +91,10 @@ public class MediaBrowser {
|
||||
*
|
||||
* With this constructor, callbacks will be executed in the main thread
|
||||
*/
|
||||
public MediaBrowser(LibVLC libvlc, EventListener listener) {
|
||||
mLibVlc = libvlc;
|
||||
mLibVlc.retain();
|
||||
public MediaBrowser(ILibVLC libvlc, EventListener listener) {
|
||||
mFactory = ((IMediaFactory) FactoryManager.getFactory(IMediaFactory.factoryId));
|
||||
mILibVlc = libvlc;
|
||||
mILibVlc.retain();
|
||||
mEventListener = listener;
|
||||
mAlive = true;
|
||||
}
|
||||
@ -100,7 +105,7 @@ public class MediaBrowser {
|
||||
* @param listener The Listener which will receive callbacks
|
||||
* @param handler Optional Handler in which callbacks will be posted. If set to null, a Handler will be created running on the main thread
|
||||
*/
|
||||
public MediaBrowser(LibVLC libvlc, EventListener listener, Handler handler) {
|
||||
public MediaBrowser(ILibVLC libvlc, EventListener listener, Handler handler) {
|
||||
this(libvlc, listener);
|
||||
mHandler = handler;
|
||||
}
|
||||
@ -129,7 +134,7 @@ public class MediaBrowser {
|
||||
reset();
|
||||
if (!mAlive)
|
||||
throw new IllegalStateException("MediaBrowser released more than one time");
|
||||
mLibVlc.release();
|
||||
mILibVlc.release();
|
||||
mAlive = false;
|
||||
}
|
||||
|
||||
@ -144,7 +149,7 @@ public class MediaBrowser {
|
||||
}
|
||||
|
||||
private void startMediaDiscoverer(String discovererName) {
|
||||
MediaDiscoverer md = new MediaDiscoverer(mLibVlc, discovererName);
|
||||
MediaDiscoverer md = new MediaDiscoverer(mILibVlc, discovererName);
|
||||
mMediaDiscoverers.add(md);
|
||||
final MediaList ml = md.getMediaList();
|
||||
ml.setEventListener(mDiscovererMediaListEventListener, mHandler);
|
||||
@ -160,7 +165,7 @@ public class MediaBrowser {
|
||||
reset();
|
||||
|
||||
final MediaDiscoverer.Description descriptions[] =
|
||||
MediaDiscoverer.list(mLibVlc, MediaDiscoverer.Description.Category.Lan);
|
||||
MediaDiscoverer.list(mILibVlc, MediaDiscoverer.Description.Category.Lan);
|
||||
if (descriptions == null)
|
||||
return;
|
||||
for (MediaDiscoverer.Description description : descriptions) {
|
||||
@ -187,7 +192,7 @@ public class MediaBrowser {
|
||||
*/
|
||||
@MainThread
|
||||
public void browse(String path, int flags) {
|
||||
final Media media = new Media(mLibVlc, path);
|
||||
final IMedia media = mFactory.getFromLocalPath(mILibVlc, path);
|
||||
browse(media, flags);
|
||||
media.release();
|
||||
}
|
||||
@ -200,7 +205,7 @@ public class MediaBrowser {
|
||||
*/
|
||||
@MainThread
|
||||
public void browse(Uri uri, int flags) {
|
||||
final Media media = new Media(mLibVlc, uri);
|
||||
final IMedia media = mFactory.getFromUri(mILibVlc, uri);
|
||||
browse(media, flags);
|
||||
media.release();
|
||||
}
|
||||
@ -212,7 +217,7 @@ public class MediaBrowser {
|
||||
* @param flags see {@link MediaBrowser.Flag}
|
||||
*/
|
||||
@MainThread
|
||||
public void browse(Media media, int flags) {
|
||||
public void browse(IMedia media, int flags) {
|
||||
/* media can be associated with a medialist,
|
||||
* so increment ref count in order to don't clean it with the medialist
|
||||
*/
|
||||
@ -222,9 +227,9 @@ public class MediaBrowser {
|
||||
media.addOption(":no-sub-autodetect-file");
|
||||
if ((flags & Flag.ShowHiddenFiles) != 0)
|
||||
media.addOption(":show-hiddenfiles");
|
||||
int mediaFlags = Media.Parse.ParseNetwork;
|
||||
int mediaFlags = IMedia.Parse.ParseNetwork;
|
||||
if ((flags & Flag.Interact) != 0)
|
||||
mediaFlags |= Media.Parse.DoInteract;
|
||||
mediaFlags |= IMedia.Parse.DoInteract;
|
||||
reset();
|
||||
mBrowserMediaList = media.subItems();
|
||||
mBrowserMediaList.setEventListener(mBrowserMediaListEventListener, mHandler);
|
||||
@ -244,10 +249,10 @@ public class MediaBrowser {
|
||||
* Get a media at a specified index. Should be released with {@link #release()}.
|
||||
*/
|
||||
@MainThread
|
||||
public Media getMediaAt(int index) {
|
||||
public IMedia getMediaAt(int index) {
|
||||
if (index < 0 || index >= getMediaCount())
|
||||
throw new IndexOutOfBoundsException();
|
||||
final Media media = mBrowserMediaList != null ? mBrowserMediaList.getMediaAt(index) :
|
||||
final IMedia media = mBrowserMediaList != null ? mBrowserMediaList.getMediaAt(index) :
|
||||
mDiscovererMediaArray.get(index);
|
||||
media.retain();
|
||||
return media;
|
||||
|
@ -28,13 +28,13 @@ import android.os.Build;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.videolan.libvlc.LibVLC;
|
||||
import org.videolan.libvlc.interfaces.ILibVLC;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.Media;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
@ -552,6 +552,7 @@ public class VLCUtil {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private static void close(Closeable closeable) {
|
||||
if (closeable != null)
|
||||
try {
|
||||
|
@ -5,6 +5,7 @@ import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractAlbum;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractArtist;
|
||||
@ -96,7 +97,7 @@ public class MLServiceLocator {
|
||||
}
|
||||
}
|
||||
|
||||
public static AbstractMediaWrapper getAbstractMediaWrapper(Media media) {
|
||||
public static AbstractMediaWrapper getAbstractMediaWrapper(IMedia media) {
|
||||
if (sMode == LocatorMode.VLC_ANDROID) {
|
||||
return new MediaWrapper(media);
|
||||
} else {
|
||||
|
@ -1,5 +1,4 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
/*****************************************************************************
|
||||
* Medialibrary.java
|
||||
*****************************************************************************
|
||||
* Copyright © 2017-2018 VLC authors and VideoLAN
|
||||
@ -59,8 +58,7 @@ public class Medialibrary extends AbstractMedialibrary {
|
||||
try {
|
||||
System.loadLibrary("c++_shared");
|
||||
System.loadLibrary("mla");
|
||||
} catch (UnsatisfiedLinkError ule)
|
||||
{
|
||||
} catch (UnsatisfiedLinkError ule) {
|
||||
Log.e(TAG, "Can't load mla: " + ule);
|
||||
return ML_INIT_FAILED;
|
||||
}
|
||||
@ -87,10 +85,12 @@ public class Medialibrary extends AbstractMedialibrary {
|
||||
Log.e(TAG, "Medialib database is corrupted. Clearing it and try to restore playlists");
|
||||
nativeClearDatabase(true);
|
||||
}
|
||||
|
||||
mIsInitiated = initCode != ML_INIT_FAILED;
|
||||
return initCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (isStarted()) return;
|
||||
nativeStart();
|
||||
|
@ -10,6 +10,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.MediaPlayer;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.libvlc.util.Extensions;
|
||||
import org.videolan.libvlc.util.VLCUtil;
|
||||
import org.videolan.medialibrary.MLServiceLocator;
|
||||
@ -100,7 +101,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
protected boolean mIsPictureParsed;
|
||||
protected int mFlags = 0;
|
||||
protected long mLastModified = 0L;
|
||||
protected Media.Slave[] mSlaves = null;
|
||||
protected IMedia.Slave[] mSlaves = null;
|
||||
|
||||
protected long mSeen = 0L;
|
||||
|
||||
@ -184,7 +185,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
*
|
||||
* @param media should be parsed and not NULL
|
||||
*/
|
||||
public AbstractMediaWrapper(Media media) {
|
||||
public AbstractMediaWrapper(IMedia media) {
|
||||
super();
|
||||
if (media == null)
|
||||
throw new NullPointerException("media was null");
|
||||
@ -208,7 +209,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
return !(mUri == null || otherUri == null) && (mUri == otherUri || mUri.equals(otherUri));
|
||||
}
|
||||
|
||||
private void init(Media media) {
|
||||
private void init(IMedia media) {
|
||||
mType = TYPE_ALL;
|
||||
|
||||
if (media != null) {
|
||||
@ -216,11 +217,11 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
mLength = media.getDuration();
|
||||
|
||||
for (int i = 0; i < media.getTrackCount(); ++i) {
|
||||
final Media.Track track = media.getTrack(i);
|
||||
final IMedia.Track track = media.getTrack(i);
|
||||
if (track == null)
|
||||
continue;
|
||||
if (track.type == Media.Track.Type.Video) {
|
||||
final Media.VideoTrack videoTrack = (Media.VideoTrack) track;
|
||||
final IMedia.VideoTrack videoTrack = (IMedia.VideoTrack) track;
|
||||
mType = TYPE_VIDEO;
|
||||
mWidth = videoTrack.width;
|
||||
mHeight = videoTrack.height;
|
||||
@ -278,7 +279,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
private void init(long time, long length, int type,
|
||||
Bitmap picture, String title, String artist, String genre, String album, String albumArtist,
|
||||
int width, int height, String artworkURL, int audio, int spu, int trackNumber, int discNumber, long lastModified,
|
||||
long seen, Media.Slave[] slaves) {
|
||||
long seen, IMedia.Slave[] slaves) {
|
||||
mFilename = null;
|
||||
mTime = time;
|
||||
mDisplayTime = time;
|
||||
@ -339,12 +340,12 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
return mUri;
|
||||
}
|
||||
|
||||
private static String getMetaId(Media media, String defaultMeta, int id, boolean trim) {
|
||||
private static String getMetaId(IMedia media, String defaultMeta, int id, boolean trim) {
|
||||
String meta = media.getMeta(id);
|
||||
return meta != null ? trim ? meta.trim() : meta : defaultMeta;
|
||||
}
|
||||
|
||||
private void updateMeta(Media media) {
|
||||
private void updateMeta(IMedia media) {
|
||||
mTitle = getMetaId(media, mTitle, Media.Meta.Title, true);
|
||||
mArtist = getMetaId(media, mArtist, Media.Meta.Artist, true);
|
||||
mAlbum = getMetaId(media, mAlbum, Media.Meta.Album, true);
|
||||
@ -369,7 +370,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
public void updateMeta(MediaPlayer mediaPlayer) {
|
||||
if (!TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mDisplayTitle))
|
||||
mDisplayTitle = mTitle;
|
||||
final Media media = mediaPlayer.getMedia();
|
||||
final IMedia media = mediaPlayer.getMedia();
|
||||
if (media == null)
|
||||
return;
|
||||
updateMeta(media);
|
||||
@ -636,7 +637,7 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Media.Slave[] getSlaves() {
|
||||
public IMedia.Slave[] getSlaves() {
|
||||
return mSlaves;
|
||||
}
|
||||
|
||||
@ -714,9 +715,9 @@ public abstract class AbstractMediaWrapper extends MediaLibraryItem implements P
|
||||
}
|
||||
};
|
||||
|
||||
protected static class PSlave extends Media.Slave implements Parcelable {
|
||||
protected static class PSlave extends IMedia.Slave implements Parcelable {
|
||||
|
||||
PSlave(Media.Slave slave) {
|
||||
PSlave(IMedia.Slave slave) {
|
||||
super(slave.type, slave.priority, slave.uri);
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ import android.os.Parcel;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.medialibrary.Tools;
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper;
|
||||
@ -57,7 +58,7 @@ public class MediaWrapper extends AbstractMediaWrapper {
|
||||
}
|
||||
|
||||
public MediaWrapper(Uri uri) { super(uri); }
|
||||
public MediaWrapper(Media media) { super(media); }
|
||||
public MediaWrapper(IMedia media) { super(media); }
|
||||
public MediaWrapper(Parcel in) { super(in); }
|
||||
|
||||
public void rename(String name) {
|
||||
|
@ -1,7 +1,10 @@
|
||||
package org.videolan.medialibrary.stubs;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.videolan.medialibrary.MLServiceLocator;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractAlbum;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractArtist;
|
||||
@ -16,6 +19,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.videolan.medialibrary.interfaces.AbstractMedialibrary.SORT_ALBUM;
|
||||
import static org.videolan.medialibrary.interfaces.AbstractMedialibrary.SORT_ALPHA;
|
||||
@ -41,7 +45,9 @@ public class StubDataSource {
|
||||
ArrayList<AbstractFolder> mFolders = new ArrayList<>();
|
||||
ArrayList<String> mDevices = new ArrayList<>();
|
||||
|
||||
private static long uuid = 2;
|
||||
private static String baseMrl = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
|
||||
|
||||
private static AtomicLong uuid = new AtomicLong(2);
|
||||
|
||||
private static StubDataSource mInstance = null;
|
||||
|
||||
@ -52,14 +58,35 @@ public class StubDataSource {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
private StubDataSource() { }
|
||||
private StubDataSource() {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
String baseMrl = "/storage/emulated/0/Movies/";
|
||||
}
|
||||
|
||||
public void resetData() {
|
||||
mFolders.clear();
|
||||
mVideoMediaWrappers.clear();
|
||||
mAudioMediaWrappers.clear();
|
||||
}
|
||||
|
||||
public void setVideoByCount(int count, @Nullable String folder) {
|
||||
mVideoMediaWrappers.clear();
|
||||
AbstractMediaWrapper media;
|
||||
String fileName;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
fileName = i + "58_foar_everywun_frum_boxxy.flv";
|
||||
String mrl = baseMrl + ((folder != null) ? folder + "/" : "") + fileName;
|
||||
media = MLServiceLocator.getAbstractMediaWrapper(getUUID(), mrl, 0L, 18820L, AbstractMediaWrapper.TYPE_VIDEO,
|
||||
fileName, fileName, "", "",
|
||||
"", "", 416, 304, "", 0, -2,
|
||||
0, 0, 1509466228L, 0L, true, 0);
|
||||
addVideo(media);
|
||||
}
|
||||
|
||||
// Video
|
||||
String fileName = "058_foar_everywun_frum_boxxy.flv";
|
||||
fileName = "058_foar_everywun_frum_boxxy.flv";
|
||||
media = MLServiceLocator.getAbstractMediaWrapper(getUUID(), baseMrl + fileName, 0L, 18820L, 0,
|
||||
fileName, fileName, "", "",
|
||||
"", "", 416, 304, "", 0, -2,
|
||||
@ -113,9 +140,43 @@ public class StubDataSource {
|
||||
addAudio(media, "", 1970, 360);
|
||||
}
|
||||
|
||||
public void setAudioByCount(int count, @Nullable String folder) {
|
||||
mAudioMediaWrappers.clear();
|
||||
String fileName;
|
||||
AbstractMediaWrapper media;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
fileName = i + "-Show Me The Way.mp3";
|
||||
String mrl = baseMrl + ((folder != null) ? folder + "/" : "") + fileName;
|
||||
media = MLServiceLocator.getAbstractMediaWrapper(getUUID(), mrl, 0L, 280244L, AbstractMediaWrapper.TYPE_AUDIO,
|
||||
i + "-Show Me The Way", fileName, "Peter Frampton", "Rock",
|
||||
"Shine On CD2", "Peter Frampton",
|
||||
0, 0, baseMrl + folder + ".jpg",
|
||||
0, -2, 1, 0,
|
||||
1547452796L, 0L, true, 0);
|
||||
addAudio(media, "", 1965, 400);
|
||||
}
|
||||
}
|
||||
|
||||
public AbstractMediaWrapper addMediaWrapper(String mrl, String title, int type) {
|
||||
AbstractMediaWrapper media = MLServiceLocator.getAbstractMediaWrapper(getUUID(), mrl, 0L, 280224L, type,
|
||||
title, title, "Artisto", "Jazz", "XYZ CD1", "", 0, 0, baseMrl + title, -2,
|
||||
1, 1, 0, 1547452796L, 0L, true, 0);
|
||||
if (type == AbstractMediaWrapper.TYPE_ALL) type = media.getType();
|
||||
if (type == AbstractMediaWrapper.TYPE_VIDEO) addVideo(media);
|
||||
else if (type == AbstractMediaWrapper.TYPE_AUDIO) addAudio(media, "", 2018, 12313);
|
||||
return media;
|
||||
}
|
||||
|
||||
public AbstractFolder createFolder(String name) {
|
||||
AbstractFolder folder = MLServiceLocator.getAbstractFolder(getUUID(), name, baseMrl + name);
|
||||
mFolders.add(folder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
<T> List<T> secureSublist(List<T> list, int offset, int nbItems) {
|
||||
int min = list.size() - 1 < 0 ? 0 : list.size();
|
||||
int secureOffset = (offset >= list.size()) && (offset > 0) ? min : offset;
|
||||
int secureOffset = (offset >= list.size()) && (offset > 0) ? min : offset;
|
||||
int end = offset + nbItems;
|
||||
int secureEnd = (end >= list.size()) && end > 0 ? min : end;
|
||||
return list.subList(secureOffset, secureEnd);
|
||||
@ -123,40 +184,58 @@ public class StubDataSource {
|
||||
|
||||
class MediaComparator implements Comparator<AbstractMediaWrapper> {
|
||||
private int sort;
|
||||
MediaComparator(int sort) { this.sort = sort; }
|
||||
|
||||
MediaComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override //TODO checkout if types of sort are verified before being used in native
|
||||
public int compare(AbstractMediaWrapper o1, AbstractMediaWrapper o2) {
|
||||
switch(sort) {
|
||||
switch (sort) {
|
||||
case SORT_DEFAULT:
|
||||
case SORT_ALPHA: return o1.getTitle().compareTo(o2.getTitle());
|
||||
case SORT_FILENAME :return o1.getFileName().compareTo(o2.getFileName());
|
||||
case SORT_DURATION: return (int)(o1.getLength() - o2.getLength());
|
||||
case SORT_INSERTIONDATE: return (int)(o1.getTime() - o2.getTime()); // TODO checkout if insertiton <=> time
|
||||
case SORT_LASTMODIFICATIONDATE: return (int)(o1.getLastModified() - o2.getLastModified());
|
||||
case SORT_ARTIST: return o1.getArtist().compareTo(o2.getArtist());
|
||||
default: return 0;
|
||||
case SORT_ALPHA:
|
||||
return o1.getTitle().compareTo(o2.getTitle());
|
||||
case SORT_FILENAME:
|
||||
return o1.getFileName().compareTo(o2.getFileName());
|
||||
case SORT_DURATION:
|
||||
return (int) (o1.getLength() - o2.getLength());
|
||||
case SORT_INSERTIONDATE:
|
||||
return (int) (o1.getTime() - o2.getTime()); // TODO checkout if insertiton <=> time
|
||||
case SORT_LASTMODIFICATIONDATE:
|
||||
return (int) (o1.getLastModified() - o2.getLastModified());
|
||||
case SORT_ARTIST:
|
||||
return o1.getArtist().compareTo(o2.getArtist());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArtistComparator implements Comparator<AbstractArtist> {
|
||||
private int sort;
|
||||
ArtistComparator(int sort) { this.sort = sort; }
|
||||
|
||||
ArtistComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AbstractArtist o1, AbstractArtist o2) {
|
||||
switch(sort) {
|
||||
switch (sort) {
|
||||
case SORT_DEFAULT:
|
||||
case SORT_ARTIST: return o1.getTitle().compareTo(o2.getTitle());
|
||||
default: return 0;
|
||||
case SORT_ARTIST:
|
||||
return o1.getTitle().compareTo(o2.getTitle());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AlbumComparator implements Comparator<AbstractAlbum> {
|
||||
private int sort;
|
||||
AlbumComparator(int sort) { this.sort = sort; }
|
||||
|
||||
AlbumComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AbstractAlbum o1, AbstractAlbum o2) {
|
||||
@ -172,43 +251,59 @@ public class StubDataSource {
|
||||
|
||||
class GenreComparator implements Comparator<AbstractGenre> {
|
||||
private int sort;
|
||||
GenreComparator(int sort) { this.sort = sort; }
|
||||
|
||||
GenreComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AbstractGenre o1, AbstractGenre o2) {
|
||||
switch(sort) {
|
||||
switch (sort) {
|
||||
case SORT_DEFAULT:
|
||||
case SORT_ALPHA: return o1.getTitle().compareTo(o2.getTitle());
|
||||
default: return 0;
|
||||
case SORT_ALPHA:
|
||||
return o1.getTitle().compareTo(o2.getTitle());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PlaylistComparator implements Comparator<AbstractPlaylist> {
|
||||
private int sort;
|
||||
PlaylistComparator(int sort) { this.sort = sort; }
|
||||
|
||||
PlaylistComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AbstractPlaylist o1, AbstractPlaylist o2) {
|
||||
switch (sort) {
|
||||
case SORT_DEFAULT:
|
||||
case SORT_ALPHA: return o1.getTitle().compareTo(o2.getTitle());
|
||||
case SORT_DURATION: return 0; //TODO WTF is there a duration attribute
|
||||
default: return 0;
|
||||
case SORT_ALPHA:
|
||||
return o1.getTitle().compareTo(o2.getTitle());
|
||||
case SORT_DURATION:
|
||||
return 0; //TODO WTF is there a duration attribute
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FolderComparator implements Comparator<AbstractFolder> {
|
||||
private int sort;
|
||||
FolderComparator(int sort) { this.sort = sort; }
|
||||
|
||||
FolderComparator(int sort) {
|
||||
this.sort = sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(AbstractFolder o1, AbstractFolder o2) {
|
||||
switch (sort) {
|
||||
case SORT_DEFAULT:
|
||||
case SORT_ALPHA: return o1.getTitle().compareTo(o2.getTitle());
|
||||
default: return 0;
|
||||
case SORT_ALPHA:
|
||||
return o1.getTitle().compareTo(o2.getTitle());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -291,9 +386,8 @@ public class StubDataSource {
|
||||
return false;
|
||||
}
|
||||
|
||||
long getUUID() {
|
||||
uuid++;
|
||||
return uuid;
|
||||
public long getUUID() {
|
||||
return uuid.addAndGet(1);
|
||||
}
|
||||
|
||||
private void addAudio(AbstractMediaWrapper media, String shortBio, int releaseYear, int albumDuration) {
|
||||
@ -328,13 +422,17 @@ public class StubDataSource {
|
||||
|
||||
private String[] getGenresString() {
|
||||
ArrayList<String> results = new ArrayList<>();
|
||||
for (AbstractGenre genre : mGenres) { results.add(genre.getTitle()); }
|
||||
for (AbstractGenre genre : mGenres) {
|
||||
results.add(genre.getTitle());
|
||||
}
|
||||
return results.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private String[] getFoldersString() {
|
||||
ArrayList<String> results = new ArrayList<>();
|
||||
for (AbstractFolder folder : mFolders) { results.add(folder.getTitle()); }
|
||||
for (AbstractFolder folder : mFolders) {
|
||||
results.add(folder.getTitle());
|
||||
}
|
||||
return results.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import android.os.Parcel;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.videolan.libvlc.Media;
|
||||
import org.videolan.libvlc.interfaces.IMedia;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper;
|
||||
|
||||
public class StubMediaWrapper extends AbstractMediaWrapper {
|
||||
@ -28,7 +29,7 @@ public class StubMediaWrapper extends AbstractMediaWrapper {
|
||||
}
|
||||
|
||||
public StubMediaWrapper(Uri uri) { super(uri); }
|
||||
public StubMediaWrapper(Media media) { super(media); }
|
||||
public StubMediaWrapper(IMedia media) { super(media); }
|
||||
public StubMediaWrapper(Parcel in) { super(in); }
|
||||
|
||||
private SparseArray<Long> mMetaLong = new SparseArray<>();
|
||||
|
@ -3,10 +3,13 @@ package org.videolan.medialibrary.stubs;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.videolan.libvlc.util.Extensions;
|
||||
import org.videolan.medialibrary.MLServiceLocator;
|
||||
import org.videolan.medialibrary.Tools;
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractAlbum;
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractArtist;
|
||||
@ -19,6 +22,7 @@ import org.videolan.medialibrary.media.SearchAggregate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
public class StubMedialibrary extends AbstractMedialibrary {
|
||||
|
||||
@ -32,6 +36,11 @@ public class StubMedialibrary extends AbstractMedialibrary {
|
||||
return ML_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStarted() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
isMedialibraryStarted = true;
|
||||
synchronized (onMedialibraryReadyListeners) {
|
||||
@ -306,8 +315,8 @@ public class StubMedialibrary extends AbstractMedialibrary {
|
||||
public AbstractMediaWrapper[] lastMediaPlayed() {
|
||||
ArrayList<AbstractMediaWrapper> results = new ArrayList<>();
|
||||
for (AbstractMediaWrapper media : dt.mHistory) {
|
||||
if (media.getItemType() == AbstractMediaWrapper.TYPE_VIDEO ||
|
||||
media.getItemType() == AbstractMediaWrapper.TYPE_AUDIO) results.add(media);
|
||||
if (media.getType() == AbstractMediaWrapper.TYPE_VIDEO ||
|
||||
media.getType() == AbstractMediaWrapper.TYPE_AUDIO) results.add(media);
|
||||
// the native method specifies an nbItems of 100, offset 0
|
||||
if (results.size() >= 100) break;
|
||||
}
|
||||
@ -317,7 +326,7 @@ public class StubMedialibrary extends AbstractMedialibrary {
|
||||
public AbstractMediaWrapper[] lastStreamsPlayed() {
|
||||
ArrayList<AbstractMediaWrapper> results = new ArrayList<>();
|
||||
for (AbstractMediaWrapper media : dt.mHistory) {
|
||||
if (media.getItemType() == AbstractMediaWrapper.TYPE_STREAM) results.add(media);
|
||||
if (media.getType() == AbstractMediaWrapper.TYPE_STREAM) results.add(media);
|
||||
// the native method specifies an nbItems of 100, offset 0
|
||||
if (results.size() >= 100) break;
|
||||
}
|
||||
@ -347,15 +356,19 @@ public class StubMedialibrary extends AbstractMedialibrary {
|
||||
|
||||
// TODO Handle uri to mrl
|
||||
private AbstractMediaWrapper getMedia(String mrl, String title) {
|
||||
mrl = Tools.encodeVLCMrl(mrl);
|
||||
for (AbstractMediaWrapper media : dt.mVideoMediaWrappers) {
|
||||
if (media.getTitle().equals(title)) return media;
|
||||
if (media.getLocation().equals(mrl)) return media;
|
||||
}
|
||||
for (AbstractMediaWrapper media : dt.mAudioMediaWrappers) {
|
||||
if (media.getTitle().equals(title)) return media;
|
||||
if (media.getLocation().equals(mrl)) return media;
|
||||
}
|
||||
for (AbstractMediaWrapper media : dt.mStreamMediaWrappers) {
|
||||
if (media.getTitle().equals(title)) return media;
|
||||
if (media.getLocation().equals(mrl)) return media;
|
||||
}
|
||||
|
||||
if (!URLUtil.isNetworkUrl(mrl))
|
||||
return dt.addMediaWrapper(mrl, title, AbstractMediaWrapper.TYPE_ALL);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -400,7 +413,7 @@ public class StubMedialibrary extends AbstractMedialibrary {
|
||||
}
|
||||
|
||||
public AbstractMediaWrapper addStream(String mrl, String title) {
|
||||
return null;
|
||||
return dt.addMediaWrapper(mrl, title, AbstractMediaWrapper.TYPE_STREAM);
|
||||
}
|
||||
|
||||
public AbstractFolder[] getFolders(int type, int sort, boolean desc, int nbItems, int offset) {
|
||||
|
@ -22,7 +22,7 @@ package org.videolan.tools
|
||||
|
||||
open class SingletonHolder<T, in A>(creator: (A) -> T) {
|
||||
private var creator: ((A) -> T)? = creator
|
||||
@Volatile protected var instance: T? = null
|
||||
@Volatile var instance: T? = null
|
||||
|
||||
fun getInstance(arg: A) = instance ?: synchronized(this) {
|
||||
val i2 = instance
|
||||
|
@ -273,6 +273,7 @@ dependencies {
|
||||
|
||||
// Kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.ext.kotlinx_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.ext.kotlinx_version"
|
||||
|
||||
@ -282,8 +283,7 @@ dependencies {
|
||||
implementation "com.squareup.moshi:moshi-adapters:$rootProject.ext.moshi"
|
||||
implementation("com.squareup.okhttp3:logging-interceptor:4.2.1")
|
||||
|
||||
// Test
|
||||
testImplementation project(":medialibrary")
|
||||
// Tests
|
||||
androidTestImplementation "androidx.test.espresso:espresso-contrib:$rootProject.espressoVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$rootProject.espressoVersion"
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
@ -295,15 +295,11 @@ dependencies {
|
||||
testImplementation "androidx.test:core:$rootProject.ext.supportTest"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$rootProject.ext.kotlinx_version"
|
||||
testImplementation "org.mockito:mockito-core:$rootProject.ext.mockito"
|
||||
testImplementation "io.mockk:mockk:$rootProject.ext.mockk"
|
||||
testImplementation "org.powermock:powermock-api-mockito2:$rootProject.ext.powerMock"
|
||||
testImplementation "org.powermock:powermock-module-junit4:$rootProject.ext.powerMock"
|
||||
// testImplementation "org.powermock:powermock-module-junit4-rule:$rootProject.ext.powerMock"
|
||||
testImplementation "org.powermock:powermock-module-junit4-rule-agent:$rootProject.ext.powerMock"
|
||||
testImplementation "org.powermock:powermock-classloading-xstream:$rootProject.ext.powerMock"
|
||||
testImplementation "com.jraska.livedata:testing-ktx:$rootProject.ext.livedataTest"
|
||||
testImplementation "org.robolectric:robolectric:$rootProject.ext.robolectric"
|
||||
|
||||
// unmock 'org.robolectric:android-all:7.1.0_r7-robolectric-0'
|
||||
androidTestImplementation 'androidx.test:rules:1.3.0-alpha01'
|
||||
}
|
||||
|
||||
|
@ -49,10 +49,10 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.SendChannel
|
||||
import kotlinx.coroutines.channels.actor
|
||||
import org.videolan.libvlc.IVLCVout
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.RendererItem
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.interfaces.IVLCVout
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
@ -184,7 +184,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
}
|
||||
MediaPlayer.Event.EncounteredError -> executeUpdate()
|
||||
MediaPlayer.Event.PositionChanged -> if (widget != 0) updateWidgetPosition(event.positionChanged)
|
||||
MediaPlayer.Event.ESAdded -> if (event.esChangedType == Media.Track.Type.Video && (playlistManager.videoBackground || !playlistManager.switchToVideo())) {
|
||||
MediaPlayer.Event.ESAdded -> if (event.esChangedType == IMedia.Track.Type.Video && (playlistManager.videoBackground || !playlistManager.switchToVideo())) {
|
||||
/* CbAction notification content intent: resume video or resume audio activity */
|
||||
updateMetadata()
|
||||
}
|
||||
@ -333,7 +333,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
@MainThread
|
||||
get() = playlistManager.player.getLength()
|
||||
|
||||
val lastStats: Media.Stats?
|
||||
val lastStats: IMedia.Stats?
|
||||
get() = playlistManager.player.previousMediaStats
|
||||
|
||||
val isPlayingPopup: Boolean
|
||||
@ -409,7 +409,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
@MainThread
|
||||
get() = playlistManager.player.getVideoTracks()
|
||||
|
||||
val currentVideoTrack: Media.VideoTrack?
|
||||
val currentVideoTrack: IMedia.VideoTrack?
|
||||
@MainThread
|
||||
get() = playlistManager.player.getCurrentVideoTrack()
|
||||
|
||||
@ -439,7 +439,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
|
||||
interface Callback {
|
||||
fun update()
|
||||
fun onMediaEvent(event: Media.Event)
|
||||
fun onMediaEvent(event: IMedia.Event)
|
||||
fun onMediaPlayerEvent(event: MediaPlayer.Event)
|
||||
}
|
||||
|
||||
@ -579,7 +579,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
dispatcher.onServicePreSuperOnBind()
|
||||
return if (MediaBrowserServiceCompat.SERVICE_INTERFACE == intent.action) super.onBind(intent) else binder
|
||||
return if (SERVICE_INTERFACE == intent.action) super.onBind(intent) else binder
|
||||
}
|
||||
|
||||
val vout: IVLCVout?
|
||||
@ -645,7 +645,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
|
||||
private fun canSwitchToVideo() = playlistManager.player.canSwitchToVideo()
|
||||
|
||||
fun onMediaEvent(event: Media.Event) = cbActor.safeOffer(CbMediaEvent(event))
|
||||
fun onMediaEvent(event: IMedia.Event) = cbActor.safeOffer(CbMediaEvent(event))
|
||||
|
||||
fun executeUpdate() {
|
||||
cbActor.safeOffer(CbUpdate)
|
||||
@ -1345,7 +1345,7 @@ class PlaybackService : MediaBrowserServiceCompat(), LifecycleOwner {
|
||||
private sealed class CbAction
|
||||
|
||||
private object CbUpdate : CbAction()
|
||||
private class CbMediaEvent(val event: Media.Event) : CbAction()
|
||||
private class CbMediaEvent(val event: IMedia.Event) : CbAction()
|
||||
private class CbMediaPlayerEvent(val event: MediaPlayer.Event) : CbAction()
|
||||
private class CbAdd(val cb: PlaybackService.Callback) : CbAction()
|
||||
private class CbRemove(val cb: PlaybackService.Callback) : CbAction()
|
||||
|
@ -11,8 +11,9 @@ import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.Surface
|
||||
import kotlinx.coroutines.*
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.FactoryManager
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory
|
||||
import org.videolan.vlc.media.MediaPlayerEventListener
|
||||
import org.videolan.vlc.media.PlayerController
|
||||
import org.videolan.vlc.util.VLCInstance
|
||||
@ -28,6 +29,8 @@ private const val TAG = "PreviewInputService"
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
class PreviewVideoInputService : TvInputService(), CoroutineScope by MainScope() {
|
||||
|
||||
internal val mFactory = FactoryManager.getFactory(IMediaFactory.factoryId) as IMediaFactory
|
||||
|
||||
override fun onCreateSession(inputId: String): TvInputService.Session? {
|
||||
return PreviewSession(this)
|
||||
}
|
||||
@ -60,7 +63,7 @@ class PreviewVideoInputService : TvInputService(), CoroutineScope by MainScope()
|
||||
return@launch
|
||||
}
|
||||
try {
|
||||
val media = Media(VLCInstance.get(this@PreviewVideoInputService), mw.uri)
|
||||
val media = mFactory.getFromUri(VLCInstance.get(this@PreviewVideoInputService), mw.uri)
|
||||
val start = if (mw.length <= 0L) 0L else mw.length.random()
|
||||
media.addOption(":start-time=${start/1000L}")
|
||||
awaitSurface()
|
||||
|
@ -38,6 +38,11 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import org.videolan.libvlc.Dialog
|
||||
import org.videolan.libvlc.FactoryManager
|
||||
import org.videolan.libvlc.LibVLCFactory
|
||||
import org.videolan.libvlc.MediaFactory
|
||||
import org.videolan.libvlc.interfaces.ILibVLCFactory
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.tools.isStarted
|
||||
import org.videolan.vlc.gui.SendCrashActivity
|
||||
@ -54,6 +59,9 @@ class VLCApplication : MultiDexApplication(), Dialog.Callbacks by DialogDelegate
|
||||
init {
|
||||
instance = this
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
|
||||
FactoryManager.registerFactory(IMediaFactory.factoryId, MediaFactory())
|
||||
FactoryManager.registerFactory(ILibVLCFactory.factoryId, LibVLCFactory())
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
|
@ -22,7 +22,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.*
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.FactoryManager
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory
|
||||
import org.videolan.libvlc.util.Extensions
|
||||
import org.videolan.medialibrary.Tools
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
@ -229,9 +231,10 @@ class InfoActivity : AudioPlayerContainerActivity(), View.OnClickListener, PathA
|
||||
class InfoModel : ViewModel() {
|
||||
|
||||
internal val hasSubs = MutableLiveData<Boolean>()
|
||||
internal val mediaTracks = MutableLiveData<List<Media.Track>>()
|
||||
internal val mediaTracks = MutableLiveData<List<IMedia.Track>>()
|
||||
internal val sizeText = MutableLiveData<String>()
|
||||
internal val cover = MutableLiveData<Bitmap>()
|
||||
internal val mMediaFactory = FactoryManager.getFactory(IMediaFactory.factoryId) as IMediaFactory
|
||||
|
||||
internal fun getCover(mrl: String?, width: Int) = viewModelScope.launch {
|
||||
cover.value = mrl?.let { withContext(Dispatchers.IO) { AudioUtil.readCoverBitmap(Uri.decode(it), width) } }
|
||||
@ -240,16 +243,16 @@ class InfoModel : ViewModel() {
|
||||
internal fun parseTracks(context: Context, mw: AbstractMediaWrapper) = viewModelScope.launch {
|
||||
val media = withContext(Dispatchers.IO) {
|
||||
val libVlc = VLCInstance[context]
|
||||
Media(libVlc, mw.uri).apply { parse() }
|
||||
mMediaFactory.getFromUri(libVlc, mw.uri).apply { parse() }
|
||||
}
|
||||
if (!isActive) return@launch
|
||||
var subs = false
|
||||
val trackCount = media.trackCount
|
||||
val tracks = LinkedList<Media.Track>()
|
||||
val tracks = LinkedList<IMedia.Track>()
|
||||
for (i in 0 until trackCount) {
|
||||
val track = media.getTrack(i)
|
||||
tracks.add(track)
|
||||
subs = subs or (track.type == Media.Track.Type.Text)
|
||||
subs = subs or (track.type == IMedia.Track.Type.Text)
|
||||
}
|
||||
media.release()
|
||||
hasSubs.value = subs
|
||||
|
@ -28,12 +28,13 @@ import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.vlc.R
|
||||
import org.videolan.vlc.util.readableSize
|
||||
|
||||
class MediaInfoAdapter : RecyclerView.Adapter<MediaInfoAdapter.ViewHolder>() {
|
||||
private lateinit var inflater: LayoutInflater
|
||||
private var dataset: List<Media.Track>? = null
|
||||
private var dataset: List<IMedia.Track>? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
if (!::inflater.isInitialized)
|
||||
@ -47,17 +48,17 @@ class MediaInfoAdapter : RecyclerView.Adapter<MediaInfoAdapter.ViewHolder>() {
|
||||
val textBuilder = StringBuilder()
|
||||
val res = holder.itemView.context.resources
|
||||
when (track.type) {
|
||||
Media.Track.Type.Audio -> {
|
||||
IMedia.Track.Type.Audio -> {
|
||||
title = res.getString(R.string.track_audio)
|
||||
appendCommon(textBuilder, res, track)
|
||||
appendAudio(textBuilder, res, track as Media.AudioTrack)
|
||||
appendAudio(textBuilder, res, track as IMedia.AudioTrack)
|
||||
}
|
||||
Media.Track.Type.Video -> {
|
||||
IMedia.Track.Type.Video -> {
|
||||
title = res.getString(R.string.track_video)
|
||||
appendCommon(textBuilder, res, track)
|
||||
appendVideo(textBuilder, res, track as Media.VideoTrack)
|
||||
appendVideo(textBuilder, res, track as IMedia.VideoTrack)
|
||||
}
|
||||
Media.Track.Type.Text -> {
|
||||
IMedia.Track.Type.Text -> {
|
||||
title = res.getString(R.string.track_text)
|
||||
appendCommon(textBuilder, res, track)
|
||||
}
|
||||
@ -69,14 +70,14 @@ class MediaInfoAdapter : RecyclerView.Adapter<MediaInfoAdapter.ViewHolder>() {
|
||||
|
||||
override fun getItemCount() = dataset?.size ?: 0
|
||||
|
||||
fun setTracks(tracks: List<Media.Track>) {
|
||||
fun setTracks(tracks: List<IMedia.Track>) {
|
||||
val size = itemCount
|
||||
dataset = tracks
|
||||
if (size > 0) notifyItemRangeRemoved(0, size - 1)
|
||||
notifyItemRangeInserted(0, tracks.size)
|
||||
}
|
||||
|
||||
private fun appendCommon(textBuilder: StringBuilder, res: Resources, track: Media.Track) {
|
||||
private fun appendCommon(textBuilder: StringBuilder, res: Resources, track: IMedia.Track) {
|
||||
if (track.bitrate != 0)
|
||||
textBuilder.append(res.getString(R.string.track_bitrate_info, track.bitrate.toLong().readableSize()))
|
||||
textBuilder.append(res.getString(R.string.track_codec_info, track.codec))
|
||||
@ -84,12 +85,12 @@ class MediaInfoAdapter : RecyclerView.Adapter<MediaInfoAdapter.ViewHolder>() {
|
||||
textBuilder.append(res.getString(R.string.track_language_info, track.language))
|
||||
}
|
||||
|
||||
private fun appendAudio(textBuilder: StringBuilder, res: Resources, track: Media.AudioTrack) {
|
||||
private fun appendAudio(textBuilder: StringBuilder, res: Resources, track: IMedia.AudioTrack) {
|
||||
textBuilder.append(res.getQuantityString(R.plurals.track_channels_info_quantity, track.channels, track.channels))
|
||||
textBuilder.append(res.getString(R.string.track_samplerate_info, track.rate))
|
||||
}
|
||||
|
||||
private fun appendVideo(textBuilder: StringBuilder, res: Resources, track: Media.VideoTrack) {
|
||||
private fun appendVideo(textBuilder: StringBuilder, res: Resources, track: IMedia.VideoTrack) {
|
||||
val framerate = track.frameRateNum / track.frameRateDen.toDouble()
|
||||
if (track.width != 0 && track.height != 0)
|
||||
textBuilder.append(res.getString(R.string.track_resolution_info, track.width, track.height))
|
||||
|
@ -36,9 +36,10 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.videolan.libvlc.IVLCVout
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.interfaces.IVLCVout
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.vlc.PlaybackService
|
||||
import org.videolan.vlc.R
|
||||
@ -184,7 +185,7 @@ class PopupManager constructor(private val mService: PlaybackService) : Playback
|
||||
|
||||
override fun update() {}
|
||||
|
||||
override fun onMediaEvent(event: Media.Event) {}
|
||||
override fun onMediaEvent(event: IMedia.Event) {}
|
||||
|
||||
override fun onMediaPlayerEvent(event: MediaPlayer.Event) {
|
||||
when (event.type) {
|
||||
|
@ -73,6 +73,7 @@ import kotlinx.coroutines.withContext
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.RendererItem
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.libvlc.util.DisplayManager
|
||||
import org.videolan.libvlc.util.VLCVideoLayout
|
||||
@ -892,7 +893,7 @@ open class VideoPlayerActivity : AppCompatActivity(), IPlaybackSettingsControlle
|
||||
if (data.hasExtra(EXTRA_MRL)) {
|
||||
service?.addSubtitleTrack(Uri.parse(data.getStringExtra(EXTRA_MRL)), false)
|
||||
service?.currentMediaWrapper?.let {
|
||||
SlaveRepository.getInstance(this).saveSlave(it.location, Media.Slave.Type.Subtitle, 2, data.getStringExtra(EXTRA_MRL))
|
||||
SlaveRepository.getInstance(this).saveSlave(it.location, IMedia.Slave.Type.Subtitle, 2, data.getStringExtra(EXTRA_MRL))
|
||||
}
|
||||
addNextTrack = true
|
||||
} else if (BuildConfig.DEBUG) Log.d(TAG, "Subtitle selection dialog was cancelled")
|
||||
@ -1459,10 +1460,10 @@ open class VideoPlayerActivity : AppCompatActivity(), IPlaybackSettingsControlle
|
||||
playlistModel?.update()
|
||||
}
|
||||
|
||||
override fun onMediaEvent(event: Media.Event) {
|
||||
override fun onMediaEvent(event: IMedia.Event) {
|
||||
when (event.type) {
|
||||
Media.Event.ParsedChanged -> updateNavStatus()
|
||||
Media.Event.MetaChanged -> {
|
||||
IMedia.Event.ParsedChanged -> updateNavStatus()
|
||||
IMedia.Event.MetaChanged -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1483,14 +1484,14 @@ open class VideoPlayerActivity : AppCompatActivity(), IPlaybackSettingsControlle
|
||||
MediaPlayer.Event.ESAdded -> {
|
||||
if (menuIdx == -1) {
|
||||
val media = medialibrary.findMedia(service.currentMediaWrapper) ?: return
|
||||
if (event.esChangedType == Media.Track.Type.Audio) {
|
||||
if (event.esChangedType == IMedia.Track.Type.Audio) {
|
||||
setESTrackLists()
|
||||
runIO(Runnable {
|
||||
val audioTrack = media.getMetaLong(AbstractMediaWrapper.META_AUDIOTRACK).toInt()
|
||||
if (audioTrack != 0 || currentAudioTrack != -2)
|
||||
service.setAudioTrack(if (media.id == 0L) currentAudioTrack else audioTrack)
|
||||
})
|
||||
} else if (event.esChangedType == Media.Track.Type.Text) {
|
||||
} else if (event.esChangedType == IMedia.Track.Type.Text) {
|
||||
setESTrackLists()
|
||||
runIO(Runnable {
|
||||
val spuTrack = media.getMetaLong(AbstractMediaWrapper.META_SUBTITLE_TRACK).toInt()
|
||||
@ -1504,23 +1505,23 @@ open class VideoPlayerActivity : AppCompatActivity(), IPlaybackSettingsControlle
|
||||
})
|
||||
}
|
||||
}
|
||||
if (menuIdx == -1 && event.esChangedType == Media.Track.Type.Video) {
|
||||
if (menuIdx == -1 && event.esChangedType == IMedia.Track.Type.Video) {
|
||||
handler.removeMessages(CHECK_VIDEO_TRACKS)
|
||||
handler.sendEmptyMessageDelayed(CHECK_VIDEO_TRACKS, 1000)
|
||||
}
|
||||
invalidateESTracks(event.esChangedType)
|
||||
}
|
||||
MediaPlayer.Event.ESDeleted -> {
|
||||
if (menuIdx == -1 && event.esChangedType == Media.Track.Type.Video) {
|
||||
if (menuIdx == -1 && event.esChangedType == IMedia.Track.Type.Video) {
|
||||
handler.removeMessages(CHECK_VIDEO_TRACKS)
|
||||
handler.sendEmptyMessageDelayed(CHECK_VIDEO_TRACKS, 1000)
|
||||
}
|
||||
invalidateESTracks(event.esChangedType)
|
||||
}
|
||||
MediaPlayer.Event.ESSelected -> if (event.esChangedType == Media.Track.Type.Video) {
|
||||
MediaPlayer.Event.ESSelected -> if (event.esChangedType == IMedia.Track.Type.Video) {
|
||||
val vt = service.currentVideoTrack
|
||||
if (vt != null)
|
||||
fov = if (vt.projection == Media.VideoTrack.Projection.Rectangular) 0f else DEFAULT_FOV
|
||||
fov = if (vt.projection == IMedia.VideoTrack.Projection.Rectangular) 0f else DEFAULT_FOV
|
||||
}
|
||||
MediaPlayer.Event.SeekableChanged -> updateSeekable(event.seekable)
|
||||
MediaPlayer.Event.PausableChanged -> updatePausable(event.pausable)
|
||||
@ -2223,8 +2224,8 @@ open class VideoPlayerActivity : AppCompatActivity(), IPlaybackSettingsControlle
|
||||
|
||||
private fun invalidateESTracks(type: Int) {
|
||||
when (type) {
|
||||
Media.Track.Type.Audio -> audioTracksList = null
|
||||
Media.Track.Type.Text -> subtitleTracksList = null
|
||||
IMedia.Track.Type.Audio -> audioTracksList = null
|
||||
IMedia.Track.Type.Text -> subtitleTracksList = null
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,10 +121,11 @@ class BenchActivity : ShallowVideoPlayer() {
|
||||
val sharedPref = Settings.getInstance(this)
|
||||
mOldOpenglValue = sharedPref.getString(PREFERENCE_OPENGL, "-1")
|
||||
mOldHistoryBoolean = sharedPref.getBoolean(PREFERENCE_PLAYBACK_HISTORY, true)
|
||||
val editor = sharedPref.edit()
|
||||
editor.putString(PREFERENCE_OPENGL, "0")
|
||||
editor.putBoolean(PREFERENCE_PLAYBACK_HISTORY, false)
|
||||
editor.commit()
|
||||
sharedPref.edit().run {
|
||||
putString(PREFERENCE_OPENGL, "0")
|
||||
putBoolean(PREFERENCE_PLAYBACK_HISTORY, false)
|
||||
commit()
|
||||
}
|
||||
VLCInstance.restart()
|
||||
this.service?.restartMediaPlayer()
|
||||
}
|
||||
@ -480,10 +481,11 @@ class BenchActivity : ShallowVideoPlayer() {
|
||||
/* Resetting vout preference to it value before the benchmark */
|
||||
if (mIsHardware && mOldOpenglValue != "-2") {
|
||||
val sharedPref = Settings.getInstance(this)
|
||||
val editor = sharedPref.edit()
|
||||
editor.putString(PREFERENCE_OPENGL, mOldOpenglValue)
|
||||
editor.putBoolean(PREFERENCE_PLAYBACK_HISTORY, mOldHistoryBoolean)
|
||||
editor.commit()
|
||||
sharedPref.edit().run {
|
||||
putString(PREFERENCE_OPENGL, mOldOpenglValue)
|
||||
putBoolean(PREFERENCE_PLAYBACK_HISTORY, mOldHistoryBoolean)
|
||||
commit()
|
||||
}
|
||||
VLCInstance.restart()
|
||||
}
|
||||
/* Case of error in VideoPlayerActivity, then finish is not overridden */
|
||||
|
@ -33,7 +33,7 @@ import android.util.AttributeSet
|
||||
import android.view.*
|
||||
import android.widget.RelativeLayout
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import org.videolan.libvlc.IVLCVout
|
||||
import org.videolan.libvlc.interfaces.IVLCVout
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.vlc.R
|
||||
|
||||
|
@ -10,6 +10,9 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.actor
|
||||
import org.videolan.libvlc.*
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.interfaces.IMediaList
|
||||
import org.videolan.libvlc.interfaces.IVLCVout
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.vlc.BuildConfig
|
||||
import org.videolan.vlc.PlaybackService
|
||||
@ -34,7 +37,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
var switchToVideo = false
|
||||
var seekable = false
|
||||
var pausable = false
|
||||
var previousMediaStats: Media.Stats? = null
|
||||
var previousMediaStats: IMedia.Stats? = null
|
||||
private set
|
||||
@Volatile var hasRenderer = false
|
||||
private set
|
||||
@ -43,7 +46,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
|
||||
fun canDoPassthrough() = mediaplayer.hasMedia() && !mediaplayer.isReleased && mediaplayer.canDoPassthrough()
|
||||
|
||||
fun getMedia(): Media? = mediaplayer.media
|
||||
fun getMedia(): IMedia? = mediaplayer.media
|
||||
|
||||
fun play() {
|
||||
if (mediaplayer.hasMedia() && !mediaplayer.isReleased) mediaplayer.play()
|
||||
@ -68,7 +71,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
}
|
||||
|
||||
private var mediaplayerEventListener: MediaPlayerEventListener? = null
|
||||
internal suspend fun startPlayback(media: Media, listener: MediaPlayerEventListener, time: Long) {
|
||||
internal suspend fun startPlayback(media: IMedia, listener: MediaPlayerEventListener, time: Long) {
|
||||
mediaplayerEventListener = listener
|
||||
resetPlaybackState(time, media.duration)
|
||||
mediaplayer.setEventListener(null)
|
||||
@ -120,7 +123,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
|
||||
fun getVideoTrack() = if (!mediaplayer.isReleased && mediaplayer.hasMedia()) mediaplayer.videoTrack else -1
|
||||
|
||||
fun getCurrentVideoTrack(): Media.VideoTrack? = if (!mediaplayer.isReleased && mediaplayer.hasMedia()) mediaplayer.currentVideoTrack else null
|
||||
fun getCurrentVideoTrack(): IMedia.VideoTrack? = if (!mediaplayer.isReleased && mediaplayer.hasMedia()) mediaplayer.currentVideoTrack else null
|
||||
|
||||
fun getAudioTracksCount() = if (!mediaplayer.isReleased && mediaplayer.hasMedia()) mediaplayer.audioTracksCount else 0
|
||||
|
||||
@ -144,9 +147,9 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
|
||||
fun setVideoTrackEnabled(enabled: Boolean) = mediaplayer.setVideoTrackEnabled(enabled)
|
||||
|
||||
fun addSubtitleTrack(path: String, select: Boolean) = mediaplayer.addSlave(Media.Slave.Type.Subtitle, path, select)
|
||||
fun addSubtitleTrack(path: String, select: Boolean) = mediaplayer.addSlave(IMedia.Slave.Type.Subtitle, path, select)
|
||||
|
||||
fun addSubtitleTrack(uri: Uri, select: Boolean) = mediaplayer.addSlave(Media.Slave.Type.Subtitle, uri, select)
|
||||
fun addSubtitleTrack(uri: Uri, select: Boolean) = mediaplayer.addSlave(IMedia.Slave.Type.Subtitle, uri, select)
|
||||
|
||||
fun getSpuTracks(): Array<out MediaPlayer.TrackDescription>? = mediaplayer.spuTracks
|
||||
|
||||
@ -190,7 +193,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
setPlaybackStopped()
|
||||
}
|
||||
|
||||
fun setSlaves(media: Media, mw: AbstractMediaWrapper) = launch {
|
||||
fun setSlaves(media: IMedia, mw: AbstractMediaWrapper) = launch {
|
||||
if (mediaplayer.isReleased) return@launch
|
||||
val slaves = mw.slaves
|
||||
slaves?.let { it.forEach { slave -> media.addSlave(slave) } }
|
||||
@ -234,9 +237,9 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
* @return true if UI needs to be updated
|
||||
*/
|
||||
internal fun updateCurrentMeta(id: Int, mw: AbstractMediaWrapper?): Boolean {
|
||||
if (id == Media.Meta.Publisher) return false
|
||||
if (id == IMedia.Meta.Publisher) return false
|
||||
mw?.updateMeta(mediaplayer)
|
||||
return id != Media.Meta.NowPlaying || mw?.nowPlaying !== null
|
||||
return id != IMedia.Meta.NowPlaying || mw?.nowPlaying !== null
|
||||
}
|
||||
|
||||
fun setPreviousStats() {
|
||||
@ -269,7 +272,7 @@ class PlayerController(val context: Context) : IVLCVout.Callback, MediaPlayer.Ev
|
||||
|
||||
fun setVolume(volume: Int) = if (!mediaplayer.isReleased) mediaplayer.setVolume(volume) else -1
|
||||
|
||||
suspend fun expand(): MediaList? {
|
||||
suspend fun expand(): IMediaList? {
|
||||
return mediaplayer.media?.let {
|
||||
return withContext(playerContext) {
|
||||
mediaplayer.setEventListener(null)
|
||||
@ -336,7 +339,7 @@ internal interface MediaPlayerEventListener {
|
||||
suspend fun onEvent(event: MediaPlayer.Event)
|
||||
}
|
||||
|
||||
private fun Array<Media.Slave>?.contains(item: Media.Slave) : Boolean {
|
||||
private fun Array<IMedia.Slave>?.contains(item: IMedia.Slave) : Boolean {
|
||||
if (this == null) return false
|
||||
for (slave in this) if (slave.uri == item.uri) return true
|
||||
return false
|
||||
|
@ -12,9 +12,9 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.actor
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.RendererItem
|
||||
import org.videolan.libvlc.*
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.interfaces.IMediaFactory
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
@ -34,7 +34,7 @@ private const val PLAYLIST_REPEAT_MODE_KEY = "audio_repeat_mode" //we keep the o
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventListener, Media.EventListener, CoroutineScope {
|
||||
class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventListener, IMedia.EventListener, CoroutineScope {
|
||||
override val coroutineContext = Dispatchers.Main.immediate + SupervisorJob()
|
||||
|
||||
companion object {
|
||||
@ -67,6 +67,8 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
|
||||
private var entryUrl : String? = null
|
||||
val abRepeat by lazy(LazyThreadSafetyMode.NONE) { MutableLiveData<ABRepeat>().apply { value = ABRepeat() } }
|
||||
|
||||
internal val mMediaFactory = FactoryManager.getFactory(IMediaFactory.factoryId) as IMediaFactory
|
||||
|
||||
fun hasCurrentMedia() = isValidPosition(currentIndex)
|
||||
|
||||
fun hasPlaylist() = mediaList.size() > 1
|
||||
@ -298,7 +300,7 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
|
||||
return
|
||||
}
|
||||
val start = getStartTime(mw)
|
||||
val media = Media(VLCInstance.get(service), uri)
|
||||
val media = mMediaFactory.getFromUri(VLCInstance.get(service), uri)
|
||||
media.addOption(":start-time=${start/1000L}")
|
||||
VLCOptions.setMediaOptions(media, ctx, flags or mw.flags)
|
||||
/* keeping only video during benchmark */
|
||||
@ -709,15 +711,15 @@ class PlaylistManager(val service: PlaybackService) : MediaWrapperList.EventList
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEvent(event: Media.Event) {
|
||||
override fun onEvent(event: IMedia.Event) {
|
||||
var update = true
|
||||
when (event.type) {
|
||||
Media.Event.MetaChanged -> {
|
||||
IMedia.Event.MetaChanged -> {
|
||||
/* Update Meta if file is already parsed */
|
||||
if (parsed && player.updateCurrentMeta(event.metaId, getCurrentMedia())) service.executeUpdate()
|
||||
if (BuildConfig.DEBUG) Log.i(TAG, "Media.Event.MetaChanged: " + event.metaId)
|
||||
}
|
||||
Media.Event.ParsedChanged -> {
|
||||
IMedia.Event.ParsedChanged -> {
|
||||
if (BuildConfig.DEBUG) Log.i(TAG, "Media.Event.ParsedChanged")
|
||||
player.updateCurrentMeta(-1, getCurrentMedia())
|
||||
parsed = true
|
||||
|
@ -33,6 +33,7 @@ import kotlinx.coroutines.channels.actor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.libvlc.util.MediaBrowser.EventListener
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
@ -41,6 +42,7 @@ import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.medialibrary.media.MediaLibraryItem
|
||||
import org.videolan.medialibrary.media.Storage
|
||||
import org.videolan.vlc.R
|
||||
import org.videolan.vlc.VLCApplication
|
||||
import org.videolan.vlc.util.*
|
||||
import java.util.*
|
||||
|
||||
@ -48,12 +50,15 @@ const val TAG = "VLC/BrowserProvider"
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
|
||||
abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<MediaLibraryItem>, val url: String?, private var showHiddenFiles: Boolean) : CoroutineScope, HeaderProvider() {
|
||||
|
||||
override val coroutineContext = Dispatchers.Main.immediate + SupervisorJob()
|
||||
val loading = MutableLiveData<Boolean>().apply { value = false }
|
||||
|
||||
protected var mediabrowser: MediaBrowser? = null
|
||||
var mediabrowser: MediaBrowser? = null
|
||||
|
||||
val coroutineContextProvider: CoroutineContextProvider
|
||||
private var parsingJob : Job? = null
|
||||
private var discoveryJob : Job? = null
|
||||
|
||||
@ -63,6 +68,12 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
val descriptionUpdate = MutableLiveData<Pair<Int, String>>()
|
||||
internal val medialibrary = AbstractMedialibrary.getInstance()
|
||||
|
||||
init {
|
||||
// BrowserProvider.registerCreator { CoroutineContextProvider() }
|
||||
// coroutineContextProvider = BrowserProvider.get(this)
|
||||
coroutineContextProvider = CoroutineContextProvider()
|
||||
}
|
||||
|
||||
private val completionHandler : CompletionHandler = object : CompletionHandler {
|
||||
override fun invoke(cause: Throwable?) {
|
||||
if (mediabrowser != null) AppScope.launch(Dispatchers.IO) { // use global scope because current is cancelled
|
||||
@ -89,6 +100,8 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
}
|
||||
|
||||
protected open fun initBrowser() {
|
||||
// BrowserProvider.registerCreator { MediaBrowser(VLCInstance[context], it, browserHandler) }
|
||||
// if (mediabrowser == null) mediabrowser = BrowserProvider.get(this)
|
||||
if (mediabrowser == null) mediabrowser = MediaBrowser(VLCInstance[context], null, browserHandler)
|
||||
}
|
||||
|
||||
@ -130,7 +143,7 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
|
||||
private suspend fun filesFlow(url: String? = this.url, interact : Boolean = true) = channelFlow {
|
||||
val listener = object : EventListener {
|
||||
override fun onMediaAdded(index: Int, media: Media) {
|
||||
override fun onMediaAdded(index: Int, media: IMedia) {
|
||||
if (!isClosedForSend) offer(media.apply { retain() })
|
||||
}
|
||||
|
||||
@ -138,7 +151,7 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
if (!isClosedForSend) close()
|
||||
}
|
||||
|
||||
override fun onMediaRemoved(index: Int, media: Media) {}
|
||||
override fun onMediaRemoved(index: Int, media: IMedia) {}
|
||||
}
|
||||
requestBrowsing(url, listener, interact)
|
||||
awaitClose { if (url != null) browserActor.post(ClearListener) }
|
||||
@ -174,12 +187,12 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
|
||||
private suspend fun parseSubDirectoriesImpl(list : List<MediaLibraryItem>? = null) {
|
||||
if (list === null && dataset.value.isEmpty()) return
|
||||
val currentMediaList = list ?: withContext(Dispatchers.Main) { dataset.value.toList() }
|
||||
val currentMediaList = list ?: withContext(coroutineContextProvider.Main) { dataset.value.toList() }
|
||||
val directories: MutableList<AbstractMediaWrapper> = ArrayList()
|
||||
val files: MutableList<AbstractMediaWrapper> = ArrayList()
|
||||
foldersContentMap.clear()
|
||||
coroutineScope { // allow child coroutine to be cancelled without closing the actor.
|
||||
parsingJob = launch (Dispatchers.IO) {
|
||||
parsingJob = launch (coroutineContextProvider.IO) {
|
||||
initBrowser()
|
||||
var currentParsedPosition = -1
|
||||
loop@ while (++currentParsedPosition < currentMediaList.size) {
|
||||
@ -210,12 +223,12 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
// all subitems are in
|
||||
getDescription(directories.size, files.size).takeIf { it.isNotEmpty() }?.let {
|
||||
val position = currentParsedPosition
|
||||
withContext(Dispatchers.Main) {
|
||||
withContext(coroutineContextProvider.Main) {
|
||||
item.description = it
|
||||
descriptionUpdate.value = Pair(position, it)
|
||||
}
|
||||
directories.addAll(files)
|
||||
withContext(Dispatchers.Main) { foldersContentMap.put(item, directories.toMutableList()) }
|
||||
withContext(coroutineContextProvider.Main) { foldersContentMap.put(item, directories.toMutableList()) }
|
||||
}
|
||||
directories.clear()
|
||||
files.clear()
|
||||
@ -238,8 +251,8 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
protected open suspend fun findMedia(media: Media): MediaLibraryItem? {
|
||||
val mw = MLServiceLocator.getAbstractMediaWrapper(media)
|
||||
protected open suspend fun findMedia(media: IMedia): MediaLibraryItem? {
|
||||
val mw: AbstractMediaWrapper = MLServiceLocator.getAbstractMediaWrapper(media)
|
||||
media.release()
|
||||
if (!mw.isMedia()) {
|
||||
if (mw.isBrowserMedia()) return mw
|
||||
@ -247,7 +260,7 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
}
|
||||
val uri = mw.uri
|
||||
if ((mw.type == AbstractMediaWrapper.TYPE_AUDIO || mw.type == AbstractMediaWrapper.TYPE_VIDEO)
|
||||
&& "file" == uri.scheme) return withContext(Dispatchers.IO) {
|
||||
&& "file" == uri.scheme) return withContext(coroutineContextProvider.IO) {
|
||||
medialibrary.getMedia(uri) ?: mw
|
||||
}
|
||||
return mw
|
||||
@ -292,11 +305,11 @@ abstract class BrowserProvider(val context: Context, val dataset: LiveDataset<Me
|
||||
|
||||
protected fun removeList(url: String) = prefetchLists.remove(url)
|
||||
|
||||
fun saveList(media: AbstractMediaWrapper) = foldersContentMap[media]?.let { if (!it.isEmpty()) prefetchLists[media.location] = it }
|
||||
fun saveList(media: AbstractMediaWrapper) = foldersContentMap[media]?.let { if (it.isNotEmpty()) prefetchLists[media.location] = it }
|
||||
|
||||
fun isFolderEmpty(mw: AbstractMediaWrapper) = foldersContentMap[mw]?.isEmpty() ?: true
|
||||
|
||||
companion object {
|
||||
companion object : DependencyProvider<EventListener>() {
|
||||
private val browserHandler by lazy {
|
||||
val handlerThread = HandlerThread("vlc-provider", Process.THREAD_PRIORITY_DEFAULT + Process.THREAD_PRIORITY_LESS_FAVORABLE)
|
||||
handlerThread.start()
|
||||
|
@ -22,6 +22,7 @@ package org.videolan.vlc.providers
|
||||
|
||||
import android.content.Context
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
@ -38,7 +39,7 @@ class FilePickerProvider(context: Context, dataset: LiveDataset<MediaLibraryItem
|
||||
mediabrowser?.setIgnoreFileTypes("db,nfo,ini,jpg,jpeg,ljpg,gif,png,pgm,pgmyuv,pbm,pam,tga,bmp,pnm,xpm,xcf,pcx,tif,tiff,lbm,sfv")
|
||||
}
|
||||
|
||||
override suspend fun findMedia(media: Media) = MLServiceLocator.getAbstractMediaWrapper(media)?.takeIf { mw ->
|
||||
override suspend fun findMedia(media: IMedia) = MLServiceLocator.getAbstractMediaWrapper(media)?.takeIf { mw ->
|
||||
mw.type == AbstractMediaWrapper.TYPE_DIR || mw.type == AbstractMediaWrapper.TYPE_SUBTITLE
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.medialibrary.media.MediaLibraryItem
|
||||
import org.videolan.medialibrary.media.Storage
|
||||
import org.videolan.vlc.R
|
||||
@ -59,9 +61,9 @@ class StorageProvider(context: Context, dataset: LiveDataset<MediaLibraryItem>,
|
||||
dataset.value = storagesList
|
||||
}
|
||||
|
||||
override suspend fun findMedia(media: Media) = media.takeIf { it.isStorage() }?.let { Storage(it.uri) }
|
||||
override suspend fun findMedia(media: IMedia) = media.takeIf { it.isStorage() }?.let { Storage(it.uri) }
|
||||
|
||||
override fun computeHeaders(value: List<MediaLibraryItem>) {}
|
||||
}
|
||||
|
||||
private fun Media.isStorage() = type == Media.Type.Directory
|
||||
private fun IMedia.isStorage() = type == IMedia.Type.Directory
|
||||
|
@ -34,10 +34,11 @@ import org.videolan.vlc.database.MediaDatabase
|
||||
import org.videolan.vlc.database.models.ExternalSub
|
||||
import org.videolan.vlc.gui.dialogs.State
|
||||
import org.videolan.vlc.gui.dialogs.SubtitleItem
|
||||
import org.videolan.vlc.util.CoroutineContextProvider
|
||||
import org.videolan.vlc.util.LiveDataMap
|
||||
import java.io.File
|
||||
|
||||
class ExternalSubRepository(private val externalSubDao: ExternalSubDao ) {
|
||||
class ExternalSubRepository(private val externalSubDao: ExternalSubDao, private val coroutineContextProvider: CoroutineContextProvider = CoroutineContextProvider()) {
|
||||
|
||||
private var _downloadingSubtitles = LiveDataMap<Long, SubtitleItem>()
|
||||
|
||||
@ -45,7 +46,7 @@ class ExternalSubRepository(private val externalSubDao: ExternalSubDao ) {
|
||||
get() = _downloadingSubtitles as LiveData<Map<Long, SubtitleItem>>
|
||||
|
||||
fun saveDownloadedSubtitle(idSubtitle: String, subtitlePath: String, mediaPath: String, language: String, movieReleaseName: String): Job {
|
||||
return GlobalScope.launch(Dispatchers.IO) { externalSubDao.insert(ExternalSub(idSubtitle, subtitlePath, mediaPath, language, movieReleaseName)) }
|
||||
return GlobalScope.launch(coroutineContextProvider.IO) { externalSubDao.insert(ExternalSub(idSubtitle, subtitlePath, mediaPath, language, movieReleaseName)) }
|
||||
}
|
||||
|
||||
fun getDownloadedSubtitles(mediaUri: Uri): LiveData<List<ExternalSub>> {
|
||||
|
@ -82,5 +82,9 @@ class OpenSubtitleRepository(private val openSubtitleService: IOpenSubtitleServi
|
||||
}
|
||||
}
|
||||
|
||||
companion object { fun getInstance() = OpenSubtitleRepository(OpenSubtitleClient.instance)}
|
||||
companion object {
|
||||
// To ensure the instance can be overridden in tests.
|
||||
var instance = lazy { OpenSubtitleRepository(OpenSubtitleClient.instance) }
|
||||
fun getInstance() = instance.value
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.tools.IOScopedObject
|
||||
import org.videolan.tools.SingletonHolder
|
||||
@ -50,7 +51,7 @@ class SlaveRepository(private val slaveDao:SlaveDao) : IOScopedObject() {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getSlaves(mrl: String): List<Media.Slave> {
|
||||
suspend fun getSlaves(mrl: String): List<IMedia.Slave> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val slaves = try {
|
||||
slaveDao.get(mrl)
|
||||
@ -61,7 +62,7 @@ class SlaveRepository(private val slaveDao:SlaveDao) : IOScopedObject() {
|
||||
var uri = it.uri
|
||||
if (uri.isNotEmpty())
|
||||
uri = Uri.decode(it.uri)
|
||||
Media.Slave(it.type, it.priority, uri)
|
||||
IMedia.Slave(it.type, it.priority, uri)
|
||||
}
|
||||
mediaSlaves
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package org.videolan.vlc.util
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
open class CoroutineContextProvider {
|
||||
open val Default by lazy { Dispatchers.Default }
|
||||
open val IO by lazy { Dispatchers.IO }
|
||||
open val Main: CoroutineDispatcher by lazy { Dispatchers.Main }
|
||||
}
|
31
vlc-android/src/org/videolan/vlc/util/DependencyProvider.kt
Normal file
31
vlc-android/src/org/videolan/vlc/util/DependencyProvider.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package org.videolan.vlc.util
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentMap
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
open class DependencyProvider<A> {
|
||||
val objectMap: ConcurrentMap<String, Any> = ConcurrentHashMap()
|
||||
val creatorMap: ConcurrentMap<String, (A) -> Any> = ConcurrentHashMap()
|
||||
|
||||
var overrideCreator = true
|
||||
|
||||
inline fun <T> getKey(clazz: Class<T>): String = clazz.name
|
||||
|
||||
inline fun <X : Any, reified T : X> registerCreator(clazz: Class<X>? = null, noinline creator: (A) -> T) {
|
||||
val key = getKey(clazz ?: T::class.java)
|
||||
if (overrideCreator || !creatorMap.containsKey(key))
|
||||
creatorMap[key] = creator
|
||||
if (objectMap.containsKey(key) && overrideCreator) {
|
||||
objectMap.remove(key)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <X : Any, reified T : X> get(arg: A, clazz: Class<X>? = null): T {
|
||||
val key = getKey(clazz ?: T::class.java)
|
||||
if (!objectMap.containsKey(key))
|
||||
objectMap[key] = creatorMap[key]?.invoke(arg)
|
||||
return objectMap[key] as T
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
@ -71,7 +72,7 @@ inline fun <reified T : ViewModel> Fragment.getModelWithActivity() = ViewModelPr
|
||||
inline fun <reified T : ViewModel> Fragment.getModel() = ViewModelProviders.of(this).get(T::class.java)
|
||||
inline fun <reified T : ViewModel> FragmentActivity.getModel() = ViewModelProviders.of(this).get(T::class.java)
|
||||
|
||||
fun Media?.canExpand() = this != null && (type == Media.Type.Directory || type == Media.Type.Playlist)
|
||||
fun Media?.canExpand() = this != null && (type == IMedia.Type.Directory || type == IMedia.Type.Playlist)
|
||||
suspend fun AppCompatActivity.share(media: AbstractMediaWrapper) {
|
||||
val intentShareFile = Intent(Intent.ACTION_SEND)
|
||||
val fileWithinMyDir = File(media.uri.path)
|
||||
|
@ -6,6 +6,8 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary.*
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractAlbum
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
@ -294,7 +296,7 @@ object ModelsHelper {
|
||||
|
||||
object EmptyPBSCallback : PlaybackService.Callback {
|
||||
override fun update() {}
|
||||
override fun onMediaEvent(event: Media.Event) {}
|
||||
override fun onMediaEvent(event: IMedia.Event) {}
|
||||
override fun onMediaPlayerEvent(event: MediaPlayer.Event) {}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ object Settings : SingletonHolder<SharedPreferences, Context>({ PreferenceManage
|
||||
var showVideoThumbs = true
|
||||
var tvUI = false
|
||||
var listTitleEllipsize = 0
|
||||
var overrideTvUI = false
|
||||
|
||||
fun init(prefs: SharedPreferences) {
|
||||
showVideoThumbs = prefs.getBoolean(SHOW_VIDEO_THUMBNAILS, true)
|
||||
@ -25,7 +26,7 @@ object Settings : SingletonHolder<SharedPreferences, Context>({ PreferenceManage
|
||||
}
|
||||
|
||||
val showTvUi : Boolean
|
||||
get() = AndroidDevices.isTv || tvUI
|
||||
get() = !overrideTvUI && AndroidDevices.isTv || tvUI
|
||||
|
||||
}
|
||||
|
||||
@ -82,4 +83,4 @@ const val CRASH_DONT_ASK_AGAIN = "crash_dont_ask_again"
|
||||
|
||||
const val PLAYBACK_HISTORY = "playback_history"
|
||||
const val RESUME_PLAYBACK = "resume_playback"
|
||||
const val AUDIO_DUCKING = "audio_ducking"
|
||||
const val AUDIO_DUCKING = "audio_ducking"
|
||||
|
@ -25,23 +25,28 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.videolan.libvlc.FactoryManager
|
||||
import org.videolan.libvlc.interfaces.ILibVLC
|
||||
import org.videolan.libvlc.interfaces.ILibVLCFactory
|
||||
import org.videolan.libvlc.util.VLCUtil
|
||||
import org.videolan.vlc.VLCApplication
|
||||
import org.videolan.vlc.VLCCrashHandler
|
||||
import org.videolan.vlc.gui.CompatErrorActivity
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
object VLCInstance {
|
||||
val TAG = "VLC/UiTools/VLCInstance"
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private var sLibVLC: LibVLC? = null
|
||||
private var sLibVLC: ILibVLC? = null
|
||||
|
||||
internal val mLibVLCFactory = FactoryManager.getFactory(ILibVLCFactory.factoryId) as ILibVLCFactory
|
||||
|
||||
/** A set of utility functions for the VLC application */
|
||||
@Synchronized
|
||||
@Throws(IllegalStateException::class)
|
||||
operator fun get(ctx: Context): LibVLC {
|
||||
operator fun get(ctx: Context): ILibVLC {
|
||||
if (sLibVLC == null) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(VLCCrashHandler())
|
||||
|
||||
@ -52,7 +57,7 @@ object VLCInstance {
|
||||
}
|
||||
|
||||
// TODO change LibVLC signature to accept a List instead of an ArrayList
|
||||
sLibVLC = LibVLC(context, VLCOptions.libOptions)
|
||||
sLibVLC = mLibVLCFactory.getFromOptions(context, VLCOptions.libOptions)
|
||||
}
|
||||
return sLibVLC!!
|
||||
}
|
||||
@ -62,7 +67,7 @@ object VLCInstance {
|
||||
fun restart() {
|
||||
if (sLibVLC != null) {
|
||||
sLibVLC!!.release()
|
||||
sLibVLC = LibVLC(VLCApplication.appContext, VLCOptions.libOptions)
|
||||
sLibVLC = mLibVLCFactory.getFromOptions(VLCApplication.appContext, VLCOptions.libOptions)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,7 @@ import android.util.Log
|
||||
import androidx.annotation.MainThread
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.MediaPlayer
|
||||
import org.videolan.libvlc.interfaces.IMedia
|
||||
import org.videolan.libvlc.util.AndroidUtil
|
||||
import org.videolan.libvlc.util.HWDecoderUtil
|
||||
import org.videolan.libvlc.util.VLCUtil
|
||||
@ -200,7 +201,7 @@ object VLCOptions {
|
||||
return ret
|
||||
}
|
||||
|
||||
fun setMediaOptions(media: Media, context: Context, flags: Int) {
|
||||
fun setMediaOptions(media: IMedia, context: Context, flags: Int) {
|
||||
val noHardwareAcceleration = flags and AbstractMediaWrapper.MEDIA_NO_HWACCEL != 0
|
||||
val noVideo = flags and AbstractMediaWrapper.MEDIA_VIDEO == 0
|
||||
val benchmark = flags and AbstractMediaWrapper.MEDIA_BENCHMARK != 0
|
||||
|
19
vlc-android/test-common/org/videolan/vlc/util/KExtensions.kt
Normal file
19
vlc-android/test-common/org/videolan/vlc/util/KExtensions.kt
Normal file
@ -0,0 +1,19 @@
|
||||
package org.videolan.vlc.util
|
||||
|
||||
import org.videolan.vlc.repository.BrowserFavRepository
|
||||
import org.videolan.vlc.repository.DirectoryRepository
|
||||
import org.videolan.vlc.repository.ExternalSubRepository
|
||||
|
||||
|
||||
// Hacky way. Don't fix it.
|
||||
fun ExternalSubRepository.Companion.applyMock(instance: ExternalSubRepository) {
|
||||
this.instance = instance
|
||||
}
|
||||
|
||||
fun DirectoryRepository.Companion.applyMock(instance: DirectoryRepository) {
|
||||
this.instance = instance
|
||||
}
|
||||
|
||||
fun BrowserFavRepository.Companion.applyMock(instance: BrowserFavRepository) {
|
||||
this.instance = instance
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package org.videolan.vlc.util
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
class TestCoroutineContextProvider : CoroutineContextProvider() {
|
||||
override val Default by lazy { Dispatchers.Unconfined }
|
||||
override val IO by lazy { Dispatchers.Unconfined }
|
||||
override val Main by lazy { Dispatchers.Unconfined }
|
||||
}
|
@ -22,11 +22,14 @@ package org.videolan.vlc.util
|
||||
|
||||
import android.net.Uri
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.vlc.api.OpenSubtitle
|
||||
import org.videolan.vlc.api.QueryParameters
|
||||
import org.videolan.vlc.database.models.BrowserFav
|
||||
import org.videolan.vlc.database.models.CustomDirectory
|
||||
import org.videolan.vlc.database.models.ExternalSub
|
||||
import org.videolan.vlc.database.models.Slave
|
||||
import java.io.File
|
||||
import org.videolan.vlc.gui.dialogs.State
|
||||
import org.videolan.vlc.gui.dialogs.SubtitleItem
|
||||
|
||||
object TestUtil {
|
||||
private const val fakeUri: String = "https://www.videolan.org/fake_"
|
||||
@ -39,15 +42,13 @@ object TestUtil {
|
||||
|
||||
fun createLocalUris(count: Int): List<String> {
|
||||
return (0 until count).map {
|
||||
"${fakeUri}_local$it"
|
||||
"${fakeMediaUri}local_$it.mp4"
|
||||
}
|
||||
}
|
||||
|
||||
fun createLocalFavs(count: Int): List<BrowserFav> {
|
||||
return (0 until count).map {
|
||||
createLocalFav(Uri.parse(fakeUri + "local" + it),
|
||||
"local" + 1,
|
||||
null)
|
||||
createLocalFav(Uri.parse("${fakeMediaUri}_$it.mp4"), "local$it", null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,9 +57,7 @@ object TestUtil {
|
||||
}
|
||||
|
||||
fun createNetworkUris(count: Int): List<String> {
|
||||
return (0 until count).map {
|
||||
"${fakeUri}_network$it"
|
||||
}
|
||||
return (0 until count).map { "${fakeUri}_network$it.mp4" }
|
||||
}
|
||||
|
||||
fun createNetworkFavs(count: Int): List<BrowserFav> {
|
||||
@ -76,13 +75,13 @@ object TestUtil {
|
||||
subtitlePath: String,
|
||||
mediaPath: String,
|
||||
subLanguageID: String,
|
||||
movieReleaseName: String ): ExternalSub {
|
||||
movieReleaseName: String): ExternalSub {
|
||||
return ExternalSub(idSubtitle, subtitlePath, mediaPath, subLanguageID, movieReleaseName)
|
||||
}
|
||||
|
||||
fun createExternalSubsForMedia(mediaPath: String, mediaName: String, count: Int): List<ExternalSub> {
|
||||
return (0 until count).map {
|
||||
ExternalSub(it.toString(),"${fakeSubUri}$mediaName$it", mediaPath, "en", mediaName)
|
||||
ExternalSub(it.toString(), "${fakeSubUri}$mediaName$it", mediaPath, "en", mediaName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,13 +89,13 @@ object TestUtil {
|
||||
return Slave(mediaPath, Media.Slave.Type.Subtitle, 2, uri)
|
||||
}
|
||||
|
||||
fun createSubtitleSlavesForMedia(mediaName: String, count:Int): List<Slave> {
|
||||
fun createSubtitleSlavesForMedia(mediaName: String, count: Int): List<Slave> {
|
||||
return (0 until count).map {
|
||||
createSubtitleSlave( "$fakeMediaUri$mediaName", "$fakeSubUri$mediaName$it.srt" )
|
||||
createSubtitleSlave("$fakeMediaUri$mediaName", "$fakeSubUri$mediaName$it.srt")
|
||||
}
|
||||
}
|
||||
|
||||
fun createCustomDirectory(path: String): CustomDirectory{
|
||||
fun createCustomDirectory(path: String): CustomDirectory {
|
||||
return CustomDirectory(path)
|
||||
}
|
||||
|
||||
@ -106,4 +105,32 @@ object TestUtil {
|
||||
createCustomDirectory("$directory$it")
|
||||
}
|
||||
}
|
||||
|
||||
fun createDownloadingSubtitleItem(
|
||||
idSubtitle: String,
|
||||
mediaUri: Uri,
|
||||
subLanguageID: String,
|
||||
movieReleaseName: String,
|
||||
zipDownloadLink: String): SubtitleItem = SubtitleItem(idSubtitle, mediaUri, subLanguageID, movieReleaseName, State.Downloading, zipDownloadLink)
|
||||
|
||||
fun createDownloadingSubtitleItem(
|
||||
idSubtitle: String,
|
||||
mediaPath: String,
|
||||
subLanguageID: String,
|
||||
movieReleaseName: String,
|
||||
zipDownloadLink: String): SubtitleItem = TestUtil.createDownloadingSubtitleItem(idSubtitle, Uri.parse(mediaPath), subLanguageID, movieReleaseName, zipDownloadLink)
|
||||
|
||||
fun createOpenSubtitle(
|
||||
idSubtitle: String,
|
||||
subLanguageID: String,
|
||||
movieReleaseName: String,
|
||||
zipDownloadLink: String) = OpenSubtitle(
|
||||
idSubtitle = idSubtitle, subLanguageID = subLanguageID, movieReleaseName = movieReleaseName, zipDownloadLink = zipDownloadLink,
|
||||
idMovie = "", idMovieImdb = "", idSubMovieFile = "", idSubtitleFile = "", infoFormat = "", infoOther = "", infoReleaseGroup = "",
|
||||
userID = "", iSO639 = "", movieFPS = "", languageName = "", subActualCD = "", subSumVotes = "", subAuthorComment = "", subComments = "",
|
||||
score = 0.0, seriesEpisode = "", seriesIMDBParent = "", seriesSeason = "", subAddDate = "", subAutoTranslation = "", subBad = "", subDownloadLink = "",
|
||||
subDownloadsCnt = "", subEncoding = "", subFeatured = "", subFileName = "", subForeignPartsOnly = "", subFormat = "", subFromTrusted = "", subHash = "",
|
||||
subHD = "", subHearingImpaired = "", subLastTS = "", subRating = "", subSize = "", subSumCD = "", subtitlesLink = "", subTranslator = "", subTSGroup = "",
|
||||
subTSGroupHash = "", movieByteSize = "", movieHash = "", movieTimeMS = "", queryParameters = QueryParameters("", "", ""), queryNumber = "",
|
||||
userNickName = "", userRank = "", matchedBy = "", movieImdbRating = "", movieKind = "", movieName = "", movieNameEng = "", movieYear = "")
|
||||
}
|
||||
|
60
vlc-android/test/org/videolan/vlc/BaseTest.kt
Normal file
60
vlc-android/test/org/videolan/vlc/BaseTest.kt
Normal file
@ -0,0 +1,60 @@
|
||||
package org.videolan.vlc
|
||||
|
||||
import android.content.Context
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.clearAllMocks
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.*
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = VLCTestApplication::class, manifest = Config.NONE)
|
||||
open class BaseTest {
|
||||
val context: Context = ApplicationProvider.getApplicationContext()
|
||||
val application = (RuntimeEnvironment.application as VLCTestApplication)
|
||||
|
||||
//To prevent Method getMainLooper in android.os.Looper not mocked error when setting value for MutableLiveData
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
init {
|
||||
MockKAnnotations.init(this)
|
||||
}
|
||||
|
||||
@Before
|
||||
open fun beforeTest() {
|
||||
println("beforeTest")
|
||||
}
|
||||
|
||||
@After
|
||||
open fun afterTest() {
|
||||
println("afterTest")
|
||||
clearAllMocks()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setupTestClass() {
|
||||
Dispatchers.setMain(Dispatchers.Unconfined)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun cleanupTestClass() {
|
||||
unmockkAll()
|
||||
}
|
||||
}
|
||||
}
|
21
vlc-android/test/org/videolan/vlc/VLCTestApplication.kt
Normal file
21
vlc-android/test/org/videolan/vlc/VLCTestApplication.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package org.videolan.vlc
|
||||
|
||||
import android.app.Application
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.videolan.libvlc.FactoryManager
|
||||
import org.videolan.libvlc.ILibVLCFactory
|
||||
import org.videolan.libvlc.IMediaFactory
|
||||
import org.videolan.libvlc.test.TestLibVLCFactory
|
||||
import org.videolan.libvlc.test.TestMediaFactory
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@ObsoleteCoroutinesApi
|
||||
class VLCTestApplication : Application() {
|
||||
init {
|
||||
MLServiceLocator.setLocatorMode(MLServiceLocator.LocatorMode.TESTS)
|
||||
FactoryManager.registerFactory(IMediaFactory.factoryId, TestMediaFactory())
|
||||
FactoryManager.registerFactory(ILibVLCFactory.factoryId, TestLibVLCFactory())
|
||||
}
|
||||
}
|
@ -97,8 +97,8 @@ class ExternalSubRepositoryTest {
|
||||
`when`(externalSubDao.get(foo)).thenReturn(fakeFooLiveDataSubtitles)
|
||||
`when`(externalSubDao.get(bar)).thenReturn(fakeBarLiveDataSubtitles)
|
||||
|
||||
val fooSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(foo))
|
||||
val barSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(bar))
|
||||
val fooSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(Uri.parse(foo)))
|
||||
val barSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(Uri.parse(bar)))
|
||||
verify(externalSubDao, times(2)).get(ArgumentMatchers.anyString())
|
||||
assertThat(fooSubtitles.size, `is`(0))
|
||||
}
|
||||
@ -139,8 +139,8 @@ class ExternalSubRepositoryTest {
|
||||
`when`(externalSubDao.get(foo)).thenReturn(fakeFooLiveDataSubtitles)
|
||||
`when`(externalSubDao.get(bar)).thenReturn(fakeBarLiveDataSubtitles)
|
||||
|
||||
val fooSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(foo))
|
||||
val barSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(bar))
|
||||
val fooSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(Uri.parse(foo)))
|
||||
val barSubtitles = getValue(externalSubRepository.getDownloadedSubtitles(Uri.parse(bar)))
|
||||
verify(externalSubDao, times(2)).get(ArgumentMatchers.anyString())
|
||||
assertThat(fooSubtitles.size, `is`(2))
|
||||
assertThat(barSubtitles.size, `is`(2))
|
||||
|
@ -0,0 +1,78 @@
|
||||
package org.videolan.vlc.viewmodels
|
||||
|
||||
import android.net.Uri
|
||||
import com.jraska.livedata.test
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Test
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
import org.videolan.medialibrary.Medialibrary
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestUtil
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class HistoryModelTest : BaseTest() {
|
||||
private val mediaLibrary: AbstractMedialibrary = AbstractMedialibrary.getInstance()
|
||||
private lateinit var historyModel: HistoryModel
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
mediaLibrary.clearHistory()
|
||||
historyModel = HistoryModel(application, TestCoroutineContextProvider())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenRefreshCalled_ListIsUpdated() {
|
||||
val fakeMediaStrings = TestUtil.createLocalUris(2)
|
||||
|
||||
historyModel.refresh()
|
||||
|
||||
historyModel.dataset.test()
|
||||
.awaitValue()
|
||||
.assertValue(Medialibrary.EMPTY_COLLECTION.toMutableList())
|
||||
|
||||
val result = fakeMediaStrings.map {
|
||||
val media = MLServiceLocator.getAbstractMediaWrapper(Uri.parse(it))
|
||||
mediaLibrary.addToHistory(media.location, media.title)
|
||||
media
|
||||
}
|
||||
|
||||
historyModel.refresh()
|
||||
|
||||
val testResult = historyModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
assertEquals(result[0], testResult[0])
|
||||
assertEquals(result[1], testResult[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenListHasTwoItemsAndLastIsMovedUp_ListHasUpdatedItemsOrder() {
|
||||
val fakeMediaStrings = TestUtil.createLocalUris(2)
|
||||
|
||||
val result = fakeMediaStrings.map {
|
||||
val media = MLServiceLocator.getAbstractMediaWrapper(Uri.parse(it)).apply { type = AbstractMediaWrapper.TYPE_VIDEO }
|
||||
mediaLibrary.addToHistory(media.location, media.title)
|
||||
media
|
||||
}
|
||||
|
||||
historyModel.refresh()
|
||||
|
||||
historyModel.moveUp(result[1])
|
||||
|
||||
val testResult = historyModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(result.size, testResult.size)
|
||||
assertEquals(result[0], testResult[1])
|
||||
assertEquals(result[1], testResult[0])
|
||||
}
|
||||
}
|
@ -1,130 +1,97 @@
|
||||
package org.videolan.vlc.viewmodels
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.every
|
||||
import io.mockk.slot
|
||||
import io.mockk.spyk
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.*
|
||||
import org.powermock.api.mockito.PowerMockito
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest
|
||||
import org.powermock.modules.junit4.PowerMockRunner
|
||||
import org.powermock.modules.junit4.PowerMockRunnerDelegate
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import org.videolan.medialibrary.MLServiceLocator
|
||||
import org.videolan.medialibrary.Medialibrary
|
||||
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.medialibrary.media.MediaWrapper
|
||||
import org.videolan.vlc.util.RoboLiteTestRunner
|
||||
import org.videolan.medialibrary.stubs.StubDataSource
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestUtil
|
||||
import org.videolan.vlc.util.mock
|
||||
import java.io.File
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.powermock.modules.junit4.rule.PowerMockRule
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
|
||||
//@RunWith(RoboLiteTestRunner::class)
|
||||
@RunWith(PowerMockRunner::class)
|
||||
//@PowerMockRunnerDelegate(RobolectricTestRunner::class)
|
||||
//@Config(sdk=[21])
|
||||
@PrepareForTest(value = [Medialibrary::class, LibVLC::class, System::class, Uri::class, TextUtils::class])
|
||||
@PowerMockIgnore(value = ["javax.management.*", "org.apache.http.conn.ssl.*", "com.amazonaws.http.conn.ssl.*", "javax.net.ssl.*", "androidx.*"])
|
||||
class StreamsModelTest {
|
||||
|
||||
private val mockedLibrary: Medialibrary = PowerMockito.spy(Medialibrary())
|
||||
|
||||
private val context: Context = mock()
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class StreamsModelTest : BaseTest() {
|
||||
private val mediaLibrary: AbstractMedialibrary = AbstractMedialibrary.getInstance()
|
||||
private lateinit var streamsModel: StreamsModel
|
||||
|
||||
// @Rule
|
||||
// @JvmField
|
||||
// val powerMockRule = PowerMockRule()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
//To prevent Method getMainLooper in android.os.Looper not mocked error when setting value for MutableLiveData
|
||||
val instantExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@Before
|
||||
fun setUp() {
|
||||
`when`(context.getExternalFilesDir(any())).thenReturn(File("./"))
|
||||
`when`(context.getDir(any(), anyInt())).thenReturn(File("./"))
|
||||
|
||||
PowerMockito.mockStatic(Medialibrary::class.java)
|
||||
PowerMockito.mockStatic(System::class.java)
|
||||
PowerMockito.mockStatic(Uri::class.java)
|
||||
PowerMockito.mockStatic(TextUtils::class.java)
|
||||
|
||||
PowerMockito.suppress(PowerMockito.method(System::class.java, "loadLibrary", String::class.java))
|
||||
PowerMockito.`when`(Medialibrary.getInstance()).thenReturn(mockedLibrary)
|
||||
|
||||
Dispatchers.setMain(Dispatchers.Unconfined)
|
||||
streamsModel = StreamsModel(context)
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
mediaLibrary.clearHistory()
|
||||
streamsModel = StreamsModel(application, TestCoroutineContextProvider())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failedInitialization_GetEmptyCollection() {
|
||||
PowerMockito.doReturn(Medialibrary.ML_INIT_FAILED).`when`(mockedLibrary, "nativeInit", any(), any())
|
||||
|
||||
mockedLibrary.init(context)
|
||||
|
||||
val testResult: MutableList<MediaWrapper> = mutableListOf()
|
||||
streamsModel.refresh()
|
||||
streamsModel.dataset
|
||||
.test()
|
||||
.awaitValue()
|
||||
.assertHasValue()
|
||||
.assertValue(testResult)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addTwoMediaHistory_GetTwoPlayedMediaStreams() {
|
||||
// Setup
|
||||
val mockedUri1: Uri = mock()
|
||||
val mockedUri2: Uri = mock()
|
||||
|
||||
fun whenRefreshCalled_ListIsUpdated() {
|
||||
val fakeMediaStrings = TestUtil.createNetworkUris(2)
|
||||
|
||||
`when`(Uri.parse(eq(fakeMediaStrings[0]))).thenReturn(mockedUri1)
|
||||
`when`(mockedUri1.lastPathSegment).thenReturn(fakeMediaStrings[0])
|
||||
`when`(mockedUri1.toString()).thenReturn(fakeMediaStrings[0])
|
||||
`when`(Uri.parse(eq(fakeMediaStrings[1]))).thenReturn(mockedUri2)
|
||||
`when`(mockedUri2.lastPathSegment).thenReturn(fakeMediaStrings[1])
|
||||
`when`(mockedUri2.toString()).thenReturn(fakeMediaStrings[1])
|
||||
streamsModel.dataset.test()
|
||||
.awaitValue()
|
||||
.assertValue(Medialibrary.EMPTY_COLLECTION.toMutableList())
|
||||
|
||||
val fakeMedias = fakeMediaStrings.map { s -> MediaWrapper(Uri.parse(s)) }
|
||||
val result = fakeMedias.toTypedArray()
|
||||
val result = fakeMediaStrings.map {
|
||||
val media = MediaWrapper(Uri.parse(it))
|
||||
println(mediaLibrary.addToHistory(media.location, media.title))
|
||||
media
|
||||
}
|
||||
|
||||
PowerMockito.doReturn(Medialibrary.ML_INIT_SUCCESS).`when`(mockedLibrary, "nativeInit", any(), any())
|
||||
PowerMockito.doReturn(result).`when`(mockedLibrary, "nativeLastStreamsPlayed")
|
||||
|
||||
// Execution
|
||||
mockedLibrary.init(context)
|
||||
streamsModel.refresh()
|
||||
|
||||
// Assertions
|
||||
val getResult = streamsModel.dataset
|
||||
val testResult = streamsModel.dataset
|
||||
.test()
|
||||
.awaitValue()
|
||||
.assertHasValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, getResult.size)
|
||||
assertEquals(result[0], getResult[0])
|
||||
assertEquals(result[1], getResult[1])
|
||||
assertEquals(result.size, testResult.size)
|
||||
assertEquals(result[0], testResult[0])
|
||||
assertEquals(result[1], testResult[1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenRenameCalledAtPos_MediaTitleIsUpdated() {
|
||||
val pos = 0
|
||||
val fakeMediaStrings = TestUtil.createNetworkUris(2)
|
||||
|
||||
val argumentName = slot<String>()
|
||||
|
||||
val result = fakeMediaStrings.map {
|
||||
val media = spyk(MediaWrapper(Uri.parse(it)))
|
||||
mediaLibrary.addToHistory(media.location, media.title)
|
||||
media
|
||||
}
|
||||
val oldMediaTitle = result[pos].title
|
||||
|
||||
val media = result[pos]
|
||||
|
||||
every { media.rename(capture(argumentName)) } answers { media.setDisplayTitle(argumentName.captured) }
|
||||
|
||||
streamsModel.refresh()
|
||||
|
||||
streamsModel.dataset.test()
|
||||
.awaitValue()
|
||||
// .assertValue(result.toMutableList())
|
||||
|
||||
val newMediaTitle = "$oldMediaTitle~new"
|
||||
|
||||
streamsModel.rename(pos, newMediaTitle)
|
||||
|
||||
val testResult = streamsModel.dataset.test()
|
||||
.awaitValue()
|
||||
.assertHasValue()
|
||||
.value()
|
||||
|
||||
assertEquals(newMediaTitle, testResult[pos].title)
|
||||
}
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
package org.videolan.vlc.viewmodels
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.*
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.R
|
||||
import org.videolan.vlc.api.NoConnectivityException
|
||||
import org.videolan.vlc.database.ExternalSubDao
|
||||
import org.videolan.vlc.database.models.ExternalSub
|
||||
import org.videolan.vlc.gui.dialogs.State
|
||||
import org.videolan.vlc.repository.ExternalSubRepository
|
||||
import org.videolan.vlc.repository.OpenSubtitleRepository
|
||||
import org.videolan.vlc.util.FileUtils
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestUtil
|
||||
import org.videolan.vlc.util.applyMock
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class SubtitlesModelTest : BaseTest() {
|
||||
@get:Rule
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
@MockK
|
||||
private lateinit var mockedOpenSubRepo: OpenSubtitleRepository
|
||||
|
||||
private val mockedDao: ExternalSubDao = mockk()
|
||||
private lateinit var mediaPath: String
|
||||
private val downloadedLiveData = MutableLiveData<List<ExternalSub>>()
|
||||
|
||||
private lateinit var subtitlesModel: SubtitlesModel
|
||||
|
||||
init {
|
||||
val subRepo = ExternalSubRepository(mockedDao, TestCoroutineContextProvider())
|
||||
ExternalSubRepository.applyMock(subRepo)
|
||||
val capturedMedia = slot<String>()
|
||||
// To mock the behavior of actual get function in DAO (filter by media path)
|
||||
every { mockedDao.get(capture(capturedMedia)) } answers { Transformations.map(downloadedLiveData) { it.filter { it.mediaPath == capturedMedia.captured } } }
|
||||
|
||||
// To use the mocked instance of OpenSubRepository
|
||||
OpenSubtitleRepository.instance = lazyOf(mockedOpenSubRepo)
|
||||
|
||||
// Used when computing hash of media file.
|
||||
mockkObject(FileUtils) {
|
||||
every { FileUtils.computeHash(any()) } returns "fake_hash"
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
mediaPath = temporaryFolder.newFile("fake_media").path
|
||||
subtitlesModel = SubtitlesModel(application, Uri.parse(mediaPath), TestCoroutineContextProvider())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addFourDownloadedSubtitlesWithThreeCorrectMediaPath_checkHistoryHasSizeAsThree() {
|
||||
val inputSubs = (0..2).map {
|
||||
val subPath = temporaryFolder.newFile("sub$it").absolutePath
|
||||
TestUtil.createExternalSub(it.toString(), subPath, mediaPath, "en", "xyz$it")
|
||||
}.toMutableList()
|
||||
val subPath = temporaryFolder.newFile("sub3").absolutePath
|
||||
inputSubs.add(TestUtil.createExternalSub("3", subPath, "/wrong", "jp", "abc4"))
|
||||
|
||||
downloadedLiveData.value = inputSubs
|
||||
|
||||
val testResult = subtitlesModel.history.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(3, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addThreeDownloadingSubtitlesWithTwoCorrectMediaPath_checkHistoryHasSizeAsTwo() {
|
||||
(0..1).map {
|
||||
ExternalSubRepository.getInstance(context).addDownloadingItem(
|
||||
it.toLong(), TestUtil.createDownloadingSubtitleItem("$it", mediaPath, "en", "xyz", "abc.com/$it")
|
||||
)
|
||||
}
|
||||
ExternalSubRepository.getInstance(context).addDownloadingItem(
|
||||
2, TestUtil.createDownloadingSubtitleItem("2", "/wrong", "en", "xyz", "abc.com/2")
|
||||
)
|
||||
|
||||
val testResult = subtitlesModel.history.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addTwoDownloadingSubtitlesAndTwoDownloadedSubtitles_checkHistoryHasSizeAsFour() {
|
||||
(0..1).map {
|
||||
ExternalSubRepository.getInstance(context).addDownloadingItem(
|
||||
it.toLong(), TestUtil.createDownloadingSubtitleItem("$it", mediaPath, "en", "xyz", "abc.com/$it")
|
||||
)
|
||||
}
|
||||
val inputSubs = (0..1).map {
|
||||
val subPath = temporaryFolder.newFile("sub$it").absolutePath
|
||||
TestUtil.createExternalSub(it.toString(), subPath, mediaPath, "en", "xyz$it")
|
||||
}.toList()
|
||||
|
||||
downloadedLiveData.value = inputSubs
|
||||
|
||||
val testResult = subtitlesModel.history.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(4, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByNameAndNoResultHasFound_checkResultIsEmpty() {
|
||||
coEvery { mockedOpenSubRepo.queryWithName(any(), any(), any(), any<List<String>>()) } returns emptyList()
|
||||
|
||||
subtitlesModel.observableSearchName.set("abc")
|
||||
subtitlesModel.search(false)
|
||||
|
||||
subtitlesModel.result.test()
|
||||
.awaitValue()
|
||||
.assertValue { it.isEmpty() }
|
||||
|
||||
assertEquals(context.getString(R.string.no_result), subtitlesModel.observableMessage.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByNameAndTwoResultHasFound_checkResultHasSizeAsTwo() {
|
||||
val openSubs = (0..1).map { TestUtil.createOpenSubtitle("$it", "en", "xyz", "abc.com/$it") }
|
||||
coEvery { mockedOpenSubRepo.queryWithName(any(), any(), any(), any<List<String>>()) } returns openSubs
|
||||
|
||||
subtitlesModel.observableSearchName.set("abc")
|
||||
subtitlesModel.search(false)
|
||||
|
||||
val testResult = subtitlesModel.result.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
assertEquals(testResult[0].state, State.NotDownloaded)
|
||||
assertEquals(testResult[1].state, State.NotDownloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByNameAndThreeResultHasFoundTwoAreInHistory_checkResultHasSizeAsThreeAndCorrectStates() {
|
||||
val openSubs = (0..2).map { TestUtil.createOpenSubtitle("$it", "en", "xyz", "abc.com/$it") }
|
||||
coEvery { mockedOpenSubRepo.queryWithName(any(), any(), any(), any<List<String>>()) } returns openSubs
|
||||
|
||||
ExternalSubRepository.getInstance(context).addDownloadingItem(
|
||||
0L, TestUtil.createDownloadingSubtitleItem("0", mediaPath, "en", "xyz", "abc.com/0")
|
||||
)
|
||||
val subPath = temporaryFolder.newFile("sub1").absolutePath
|
||||
downloadedLiveData.value = listOf(TestUtil.createExternalSub("1", subPath, mediaPath, "en", "xyz"))
|
||||
|
||||
subtitlesModel.observableSearchName.set("abc")
|
||||
subtitlesModel.search(false)
|
||||
|
||||
subtitlesModel.history.test()
|
||||
.awaitValue()
|
||||
|
||||
val testResult = subtitlesModel.result.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(3, testResult.size)
|
||||
assertEquals(testResult[0].state, State.Downloading)
|
||||
assertEquals(testResult[1].state, State.Downloaded)
|
||||
assertEquals(testResult[2].state, State.NotDownloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addTwoDownloadedSubtitlesAndDeleteTwo_verifyDaoDeleteCalled() {
|
||||
val inputSubs = (0..1).map {
|
||||
val subPath = temporaryFolder.newFile("sub$it").absolutePath
|
||||
TestUtil.createExternalSub(it.toString(), subPath, mediaPath, "en", "xyz$it")
|
||||
}.toMutableList()
|
||||
|
||||
every { mockedDao.delete(any(), any()) } just runs
|
||||
|
||||
downloadedLiveData.value = inputSubs
|
||||
|
||||
(0..1).map { subtitlesModel.deleteSubtitle(mediaPath, "$it") }
|
||||
|
||||
verify(exactly = 2) { mockedDao.delete(any(), any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByHashAndNoResultHasFound_checkResultIsEmpty() {
|
||||
coEvery { mockedOpenSubRepo.queryWithHash(any(), any(), any<List<String>>()) } returns emptyList()
|
||||
|
||||
subtitlesModel.search(true)
|
||||
|
||||
subtitlesModel.result.test()
|
||||
.awaitValue()
|
||||
.assertValue { it.isEmpty() }
|
||||
|
||||
assertEquals(context.getString(R.string.no_result), subtitlesModel.observableMessage.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByHashAndTwoResultHasFound_checkResultHasSizeAsTwo() {
|
||||
val openSubs = (0..1).map { TestUtil.createOpenSubtitle("$it", "en", "xyz", "abc.com/$it") }
|
||||
coEvery { mockedOpenSubRepo.queryWithHash(any(), any(), any<List<String>>()) } returns openSubs
|
||||
|
||||
subtitlesModel.search(true)
|
||||
|
||||
val testResult = subtitlesModel.result.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
assertEquals(testResult[0].state, State.NotDownloaded)
|
||||
assertEquals(testResult[1].state, State.NotDownloaded)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByNameWithNoConnection_checkValidMessage() {
|
||||
coEvery { mockedOpenSubRepo.queryWithName(any(), any(), any(), any<List<String>>()) } throws NoConnectivityException()
|
||||
|
||||
subtitlesModel.observableSearchName.set("abc")
|
||||
subtitlesModel.search(false)
|
||||
|
||||
subtitlesModel.result.test()
|
||||
.awaitValue(3, TimeUnit.SECONDS)
|
||||
.assertValue { it.isEmpty() }
|
||||
|
||||
assertEquals(context.getString(R.string.no_internet_connection), subtitlesModel.observableMessage.get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchByNameWithConverterError_checkValidMessage() {
|
||||
coEvery { mockedOpenSubRepo.queryWithName(any(), any(), any(), any<List<String>>()) } throws IOException()
|
||||
|
||||
subtitlesModel.observableSearchName.set("abc")
|
||||
subtitlesModel.search(false)
|
||||
|
||||
subtitlesModel.result.test()
|
||||
.awaitValue(3, TimeUnit.SECONDS)
|
||||
.assertValue { it.isEmpty() }
|
||||
|
||||
assertEquals(context.getString(R.string.some_error_occurred), subtitlesModel.observableMessage.get())
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package org.videolan.vlc.viewmodels.browser
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import junit.framework.Assert.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.test.TestMedia
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.database.BrowserFavDao
|
||||
import org.videolan.vlc.database.models.BrowserFav
|
||||
import org.videolan.vlc.providers.BrowserProvider
|
||||
import org.videolan.vlc.repository.BrowserFavRepository
|
||||
import org.videolan.vlc.util.CoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.applyMock
|
||||
import java.io.File
|
||||
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class FileBrowserModelTest : BaseTest() {
|
||||
// Preferences choose directories to add in medialibrary scan.
|
||||
|
||||
@get:Rule
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
private val mockedLibVlc: LibVLC = mockk(relaxed = true)
|
||||
private lateinit var mediaBrowser: MediaBrowser
|
||||
private val mockedFavoritesDao: BrowserFavDao = mockk(relaxed = true)
|
||||
private val mockedFavoritesRepo: BrowserFavRepository = spyk(BrowserFavRepository(mockedFavoritesDao))
|
||||
|
||||
private lateinit var browserModel: BrowserModel
|
||||
private lateinit var browserProvider: BrowserProvider
|
||||
|
||||
private val countVideos = 2
|
||||
private val countDirs = 4
|
||||
|
||||
init {
|
||||
BrowserFavRepository.applyMock(mockedFavoritesRepo)
|
||||
|
||||
// BrowserHandler mocked.
|
||||
val handler: Handler = mockk()
|
||||
|
||||
BrowserProvider.overrideCreator = false
|
||||
BrowserProvider.registerCreator {
|
||||
this@FileBrowserModelTest.mediaBrowser = spyk(MediaBrowser(mockedLibVlc, it, handler))
|
||||
mediaBrowser
|
||||
}
|
||||
BrowserProvider.registerCreator(clazz = CoroutineContextProvider::class.java) { TestCoroutineContextProvider() }
|
||||
}
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
setupTestFiles()
|
||||
}
|
||||
|
||||
private fun initBrowserModel(url: String?, showHiddenFiles: Boolean, showDummyCategory: Boolean = false) {
|
||||
browserModel = BrowserModel(application, url, TYPE_FILE, showHiddenFiles, showDummyCategory, TestCoroutineContextProvider())
|
||||
browserProvider = browserModel.provider
|
||||
mediaBrowser = BrowserProvider.get(browserProvider)
|
||||
}
|
||||
|
||||
private fun setupTestFiles() {
|
||||
(1..countDirs).map { temporaryFolder.newFile("dir$it") }
|
||||
(1..countVideos).map { temporaryFolder.newFile("video$it.mp4") }
|
||||
}
|
||||
|
||||
private fun addFileToProvider(i: Int, file: File) {
|
||||
val t = TestMedia(mockedLibVlc, "file://${file.path}").apply { if (!file.name.endsWith(".mp4")) type = Media.Type.Directory }
|
||||
browserProvider.onMediaAdded(i, t)
|
||||
}
|
||||
|
||||
private fun fillFilesInDataset(file: File) {
|
||||
file.listFiles().sorted().mapIndexed(this::addFileToProvider)
|
||||
browserProvider.onBrowseEnd()
|
||||
}
|
||||
|
||||
private fun getFakeBrowserFav(index: Int): BrowserFav {
|
||||
val t = temporaryFolder.newFile("fake_media$index")
|
||||
return BrowserFav(Uri.parse(t.path), 0, "vid_$index", null)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndInternalStorageIsEmpty_checkShowsFolderIsEmpty() {
|
||||
initBrowserModel(null, showHiddenFiles = false)
|
||||
|
||||
val internalStorage = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()[0]
|
||||
|
||||
// TODO Has to wait for browserChannel queue
|
||||
Thread.sleep(1000)
|
||||
browserProvider.onBrowseEnd()
|
||||
Thread.sleep(1000)
|
||||
|
||||
assertTrue(browserModel.isFolderEmpty(internalStorage as AbstractMediaWrapper))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndInternalStorageHasDirectories_checkShowsFolderIsNotEmpty() {
|
||||
initBrowserModel(null, showHiddenFiles = false)
|
||||
|
||||
val internalStorage = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()[0]
|
||||
|
||||
Thread.sleep(1000)
|
||||
// TODO Hack because parseSubDirectories is called twice for some reason.
|
||||
// browserProvider.onBrowseEnd()
|
||||
fillFilesInDataset(temporaryFolder.root)
|
||||
Thread.sleep(1000)
|
||||
|
||||
assertFalse(browserModel.isFolderEmpty(internalStorage as AbstractMediaWrapper))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndSavedList_checkPrefetchListIsFilled() {
|
||||
initBrowserModel(null, showHiddenFiles = false)
|
||||
|
||||
val internalStorage = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()[0]
|
||||
|
||||
Thread.sleep(1000)
|
||||
// browserProvider.onBrowseEnd()
|
||||
fillFilesInDataset(temporaryFolder.root)
|
||||
Thread.sleep(1000)
|
||||
|
||||
browserModel.saveList(internalStorage as AbstractMediaWrapper)
|
||||
|
||||
initBrowserModel(internalStorage.uri.toString(), false)
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
assertEquals(countDirs + countVideos, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndHasLocalFavorite_checkDataSetContainsIt() {
|
||||
val liveFavorites: MutableLiveData<List<BrowserFav>> = MutableLiveData()
|
||||
every { mockedFavoritesRepo.localFavorites } returns liveFavorites
|
||||
|
||||
initBrowserModel(null, showHiddenFiles = false)
|
||||
|
||||
val noFav = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(1, noFav.size)
|
||||
|
||||
liveFavorites.value = listOf(getFakeBrowserFav(0))
|
||||
|
||||
val hasFav = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(3, hasFav.size)
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package org.videolan.vlc.viewmodels.browser
|
||||
|
||||
import android.os.Handler
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import junit.framework.Assert.assertEquals
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.test.TestMedia
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.providers.BrowserProvider
|
||||
import org.videolan.vlc.util.CoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import java.io.File
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class FilePickerModelTest : BaseTest() {
|
||||
@get:Rule
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
private val mockedLibVlc: LibVLC = mockk(relaxed = true)
|
||||
private lateinit var dummyUrl: String
|
||||
private lateinit var mediaBrowser: MediaBrowser
|
||||
|
||||
private lateinit var browserModel: BrowserModel
|
||||
private lateinit var browserProvider: BrowserProvider
|
||||
|
||||
private val countVideos = 2
|
||||
private val countDirs = 4
|
||||
|
||||
init {
|
||||
// BrowserHandler mocked.
|
||||
val handler: Handler = mockk()
|
||||
|
||||
BrowserProvider.overrideCreator = false
|
||||
BrowserProvider.registerCreator {
|
||||
mediaBrowser = spyk(MediaBrowser(mockedLibVlc, it, handler))
|
||||
mediaBrowser
|
||||
}
|
||||
BrowserProvider.registerCreator(clazz = CoroutineContextProvider::class.java) { TestCoroutineContextProvider() }
|
||||
}
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
dummyUrl = temporaryFolder.root.absolutePath
|
||||
|
||||
browserModel = BrowserModel(application, dummyUrl, TYPE_PICKER, false, true, TestCoroutineContextProvider())
|
||||
browserProvider = browserModel.provider
|
||||
|
||||
setupTestFiles()
|
||||
}
|
||||
|
||||
private fun setupTestFiles() {
|
||||
(1..countDirs).map { temporaryFolder.newFile("dir$it") }
|
||||
(1..countVideos).map { temporaryFolder.newFile("video$it.mp4") }
|
||||
}
|
||||
|
||||
private fun addFileToProvider(i: Int, file: File) {
|
||||
val t = TestMedia(mockedLibVlc, "file://${file.path}").apply { if (!file.name.endsWith(".mp4")) type = Media.Type.Directory }
|
||||
browserProvider.onMediaAdded(i, t)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenBrowseRootAndRootHasFiles_getListOfDirectories() {
|
||||
temporaryFolder.root.listFiles().mapIndexed(this::addFileToProvider)
|
||||
browserProvider.onBrowseEnd()
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
assertEquals(countDirs, testResult.size)
|
||||
assert(testResult[0].title.startsWith("dir"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenBrowseRootAndRootEmpty_checkTheFolderIsEmpty() {
|
||||
browserProvider.onBrowseEnd()
|
||||
|
||||
browserModel.dataset.test()
|
||||
.assertValue { it.isEmpty() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenBrowseRootAndBrowseEndEventTriggered_ensureLoadingIsSetToFalse() {
|
||||
browserModel.loading.test()
|
||||
.assertValue(true)
|
||||
|
||||
browserProvider.onBrowseEnd()
|
||||
|
||||
browserModel.loading.test()
|
||||
.awaitValue()
|
||||
.assertValue(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenRootHasFilesAndRefreshCalled_getUpdatedListOfDirectories() {
|
||||
temporaryFolder.root.listFiles().mapIndexed(this::addFileToProvider)
|
||||
browserProvider.onBrowseEnd()
|
||||
|
||||
var result = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
assertEquals(countDirs, result.size)
|
||||
|
||||
browserModel.refresh()
|
||||
|
||||
temporaryFolder.newFile("dir${countDirs + 1}")
|
||||
temporaryFolder.root.listFiles().mapIndexed(this::addFileToProvider)
|
||||
browserProvider.onBrowseEnd()
|
||||
|
||||
result = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
// TODO: This will fail because the refresh behavior is buggy of BrowserProvider subclasses.
|
||||
assertEquals(countDirs + 1, result.size)
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.videolan.vlc.viewmodels.browser
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import junit.framework.Assert.assertEquals
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractMediaWrapper
|
||||
import org.videolan.medialibrary.media.MediaLibraryItem
|
||||
import org.videolan.medialibrary.media.MediaWrapper
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.database.BrowserFavDao
|
||||
import org.videolan.vlc.providers.BrowserProvider
|
||||
import org.videolan.vlc.repository.BrowserFavRepository
|
||||
import org.videolan.vlc.util.CoroutineContextProvider
|
||||
import org.videolan.vlc.util.Settings
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.applyMock
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class NetworkModelTest : BaseTest() {
|
||||
@get:Rule
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
private val mockedLibVlc: LibVLC = mockk(relaxed = true)
|
||||
private val mockedFavoritesDao: BrowserFavDao = mockk(relaxed = true)
|
||||
private val mockedFavoritesRepo: BrowserFavRepository = spyk(BrowserFavRepository(mockedFavoritesDao))
|
||||
|
||||
private lateinit var mediaBrowser: MediaBrowser
|
||||
private lateinit var browserModel: BrowserModel
|
||||
private lateinit var browserProvider: BrowserProvider
|
||||
|
||||
private val countVideos = 2
|
||||
private val countDirs = 4
|
||||
|
||||
init {
|
||||
BrowserFavRepository.applyMock(mockedFavoritesRepo)
|
||||
|
||||
// BrowserHandler mocked.
|
||||
val handler: Handler = mockk()
|
||||
|
||||
BrowserProvider.overrideCreator = false
|
||||
BrowserProvider.registerCreator {
|
||||
mediaBrowser = spyk(MediaBrowser(mockedLibVlc, it, handler))
|
||||
mediaBrowser
|
||||
}
|
||||
BrowserProvider.registerCreator(clazz = CoroutineContextProvider::class.java) { TestCoroutineContextProvider() }
|
||||
}
|
||||
|
||||
private fun initNetworkModel(url: String?, showHiddenFiles: Boolean = false) {
|
||||
browserModel = NetworkModel(application, url, showHiddenFiles, TestCoroutineContextProvider())
|
||||
browserProvider = browserModel.provider
|
||||
}
|
||||
|
||||
private fun getFakeMediaWrapper(index: Int): MediaWrapper = MediaWrapper(Uri.parse("http://fake_media.io/vid_$index.mp4"))
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndNoFavorites_checkResultIsEmpty() {
|
||||
Settings.overrideTvUI = true
|
||||
every { mockedFavoritesRepo.networkFavorites } returns MediatorLiveData<List<AbstractMediaWrapper>>().apply { value = emptyList() }
|
||||
initNetworkModel(null)
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(0, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootWithOneFavoriteAndOneFavoriteAddedLater_checkResultIsNotEmptyAndContainsThem() {
|
||||
val liveFavorites: MediatorLiveData<List<AbstractMediaWrapper>> = MediatorLiveData()
|
||||
Settings.overrideTvUI = true
|
||||
every { mockedFavoritesRepo.networkFavorites } returns liveFavorites
|
||||
|
||||
liveFavorites.value = listOf(getFakeMediaWrapper(0).apply { setStateFlags(MediaLibraryItem.FLAG_FAVORITE) })
|
||||
initNetworkModel(null)
|
||||
|
||||
val oldResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(3, oldResult.size)
|
||||
assertEquals(getFakeMediaWrapper(0), oldResult[1])
|
||||
|
||||
liveFavorites.value = ArrayList<AbstractMediaWrapper>().apply {
|
||||
liveFavorites.value?.let { addAll(it) }
|
||||
add(getFakeMediaWrapper(1).apply { setStateFlags(MediaLibraryItem.FLAG_FAVORITE) })
|
||||
}
|
||||
|
||||
val newResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(4, newResult.size)
|
||||
assertEquals(getFakeMediaWrapper(0), newResult[1])
|
||||
assertEquals(getFakeMediaWrapper(1), newResult[2])
|
||||
}
|
||||
}
|
@ -0,0 +1,180 @@
|
||||
package org.videolan.vlc.viewmodels.browser
|
||||
|
||||
import android.os.Environment
|
||||
import android.os.Handler
|
||||
import com.jraska.livedata.test
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import junit.framework.Assert.assertEquals
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import org.videolan.libvlc.LibVLC
|
||||
import org.videolan.libvlc.Media
|
||||
import org.videolan.libvlc.test.TestMedia
|
||||
import org.videolan.libvlc.util.MediaBrowser
|
||||
import org.videolan.medialibrary.Medialibrary
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.R
|
||||
import org.videolan.vlc.database.CustomDirectoryDao
|
||||
import org.videolan.vlc.database.models.CustomDirectory
|
||||
import org.videolan.vlc.providers.BrowserProvider
|
||||
import org.videolan.vlc.repository.DirectoryRepository
|
||||
import org.videolan.vlc.util.CoroutineContextProvider
|
||||
import org.videolan.vlc.util.TestCoroutineContextProvider
|
||||
import org.videolan.vlc.util.applyMock
|
||||
import java.io.File
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class StorageModelTest : BaseTest() {
|
||||
// Preferences choose directories to add in medialibrary scan.
|
||||
|
||||
@get:Rule
|
||||
val temporaryFolder = TemporaryFolder()
|
||||
|
||||
private val mockedLibVlc: LibVLC = mockk(relaxed = true)
|
||||
private val mockedDirectoryDao: CustomDirectoryDao = mockk()
|
||||
private val mockedDirectoryRepo: DirectoryRepository = spyk(DirectoryRepository(mockedDirectoryDao))
|
||||
private lateinit var mediaBrowser: MediaBrowser
|
||||
|
||||
private lateinit var browserModel: BrowserModel
|
||||
private lateinit var browserProvider: BrowserProvider
|
||||
|
||||
private var showHiddenFiles: Boolean = false
|
||||
|
||||
private val countVideos = 2
|
||||
private val countDirs = 4
|
||||
private val countHiddenDirs = 2
|
||||
|
||||
init {
|
||||
DirectoryRepository.applyMock(mockedDirectoryRepo)
|
||||
|
||||
// BrowserHandler mocked.
|
||||
val handler: Handler = mockk()
|
||||
|
||||
BrowserProvider.overrideCreator = false
|
||||
BrowserProvider.registerCreator {
|
||||
mediaBrowser = spyk(MediaBrowser(mockedLibVlc, it, handler))
|
||||
mediaBrowser
|
||||
}
|
||||
BrowserProvider.registerCreator(clazz = CoroutineContextProvider::class.java) { TestCoroutineContextProvider() }
|
||||
}
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
setupTestFiles()
|
||||
}
|
||||
|
||||
/**
|
||||
* Setups the browser model.
|
||||
*
|
||||
* @param [showHiddenFiles] - whether to show hidden directories too when browsing.
|
||||
* @param [url] - URL to browse. null for root.
|
||||
*
|
||||
*/
|
||||
private fun initBrowserModel(showHiddenFiles: Boolean, url: String?) {
|
||||
this.showHiddenFiles = showHiddenFiles
|
||||
browserModel = BrowserModel(application, url, TYPE_STORAGE, showHiddenFiles, false, TestCoroutineContextProvider())
|
||||
browserProvider = browserModel.provider
|
||||
}
|
||||
|
||||
private fun setupTestFiles() {
|
||||
(1..countDirs).map { temporaryFolder.newFile("dir$it") }
|
||||
(1..countHiddenDirs).map { temporaryFolder.newFile(".hiddenDir$it") }
|
||||
(1..countVideos).map { temporaryFolder.newFile("video$it.mp4") }
|
||||
}
|
||||
|
||||
private fun addFileToProvider(i: Int, file: File) {
|
||||
if (file.name.startsWith(".") && !showHiddenFiles)
|
||||
return
|
||||
val t = TestMedia(mockedLibVlc, "file://${file.path}").apply { if (!file.name.endsWith(".mp4")) type = Media.Type.Directory }
|
||||
browserProvider.onMediaAdded(i, t)
|
||||
}
|
||||
|
||||
private fun fillFilesInDataset(file: File) {
|
||||
file.listFiles().sorted().mapIndexed(this::addFileToProvider)
|
||||
browserProvider.onBrowseEnd()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndNoCustomDirectory_checkOnlyInternalStorageShows() {
|
||||
initBrowserModel(false, null)
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(1, testResult.size)
|
||||
assertEquals(context.getString(R.string.internal_memory), testResult[0].title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtRootAndTwoCustomDirectoriesWithOneChildOfInternalStorage_checkTwoResultsAreObtained() {
|
||||
val customDir = CustomDirectory(temporaryFolder.newFile("custom1").path)
|
||||
val newDirInsideInternalStorage = File("${Environment.getExternalStorageDirectory().path}/custom2")
|
||||
newDirInsideInternalStorage.mkdir()
|
||||
val customDirInsideInternalStorage = CustomDirectory(newDirInsideInternalStorage.path)
|
||||
coEvery { mockedDirectoryRepo.getCustomDirectories() } returns listOf(customDir, customDirInsideInternalStorage)
|
||||
initBrowserModel(false, null)
|
||||
|
||||
// TODO: This test will fail because nested directories should not be shown.
|
||||
val testResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
assertEquals(context.getString(R.string.internal_memory), testResult[0].title)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtCustomDirAndHiddenDirectoryPresentWithHideHiddenFiles_checkResultHasCorrectDirectoriesAndFlagIsNotShowHiddenFiles() {
|
||||
val customDir = CustomDirectory(temporaryFolder.root.path)
|
||||
coEvery { mockedDirectoryRepo.getCustomDirectories() } returns listOf(customDir)
|
||||
|
||||
initBrowserModel(false, customDir.path)
|
||||
fillFilesInDataset(temporaryFolder.root)
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
assertEquals(countDirs, testResult.size)
|
||||
assertEquals(0, browserProvider.getFlags() and MediaBrowser.Flag.ShowHiddenFiles)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtCustomDirAndHiddenDirectoryPresentWithShowHiddenFiles_checkResultHasHiddenDirectoriesAndFlagIsShowHiddenFiles() {
|
||||
val customDir = CustomDirectory(temporaryFolder.root.path)
|
||||
coEvery { mockedDirectoryRepo.getCustomDirectories() } returns listOf(customDir)
|
||||
|
||||
initBrowserModel(true, customDir.path)
|
||||
fillFilesInDataset(temporaryFolder.root)
|
||||
|
||||
val testResult = browserModel.dataset.test()
|
||||
.value()
|
||||
|
||||
assertEquals(countDirs + countHiddenDirs, testResult.size)
|
||||
assertNotEquals(0, browserProvider.getFlags() and MediaBrowser.Flag.ShowHiddenFiles)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAtInternalStorageAndSortedDescending_checkResultIsReversed() {
|
||||
initBrowserModel(false, Environment.getExternalStorageDirectory().path)
|
||||
fillFilesInDataset(temporaryFolder.root)
|
||||
|
||||
val oldResult = browserModel.dataset.test()
|
||||
.value().toList()
|
||||
|
||||
browserModel.sort(Medialibrary.SORT_ALPHA)
|
||||
|
||||
val newResult = browserModel.dataset.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(oldResult.reversed(), newResult)
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package org.videolan.vlc.viewmodels.mobile
|
||||
|
||||
import com.jraska.livedata.test
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.videolan.medialibrary.interfaces.media.AbstractFolder
|
||||
import org.videolan.medialibrary.stubs.StubDataSource
|
||||
import org.videolan.vlc.BaseTest
|
||||
import org.videolan.vlc.util.MEDIALIBRARY_PAGE_SIZE
|
||||
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
class VideosViewModelTest : BaseTest() {
|
||||
private lateinit var videosViewModel: VideosViewModel
|
||||
|
||||
override fun beforeTest() {
|
||||
super.beforeTest()
|
||||
StubDataSource.getInstance().resetData()
|
||||
}
|
||||
|
||||
private fun setupViewModel(folder: AbstractFolder?) {
|
||||
videosViewModel = VideosViewModel(context, application, folder)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsNull_checkResultIsEmpty() {
|
||||
setupViewModel(null)
|
||||
|
||||
videosViewModel.provider.pagedList.test()
|
||||
.awaitValue()
|
||||
|
||||
assertTrue(videosViewModel.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsNullAndMediaLibraryHasPagedVideos_checkResultContainsThem() {
|
||||
val videoCount = 2
|
||||
setupViewModel(null)
|
||||
|
||||
StubDataSource.getInstance().setVideoByCount(videoCount, null)
|
||||
|
||||
val testResult = videosViewModel.provider.pagedList.test()
|
||||
.awaitValue().value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsGivenAndItIsEmpty_checkResultIsEmpty() {
|
||||
setupViewModel(StubDataSource.getInstance().createFolder("test"))
|
||||
|
||||
videosViewModel.provider.pagedList.test()
|
||||
.awaitValue()
|
||||
|
||||
assertTrue(videosViewModel.isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsGivenAndItHasVideosAndAudio_checkPagedListContainsOnlyVideos() {
|
||||
val videoCount = 2
|
||||
val audioCount = 1
|
||||
setupViewModel(StubDataSource.getInstance().createFolder("test"))
|
||||
|
||||
StubDataSource.getInstance().setVideoByCount(videoCount, "test")
|
||||
StubDataSource.getInstance().setAudioByCount(audioCount, "test")
|
||||
|
||||
val testResult = videosViewModel.provider.pagedList.test()
|
||||
.awaitValue().value()
|
||||
|
||||
assertEquals(2, testResult.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsGivenAndItHasVideosAndAudio_checkTotalCountHasOnlyVideos() {
|
||||
val videoCount = 2
|
||||
val audioCount = 1
|
||||
setupViewModel(StubDataSource.getInstance().createFolder("test"))
|
||||
|
||||
StubDataSource.getInstance().setVideoByCount(videoCount, "test")
|
||||
StubDataSource.getInstance().setAudioByCount(audioCount, "test")
|
||||
|
||||
videosViewModel.provider.pagedList.test()
|
||||
.awaitValue()
|
||||
|
||||
assertEquals(2, videosViewModel.provider.getTotalCount())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsNullAndVideosAreMoreThanMaxSize_checkLastIsNotLoadedYet() {
|
||||
val videoCount = MEDIALIBRARY_PAGE_SIZE * 3 + 1
|
||||
setupViewModel(null)
|
||||
StubDataSource.getInstance().setVideoByCount(videoCount, "test")
|
||||
|
||||
val testResult = videosViewModel.provider.pagedList.test()
|
||||
.awaitValue()
|
||||
.value()
|
||||
|
||||
assertEquals(null, testResult[videoCount - 1])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenFolderIsNullAndItHasVideos_checkGetAllReturnsAll() {
|
||||
val videoCount = MEDIALIBRARY_PAGE_SIZE + 1
|
||||
val audioCount = 200
|
||||
setupViewModel(StubDataSource.getInstance().createFolder("test"))
|
||||
|
||||
StubDataSource.getInstance().setVideoByCount(videoCount, "test")
|
||||
StubDataSource.getInstance().setAudioByCount(audioCount, "test")
|
||||
|
||||
val testResult = videosViewModel.provider.getAll()
|
||||
|
||||
assertEquals(videoCount, testResult.size)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user