Convert Figma logo to code with AI

danylovolokh logoVideoPlayerManager

This is a project designed to help controlling Android MediaPlayer class. It makes it easier to use MediaPlayer ListView and RecyclerView. Also it tracks the most visible item in scrolling list. When new item in the list become the most visible, this library gives an API to track it.

3,149
746
3,149
93

Top Related Projects

21,700

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media

32,417

Android/iOS video player based on FFmpeg n3.4, with MediaCodec, VideoToolbox support.

视频播放器(IJKplayer、ExoPlayer、MediaPlayer),HTTPS,16k page size,支持弹幕,外挂字幕,支持滤镜、水印、gif截图,片头广告、中间广告,多个同时播放,支持基本的拖动,声音、亮度调节,支持边播边缓存,支持视频自带rotation的旋转(90,270之类),重力旋转与手动旋转的同步支持,支持列表播放 ,列表全屏动画,视频加载速度,列表小窗口支持拖动,动画效果,调整比例,多分辨率切换,支持切换播放器,进度条小窗口预览,列表切换详情页面无缝播放,rtsp、concat、mpeg。

MediaPlayer exoplayer ijkplayer ffmpeg

An Android ExoPlayer wrapper to simplify Audio and Video implementations

Vitamio for Android

Quick Overview

VideoPlayerManager is an Android library that simplifies the management of multiple video players in a single view or RecyclerView. It handles the complexities of playing, pausing, and releasing video resources efficiently, especially when scrolling through a list of videos.

Pros

  • Efficient management of multiple video players in a scrollable list
  • Automatic handling of video playback states during scrolling
  • Supports both single video view and RecyclerView implementations
  • Customizable video player UI and controls

Cons

  • Limited documentation and examples
  • May require additional setup for more complex video playback scenarios
  • Not actively maintained (last update was in 2017)
  • Potential performance issues with a large number of video players

Code Examples

  1. Setting up a single video player:
val videoPlayerManager = SingleVideoPlayerManager(PlayerItemChangeListener())
val videoPlayerView = findViewById<VideoPlayerView>(R.id.video_player_view)

videoPlayerManager.playNewVideo(null, videoPlayerView, "https://example.com/video.mp4")
  1. Implementing a video player in a RecyclerView:
class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val videoPlayerView: VideoPlayerView = itemView.findViewById(R.id.video_player_view)
}

override fun onBindViewHolder(holder: VideoViewHolder, position: Int) {
    val videoItem = videoList[position]
    videoPlayerManager.playNewVideo(null, holder.videoPlayerView, videoItem.videoUrl)
}
  1. Handling video player lifecycle:
override fun onResume() {
    super.onResume()
    videoPlayerManager.resumeCurrentVideo()
}

override fun onPause() {
    super.onPause()
    videoPlayerManager.pauseCurrentVideo()
}

override fun onDestroy() {
    super.onDestroy()
    videoPlayerManager.releaseAllVideos()
}

Getting Started

  1. Add the dependency to your build.gradle file:
dependencies {
    implementation 'com.github.danylovolokh:video-player-manager:0.2.0'
}
  1. Initialize the VideoPlayerManager in your activity or fragment:
private lateinit var videoPlayerManager: SingleVideoPlayerManager

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    videoPlayerManager = SingleVideoPlayerManager(PlayerItemChangeListener())
}
  1. Use the VideoPlayerManager to play videos in your layout:
val videoPlayerView = findViewById<VideoPlayerView>(R.id.video_player_view)
videoPlayerManager.playNewVideo(null, videoPlayerView, "https://example.com/video.mp4")

Remember to handle the video player lifecycle in your activity or fragment's lifecycle methods as shown in the code examples above.

Competitor Comparisons

21,700

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media

Pros of ExoPlayer

  • More comprehensive and feature-rich, supporting a wide range of media formats and streaming protocols
  • Actively maintained by Google, ensuring regular updates and improvements
  • Extensive documentation and community support

Cons of ExoPlayer

  • Steeper learning curve due to its complexity and extensive feature set
  • Larger library size, which may impact app size and performance

Code Comparison

VideoPlayerManager:

VideoPlayerManager videoPlayerManager = new VideoPlayerManager();
videoPlayerManager.playNewVideo(videoItem, videoView);

ExoPlayer:

SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build();
player.setMediaItem(MediaItem.fromUri(videoUri));
player.prepare();
player.play();

Key Differences

  • VideoPlayerManager focuses on managing multiple video players in a list, while ExoPlayer is a more general-purpose media player
  • ExoPlayer offers more advanced features like adaptive streaming and custom renderers
  • VideoPlayerManager provides a simpler API for basic video playback scenarios

Use Cases

  • Choose VideoPlayerManager for simpler projects with basic video playback needs, especially in scrollable lists
  • Opt for ExoPlayer when building more complex media applications with advanced streaming requirements or custom playback features
32,417

Android/iOS video player based on FFmpeg n3.4, with MediaCodec, VideoToolbox support.

Pros of ijkplayer

  • More comprehensive and feature-rich video player solution
  • Supports a wider range of video formats and codecs
  • Better performance for high-quality video playback

Cons of ijkplayer

  • More complex to implement and integrate
  • Larger library size, which may increase app size
  • Steeper learning curve for developers

Code Comparison

VideoPlayerManager focuses on managing multiple video players:

VideoPlayerManager<MetaData> videoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
    @Override
    public void onPlayerItemChanged(MetaData metaData) {
        // Handle player item change
    }
});

ijkplayer provides lower-level control over video playback:

IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.setDataSource(videoPath);
ijkMediaPlayer.prepareAsync();
ijkMediaPlayer.start();

Summary

VideoPlayerManager is a simpler solution focused on managing multiple video players, while ijkplayer is a more comprehensive video player library with broader format support and lower-level control. VideoPlayerManager may be easier to implement for basic use cases, while ijkplayer offers more advanced features and better performance for complex video playback scenarios.

视频播放器(IJKplayer、ExoPlayer、MediaPlayer),HTTPS,16k page size,支持弹幕,外挂字幕,支持滤镜、水印、gif截图,片头广告、中间广告,多个同时播放,支持基本的拖动,声音、亮度调节,支持边播边缓存,支持视频自带rotation的旋转(90,270之类),重力旋转与手动旋转的同步支持,支持列表播放 ,列表全屏动画,视频加载速度,列表小窗口支持拖动,动画效果,调整比例,多分辨率切换,支持切换播放器,进度条小窗口预览,列表切换详情页面无缝播放,rtsp、concat、mpeg。

Pros of GSYVideoPlayer

  • More comprehensive feature set, including support for various video formats and streaming protocols
  • Active development with frequent updates and bug fixes
  • Extensive documentation and examples for easier implementation

Cons of GSYVideoPlayer

  • Larger codebase and potentially higher complexity for simple use cases
  • May have a steeper learning curve for developers new to video playback in Android

Code Comparison

VideoPlayerManager:

ItemsPositionGetter itemsPositionGetter = new RecyclerViewItemPositionGetter(mRecyclerView, layoutManager);
VideoPlayerManager<MetaData> videoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
    @Override
    public void onPlayerItemChanged(MetaData metaData) {
        // Handle player item change
    }
});

GSYVideoPlayer:

GSYVideoOptionBuilder gsyVideoOption = new GSYVideoOptionBuilder();
gsyVideoOption.setIsTouchWiget(true)
    .setRotateViewAuto(false)
    .setLockLand(false)
    .setShowFullAnimation(false)
    .setNeedLockFull(true)
    .setSeekRatio(1f)
    .build(videoPlayer);

Summary

GSYVideoPlayer offers a more feature-rich and actively maintained solution for video playback in Android applications. It provides extensive customization options and supports various video formats. However, this comes at the cost of increased complexity and a potentially steeper learning curve. VideoPlayerManager, on the other hand, may be simpler to implement for basic video playback needs but lacks some of the advanced features and ongoing development of GSYVideoPlayer.

MediaPlayer exoplayer ijkplayer ffmpeg

Pros of JiaoZiVideoPlayer

  • More comprehensive feature set, including support for various video sources and formats
  • Better documentation and examples, making it easier for developers to implement
  • Active community and frequent updates, ensuring ongoing support and improvements

Cons of JiaoZiVideoPlayer

  • Larger codebase and potentially higher complexity, which may impact app size and performance
  • Less focused on specific use cases, potentially making it overkill for simpler video playback needs

Code Comparison

VideoPlayerManager:

VideoPlayerManager<MetaData> videoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
    @Override
    public void onPlayerItemChanged(MetaData metaData) {
        // Handle player item change
    }
});

JiaoZiVideoPlayer:

JzvdStd jzvdStd = findViewById(R.id.videoplayer);
jzvdStd.setUp("http://jzvd.nathen.cn/c6e3dc12a1154626b3476d9bf3bd7266/6b56c5f0dc31428083757a45764763b0-5287d2089db37e62345123a1be272f8b.mp4"
              , "Title");
jzvdStd.posterImageView.setImage("http://p.qpic.cn/videoyun/0/2449_43b6f696980311e59ed467f22794e792_1/640");

The code comparison shows that JiaoZiVideoPlayer offers a more straightforward setup process with built-in UI components, while VideoPlayerManager focuses on managing video playback without providing a default UI.

An Android ExoPlayer wrapper to simplify Audio and Video implementations

Pros of ExoMedia

  • Built on top of ExoPlayer, providing a more robust and feature-rich foundation
  • Supports a wider range of media formats and streaming protocols
  • Offers better customization options and easier integration with Android UI components

Cons of ExoMedia

  • Larger library size due to ExoPlayer dependency
  • May have a steeper learning curve for developers unfamiliar with ExoPlayer
  • Potentially higher memory usage compared to VideoPlayerManager

Code Comparison

VideoPlayerManager:

val videoPlayerManager = VideoPlayerManager()
videoPlayerManager.playNewVideo(null, videoListItem)

ExoMedia:

val videoView = ExoVideoView(context)
videoView.setMedia(Uri.parse(videoUrl))
videoView.start()

Both libraries aim to simplify video playback in Android applications, but they take different approaches. VideoPlayerManager focuses on managing multiple video players and handling transitions between them, while ExoMedia provides a more comprehensive media playback solution built on top of ExoPlayer.

ExoMedia offers more advanced features and better format support, making it suitable for complex media playback requirements. However, VideoPlayerManager may be a lighter-weight option for simpler use cases, especially when dealing with multiple video players in a single view.

The choice between these libraries depends on the specific needs of your project, such as required media formats, streaming protocols, and the complexity of your video playback implementation.

Vitamio for Android

Pros of VitamioBundle

  • Supports a wider range of video formats and codecs
  • Offers hardware acceleration for improved performance
  • Provides cross-platform compatibility (Android, iOS, and PC)

Cons of VitamioBundle

  • Less actively maintained (last update in 2017)
  • Larger library size, potentially increasing app size
  • More complex setup and integration process

Code Comparison

VideoPlayerManager:

VideoPlayerManager<MetaData> videoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
    @Override
    public void onPlayerItemChanged(MetaData metaData) {
        // Handle player item change
    }
});

VitamioBundle:

IO.vInit(this);
mVideoView = (VideoView) findViewById(R.id.surface_view);
mVideoView.setVideoPath(path);
mVideoView.setMediaController(new MediaController(this));
mVideoView.requestFocus();

VideoPlayerManager focuses on managing multiple video players, while VitamioBundle provides a more comprehensive media playback solution with broader format support. VideoPlayerManager offers a simpler integration process and is more actively maintained, making it suitable for projects requiring basic video playback functionality. VitamioBundle is better suited for applications needing extensive format support and cross-platform compatibility, despite its larger size and more complex setup.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

VideoPlayerManager

This is a project designed to help controlling Android MediaPlayer class. It makes it easier to use MediaPlayer ListView and RecyclerView. Also it tracks the most visible item in scrolling list. When new item in the list become the most visible, this library gives and API to track it.

It consists from two libraries:

  1. Video-Player-Manager - it gives the ability to invoke MediaPlayer methods in a background thread. It has utilities to have only one playback when multiple media files are in the list. Before new playback starts, it stops the old playback and releases all the resources.

  2. List-Visibility-Utils - it's a library that tracks the most visible item in the list and notifies when it changes. NOTE: there should be the most visible item. If there will be 3 or more items with the same visibility percent the result might be unpredictable. Recommendation is to have few views visible on the screen. View that are big enough so that only one view is the most visible, look at the demo below.

These two libraries combined are the tool to get a Video Playback in the scrolling list: ListView, RecyclerView.

Details of implementation

Medium

Android_weekly

Android Arsenal

Problems with video list

  1. We cannot use usual VideoView in the list. VideoView extends SurfaceView, and SurfaceView doesn't have UI synchronization buffers. All this will lead us to the situation where video that is playing is trying to catch up the list when you scroll it. Synchronization buffers are present in TextureView but there is no VideoView that is based on TextureView in Android SDK version 15. So we need a view that extends TextureView and works with Android MediaPlayer.

  2. Almost all methods (prepare, start, stop etc...) from MediaPlayer are basically calling native methods that work with hardware. Hardware can be tricky and if will do any work longer than 16ms (And it sure will) then we will see a lagging list. That's why need to call them from background thread.

Usage

Add this snippet to your project build.gradle file:

buildscript {
    repositories {
        jcenter()
    }
}

Usage of Video-Player-Manager

dependencies {
    compile 'com.github.danylovolokh:video-player-manager:0.2.0'
}

Put multiple VideoPlayerViews into your xml file. In most cases you also need a images above that will be shown when playback is stopped.

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!-- Top Player-->
        <com.volokh.danylo.video_player_manager.ui.VideoPlayerView
            android:id="@+id/video_player_1"
            android:layout_height="0dp"
            android:layout_width="match_parent"
            android:layout_weight="1"
            />

        <com.volokh.danylo.video_player_manager.ui.VideoPlayerView
            android:id="@+id/video_player_2"
            android:layout_height="0dp"
            android:layout_width="match_parent"
            android:layout_weight="1"
            />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!-- Top Player-->
        <ImageView
            android:id="@+id/video_cover_1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            android:layout_weight="1"/>

        <ImageView
            android:id="@+id/video_cover_2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:scaleType="centerCrop"
            android:layout_weight="1"/>
    </LinearLayout>

Now you can use SingleVideoPlayerManager to playback only a single video at once:

//... some code
private VideoPlayerManager<MetaData> mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
    @Override
    public void onPlayerItemChanged(MetaData metaData) {

    }
});
//... some code

mVideoPlayer_1 = (VideoPlayerView)root.findViewById(R.id.video_player_1);
mVideoPlayer_1.addMediaPlayerListener(new SimpleMainThreadMediaPlayerListener(){
   @Override
   public void onVideoPreparedMainThread() {
    // We hide the cover when video is prepared. Playback is about to start
    mVideoCover.setVisibility(View.INVISIBLE);
   }

   @Override
   public void onVideoStoppedMainThread() {
   // We show the cover when video is stopped
    mVideoCover.setVisibility(View.VISIBLE);
   }

   @Override
   public void onVideoCompletionMainThread() {
       // We show the cover when video is completed
       mVideoCover.setVisibility(View.VISIBLE);
   }
});
mVideoCover = (ImageView)root.findViewById(R.id.video_cover_1);
mVideoCover.setOnClickListener(this);

mVideoPlayer_2 = (VideoPlayerView)root.findViewById(R.id.video_player_2);
mVideoPlayer_2.addMediaPlayerListener(new SimpleMainThreadMediaPlayerListener(){
   @Override
   public void onVideoPreparedMainThread() {
       // We hide the cover when video is prepared. Playback is about to start
        mVideoCover2.setVisibility(View.INVISIBLE);
   }

   @Override
   public void onVideoStoppedMainThread() {
        // We show the cover when video is stopped
      mVideoCover2.setVisibility(View.VISIBLE);
   }

   @Override
   public void onVideoCompletionMainThread() {
      // We show the cover when video is completed
      mVideoCover2.setVisibility(View.VISIBLE);
   }
});
mVideoCover2 = (ImageView)root.findViewById(R.id.video_cover_2);
mVideoCover2.setOnClickListener(this);

// some code
@Override
public void onClick(View v) {
        switch (v.getId()){
            case R.id.video_cover_1:
                mVideoPlayerManager.playNewVideo(null, mVideoPlayer_1, "http:\\url_to_you_video_1_source");
                break;
            case R.id.video_cover_2:
                mVideoPlayerManager.playNewVideo(null, mVideoPlayer_2, "http:\\url_to_you_video_2_source");
                break;
        }
}

The Demo of Video-Player-Manager:

video_player_manager_demo

Usage of List-Visibility-Utils

dependencies {
    compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'
}

The models of your adapter need to implement ListItem

public interface ListItem {
    int getVisibilityPercents(View view);
    void setActive(View newActiveView, int newActiveViewPosition);
    void deactivate(View currentView, int position);
}

This messes up a bit with separating model from the logic. Here you need to handle the login in the model.

The ListItemsVisibilityCalculator will call according methods to:

  1. Get view visibility

  2. Set this item to active

  3. Deactivate the item

// some code...
private final ListItemsVisibilityCalculator mListItemVisibilityCalculator =
            new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mList);
// some code...

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {
                mScrollState = scrollState;
                if(scrollState == RecyclerView.SCROLL_STATE_IDLE && !mList.isEmpty()){

                    mListItemVisibilityCalculator.onScrollStateIdle(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition());
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                if(!mList.isEmpty()){
                    mListItemVisibilityCalculator.onScroll(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1,
                            mScrollState);
                }
            }
        });

mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);

    @Override
    public void onResume() {
        super.onResume();
        if(!mList.isEmpty()){
            // need to call this method from list view handler in order to have filled list

            mRecyclerView.post(new Runnable() {
                @Override
                public void run() {

                    mListItemVisibilityCalculator.onScrollStateIdle(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition());

                }
            });
        }
    }
	

The Demo of List-Visibility-Utils:

visibility_utils_demo

Usage in scrolling list (ListView, RecyclerView)

Add this snippet to your module build.gradle file:

dependencies {
    compile 'com.github.danylovolokh:video-player-manager:0.2.0'
    compile 'com.github.danylovolokh:list-visibility-utils:0.2.0'
}

Here is the relevant code combanation of two libraries fro implementing Video Playback in scrolling list.

// Code of your acitivty
    /**
     * Only the one (most visible) view should be active (and playing).
     * To calculate visibility of views we use {@link SingleListViewItemActiveCalculator}
     */
    private final ListItemsVisibilityCalculator mVideoVisibilityCalculator =
            new SingleListViewItemActiveCalculator(new DefaultSingleItemCalculatorCallback(), mList);

    /**
     * ItemsPositionGetter is used by {@link ListItemsVisibilityCalculator} for getting information about
     * items position in the RecyclerView and LayoutManager
     */
    private ItemsPositionGetter mItemsPositionGetter;

    /**
     * Here we use {@link SingleVideoPlayerManager}, which means that only one video playback is possible.
     */
    private final VideoPlayerManager<MetaData> mVideoPlayerManager = new SingleVideoPlayerManager(new PlayerItemChangeListener() {
        @Override
        public void onPlayerItemChanged(MetaData metaData) {

        }
    });

    private int mScrollState = AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

	// fill the list of items with an items

	// some initialization code here

        VideoRecyclerViewAdapter videoRecyclerViewAdapter = new VideoRecyclerViewAdapter(mVideoPlayerManager, getActivity(), mList);

        mRecyclerView.setAdapter(videoRecyclerViewAdapter);
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int scrollState) {
                mScrollState = scrollState;
                if(scrollState == RecyclerView.SCROLL_STATE_IDLE && !mList.isEmpty()){

                    mVideoVisibilityCalculator.onScrollStateIdle(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition());
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                if(!mList.isEmpty()){
                    mVideoVisibilityCalculator.onScroll(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition() - mLayoutManager.findFirstVisibleItemPosition() + 1,
                            mScrollState);
                }
            }
        });
        mItemsPositionGetter = new RecyclerViewItemPositionGetter(mLayoutManager, mRecyclerView);

        return rootView;
    }

    @Override
    public void onResume() {
        super.onResume();
        if(!mList.isEmpty()){
            // need to call this method from list view handler in order to have filled list

            mRecyclerView.post(new Runnable() {
                @Override
                public void run() {

                    mVideoVisibilityCalculator.onScrollStateIdle(
                            mItemsPositionGetter,
                            mLayoutManager.findFirstVisibleItemPosition(),
                            mLayoutManager.findLastVisibleItemPosition());

                }
            });
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        // we have to stop any playback in onStop
        mVideoPlayerManager.resetMediaPlayer();
    }    

When visibility utils calls method "setActive" on your implementation of ListItem you have to call "playNewVideo". Please find the working code on this Demo application.

    /**
     * When this item becomes active we start playback on the video in this item
     */
    @Override
    public void setActive(View newActiveView, int newActiveViewPosition) {
        VideoViewHolder viewHolder = (VideoViewHolder) newActiveView.getTag();
        playNewVideo(new CurrentItemMetaData(newActiveViewPosition, newActiveView), viewHolder.mPlayer, mVideoPlayerManager);
    }
    
    @Override
    public void playNewVideo(MetaData currentItemMetaData, VideoPlayerView player, VideoPlayerManager<MetaData> videoPlayerManager) {
        videoPlayerManager.playNewVideo(currentItemMetaData, player, mDirectUrl);
    }

Demo of usage in scrolling list (ListView, RecyclerView)

recycler_view_demo list_view_demo

AndroidX support

Migration to AndroidX gladly provided by https://github.com/prensmiskin

License

Copyright 2015 Danylo Volokh

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.