MasterPuppeteer

Initial commit

1 +# built application files
2 +*.apk
3 +*.ap_
4 +
5 +# files for the dex VM
6 +*.dex
7 +
8 +# Java class files
9 +*.class
10 +
11 +# generated files
12 +bin/
13 +gen/
14 +
15 +# Local configuration file (sdk path, etc)
16 +local.properties
17 +
18 +# Windows thumbnail db
19 +Thumbs.db
20 +
21 +# OSX files
22 +.DS_Store
23 +
24 +# Eclipse project files
25 +.classpath
26 +.project
27 +
28 +# Android Studio
29 +.idea
30 +*.iml
31 +
32 +.gradle
33 +build/
34 +*/build/
35 +/gradle
36 +
37 +# Internal staging area
38 +*/extra/
This diff is collapsed. Click to expand it.
1 +# Puppets
2 +Java interfaces that define puppets for the PuppetMaster.TV Android TV app and sample Groovy implementations.
1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
2 +
3 +buildscript {
4 + repositories {
5 + jcenter()
6 + }
7 + dependencies {
8 + classpath 'com.android.tools.build:gradle:2.2.0'
9 + // NOTE: Do not place your application dependencies here; they belong
10 + // in the individual module build.gradle files
11 + }
12 +}
13 +
14 +allprojects {
15 + repositories {
16 + jcenter()
17 + }
18 +}
19 +
20 +task clean(type: Delete) {
21 + delete rootProject.buildDir
22 +}
...\ No newline at end of file ...\ No newline at end of file
1 +apply plugin: 'java'
2 +
3 +dependencies {
4 + sourceCompatibility = JavaVersion.VERSION_1_7
5 + targetCompatibility = JavaVersion.VERSION_1_7
6 +
7 + compile fileTree(dir: 'libs', include: ['*.jar'])
8 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data;
2 +
3 +import java.util.List;
4 +import java.util.Set;
5 +
6 +import tv.puppetmaster.data.i.ParentPuppet;
7 +import tv.puppetmaster.data.i.SettingsPuppet;
8 +
9 +public class PreferenceSettingsPuppet implements SettingsPuppet {
10 +
11 + private String mXml;
12 + private Storage mStorage;
13 +
14 + private ParentPuppet mParent;
15 + private String mName;
16 + private String mShortDescription;
17 + private String mImageUrl;
18 + private String mBackgroundImageUrl;
19 +
20 + public PreferenceSettingsPuppet(ParentPuppet parent, String name, String shortDescription, String imageUrl, String backgroundImageUrl, String xml) {
21 + mParent = parent;
22 + mName = name;
23 + mShortDescription = shortDescription;
24 + mImageUrl = imageUrl;
25 + mBackgroundImageUrl = backgroundImageUrl;
26 + mXml = xml;
27 + }
28 +
29 + @Override
30 + public String getDefinition() {
31 + return mXml;
32 + }
33 +
34 + @Override
35 + public Storage getStorage() {
36 + return mStorage;
37 + }
38 +
39 + @Override
40 + public void setStorage(Storage storage) {
41 + mStorage = storage;
42 + }
43 +
44 + @Override
45 + public boolean getBoolean(String key, boolean defValue) {
46 + return mStorage == null? defValue : mStorage.getBoolean(key, defValue);
47 + }
48 +
49 + @Override
50 + public float getFloat(String key, float defValue) {
51 + return mStorage == null? defValue : mStorage.getFloat(key, defValue);
52 + }
53 +
54 + @Override
55 + public int getInt(String key, int defValue) {
56 + return mStorage == null? defValue : mStorage.getInt(key, defValue);
57 + }
58 +
59 + @Override
60 + public long getLong(String key, long defValue) {
61 + return mStorage == null? defValue : mStorage.getLong(key, defValue);
62 + }
63 +
64 + @Override
65 + public String getString(String key, String defValue) {
66 + return mStorage == null? defValue : mStorage.getString(key, defValue);
67 + }
68 +
69 + @Override
70 + public List<String> getStringList(String key, List<String> defValue) {
71 + return mStorage == null? defValue : mStorage.getStringList(key, defValue);
72 + }
73 +
74 + @Override
75 + public Set<String> getStringSet(String key, Set<String> defValue) {
76 + return mStorage == null? defValue : mStorage.getStringSet(key, defValue);
77 + }
78 +
79 + @Override
80 + public String getName() {
81 + return mName;
82 + }
83 +
84 + @Override
85 + public String getCategory() {
86 + return null;
87 + }
88 +
89 + @Override
90 + public String getShortDescription() {
91 + return mShortDescription;
92 + }
93 +
94 + @Override
95 + public String getImageUrl() {
96 + return mImageUrl;
97 + }
98 +
99 + @Override
100 + public String getBackgroundImageUrl() {
101 + return mBackgroundImageUrl;
102 + }
103 +
104 + @Override
105 + public boolean isUnavailableIn(String region) {
106 + return false;
107 + }
108 +
109 + @Override
110 + public String getPreferredRegion() {
111 + return null;
112 + }
113 +
114 + @Override
115 + public int getShieldLevel() {
116 + return 0;
117 + }
118 +
119 + @Override
120 + public ParentPuppet getParent() {
121 + return mParent;
122 + }
123 +
124 + @Override
125 + public PuppetIterator getRelated() {
126 + return null;
127 + }
128 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +import java.util.List;
4 +import java.util.Map;
5 +
6 +public interface InstallablePuppet extends ParentPuppet {
7 +
8 + /*
9 + * The version code implies which API features are supported:
10 + * Live channels >= 2
11 + * Branding >= 2
12 + * Region handling >= 4
13 + * Settings >= 5
14 + * User-Agent >= 6
15 + */
16 + int getVersionCode();
17 +
18 + /*
19 + * If the puppet provides the ability to search, this provider is passed the input
20 + */
21 + SearchesPuppet getSearchProvider();
22 +
23 + /*
24 + * Exposes settings that may be optional or required to retrieve content
25 + */
26 + SettingsPuppet getSettingsProvider();
27 +
28 + /*
29 + * Custom branding for this puppet, specifies the fastlane background color
30 + */
31 + int getFastlaneBackgroundColor();
32 +
33 + /*
34 + * Custom branding for this puppet, specifies the search affordance background color
35 + */
36 + int getSearchAffordanceBackgroundColor();
37 +
38 + /*
39 + * Custom branding for this puppet, specifies the selected item's background color
40 + */
41 + int getSelectedBackgroundColor();
42 +
43 + /*
44 + * Custom branding for this puppet, specifies the player background color
45 + */
46 + int getPlayerBackgroundColor();
47 +
48 + /*
49 + * Expose live channels through a list of maps, each declaring at least: {name, url}
50 + * Optionally declare {description, logo}
51 + */
52 + List<Map<String, String>> getLiveChannelsMetaData();
53 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +public interface ParentPuppet extends Puppet {
4 +
5 + /*
6 + * A parent puppet acts as a directory and getChildren lists its items
7 + */
8 + PuppetIterator getChildren();
9 +
10 + /*
11 + * If top-level, it gets its own row in the main screen with its children listed immediately,
12 + * otherwise it appears within an existing row and children are exposed when clicked upon
13 + */
14 + boolean isTopLevel();
15 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +import java.io.Serializable;
4 +import java.util.Iterator;
5 +
6 +public interface Puppet extends Serializable {
7 +
8 + /*
9 + * The name of this puppet, usually used to display the title of the content
10 + */
11 + String getName();
12 +
13 + /*
14 + * The broader category that describes the content represented by this puppet
15 + */
16 + String getCategory();
17 +
18 + /*
19 + * A brief description of the content
20 + */
21 + String getShortDescription();
22 +
23 + /*
24 + * Url for a thumbnail preferably a 300px^2 .png
25 + */
26 + String getImageUrl();
27 +
28 + /*
29 + * Url for a large image to display as a background, preferably in a compressed format
30 + */
31 + String getBackgroundImageUrl();
32 +
33 + /*
34 + * If the content is unavailable in the given region
35 + * @param region Usually a 2 character lower-case country code (ISO 3166 alpha-2)
36 + * @return If the content represented by this puppet is available in the given region
37 + */
38 + boolean isUnavailableIn(String region);
39 +
40 + /*
41 + * @return A region best supporting access to the content or null if all regions are supported
42 + */
43 + String getPreferredRegion();
44 +
45 + /*
46 + * @return An integer between 0-9 indicating the level of defense employed by the source
47 + * 0 == none
48 + * 9 == impenetrable
49 + */
50 + int getShieldLevel();
51 +
52 + /*
53 + * The parent of this puppet or null if it is a top-level item
54 + */
55 + ParentPuppet getParent();
56 +
57 + /*
58 + * Media related to this puppet
59 + */
60 + PuppetIterator getRelated();
61 +
62 + /*
63 + * Whatever describes this puppet best, usually an internal String representation in the form
64 + * of: top-level < first descendant < ... < current item
65 + */
66 + String toString();
67 +
68 + abstract class PuppetIterator implements Iterator<Puppet>, Serializable {
69 + public abstract void add(Puppet puppet);
70 + }
71 +
72 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +public interface SearchesPuppet extends ParentPuppet {
4 +
5 + /*
6 + * Children are results that match the search query
7 + */
8 + void setSearchQuery(String searchQuery);
9 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +import java.util.List;
4 +import java.util.Set;
5 +
6 +public interface SettingsPuppet extends Puppet {
7 +
8 + String getDefinition();
9 +
10 + Storage getStorage();
11 + void setStorage(Storage storage);
12 +
13 + boolean getBoolean(String key, boolean defValue);
14 + float getFloat(String key, float defValue);
15 + int getInt(String key, int defValue);
16 + long getLong(String key, long defValue);
17 + String getString(String key, String defValue);
18 + List<String> getStringList(String key, List<String> defValue);
19 + Set<String> getStringSet(String key, Set<String> defValue);
20 +
21 + interface Storage {
22 + boolean getBoolean(String key, boolean defValue);
23 + void setBoolean(String key, boolean value);
24 + float getFloat(String key, float defValue);
25 + void setFloat(String key, float value);
26 + int getInt(String key, int defValue);
27 + void setInt(String key, int value);
28 + long getLong(String key, long defValue);
29 + void setLong(String key, long value);
30 + String getString(String key, String defValue);
31 + void setString(String key, String value);
32 + List<String> getStringList(String key, List<String> defValue);
33 + void setStringList(String key, List<String> value);
34 + Set<String> getStringSet(String key, Set<String> defValue);
35 + void setStringSet(String key, Set<String> value);
36 + }
37 +}
...\ No newline at end of file ...\ No newline at end of file
1 +package tv.puppetmaster.data.i;
2 +
3 +import java.io.Serializable;
4 +import java.util.Iterator;
5 +import java.util.List;
6 +import java.util.Locale;
7 +import java.util.Map;
8 +
9 +public interface SourcesPuppet extends Puppet {
10 + /*
11 + * The date of publication for this content
12 + */
13 + String getPublicationDate();
14 +
15 + /*
16 + * Its duration in milliseconds
17 + */
18 + long getDuration();
19 +
20 + /*
21 + * An iterator listing the actual sources of this content
22 + */
23 + SourceIterator getSources();
24 +
25 + /*
26 + * If it is live
27 + */
28 + boolean isLive();
29 +
30 + /*
31 + * Uri => Description
32 + */
33 + List<SubtitleDescription> getSubtitles();
34 +
35 + /*
36 + * We want to lazy-load sources in case the implementation requires network access to parse each
37 + */
38 + interface SourceIterator extends Iterator<SourceDescription>, Serializable {
39 + }
40 +
41 + class SourceDescription implements Serializable {
42 + public String url;
43 + public long duration;
44 + public String height;
45 + public String width;
46 + public String rating;
47 + public boolean isAudioOnly;
48 + public long bitrate;
49 + public String userAgent;
50 + public String referer;
51 + public Map<String, String> requestHeaders;
52 + public String drmScheme;
53 + public String drmLicenseUrl;
54 + }
55 +
56 + class SubtitleDescription implements Serializable {
57 + public String locale = Locale.ENGLISH.getLanguage();
58 + public String mime = "text/srt";
59 + public String url;
60 + }
61 +}
...\ No newline at end of file ...\ No newline at end of file
1 +apply plugin: 'java'
2 +apply plugin: 'groovy'
3 +
4 +def ANDROID_SDK_HOME = System.properties['user.home'] + '/Library/Android/sdk/build-tools/24.0.0'
5 +
6 +task jarEach << {
7 + delete fileTree(dir: 'build/libs/' , include: '*.jar')
8 + def paths = []
9 + fileTree("src").visit { FileVisitDetails details ->
10 + if (details.file.path.contains("/puppetimpl/src/main/groovy/") && details.file.path.endsWith(".groovy")) {
11 + paths << details.file.path.split("/puppetimpl/src/main/groovy/")[1].replace(".groovy", "*.class")
12 + }
13 + }
14 + paths.each { path ->
15 + def className = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("*.class"))
16 + def fullyQualifiedClassName = path.replace("/", ".").replace("*.class", "")
17 + def jarIt = tasks.create(name: "jar${className}", type: Jar) {
18 + baseName = fullyQualifiedClassName
19 + manifest {
20 + attributes("Module-Class": fullyQualifiedClassName)
21 + }
22 + from(sourceSets.main.output) {
23 + include path
24 + }
25 + }
26 + def dexIt = tasks.create(name: "dex${className}", type: Exec) {
27 + commandLine "${ANDROID_SDK_HOME}/dx", "--dex", "--output=classes.dex", "build/libs/${fullyQualifiedClassName}.jar"
28 + }
29 + def aaptIt = tasks.create(name: "aapt${className}", type: Exec) {
30 + commandLine "${ANDROID_SDK_HOME}/aapt", "add", "build/libs/${fullyQualifiedClassName}.jar", "classes.dex"
31 + }
32 + def cleanIt = tasks.create(name: "clean${className}", type: Exec) {
33 + commandLine "rm", "classes.dex"
34 + }
35 + [jarIt, dexIt, aaptIt, cleanIt].each {
36 + it.execute()
37 + }
38 + }
39 +}
40 +
41 +dependencies {
42 + compile fileTree(dir: 'libs', include: ['*.jar'])
43 + compile 'org.jsoup:jsoup:1.9.2'
44 + compile 'org.json:json:20151123'
45 + compile 'org.codehaus.groovy:groovy:2.4.7'
46 + compile project(':puppet')
47 +}
...\ No newline at end of file ...\ No newline at end of file
1 +import tv.puppetmaster.data.i.InstallablePuppet
2 +import tv.puppetmaster.data.i.ParentPuppet
3 +import tv.puppetmaster.data.i.Puppet
4 +import tv.puppetmaster.data.i.SourcesPuppet
5 +
6 +import static groovy.io.FileType.FILES
7 +
8 +def puppetClassNames = []
9 +def puppetFullyQualifiedClassNames = []
10 +new File('./puppetimpl/src/main/groovy/').eachFileRecurse(FILES) {
11 + if (it.name.endsWith('Puppet.groovy')) {
12 + puppetClassNames << it.name.replaceFirst(~/\.[^\.]+$/, '')
13 + puppetFullyQualifiedClassNames << it.path.replace('./puppetimpl/src/main/groovy/', '').replace('/', '.').replace('.groovy', '')
14 + }
15 +}
16 +
17 +if (puppetClassNames.size() > 0) {
18 + puppetClassNames.eachWithIndex { it, i ->
19 + println "${i}) ${it}"
20 + }
21 + def selection = inputNumber("Select a puppet", 0, puppetClassNames.size() - 1)
22 + def selectedPuppet = (InstallablePuppet) this.class.classLoader.loadClass(puppetFullyQualifiedClassNames[selection])?.newInstance()
23 + processPuppet(selectedPuppet, 0)
24 +} else {
25 + System.err.println "No puppets found to test, double-check your setup."
26 +}
27 +
28 +def int inputNumber(String text, int low, int high) {
29 + def selection = -1
30 +
31 + while (selection < low || selection >= high) {
32 + print "\n${text} [${low}-${high}]: "
33 + try {
34 + selection = System.in.newReader().readLine() as int
35 + if (selection >= low && selection <= high) {
36 + break
37 + }
38 + } catch (ignore) {
39 + }
40 + selection = -1
41 + System.err.println "Invalid input, try again."
42 + }
43 + return selection
44 +}
45 +
46 +def processPuppet(Puppet selectedPuppet, int level) {
47 + println "-".multiply(80)
48 + println "${selectedPuppet.name} (${selectedPuppet instanceof SourcesPuppet ? "SourcesPuppet" : "ParentPuppet"})"
49 + if (selectedPuppet instanceof SourcesPuppet) {
50 + def children = selectedPuppet.getSources()
51 + def i = 0
52 + while (children != null && children.hasNext()) {
53 + def next = children.next()
54 + println "\t".multiply(level + 1) + i++ + ") ${next.url}"
55 + }
56 + } else if (selectedPuppet instanceof ParentPuppet) {
57 + def children = selectedPuppet.getChildren()
58 + def i = 0
59 + def childPuppets = []
60 + while (children != null && children.hasNext()) {
61 + def next = children.next()
62 + childPuppets << next
63 + println "\t".multiply(level + 1) + i++ + ") ${next.name} (${next instanceof SourcesPuppet ? "SourcesPuppet" : "ParentPuppet"})"
64 + }
65 + def selection = inputNumber("Select a puppet", 0, childPuppets.size() - 1)
66 + processPuppet(childPuppets[selection] as Puppet, level + 1)
67 + }
68 + println "-".multiply(80)
69 +}
...\ No newline at end of file ...\ No newline at end of file
1 +include ':puppet', ':puppetimpl'
...\ No newline at end of file ...\ No newline at end of file