package ca.canucksoftware.themebuilder;
import com.twicom.qdparser.Element;
import com.twicom.qdparser.TaggedElement;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* @author Jason
*/
public class ZipTheme {
public String name;
public String version;
public String creator;
public String website;
public String donateURL;
public String id;
public String description;
public ArrayList<File> screenshots;
public ArrayList<String> devices;
public VersionControl controller;
public ZipTheme() {
reset();
}
public String toString() {
String output = "Name: " + name + "\n";
output += "Version: " + version + "\n";
output += "Creator: " + creator + "\n";
output += "Website: " + website + "\n";
output += "Donation URL: " + donateURL + "\n";
output += "ID: " + id + "\n";
output += "Description: " + version + "\n";
output += "Screenshots:\n";
for(int i=0; i<screenshots.size(); i++) {
output += "\t" + screenshots.get(i) + "\n";
}
output += controller + "\n";
return output;
}
public void reset() {
name = "";
version = "";
creator = "";
website = "";
donateURL = "";
id = "";
description = "";
screenshots = new ArrayList<File>();
devices = new ArrayList<String>();
controller = new VersionControl();
}
public File generateXML() {
String tmpFilePath = System.getProperty("java.io.tmpdir");
File xml = new File(tmpFilePath, "theme.xml");
if(xml.exists()) {
xml.delete();
}
XMLHandler theme = XMLHandler.createNewXML(xml, "data");
TaggedElement info, previews, content, iconset, wp;
info = (TaggedElement)Element.newElement("<themeinfo></themeinfo>");
content = (TaggedElement)Element.newElement("<themelist></themelist>");
theme.getRoot().add(info);
theme.getRoot().add(content);
info.add((TaggedElement)Element.newElement("<name>" + encodeXml(name) + "</name>"));
info.add((TaggedElement)Element.newElement("<version>" + encodeXml(version) + "</version>"));
info.add((TaggedElement)Element.newElement("<creator>" + encodeXml(creator) + "</creator>"));
info.add((TaggedElement)Element.newElement("<description>" + encodeXml(description) + "</description>"));
info.add((TaggedElement)Element.newElement("<website>" + encodeXml(website) + "</website>"));
previews = (TaggedElement)Element.newElement("<screenshots></screenshots>");
info.add(previews);
for(int i=0; i<screenshots.size(); i++) {
if(screenshots.get(i).exists()) {
previews.add((TaggedElement)Element.newElement("<image>" + id + "/screenshots/" + (i+1) +
fileExt(screenshots.get(i)) + "</image>"));
}
}
VersionTheme firstTheme = controller.getVersion(controller.list(false).get(0));
if(firstTheme.wallpaper!=null) {
wp = (TaggedElement)Element.newElement("<wallpaper></wallpaper>");
content.add(wp);
wp.add((TaggedElement)Element.newElement("<image>" + id + "/" +firstTheme.version + "/wallpaper" +
fileExt(firstTheme.wallpaper) + "</image>"));
}
for(int i=0; i<firstTheme.files.size(); i++) {
if(firstTheme.files.get(i).file!=null && firstTheme.files.get(i).file.exists()) {
TaggedElement file = (TaggedElement)Element.newElement("<file></file>");
content.add(file);
file.add((TaggedElement)Element.newElement("<filename>" + id + "/" + firstTheme.version + "/" +
firstTheme.files.get(i).zipResourcePath() + "</filename>"));
file.add((TaggedElement)Element.newElement("<destination>" + firstTheme.files.get(i).dest +
"</destination>"));
}
}
for(int i=0; i<firstTheme.patches.size(); i++) {
if(firstTheme.patches.get(i).file!=null && firstTheme.patches.get(i).file.exists()) {
TaggedElement patch = (TaggedElement)Element.newElement("<patch></patch>");
content.add(patch);
patch.add((TaggedElement)Element.newElement("<diff>" + id + "/" + firstTheme.version + "/" +
firstTheme.patches.get(i).zipResourcePath() + "</diff>"));
}
}
if(firstTheme.icons.size()>0) {
iconset = (TaggedElement)Element.newElement("<iconset></iconset>");
content.add(iconset);
for(int i=0; i<firstTheme.icons.size(); i++) {
if(firstTheme.icons.get(i).image!=null && firstTheme.icons.get(i).image.exists()) {
TaggedElement icon = (TaggedElement)Element.newElement("<icon></icon>");
iconset.add(icon);
icon.add((TaggedElement)Element.newElement("<appid>" + firstTheme.icons.get(i).appID +
"</appid>"));
icon.add((TaggedElement)Element.newElement("<image>" + id + "/" + firstTheme.version + "/" +
firstTheme.icons.get(i).zipResourcePath() + "</image>"));
}
}
}
theme.updateFile();
return xml;
}
private String encodeXml(String text) {
return text.replaceAll("&", "&");
}
private String decodeXml(String text) {
return text.replaceAll("&", "&");
}
public File generateJSON() {
String tmpFilePath = System.getProperty("java.io.tmpdir");
File json = new File(tmpFilePath, "theme.json");
if(json.exists()) {
json.delete();
}
JSONObject content = new JSONObject();
try {
content.put("name", name);
content.put("version", version);
content.put("creator", creator);
content.put("description", description);
content.put("website", website);
if(donateURL!=null) {
content.put("donations", donateURL);
}
ArrayList<String> ss = new ArrayList<String>();
for(int i=0; i<screenshots.size(); i++) {
if(screenshots.get(i).exists()) {
ss.add("screenshots/" + (i+1) + fileExt(screenshots.get(i)));
}
}
content.put("screenshots", new JSONArray(ss));
content.put("devices", new JSONArray(devices));
JSONObject themeData = new JSONObject();
List<VersionTheme> versions = controller.listVersions();
for(int i=0; i<versions.size(); i++) {
JSONObject currVer = new JSONObject();
List<String> categories = versions.get(i).categories();
for(int j=0; j<categories.size(); j++) {
JSONObject currCat = new JSONObject();
String catStr = categories.get(j);
List<JSONObject> images = new ArrayList<JSONObject>();
List<JSONObject> patches = new ArrayList<JSONObject>();
List<JSONObject> sounds = new ArrayList<JSONObject>();
if(catStr.equals("wallpapers")) {
JSONObject wallpaper = new JSONObject();
if(versions.get(i).wallpaper!=null) {
wallpaper.put("path", "/media/internal/wallpapers/wallpaper.jpg");
wallpaper.put("file", versions.get(i).version + "/wallpaper" +
fileExt(versions.get(i).wallpaper));
}
images.add(wallpaper);
} else if(catStr.equals("app_icons")) {
for(int k=0; k<versions.get(i).icons.size(); k++) {
JSONObject currFile = new JSONObject();
currFile.put("path", versions.get(i).icons.get(k).dest());
currFile.put("file", versions.get(i).version + "/" +
versions.get(i).icons.get(k).zipResourcePath());
images.add(currFile);
}
}
List<FileEntry> catFiles = versions.get(i).filesByCategory(catStr);
for(int k=0; k<catFiles.size(); k++) {
JSONObject currFile = new JSONObject();
currFile.put("path", catFiles.get(k).dest);
currFile.put("file", versions.get(i).version + "/" + catFiles.get(k).zipResourcePath());
if(catFiles.get(k).dest.endsWith(".mp3") || catFiles.get(k).dest.endsWith(".wav")) {
sounds.add(currFile);
} else {
images.add(currFile);
}
}
List<PatchEntry> catPatches = versions.get(i).patchesByCategory(catStr);
for(int k=0; k<catPatches.size(); k++) {
catPatches.get(k).splitPatch();
for(int l=0; l<catPatches.get(k).subpatches.size(); l++) {
JSONObject currFile = new JSONObject();
currFile.put("path", catPatches.get(k).paths.get(l));
currFile.put("file", versions.get(i).version + "/" +
catPatches.get(k).zipResourcePrefix() + (l+1) + ".patch");
patches.add(currFile);
}
}
if(images.size()>0) {
currCat.put("images", new JSONArray(images));
}
if(patches.size()>0) {
currCat.put("patches", new JSONArray(patches));
}
if(sounds.size()>0) {
currCat.put("sounds", new JSONArray(sounds));
}
if(currCat.length()>0) {
currVer.put(catStr, currCat);
}
}
if(currVer.length()>0) {
themeData.put(versions.get(i).version, currVer);
}
}
List<AliasVersion> aliases = controller.listAliases();
for(int i=0; i<aliases.size(); i++) {
JSONObject currAlias = new JSONObject();
currAlias.put("alias", aliases.get(i).realVersion);
themeData.put(aliases.get(i).version, currAlias);
}
content.put("themedata", themeData);
}catch(Exception e) {
e.printStackTrace();
}
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(json));
bw.write(content.toString(4));
bw.close();
} catch(Exception e) {
e.printStackTrace();
}
return json;
}
public File buildZip(File out) {
try {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(out));
File xml = generateXML();
addToZip(zos, xml, "theme.xml");
File json = generateJSON();
addToZip(zos, json, id + "/theme.json");
for(int i=0; i<screenshots.size(); i++) {
if(screenshots.get(i).exists()) {
addToZip(zos, screenshots.get(i), id + "/screenshots/" + (i+1) + fileExt(screenshots.get(i)));
}
}
List<VersionTheme> versions = controller.listVersions();
for(int i=0; i<versions.size(); i++) {
if(versions.get(i).wallpaper!=null && versions.get(i).wallpaper.exists()) {
addToZip(zos, versions.get(i).wallpaper, id + "/" +versions.get(i).version + "/wallpaper" +
fileExt(versions.get(i).wallpaper));
}
for(int j=0; j<versions.get(i).icons.size(); j++) {
if(versions.get(i).icons.get(j).image.exists()) {
addToZip(zos, versions.get(i).icons.get(j).image, id + "/" + versions.get(i).version +
"/" + versions.get(i).icons.get(j).zipResourcePath());
}
}
for(int j=0; j<versions.get(i).files.size(); j++) {
if(versions.get(i).files.get(j).file.exists()) {
addToZip(zos, versions.get(i).files.get(j).file, id + "/" + versions.get(i).version +
"/" + versions.get(i).files.get(j).zipResourcePath());
}
}
for(int j=0; j<versions.get(i).patches.size(); j++) {
if(versions.get(i).patches.get(j).file.exists()) {
addToZip(zos, versions.get(i).patches.get(j).file, id + "/" + versions.get(i).version +
"/" + versions.get(i).patches.get(j).zipResourcePath());
for(int k=0; k<versions.get(i).patches.get(j).subpatches.size(); k++) {
if(versions.get(i).patches.get(j).subpatches.get(k).exists()) {
addToZip(zos, versions.get(i).patches.get(j).subpatches.get(k),
id + "/" + versions.get(i).version + "/" +
versions.get(i).patches.get(j).zipResourcePrefix() + (k+1) + ".patch");
}
}
}
}
}
zos.flush();
zos.close();
if(xml.exists()) {
xml.delete();
}
if(json.exists()) {
json.delete();
}
} catch(Exception e) {
e.printStackTrace();
}
return out;
}
private void addToZip(ZipOutputStream out, File f, String s) throws Exception {
byte[] buf = new byte[2048];
FileInputStream in = new FileInputStream(f);
out.putNextEntry(new ZipEntry(s));
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.closeEntry();
in.close();
}
private String getFilename(File f) {
String filename = f.getName();
int index = filename.lastIndexOf("\\");
if(index>-1) {
filename = filename.substring(index+1, filename.length());
} else {
index = filename.lastIndexOf("/");
if(index>-1)
filename = filename.substring(index+1, filename.length());
}
return filename;
}
private String fileExt(File f) {
String ext = "";
int index = f.getName().lastIndexOf(".");
if(index>=0) {
ext = f.getName().substring(index);
}
return ext;
}
public File extractTheme(File zip) throws Exception {
final int BUFFER = 2048;
int count;
String tmpFilePath = System.getProperty("java.io.tmpdir");
File outDir = new File(tmpFilePath, getFilename(zip) + System.currentTimeMillis());
byte data[] = new byte[BUFFER];
outDir.mkdirs();
ZipFile zipF = new ZipFile(zip);
Enumeration entries = zipF.entries();
while(entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry)entries.nextElement();
if(entry.isDirectory()) {
File dir = new File(outDir, entry.getName());
dir.mkdirs();
} else {
InputStream is = zipF.getInputStream(entry);
File out = new File(outDir, entry.getName());
if(!out.getParentFile().isDirectory()) {
out.getParentFile().mkdirs();
}
FileOutputStream fos = new FileOutputStream(out);
while ((count = is.read(data,0,BUFFER)) != -1) {
fos.write(data,0,count);
}
fos.flush();
fos.close();
is.close();
}
}
zipF.close();
return outDir;
}
public boolean isNewFormat(File extractedDir) {
return new File(extractedDir.listFiles(new DirFilter())[0], "theme.json").exists();
}
public void loadFromXML(File extractedDir, ArrayList<String> devices, String version) {
reset();
try {
int i;
File curr;
File xml = new File(extractedDir, "theme.xml");
if(xml.exists()) {
XMLHandler theme = new XMLHandler(xml);
TaggedElement info = theme.getRoot().find("themeinfo");
name = theme.getContent(info.find("name"));
this.version = theme.getContent(info.find("version"));
creator = theme.getContent(info.find("creator"));
website = theme.getContent(info.find("website"));
description = theme.getContent(info.find("description"));
TaggedElement ss = info.find("screenshots");
for(i=0; i<ss.elements(); i++) {
curr = new File(extractedDir, theme.getContent(ss.getChild(i)));
if(curr.exists()) {
screenshots.add(curr);
}
}
this.devices = devices;
VersionTheme verTheme = controller.addVersion(version);
TaggedElement content = theme.getRoot().find("themelist");
for(i=0; i<content.elements(); i++) {
TaggedElement currEle = (TaggedElement) content.getChild(i);
if(currEle.getTag().equalsIgnoreCase("file")) {
Element src = currEle.find("filename");
Element dest = currEle.find("destination");
if(src!=null && dest !=null) {
curr = new File(extractedDir, theme.getContent(src));
if(curr.exists()) {
verTheme.files.add(new FileEntry(theme.getContent(dest), curr, "applications"));
}
}
} else if(currEle.getTag().equalsIgnoreCase("wallpaper")) {
Element wp = currEle.find("image");
if(wp!=null) {
curr = new File(extractedDir, theme.getContent(wp));
if(curr.exists()) {
verTheme.wallpaper = curr;
}
}
} else if(currEle.getTag().equalsIgnoreCase("iconset")) {
for(int j=0; j<currEle.elements(); j++) {
TaggedElement currIcon = (TaggedElement) currEle.getChild(j);
Element src = currIcon.find("image");
Element id = currIcon.find("appid");
if(src!=null && id!=null) {
curr = new File(extractedDir, theme.getContent(src));
if(curr.exists()) {
verTheme.icons.add(new IconEntry(theme.getContent(id), curr));
}
}
}
} else if(currEle.getTag().equalsIgnoreCase("patch")) {
Element diff = currEle.find("diff");
if(diff!=null) {
curr = new File(extractedDir, theme.getContent(diff));
if(curr.exists()) {
verTheme.patches.add(new PatchEntry(curr, "applications"));
}
}
}
}
xml.delete();
}
} catch(Exception e) {
e.printStackTrace();
}
}
public void loadFromJSON(File extractedDir) {
reset();
try {
File curr;
extractedDir = extractedDir.listFiles(new DirFilter())[0];
id = getFilename(extractedDir);
File json = new File(extractedDir, "theme.json");
if(json.exists()) {
String jsonStr = "";
try{
BufferedReader br = new BufferedReader(new FileReader(json));
String line = br.readLine();
while(line!=null) {
jsonStr += line;
line = br.readLine();
}
br.close();
} catch(Exception e) {
e.printStackTrace();
}
JSONObject theme = new JSONObject(jsonStr);
name = theme.getString("name");
version = theme.getString("version");
creator = theme.getString("creator");
website = theme.getString("website");
if(theme.has("donations")) {
donateURL = theme.getString("donations");
}
description = theme.getString("description");
JSONArray jsonDevices = theme.getJSONArray("devices");
for(int i=0; i<jsonDevices.length(); i++) {
devices.add(jsonDevices.getString(i));
}
JSONArray jsonSS = theme.getJSONArray("screenshots");
for(int i=0; i<jsonSS.length(); i++) {
curr = new File(extractedDir, jsonSS.getString(i));
if(curr.exists()) {
screenshots.add(curr);
}
}
JSONObject themeData = theme.getJSONObject("themedata");
Iterator iter1 = themeData.keys();
while(iter1.hasNext()) {
String verStr = (String) iter1.next();
JSONObject verContent = themeData.getJSONObject(verStr);
if(verContent.has("alias")) {
controller.addAlias(verStr, verContent.getString("alias"));
} else {
VersionTheme verTheme = controller.addVersion(verStr);
Iterator iter2 = verContent.keys();
while(iter2.hasNext()) {
String catStr = (String) iter2.next();
JSONObject catContent = verContent.getJSONObject(catStr);
if(catContent.has("images")) {
JSONArray images = catContent.getJSONArray("images");
for(int i=0; i<images.length(); i++) {
JSONObject currObj = images.getJSONObject(i);
if(catStr.equals("wallpapers") && i==0) {
if(currObj.has("file")) { //may be empty
curr = new File(extractedDir, currObj.getString("file"));
if(curr.exists()) {
verTheme.wallpaper = curr;
}
}
} else if(catStr.equals("app_icons")) {
curr = new File(extractedDir, currObj.getString("file"));
String dest = currObj.getString("path");
if(dest.startsWith("/usr/palm/applications/")) {
String appid = dest.replace("/usr/palm/applications/", "");
String iconName = appid.substring(appid.indexOf("/")+1);
if(iconName.equals("icon.png")) {
appid = appid.substring(0, appid.indexOf("/"));
if(curr.exists()) {
verTheme.icons.add(new IconEntry(appid, curr));
}
} else {
if(curr.exists()) {
verTheme.files.add(new FileEntry(dest, curr, catStr));
}
}
} else {
if(curr.exists()) {
verTheme.files.add(new FileEntry(dest, curr, catStr));
}
}
} else {
curr = new File(extractedDir, currObj.getString("file"));
if(curr.exists()) {
String dest = currObj.getString("path");
verTheme.files.add(new FileEntry(dest, curr, catStr));
}
}
}
}
if(catContent.has("sounds")) {
JSONArray sounds = catContent.getJSONArray("sounds");
for(int i=0; i<sounds.length(); i++) {
JSONObject currObj = sounds.getJSONObject(i);
curr = new File(extractedDir, currObj.getString("file"));
if(curr.exists()) {
String dest = currObj.getString("path");
verTheme.files.add(new FileEntry(dest, curr, catStr));
}
}
}
if(catContent.has("patches")) {
JSONArray patches = catContent.getJSONArray("patches");
for(int i=0; i<patches.length(); i++) {
JSONObject currObj = patches.getJSONObject(i);
String filepath = currObj.getString("file");
String testpath = filepath.substring(0, filepath.lastIndexOf("/")) + ".patch";
curr = new File(extractedDir, testpath);
if(curr.exists()) {
verTheme.patches.add(new PatchEntry(curr, catStr));
} else {
curr = new File(extractedDir, filepath);
if(curr.exists()) {
verTheme.patches.add(new PatchEntry(curr, catStr));
}
}
}
}
}
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
private class DirFilter implements FileFilter {
public boolean accept(File file) {
return file.isDirectory();
}
}
}