├── .gitignore
├── .idea
└── libraries
│ └── Patched_Classes.xml
├── KindleLauncher.iml
├── META-INF
└── MANIFEST.MF
├── README
└── src
└── com
└── yifanlu
└── Kindle
├── Extension.java
├── ExtensionsLoader.java
├── JARClassLoader.java
├── JSONMenu.java
├── KindleLauncher.java
├── KindleLauncherResources.java
├── LauncherAction.java
├── LauncherMenu.java
├── LauncherScript.java
├── Menuable.java
└── ReaderPluginLauncher.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | lib/
3 | out/
4 |
--------------------------------------------------------------------------------
/.idea/libraries/Patched_Classes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/KindleLauncher.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 |
3 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | More coming soon...
2 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/Extension.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | /**
22 | * This class represents the extension and is parsed by the config.xml file.
23 | * Currently, the only thing of use is the {@link Menuable}, which
24 | * holds the menu items.
25 | *
26 | * @author Yifan Lu
27 | * @version 1.0
28 | */
29 | public class Extension {
30 | private String mName;
31 | private String mVersion;
32 | private String mAuthor;
33 | private String mId;
34 | private Menuable mMenu;
35 |
36 | /**
37 | * We want all the fields to be set by the parser, but we place default values in just in case.
38 | */
39 | public Extension() {
40 | this.mName = "Plugin";
41 | this.mVersion = "0.0";
42 | this.mAuthor = "Author";
43 | this.mId = super.toString();
44 | }
45 |
46 | /**
47 | * Sets the name of the extension.
48 | *
49 | * @param name The name of the extension.
50 | */
51 | public void setName(String name) {
52 | this.mName = name;
53 | }
54 |
55 | /**
56 | * Gets the name of the extension.
57 | * This is currently unused.
58 | *
59 | * @return The name of the extension.
60 | */
61 | public String getName() {
62 | return this.mName;
63 | }
64 |
65 | /**
66 | * Sets the version of the extension.
67 | *
68 | * @param version The version of the extension.
69 | */
70 | public void setVersion(String version) {
71 | this.mVersion = version;
72 | }
73 |
74 | /**
75 | * Gets the version of the extension.
76 | * This is currently unused.
77 | *
78 | * @return The version of the extension.
79 | */
80 | public String getVersion() {
81 | return this.mVersion;
82 | }
83 |
84 | /**
85 | * Sets the author of the extension.
86 | *
87 | * @param author The author of the extension.
88 | */
89 | public void setAuthor(String author) {
90 | this.mAuthor = author;
91 | }
92 |
93 | /**
94 | * Gets the author of the extension.
95 | * This is currently unused.
96 | *
97 | * @return The author of the extension.
98 | */
99 | public String getAuthor() {
100 | return this.mAuthor;
101 | }
102 |
103 | /**
104 | * Sets the id of the extension. Must be unique.
105 | *
106 | * @param id The id of the extension.
107 | */
108 | public void setId(String id) {
109 | this.mId = id;
110 | }
111 |
112 | /**
113 | * Gets the id of the extension.
114 | * This is currently unused.
115 | *
116 | * @return The id of the extension.
117 | */
118 | public String getId() {
119 | return this.mId;
120 | }
121 |
122 | /**
123 | * Gets the menu options for this extension.
124 | *
125 | * @return The menu options for this extension.
126 | * @see com.yifanlu.Kindle.Menuable
127 | */
128 | public Menuable getMenu() {
129 | return this.mMenu;
130 | }
131 |
132 | /**
133 | * Sets the menu options for this extension.
134 | *
135 | * @param menu The menu option for this extension.
136 | * @see com.yifanlu.Kindle.Menuable
137 | */
138 | public void setMenu(Menuable menu) {
139 | this.mMenu = menu;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/ExtensionsLoader.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.util.log.LogMessage;
22 | import org.json.simple.parser.ParseException;
23 | import org.kxml2.io.KXmlParser;
24 | import org.xmlpull.v1.XmlPullParser;
25 | import org.xmlpull.v1.XmlPullParserException;
26 |
27 | import java.io.*;
28 | import java.util.ArrayList;
29 |
30 | /**
31 | * This class holds the functions for loading and parsing the XML configs from extensions.
32 | *
33 | * @author Yifan Lu
34 | * @version 1.0
35 | */
36 | public class ExtensionsLoader {
37 | private static final LogMessage LOADED_EXTENSION = new LogMessage("ExtensionLoad", new String[]{"name", "version", "author", "id"});
38 | private static final LogMessage MENU_ATTRIBUTES = new LogMessage("MenuLoad", new String[]{"name", "value"});
39 | private File mDirectory;
40 |
41 | /**
42 | * Create a new extension loader using "directory" as the base
43 | *
44 | * @param directory Where the extensions are located. Each extension must be in its own folder and contain config.xml.
45 | */
46 | public ExtensionsLoader(File directory) {
47 | mDirectory = directory;
48 | }
49 |
50 | /**
51 | * This converts all the extensions into objects using information from their config.xml and returns an array.
52 | *
53 | * @return A list of extensions that are loaded or an empty list of no extension is loaded successfully.
54 | */
55 | public ArrayList loadExtensions() {
56 | KXmlParser parser = new KXmlParser();
57 | ArrayList extList = new ArrayList();
58 | File[] extensions = mDirectory.listFiles(new FileFilter() {
59 | public boolean accept(File file) {
60 | return file.isDirectory();
61 | }
62 | });
63 | int i;
64 | try {
65 | for (i = 0; i < extensions.length; i++) {
66 | File ext = extensions[i];
67 | KindleLauncher.LOG.info("Loading: " + ext.getAbsolutePath());
68 | File config = new File(ext, "config.xml");
69 | Extension extObj = new Extension();
70 | parser.setInput(new InputStreamReader(new FileInputStream(config)));
71 | parser.nextTag();
72 | parser.require(XmlPullParser.START_TAG, null, "extension");
73 | while (parser.nextTag() != XmlPullParser.END_TAG) {
74 | parser.require(XmlPullParser.START_TAG, null, null);
75 | String name = parser.getName();
76 | if (name.equals("information"))
77 | parseInformation(parser, extObj);
78 | else if (name.equals("menus"))
79 | parseMenus(parser, extObj, ext);
80 | else //skip
81 | while (parser.next() != XmlPullParser.END_TAG || !parser.getName().equals(name))
82 | ; // this one line does magic to skip all unrelated events
83 | parser.require(XmlPullParser.END_TAG, null, name);
84 | }
85 | parser.require(XmlPullParser.END_TAG, null, "extension");
86 | parser.next();
87 | parser.require(XmlPullParser.END_DOCUMENT, null, null);
88 | extList.add(extObj);
89 | KindleLauncher.LOG.info(LOADED_EXTENSION, new String[]{extObj.getName(), extObj.getVersion(), extObj.getAuthor(), extObj.getId()}, "");
90 | }
91 | } catch (IOException e) {
92 | KindleLauncher.LOG.error("Error reading extension config. " + e.getMessage());
93 | e.printStackTrace();
94 | } catch (XmlPullParserException e) {
95 | KindleLauncher.LOG.error("Error parsing extension config. " + e.getMessage());
96 | e.printStackTrace();
97 | } catch (NullPointerException e) {
98 | KindleLauncher.LOG.error("Error parsing extension menu. Required attribute not found. " + e.getMessage());
99 | e.printStackTrace();
100 | } catch (ClassNotFoundException e) {
101 | KindleLauncher.LOG.error("Error parsing extension menu. Cannot find menu Java class. " + e.getMessage());
102 | e.printStackTrace();
103 | } catch (ClassCastException e) {
104 | KindleLauncher.LOG.error("Error parsing extension menu. " + e.getMessage());
105 | e.printStackTrace();
106 | } catch (InstantiationException e) {
107 | KindleLauncher.LOG.error("Error parsing extension menu. Cannot create object from Java class. " + e.getMessage());
108 | e.printStackTrace();
109 | } catch (IllegalAccessException e) {
110 | KindleLauncher.LOG.error("Error parsing extension menu. Cannot create object from Java class. " + e.getMessage());
111 | e.printStackTrace();
112 | } catch (ParseException e) {
113 | KindleLauncher.LOG.error("Error parsing extension menu. Cannot parse JSON menu. " + e.getMessage());
114 | e.printStackTrace();
115 | }
116 | return extList;
117 | }
118 |
119 | /**
120 | * This parses the "information" section of the config.xml file. This section contains information about the
121 | * extension. This information is currently unused, but may be useful in the future so it is recommended that
122 | * all extensions contain it. The format is
123 | *
124 | *
125 | * <information>
126 | * <name>Name of the plugin</name>
127 | * <version>Version of the plugin</version>
128 | * <author>Author of the plugin</author>
129 | * <id>Unique id of the plugin</id>
130 | * </information>
131 | *
132 | *
133 | * @param parser {@link org.kxml2.io.KXmlParser}
134 | * @param extObj The {@link Extension} object to fill.
135 | * @throws IOException if the config cannot be read cannot be accessed
136 | * @throws XmlPullParserException if the config is malformed
137 | */
138 | protected void parseInformation(KXmlParser parser, Extension extObj) throws IOException, XmlPullParserException {
139 | while (parser.nextTag() != XmlPullParser.END_TAG) {
140 | parser.require(XmlPullParser.START_TAG, null, null);
141 | String name = parser.getName();
142 | String text = parser.nextText();
143 |
144 | if (name.equals("name"))
145 | extObj.setName(text);
146 | else if (name.equals("version"))
147 | extObj.setVersion(text);
148 | else if (name.equals("author"))
149 | extObj.setAuthor(text);
150 | else if (name.equals("id"))
151 | extObj.setId(text);
152 |
153 | parser.require(XmlPullParser.END_TAG, null, name);
154 | }
155 | }
156 |
157 | /**
158 | * This parses the "menus" section of the config.xml file. The "menus" section can contain one or more list of
159 | * menu options that are loaded by either JSON or Java. All menu options from each list are added to the main menu.
160 | * The format of the entries in config.xml for the menus section is
161 | *
162 | *
163 | * <menus>
164 | * <menu type="VALUE" jar="VALUE" dynamic="VALUE">TEXT</menu>
165 | * </menus>
166 | *
167 | *
168 | * Where type is either "java" or "json", jar is required only for "java" types and is the path to the Jar file
169 | * containing the Menuable class relative to config.xml, dynamic is required only for JSON types and tells the
170 | * loader to re-read the JSON file each time the menu is opened, and the text of the element is either the path
171 | * to the JSON menu file or the Java class to load.
172 | *
173 | * @param parser {@link org.kxml2.io.KXmlParser}
174 | * @param extObj The {@link Extension} object to fill.
175 | * @param extDir The path to the extension's directory
176 | * @throws IOException if the config cannot be read or the extension directory cannot be accessed
177 | * @throws XmlPullParserException if the config is malformed
178 | * @throws ClassNotFoundException if the menu type is Java and the class specified cannot be loaded
179 | * @throws IllegalAccessException if the menu type is Java and the class specified cannot be accessed
180 | * @throws InstantiationException if the menu type is Java and the class specified cannot be instantiated
181 | * @throws ParseException if the config file cannot be parsed correctly
182 | */
183 | protected void parseMenus(KXmlParser parser, Extension extObj, File extDir) throws IOException, XmlPullParserException, ClassNotFoundException, IllegalAccessException, InstantiationException, ParseException {
184 | while (parser.nextTag() != XmlPullParser.END_TAG) {
185 | parser.require(XmlPullParser.START_TAG, null, "menu");
186 |
187 | Menuable menu;
188 | boolean isJava = false;
189 | boolean isDynamic = false;
190 | String jarFile = null;
191 | int i = parser.getAttributeCount();
192 | KindleLauncher.LOG.debug("Attributes: " + parser.getAttributeCount());
193 | while (i-- > 0) {
194 | String name = parser.getAttributeName(i);
195 | String value = parser.getAttributeValue(i);
196 | if (name.equals("type"))
197 | isJava = value.equals("java");
198 | else if (name.equals("dynamic"))
199 | isDynamic = value.equals("true");
200 | else if (name.equals("jar"))
201 | jarFile = value;
202 | KindleLauncher.LOG.debug(MENU_ATTRIBUTES, new String[]{name, value}, "");
203 | }
204 |
205 | String text = parser.nextText();
206 |
207 | if (isJava) {
208 | JARClassLoader classLoader = JARClassLoader.getInstance();
209 | classLoader.addJar(new File(extDir, jarFile));
210 | Class cls = classLoader.loadClass(text);
211 | menu = (Menuable) cls.newInstance();
212 | KindleLauncher.LOG.debug("Loaded class: " + text);
213 | } else {
214 | File jsonFile = new File(extDir, text);
215 | JSONMenu jsonMenu = new JSONMenu(jsonFile);
216 | jsonMenu.setDynamic(isDynamic);
217 | jsonMenu.parseJSONMenu();
218 | menu = jsonMenu;
219 | KindleLauncher.LOG.debug("Loaded menu: " + jsonFile.getAbsolutePath());
220 | }
221 |
222 | extObj.setMenu(menu);
223 |
224 | parser.require(XmlPullParser.END_TAG, null, "menu");
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/JARClassLoader.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.util.log.LogMessage;
22 |
23 | import java.io.File;
24 | import java.net.MalformedURLException;
25 | import java.net.URL;
26 | import java.net.URLClassLoader;
27 |
28 | /**
29 | * This classloader allows loading JARs from any URL and does not require the URL to be passed on initiation.
30 | *
31 | * @author Yifan Lu
32 | * @version 1.0
33 | */
34 | public class JARClassLoader extends URLClassLoader {
35 | private static JARClassLoader myInstance;
36 | private static final LogMessage JAR_LOAD_ERROR = new LogMessage("JarLoad", new String[]{"jar file", "java error"});
37 |
38 | private JARClassLoader() {
39 | super(new URL[]{});
40 | }
41 |
42 | /**
43 | * Adds an URL to the class path.
44 | *
45 | * @param url The URL of the class path.
46 | */
47 | public void addURL(URL url) {
48 | super.addURL(url);
49 | }
50 |
51 | /**
52 | * We are a singleton, this gets the single instance of this class.
53 | *
54 | * @return The single instance of this class loader.
55 | */
56 | public static JARClassLoader getInstance() {
57 | if (myInstance == null)
58 | myInstance = new JARClassLoader();
59 | return myInstance;
60 | }
61 |
62 | /**
63 | * Adds a JAR file to the class path.
64 | *
65 | * @param jarFile The {@link java.io.File} that contains the JAR to load.
66 | */
67 | public void addJar(File jarFile) {
68 | try {
69 | URL jarURL = jarFile.toURI().toURL();
70 | addURL(jarURL);
71 | } catch (MalformedURLException e) {
72 | KindleLauncher.LOG.error(JAR_LOAD_ERROR, new String[]{jarFile.getAbsolutePath(), e.toString()}, "");
73 | e.printStackTrace();
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/JSONMenu.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.util.log.LogMessage;
22 | import org.json.simple.JSONArray;
23 | import org.json.simple.JSONObject;
24 | import org.json.simple.parser.JSONParser;
25 | import org.json.simple.parser.ParseException;
26 |
27 | import java.io.File;
28 | import java.io.FileReader;
29 | import java.io.IOException;
30 | import java.util.Iterator;
31 |
32 | /**
33 | * This class holds the menu options for a JSON menu.
34 | *
35 | * @author Yifan Lu
36 | * @version 1.0
37 | */
38 | public class JSONMenu implements Menuable {
39 | private static final LogMessage JSON_MENU_ITEM = new LogMessage("JsonMenuItem", new String[]{"name", "action", "params"});
40 | private File mJsonFile;
41 | private LauncherAction[] mMenuItems;
42 | private boolean mDynamic;
43 |
44 | /**
45 | * Create a new JSONMenu object with the JSON file. The format of the JSON menu is
46 | *
47 | *
48 | * {
49 | * "items": [
50 | * {"name": "Main Menu, Item 1", "priority": 0, "action": "bin/action.sh", "params": "item1"},
51 | * {
52 | * "name": "Main Menu, Submenu 1",
53 | * "priority": 1,
54 | * "items": [
55 | * {"name": "Submenu 1, Item 1", "priority": 0, "action": "/sbin/restart", "params": ""}
56 | * ]
57 | * }
58 | * ]
59 | * }
60 | *
61 | *
62 | * For each menu item, the "name" is the text that shows up and the "priority" is the order that they show up in
63 | * (if two items have the same priority, they are sorted in alphabetical order). If the item is an action, the
64 | * "action" contains the script to run and "params" contains the parameters. If the script name starts with a slash,
65 | * it is an absolute path, otherwise it is relative to the path of the JSON file. If the item is a submenu, it
66 | * will contain "items" instead of "action" and "params" and "items" is an array of menu items.
67 | *
68 | * @param jsonFile A text file that contains the JSON data.
69 | */
70 | public JSONMenu(File jsonFile) {
71 | mJsonFile = jsonFile;
72 | mMenuItems = new LauncherAction[]{};
73 | mDynamic = false;
74 | }
75 |
76 | /**
77 | * If the menu is dynamic as specified in config.xml, it will first call {@link JSONMenu#parseJSONMenu()}. Then it
78 | * adds the menu options specified by the JSON file to the menu of the caller. The caller will most likely be
79 | * the main Launcher menu.
80 | *
81 | * @param menu The menu of the caller.
82 | */
83 | public void addItemsToMenu(LauncherMenu menu) {
84 | if (isDynamic()) {
85 | KindleLauncher.LOG.debug("Reloading the menu.");
86 | mJsonFile = new File(mJsonFile.getAbsolutePath());
87 | try {
88 | parseJSONMenu();
89 | } catch (IOException e) {
90 | KindleLauncher.LOG.error("Cannot create menu because file " + mJsonFile.getAbsolutePath() + " cannot be read.");
91 | e.printStackTrace();
92 | } catch (ParseException e) {
93 | KindleLauncher.LOG.error("Cannot create menu because file " + mJsonFile.getAbsolutePath() + " cannot be parsed.");
94 | e.printStackTrace();
95 | }
96 | }
97 | for (int i = 0; i < mMenuItems.length; i++) {
98 | if (mMenuItems[i] instanceof LauncherMenu)
99 | ((LauncherMenu) mMenuItems[i]).setParent(menu);
100 | menu.addMenuItem(mMenuItems[i]);
101 | }
102 | }
103 |
104 | /**
105 | * Sets the dynamic status of the menu
106 | *
107 | * @param dynamic Should the menu be dynamic?
108 | * @see com.yifanlu.Kindle.JSONMenu#isDynamic()
109 | */
110 | public void setDynamic(boolean dynamic) {
111 | this.mDynamic = dynamic;
112 | }
113 |
114 | /**
115 | * If dynamic, the menu will be re-parsed from the JSON file each time it is rendered. This is useful if you have
116 | * a dynamic script that can alter the JSON menu when ran.
117 | *
118 | * @return The dynamic status of the menu
119 | */
120 | public boolean isDynamic() {
121 | return mDynamic;
122 | }
123 |
124 | /**
125 | * Takes the JSON file stored in the object and converts it to menu item objects.
126 | *
127 | * @throws IOException if there is a problem reading the JSON text
128 | * @throws ParseException if there is a problem parsing the JSON text
129 | */
130 | public void parseJSONMenu() throws IOException, ParseException {
131 | mMenuItems = new LauncherMenu[]{};
132 | FileReader read = new FileReader(mJsonFile);
133 | JSONParser parse = new JSONParser();
134 | JSONObject obj = (JSONObject) parse.parse(read);
135 | LauncherAction action = jsonToAction(obj, null);
136 | if (action instanceof LauncherMenu && action.getValue().equals("No Text")) {
137 | mMenuItems = ((LauncherMenu) action).getMenuItems();
138 | } else {
139 | mMenuItems = new LauncherAction[]{action};
140 | }
141 | }
142 |
143 | /**
144 | * Recursively reads the JSON data and converts it to a {@link LauncherAction} which contains either a
145 | * {@link LauncherScript} which executes a shell command when called or a {@link LauncherMenu} which contains
146 | * other {@link LauncherAction} items.
147 | *
148 | * @param json The JSON object to parse
149 | * @param parent The menu to add the items to
150 | * @return The {@link LauncherAction} that is defined by the JSON data
151 | * @throws IOException if there are problems reading the JSON file
152 | */
153 | protected LauncherAction jsonToAction(JSONObject json, LauncherMenu parent) throws IOException {
154 | String name = (String) json.get("name");
155 | Number priorityNum = (Number) json.get("priority");
156 | int priority;
157 | String action = (String) json.get("action");
158 | String params = (String) json.get("params");
159 | JSONArray items = (JSONArray) json.get("items");
160 | LauncherAction launcherAction;
161 | if (name == null)
162 | name = "No Text";
163 | if (priorityNum == null)
164 | priority = 0;
165 | else
166 | priority = priorityNum.intValue();
167 | if (items != null) {
168 | launcherAction = new LauncherMenu(name, priority, parent);
169 | KindleLauncher.LOG.debug(JSON_MENU_ITEM, new String[]{name, "submenu", ""}, "");
170 | Iterator it = items.iterator();
171 | while (it.hasNext()) {
172 | JSONObject itemObj = (JSONObject) it.next();
173 | ((LauncherMenu) launcherAction).addMenuItem(jsonToAction(itemObj, (LauncherMenu) launcherAction));
174 | }
175 | } else if (action != null) {
176 | if (params == null)
177 | params = "";
178 | File script;
179 | if (action.startsWith("/"))
180 | script = new File(action);
181 | else
182 | script = new File(mJsonFile.getParentFile(), action);
183 | launcherAction = new LauncherScript(name, priority, script, params);
184 | KindleLauncher.LOG.debug(JSON_MENU_ITEM, new String[]{name, action, params}, "");
185 | } else {
186 | throw new IOException("No valid action found for menu item: " + json.toJSONString());
187 | }
188 | return launcherAction;
189 |
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/KindleLauncher.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.booklet.reader.sdk.ui.MenuInserter;
22 | import com.amazon.ebook.booklet.reader.sdk.ui.ReaderAction;
23 | import com.amazon.ebook.pl.SystemServices;
24 | import com.amazon.ebook.util.log.Log;
25 | import com.amazon.kindle.restricted.runtime.Framework;
26 |
27 | import javax.swing.*;
28 | import java.io.File;
29 | import java.util.ArrayList;
30 | import java.util.Iterator;
31 | import java.util.ResourceBundle;
32 |
33 | /**
34 | * The main class that is called when the Launcher menu is opened.
35 | *
36 | * @author Yifan Lu
37 | * @version 1.0
38 | */
39 | public class KindleLauncher extends LauncherMenu implements MenuInserter {
40 | /**
41 | * Contains localized text for the menu items.
42 | */
43 | public static final ResourceBundle RESOURCES = ResourceBundle.getBundle("com.yifanlu.Kindle.KindleLauncherResources");
44 |
45 | /**
46 | * Contains Kindle services that can be used
47 | */
48 | public static final SystemServices SERVICES = (SystemServices) Framework.getService(SystemServices.class);
49 |
50 | /**
51 | * The default {@link ExtensionsLoader} that reads from /mnt/us/extensions
52 | */
53 | private static final ExtensionsLoader EXTENSIONS_LOADER = new ExtensionsLoader(new File("/mnt/us/extensions"));
54 |
55 | /**
56 | * The default logger that tags all logs with KindleLauncher
57 | */
58 | public static final Log LOG = Log.getInstance("KindleLauncher");
59 |
60 |
61 | private static KindleLauncher myInstance;
62 | private ArrayList mExtensions;
63 |
64 | private KindleLauncher() {
65 | super(RESOURCES.getString("menu.launcher.label"), 0);
66 | mExtensions = new ArrayList();
67 | }
68 |
69 | /**
70 | * Gets the instance of the singleton.
71 | *
72 | * @return the single instance of this class as an {@link Action}
73 | */
74 | public static Action getInstance() {
75 | LOG.debug("We are being called!");
76 | if (myInstance == null)
77 | myInstance = new KindleLauncher();
78 | return myInstance;
79 | }
80 |
81 | /**
82 | * Loads the extensions using {@link ExtensionsLoader}
83 | */
84 | public void reloadExtensions() {
85 | LOG.debug("Loading extensions");
86 | mExtensions = EXTENSIONS_LOADER.loadExtensions();
87 | }
88 |
89 | /**
90 | * Creates the main menu for the Launcher using menu items from extensions.
91 | * We call {@link com.yifanlu.Kindle.KindleLauncher#reloadExtensions()} if the extensions have not been loaded.
92 | * Then, we tell each extensions' {@link Menuable} to {@link Menuable#addItemsToMenu(LauncherMenu)} with the main menu.
93 | */
94 | public void createMenu() {
95 | if (mExtensions.isEmpty() || isMenuEmpty())
96 | reloadExtensions();
97 | clearMenu();
98 | Iterator it = mExtensions.iterator();
99 | while (it.hasNext()) {
100 | Extension ext = (Extension) it.next();
101 | ext.getMenu().addItemsToMenu(this);
102 | }
103 | }
104 |
105 | /**
106 | * Called when user chooses the Launcher item from the Kindle menu.
107 | * This calls the menu creation method.
108 | *
109 | * @see com.yifanlu.Kindle.KindleLauncher#createMenu()
110 | */
111 | public synchronized void doAction() {
112 | createMenu();
113 | super.doAction();
114 | }
115 |
116 | /**
117 | * The Kindle Reader SDK tells plugins to give a list of menu items to add to the Kindle menu. We will add ourselves
118 | * to it.
119 | *
120 | * @return an array containing this {@link KindleLauncher} object
121 | */
122 | public ReaderAction[] getMenuActions() {
123 | return new ReaderAction[]{this};
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/KindleLauncherResources.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import java.util.ListResourceBundle;
22 |
23 | /**
24 | * Localization for the Launcher menu.
25 | *
26 | * @author Yifan Lu
27 | * @version 1.0
28 | */
29 | public class KindleLauncherResources extends ListResourceBundle {
30 | /**
31 | * Labels and their associated text
32 | */
33 | static final Object[][] RESOURCES = {
34 | {"menu.launcher.label", "Launcher"},
35 | {"menu.launcher.next_page.label", "Next Page"},
36 | {"menu.launcher.previous_page.label", "Previous Page"}
37 | };
38 |
39 | /**
40 | * Gets the labels and their associated text
41 | *
42 | * @return localization strings
43 | */
44 | protected Object[][] getContents() {
45 | return RESOURCES;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/LauncherAction.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.booklet.reader.sdk.ui.ReaderAction;
22 | import com.amazon.ebook.util.thread.ThreadPool;
23 |
24 | import javax.swing.*;
25 | import java.awt.event.ActionEvent;
26 |
27 | /**
28 | * A menu item for Launcher. Contains the item's text, its priority, and the task it should perform when called.
29 | *
30 | * @author Yifan Lu
31 | * @version 1.0
32 | */
33 | public abstract class LauncherAction extends ReaderAction implements Runnable {
34 | private int mPriority;
35 | private String mName;
36 | private boolean mHasArrow;
37 |
38 | /**
39 | * Creates a new menu item with the text to show.
40 | *
41 | * @param name The text of the menu item.
42 | */
43 | public LauncherAction(String name) {
44 | this(name, ReaderAction.PRIORITY_PLUGINS);
45 | }
46 |
47 | /**
48 | * Creates a new menu item with the text to show and the priority.
49 | *
50 | * @param name The text of the menu item.
51 | * @param priority The order to show the item.
52 | */
53 | public LauncherAction(String name, int priority) {
54 | this(name, priority, null);
55 | }
56 |
57 | /**
58 | * Creates a new menu item with the text to show, the priority, and an icon.
59 | *
60 | * @param name The text of the menu item.
61 | * @param priority The order to show the item.
62 | * @param icon Unused.
63 | */
64 | public LauncherAction(String name, int priority, Icon icon) {
65 | super(name, icon);
66 | this.mPriority = priority;
67 | this.mName = name;
68 | this.mHasArrow = false;
69 | this.setEnabled(true);
70 | }
71 |
72 | /**
73 | * Called when the menu item is tapped by the user. It creates a new thread and runs it.
74 | *
75 | * @param actionEvent The action performed
76 | */
77 | public final void actionPerformed(ActionEvent actionEvent) {
78 | ThreadPool.getInstance().runIt(this, "Launcher");
79 | }
80 |
81 | /**
82 | * Called when the new thread is ran. It wraps {@link com.yifanlu.Kindle.LauncherAction#doAction()}
83 | */
84 | public final void run() {
85 | doAction();
86 | }
87 |
88 | /**
89 | * The value is the text label of the item
90 | *
91 | * @return the text label of the item
92 | */
93 | public String getValue() {
94 | return this.mName;
95 | }
96 |
97 | /**
98 | * This is the task the item performs when called. Overwritten by subclasses.
99 | */
100 | public abstract void doAction();
101 |
102 | /**
103 | * Compares this item to another one. They are compared first by priority and next by their text labels.
104 | *
105 | * @param other The other object.
106 | * @return The difference in priority between the two or {@link String#compareTo(Object)} if equal.
107 | */
108 | public int compareTo(Object other) {
109 | if (!(other instanceof LauncherAction))
110 | return -1;
111 | if (this.mPriority == ((LauncherAction) other).mPriority)
112 | return this.mName.compareTo(((LauncherAction) other).mName);
113 | return this.mPriority - ((LauncherAction) other).mPriority;
114 | }
115 |
116 | /**
117 | * Sets the visibility of the arrow
118 | *
119 | * @param hasArrow Does this item have an arrow?
120 | * @see com.yifanlu.Kindle.LauncherMenu#hasArrow()
121 | */
122 | public void setHasArrow(boolean hasArrow) {
123 | mHasArrow = hasArrow;
124 | }
125 |
126 | /**
127 | * If true, the menu item will have a small arrow to the far right of the text.
128 | *
129 | * @return Does this item have an arrow?
130 | */
131 | public boolean hasArrow() {
132 | return mHasArrow;
133 | }
134 |
135 | /**
136 | * The display order of this item as specified on creation.
137 | *
138 | * @return The priority of the item
139 | */
140 | public int getPriority() {
141 | return mPriority;
142 | }
143 |
144 | /**
145 | * Tells the Kindle Reader SDK that this item should be enabled.
146 | *
147 | * @return The SDK type of the item
148 | */
149 | public int getType() {
150 | return ReaderAction.TYPE_ALL;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/LauncherMenu.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.agui.swing.MenuDialog;
22 | import com.amazon.kindle.util.lipc.LipcException;
23 | import com.amazon.kindle.util.lipc.LipcService;
24 | import edu.emory.mathcs.backport.java.util.PriorityQueue;
25 |
26 | import javax.swing.*;
27 |
28 | /**
29 | * A menu item that contains a submenu.
30 | *
31 | * @author Yifan Lu
32 | * @version 1.0
33 | */
34 | public class LauncherMenu extends LauncherAction {
35 | private MenuDialog mDialog;
36 | private PriorityQueue mItems;
37 | private LauncherMenu mParent;
38 |
39 | /**
40 | * Creates a new submenu with its text label and priority
41 | *
42 | * @param name The text to show
43 | * @param priority The order of this item in comparison to others
44 | */
45 | public LauncherMenu(String name, int priority) {
46 | this(name, priority, null);
47 | }
48 |
49 | /**
50 | * Creates a new submenu with its text label, priority, and parent menu
51 | *
52 | * @param name The text to show
53 | * @param priority The order of this item in comparison to others
54 | * @param parent The menu to go to when the "Previous" item is selected
55 | */
56 | public LauncherMenu(String name, int priority, LauncherMenu parent) {
57 | super(name, priority);
58 | mItems = new PriorityQueue();
59 | mParent = parent;
60 | setHasArrow(true);
61 | }
62 |
63 | /**
64 | * Renders the dialog on screen.
65 | *
66 | * @see LauncherMenu#getMenu()
67 | */
68 | public synchronized void doAction() {
69 | getMenu().postDialog(getCurrentAppId(), true);
70 | if (mParent != null) {
71 | mParent.putValue(Action.NAME, mParent.getValue());
72 | }
73 | }
74 |
75 | /**
76 | * The menu must be displayed in context to the current booklet on screen, this method finds it.
77 | *
78 | * @return The current app that is displayed.
79 | */
80 | private String getCurrentAppId() {
81 | try {
82 | return LipcService.getInstance().getDefaultSource().getTarget("com.lab126.appmgrd").getStringProperty("activeApp");
83 | } catch (LipcException e) {
84 | KindleLauncher.LOG.error("Cannot get current running app from lipc.");
85 | e.printStackTrace();
86 | }
87 | return "com.lab126.booklet.home";
88 | }
89 |
90 | /**
91 | * Converts the list of menu items into a dialog containing the items.
92 | */
93 | public void initMenu() {
94 | mDialog = new MenuDialog(getCurrentAppId());
95 | mDialog.setTitle(this.getValue());
96 | PriorityQueue copy = new PriorityQueue();
97 | while (!mItems.isEmpty()) {
98 | LauncherAction item = (LauncherAction) mItems.remove();
99 | if (item.hasArrow())
100 | mDialog.addActionWithIndicator(item);
101 | else
102 | mDialog.addAction(item);
103 | copy.add(item);
104 | }
105 | if (mParent != null) {
106 | mParent.putValue(Action.NAME, "« Previous");
107 | mDialog.addAction(mParent);
108 | }
109 | mItems = copy;
110 | }
111 |
112 | /**
113 | * Adds an item to the menu.
114 | *
115 | * @param item The item to add.
116 | */
117 | public void addMenuItem(LauncherAction item) {
118 | mItems.add(item);
119 | mDialog = null;
120 | }
121 |
122 | /**
123 | * Removes an item from the menu.
124 | *
125 | * @param item The item to remove.
126 | */
127 | public void removeMenuItem(LauncherAction item) {
128 | mItems.remove(item);
129 | mDialog = null;
130 | }
131 |
132 | /**
133 | * All the menu items added.
134 | *
135 | * @return An array containing all the menu items added.
136 | */
137 | public LauncherAction[] getMenuItems() {
138 | return (LauncherAction[]) mItems.toArray(new LauncherAction[0]);
139 | }
140 |
141 | /**
142 | * Is the menu empty?
143 | *
144 | * @return The emptiness of the menu.
145 | */
146 | public boolean isMenuEmpty() {
147 | return mItems.isEmpty();
148 | }
149 |
150 | /**
151 | * Empty the menu.
152 | */
153 | public void clearMenu() {
154 | mItems.clear();
155 | }
156 |
157 | /**
158 | * Gets the dialog generated by {@link com.yifanlu.Kindle.LauncherMenu#initMenu()} and calls it if the dialog
159 | * has not been generated yet.
160 | *
161 | * @return A dialog containing the menu items.
162 | * @see com.yifanlu.Kindle.LauncherMenu#initMenu()
163 | */
164 | public MenuDialog getMenu() {
165 | if (mDialog == null) {
166 | this.initMenu();
167 | }
168 | return mDialog;
169 | }
170 |
171 | /**
172 | * Sets the parent menu so an option to go to the previous menu is added.
173 | *
174 | * @param parent The menu to go back to.
175 | */
176 | public void setParent(LauncherMenu parent) {
177 | this.mParent = parent;
178 | }
179 |
180 | /**
181 | * Gets the parent menu.
182 | *
183 | * @return The parent menu
184 | * @see LauncherMenu#setParent(LauncherMenu)
185 | */
186 | public LauncherMenu getParent() {
187 | return this.mParent;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/LauncherScript.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import java.io.File;
22 |
23 | /**
24 | * A menu item that executes a shell command.
25 | *
26 | * @author Yifan Lu
27 | * @version 1.0
28 | * @see JSONMenu
29 | */
30 | public class LauncherScript extends LauncherAction {
31 | private File mScript;
32 | private String mArgs;
33 |
34 | /**
35 | * Creates a new launch script menu item.
36 | *
37 | * @param name The text to show
38 | * @param priority The order of this item in comparison to others
39 | * @param script The shell script or command to run
40 | * @param args The arguments of the script
41 | */
42 | public LauncherScript(String name, int priority, File script, String args) {
43 | super(name, priority);
44 | this.mScript = script;
45 | this.mArgs = args;
46 | }
47 |
48 | /**
49 | * Executes the shell command with parameters
50 | */
51 | public synchronized void doAction() {
52 | StringBuffer out = new StringBuffer();
53 | StringBuffer err = new StringBuffer();
54 | KindleLauncher.SERVICES.getDeviceService().exec(mScript.getAbsolutePath() + " " + mArgs, out, err);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/Menuable.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | /**
22 | * Represents an object that stores menu items. All Java menu extensions must implement this.
23 | *
24 | * @author Yifan Lu
25 | * @version 1.0
26 | * @see ExtensionsLoader
27 | */
28 | public interface Menuable {
29 | /**
30 | * Adds menu items to the parent menu. The parent is usually the main Launcher menu.
31 | *
32 | * @param menu The parent menu.
33 | */
34 | public void addItemsToMenu(LauncherMenu menu);
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/yifanlu/Kindle/ReaderPluginLauncher.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Kindle Launcher
3 | * GUI menu launcher for Kindle Touch
4 | * Copyright (C) 2011 Yifan Lu
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU General Public License as published by
8 | * the Free Software Foundation, either version 3 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU General Public License
17 | * along with this program. If not, see .
18 | */
19 | package com.yifanlu.Kindle;
20 |
21 | import com.amazon.ebook.booklet.reader.sdk.BookException;
22 | import com.amazon.ebook.booklet.reader.sdk.ReaderPlugin;
23 | import com.amazon.ebook.booklet.reader.sdk.ReaderSDK;
24 | import com.amazon.ebook.booklet.reader.sdk.ui.MenuInserter;
25 |
26 | /**
27 | * A Kindle Reader SDK plugin that adds the Launcher menu item to the main Kindle menu.
28 | *
29 | * @author Yifan Lu
30 | * @version 1.0
31 | * @see KindleLauncher
32 | */
33 | public class ReaderPluginLauncher extends ReaderPlugin {
34 | private ReaderSDK mSdk;
35 |
36 | /**
37 | * Called by the plugin loader. Adds the Launcher menu item to the main menu.
38 | *
39 | * @param readerSDK The SDK object
40 | * @throws BookException if there is a problem creating the plugin
41 | */
42 | public void pluginLoad(ReaderSDK readerSDK) throws BookException {
43 | mSdk = readerSDK;
44 | mSdk.getRegistry().registerMenuInserter((MenuInserter) KindleLauncher.getInstance());
45 | }
46 |
47 | /**
48 | * Called by the plugin unloader. Removes the Launcher menu item to the main menu.
49 | */
50 | public void pluginUnload() {
51 | mSdk.getRegistry().deregisterMenuInserter((MenuInserter) KindleLauncher.getInstance());
52 | }
53 |
54 | /**
55 | * The name of the plugin.
56 | *
57 | * @return The name of the plugin.
58 | */
59 | public String getName() {
60 | return "Launcher";
61 | }
62 | }
63 |
--------------------------------------------------------------------------------