Giter Club home page Giter Club logo

cumulustv's Introduction

CumulusTV

Android TV Live Channel Plugin with user-entered stream files, powered by the TIF Companion Library.

CumulusTV is a service that allows users to add HLS (HTTP Live Streaming) files or any website and play them through the Live Channels app on Android TV. Channels can be added through Android TV, or sync your data with Google Drive and edit the data on any computer.

Documentation and short guides can be found on the project's website.

Discussion can happen on the subreddit.

Contributing

This is a community project. I welcome any other developers to contribute to this project as well, whether in the form of a web app, a phone app, or on the Android TV app. Please submit a pull request.

If you can't develop, you can create an issue here, or send inquires @HandNF.

Create a Plugin

CumulusTV supports a plugin infrastructure to make it easier to setup certain types of sources when the URL may change.

Want to make it easy for users to add a Twitch stream without entering a complicated URL? Then you want to create a plugin. This is an app that works easily with CumulusTV, simplifying the amount of work you need to do for a live channel to appear.

repositories {
	...
	maven { url 'https://jitpack.io' }
}

compile 'com.github.Fleker:CumulusTV:1.7.+'

To learn how to do this, read this guide.

Download it Now

The app is available for free on Google Play as a beta. Anyone who downloads and uses the beta will be using the most recent release.

History

This originally started from a thread on Reddit where a user wanted to add user-defined channels using m3u8 files.

To-Do

  • Find m3u streams to provide as samples
  • Expand the types of data to be imported
  • Expand the locations where data can be imported
  • Android Auto support

Suggested Streams

To help users quickly setup, I want a set of suggested streams (m3u8 files) that users can add. If you have a suggestion, add it as an issue for this project so I can add it to the list.

JSON Format

Google Drive syncs a JSON file between all your devices that can be easily edited. The format is below. Of course, you can easily import and export M3U playlists using the built-in parser from a local file or from the web.

Screenshots



Selecting http://time.is as a channel source

cumulustv's People

Contributors

fleker avatar ryao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cumulustv's Issues

Add TWiT as a sample

TWiT.tv is a great free streaming Tech Network airing 24/7 with new shows every day.

Here's the JSON I made to make it easier:

{
  "channels":[
        {
            "number":"88",
            "name":"TWiT",
            "logo":"http:\/\/wiki.twit.tv\/w\/images\/thumb\/TWiT_Logo.svg.png\/487px-TWiT_Logo.svg.png",
            "url":"http:\/\/twit.live-s.cdn.bitgravity.com\/cdn-live-s1\/_definst_\/twit\/live\/high\/playlist.m3u8",
            "splashscreen":"",
            "genres":"NEWS,TECH_SCIENCE"
        }
    ],
    "modified":"1439728483656",
    "possibleGenres":["ANIMAL_WILDLIFE","ARTS","COMEDY","DRAMA","EDUCATION","ENTERTAINMENT","FAMILY_KIDS","GAMING","LIFE_STYLE","MOVIES","MUSIC","NEWS","PREMIER","SHOPPING","SPORTS","TECH_SCIENCE","TRAVEL"]
}

Graphics overhaul

Obviously I need to improve the current paint-created images that I have set for the app.

Custom Info Screen

When buffering, have a custom screen like Pluto TV. Also, make its appearance more consistent if possible with handlers.

Retrieve previous files

Don't force the user to create a new file if none is listed -- allow them to open a previous file

https://github.com/googledrive/android-demos/blob/master/src/com/google/android/gms/drive/sample/demo/RetrieveContentsWithProgressDialogActivity.java

// Let the user pick an mp4 or a jpeg file if there are
// no files selected by the user.
IntentSender intentSender = Drive.DriveApi
.newOpenFileActivityBuilder()
.setMimeType(new String[]{ "video/mp4", "image/jpeg" })
.build(getGoogleApiClient());
try {
startIntentSenderForResult(intentSender, REQUEST_CODE_OPENER, null, 0, 0, 0);
} catch (SendIntentException e) {
Log.w(TAG, "Unable to send intent", e);
}

Got NullPointer in JSONChannel

Will investigate this crash

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.isEmpty()' on a null object reference
   at com.felkertech.n.cumulustv.JSONChannel.hasSource(JSONChannel.java:91)
   at com.felkertech.n.cumulustv.MainActivity$2$2.onSelection(MainActivity.java:119)
   at com.afollestad.materialdialogs.MaterialDialog.onItemClick(MaterialDialog.java:182)
   at android.widget.AdapterView.performItemClick(AdapterView.java:305)
   at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
   at android.widget.AbsListView.onKeyUp(AbsListView.java:3198)
   at android.widget.ListView.commonKey(ListView.java:2300)
   at android.widget.ListView.onKeyUp(ListView.java:2155)
   at android.view.KeyEvent.dispatch(KeyEvent.java:2644)
   at android.view.View.dispatchKeyEvent(View.java:8412)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1490)
   at android.widget.ListView.dispatchKeyEvent(ListView.java:2130)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
   at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:2361)
   at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1709)
   at android.app.Dialog.dispatchKeyEvent(Dialog.java:731)
   at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:2276)
   at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4129)
   at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4091)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3648)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3701)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3667)
   at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3787)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3675)
   at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3844)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3648)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3701)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3667)
   at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3675)
   at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3648)
   at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3701)
   at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3667)
   at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3820)
   at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:3981)
   at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2219)
   at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1860)
   at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1851)
   at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2196)
   at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
   at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
   at android.os.MessageQueue.next(MessageQueue.java:143)
   at android.os.Looper.loop(Looper.java:122)
   at android.app.ActivityThread.main(ActivityThread.java:5314)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

AppIntro

Introduce users to the app and how it works

Channel list and editing

Hallo Nick Felker,

I tried your Cumulus app. Thanks for the work. It looks to me like the app I'am waiting for, since using the NP.
Your preinstalled channels work. A bit slow, but okay.

My hardware: Nexus player, Android TV 5.1.1. For remote control "Samsung S5", "Nexus 7 (2012)" (both with Lollipop and ).

I have some problems using the app.

  1. To add an own channel gives me a hard time. I tried to use the official nexus remote app, at Mobile and Tablet. After inserting the channel name and the stream-adress, the result at the TV-screen is different of the Tablet or Mobile screen.

  2. I have problems to edit a stream. After "pressing" the update button, no change is saved. It is not possible to edit the entry.

Suggestions:

  1. What do you think about an "Export" (and Import-) function for the channel list? So the user could edit/handle the list more easy. Guess, to use the small nexus-player-remote-control to insert each channel by hand is a crime ;)
  2. It would be great to have an option to (easy) use/load "Channel-Logos" from a local folder.
  3. What is your idea regarding the epg?

Thank you in advance

Matthias

New graphics again

Now that I have reinstalled Photoshop, I'd like to get a little more creative with the appicon and other graphics. I like the purple & white, but a more creative logo would be neat.

Custom programming

Allow users to specify custom XMLtv files so you get accurate programming information.

Resync when new content is received

#2 (comment)

This mostly just applies to when the app is started, I think, that the app doesn't automatically force a resync which in turn alters the channel line-up.

This requires more investigation.

Companion application for Android mobile

As a side project, it would be great if there was a companion application that would allow you to import streams from m3u or m3u8 into the json file held within your Google drive. Kind of like a cumulus TV editor to make it easy to copy and paste via android phone then fire up your Android TV for import.

Intent crash

From crash report

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.content.Intent.migrateExtraStreamToClipData()' on a null object reference
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1494)
at android.app.Activity.startActivityForResult(Activity.java:3745)
at android.app.Activity.startActivityForResult(Activity.java:3706)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:820)
at android.app.Activity.startActivity(Activity.java:4016)
at android.app.Activity.startActivity(Activity.java:3984)
at com.felkertech.n.cumulustv.MainActivity$4.onClick(MainActivity.java:250)
at android.view.View.performClick(View.java:4780)
at android.view.View.onKeyUp(View.java:9023)
at android.widget.TextView.onKeyUp(TextView.java:5899)
at android.view.KeyEvent.dispatch(KeyEvent.java:2643)
at android.view.View.dispatchKeyEvent(View.java:8411)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1495)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:2401)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1715)
at android.app.Activity.dispatchKeyEvent(Activity.java:2702)
at android.support.v7.internal.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:49)
at android.support.v7.app.AppCompatDelegateImplBase$AppCompatWindowCallbackBase.dispatchKeyEvent(AppCompatDelegateImplBase.java:265)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:2316)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:4020)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3982)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3544)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3597)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3563)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3680)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3571)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3737)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3544)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3597)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3563)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3571)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3544)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3597)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3563)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3713)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:3874)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2208)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:1849)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:1840)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2185)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:143)
at android.os.Looper.loop(Looper.java:122)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Allow users to import m3u files

Maybe they don't open the file on their TV, but have it in their Drive. Let users import a file from Drive.

This can be an additional plugin.

Setup Crashes

Seems to be some weird length issue. From crash report:

java.lang.ArrayIndexOutOfBoundsException: length=3; index=3
at com.felkertech.n.cumulustv.SampleSetup$3.handleMessage(SampleSetup.java:90)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5314)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Crash Report [1.3.0, SyncUtils]

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.felkertech.n.cumulustv/com.felkertech.n.cumulustv.SampleSetup}: java.lang.SecurityException: caller uid 10061 is different than the authenticator's uid
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
   at android.app.ActivityThread.access$800(ActivityThread.java:151)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5254)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
 Caused by: java.lang.SecurityException: caller uid 10061 is different than the authenticator's uid
   at android.os.Parcel.readException(Parcel.java:1546)
   at android.os.Parcel.readException(Parcel.java:1499)
   at android.accounts.IAccountManager$Stub$Proxy.addAccountExplicitly(IAccountManager.java:857)
   at android.accounts.AccountManager.addAccountExplicitly(AccountManager.java:658)
   at com.example.android.sampletvinput.syncadapter.SyncUtils.setUpPeriodicSync(SyncUtils.java:39)
   at com.felkertech.n.cumulustv.SampleSetup.onCreate(SampleSetup.java:63)
   at android.app.Activity.performCreate(Activity.java:5990)
   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
   at android.app.ActivityThread.access$800(ActivityThread.java:151)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:135)
   at android.app.ActivityThread.main(ActivityThread.java:5254)
   at java.lang.reflect.Method.invoke(Method.java)
   at java.lang.reflect.Method.invoke(Method.java:372)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

Miniplayer Expands

Allow users to expand the miniplayer to actually stream the channel in the app.

Leanback UI on TV

This is the major feature in 1.4, using a leanback UI for the TV app instead of relying on a simple button layout

Crash Report [1.3.0, MainActivity:588]

java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.felkertech.n.cumulustv.xmltv.Program
   at com.felkertech.n.cumulustv.MainActivity$12$4.run(MainActivity.java:588)
   at java.lang.Thread.run(Thread.java:818)

[Proposal] Android Auto

Android Auto allows users access to media and messages through a consistent interface on their car's infotainment system.

Cumulus, being a media app, may be well poised for this sort of platform. Some streams that are heavy on video, like the Montery Bay Aquarium, might not be a good match, but OutOfFocus.TV is primarily audio and could be a good extension of this app.

This would be a later down the line sort of feature that would depend on how much time I'll have. As I don't have an actual model I will have to use an emulator.

Support Marshmallow

Got this error. Seems to be on lollipop though so I don't really know why it's wigging out:

java.lang.SecurityException: Permission Denial: writing com.android.providers.tv.TvProvider uri content://android.media.tv/program/371183 from pid=6356, uid=10077 requires com.android.providers.tv.permission.WRITE_EPG_DATA, or grantUriPermission()
   at android.os.Parcel.readException(Parcel.java:1546)
   at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
   at android.database.DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(DatabaseUtils.java:160)
   at android.content.ContentProviderProxy.applyBatch(ContentProviderNative.java:520)
   at android.content.ContentProviderClient.applyBatch(ContentProviderClient.java:377)
   at android.content.ContentResolver.applyBatch(ContentResolver.java:1244)
   at com.example.android.sampletvinput.syncadapter.SyncAdapter$1.run(SyncAdapter.java:497)
   at java.lang.Thread.run(Thread.java:818)

[Investigate] Handle low memory

A large number of crashes occur when the device memory is low. This should be examined in more detail including a way to prevent crashes from happening.

Integrate XMLTV Parser into Program Guide

Be able to clear the program guide and refresh it every time the content is synced.

Fill program guide with custom data, coming from a file loaded from a URL and only focus on a section of it, the part that makes sense.

Better feedback on I/O ops

On the homescreen, when a read op is done, send a toast or something. This will require a bit of adjusting in the SettingsManager

Provide examples of Splashscreen

I have had fun playing with this app the past few days. I would really appreciate it if you were able to provide an example of compatible sizes for Splashscreen element.

Recognize invalid logos

From this crash

java.lang.IllegalStateException: Unrecognized type of request: Request{aljazeera.jpg}
   at com.squareup.picasso.BitmapHunter$2.load(BitmapHunter.java:66)
   at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:206)
   at com.squareup.picasso.RequestCreator.get(RequestCreator.java:396)
   at com.felkertech.n.cumulustv.SampleTvInput$SimpleSessionImpl$1.run(SampleTvInput.java:211)
   at java.lang.Thread.run(Thread.java:818)

Use m3u playlist spec for import/export

Build a two-way parser for m3u playlists. This is a common format for many people already using this technology, as opposed to the custom JSON format.

JSON might remain the primary format underneath, but make it easy to import an m3u playlist from Google Drive to be your channel list. Then make it export to that same file type.

Maybe export to both JSON and m3u.

Create Plugin Model

It's not very straightforward to get a Twitch stream: http://www.johannesbader.ch/2014/01/find-video-url-of-twitch-tv-live-streams-or-past-broadcasts/

The process is complex enough that it would require a little bit of effort for a special case, but not enough that it's impossible. This creates the question of what direction CumulusTV should go in. Should I spend my time working in special cases?

I've decided to go to the plugin model, like Muzei or Tasker. This would allow me to keep a relatively vanilla interface, but allow specific users to add plugins to get specific streams.

Though I haven't decided on everything yet, here's the basic gist for this issue:

  • Allow 3rd party apps to be called from within the channel creation
  • Allow 3rd party apps to send a JSONChannel back for writing

Android M Preview crashes

On the M preview, it crashes on basically everything.

What works

  • App loads
  • Can connect to Drive

What doesn't

  • Creating station
    • From suggested
    • From Create plugin
    • From JSON File
  • Loading station
  • View streams

I know that this is a preview, I just want to make sure you will have it ready in time for release! @Fleker, anything I can do to help?

Custom genres

Give channels a genre picker and later take info from current& next program

Google Drive support

Let users sync data to a Google Drive file that can be opened and edited on another device, then saved and loaded back into the app.

This will mean some work is done in the SyncAdapter, as well as including something in the main activity that will update the file when things need to be updated.

Display version number

This should be posted somewhere, just for technical purposes. Perhaps it could go in the bottom right corner of the screen.

Open m3u files

Allow the app to open this type of file, add it to your list, and let you configure properties.

This can open an intent in the New Channel activity and read the channel URL from an intent.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.