package org.voidsink.library.contributors;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/**
* Display a dialog showing contributors.
*/
public class Contributors {
/**
* Tag that is used when sending error/debug messages to the log.
*/
protected static final String LOG_TAG = "voidsinkContributors";
/**
* Default CSS styles used to format contributors.
*/
public static final String DEFAULT_CSS = "h1 { margin: 0.4em; margin-left: 0px; font-size: 1.2em;}"
+ "\n"
+ "dl { margin: 0px;}"
+ "\n"
+ "dt { margin-bottom: 0.4em; margin-top: 0.4em; padding-left: 1em}";
/**
* Context that is used to access the resources and to create the
* contributor dialog.
*/
protected final Context mContext;
/**
* Contains the CSS rules used to format contributors.
*/
protected final String mCss;
/**
* Contains constants for the root element of {@code contributors.xml}.
*/
protected interface ContributorsTag {
static final String NAME = "contributors";
}
/**
* Contains constants for the contributor element of
* {@code contributors.xml}.
*/
protected interface ContributorTag {
static final String NAME = "contributor";
static final String ATTRIBUTE_TITLE = "title";
}
/**
* Contains constants for the name element of {@code contributors.xml}.
*/
protected interface NameTag {
static final String NAME = "name";
}
/**
* Contains constants for the email element of {@code contributors.xml}.
*/
protected interface EMailTag {
static final String NAME = "email";
}
/**
* Contains constants for the website element of {@code contributors.xml}.
*/
protected interface WebSiteTag {
static final String NAME = "website";
static final String ATTRIBUTE_TITLE = "title";
}
/**
* Create a {@code Contributors} instance using the default
* {@link SharedPreferences} file.
*
* @param context
* Context that is used to access the resources and to create the
* Contributors dialog.
*/
public Contributors(Context context) {
this(context, DEFAULT_CSS);
}
/**
* Create a {@code Contributors} instance.
*
* @param context
* Context that is used to access the resources and to create the
* Contributors dialog.
* @param css
* CSS styles used to format the contributor list (excluding
* {@code <style>} and {@code </style>}).
*
*/
public Contributors(Context context, String css) {
mContext = context;
mCss = css;
}
/**
* Create a dialog containing all contributors.
*
* @return A dialog containing all contributors
*/
public AlertDialog getDialog(int resId) {
return getDialog(resId, false);
}
/**
* Create a dialog containing all contributors.
*
* @return A dialog containing all contributors
*/
public AlertDialog getDialog(int resId, boolean shuffle) {
WebView wv = new WebView(mContext);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
final String MAILTO = "mailto:";
if (url.startsWith(MAILTO)) {
// send me an eMail
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri
.fromParts("mailto",
url.substring(MAILTO.length()), null));
emailIntent
.putExtra(
android.content.Intent.EXTRA_SUBJECT,
mContext.getString(R.string.contributor_email_subject));
emailIntent
.putExtra(
android.content.Intent.EXTRA_TEXT,
mContext.getString(R.string.contributor_email_text));
mContext.startActivity(Intent.createChooser(emailIntent,
mContext.getString(R.string.contributor_send_email)));
return true;
} else {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
mContext.startActivity(i);
return true;
}
}
});
ContributorItems ci = getContributorItems(resId, shuffle);
// wv.setBackgroundColor(0); // transparent
String html = generateContributorsHtml(ci.items);
// Log.d(LOG_TAG, html);
wv.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle(ci.title)
.setView(wv)
.setCancelable(false)
// OK button
.setPositiveButton(
mContext.getString(R.string.contributor_ok_button),
null);
return builder.create();
}
/**
* Get all contributors as HTML string.
*
* @return the contributors
*/
protected String generateContributorsHtml(List<ContributorItem> items) {
StringBuilder sb = new StringBuilder();
sb.append("<html><head><style type=\"text/css\">");
sb.append(mCss);
sb.append("</style></head><body>");
for (ContributorItem c : items) {
sb.append("<h1>");
sb.append(c.name);
sb.append("</h1><dl>");
if (c.hasEMail()) {
sb.append("<dt>");
sb.append(String.format("<a href=\"mailto:%s\">%s</a>",
c.eMail, c.eMail));
sb.append("</dt>");
}
if (c.hasWebsite()) {
sb.append("<dt>");
sb.append(String.format("<a href=\"%s\">%s</a>", c.website,
c.websiteTitle));
sb.append("</dt>");
}
sb.append("</dl>");
}
sb.append("</body></html>");
return sb.toString();
}
/**
* Returns the contributors.
*
* @param shuffle
* If this is {@code true} the contributors get shuffled
*
* @return A {@code List} containing {@link ContributorItem}s
*/
public ContributorItems getContributorItems(int resId, boolean shuffle) {
ContributorItems result = readContributorsFromResource(resId);
if (shuffle) {
Collections.shuffle(result.items);
}
return result;
}
/**
* Read contributors from XML resource file.
*
* @param resId
* Resource ID of the XML file to read the contributors from.
*
* @return A {@code List} containing {@link ContributorItem}s representing
* contributors.
*/
protected final ContributorItems readContributorsFromResource(int resId) {
XmlResourceParser xml = mContext.getResources().getXml(resId);
try {
return readContributors(xml);
} finally {
xml.close();
}
}
/**
* Read contributors from an XML file.
*
* @param xml
* The {@code XmlPullParser} instance used to read the
* contributors.
*
* @return A {@code List} containing the contributors.
*/
protected ContributorItems readContributors(XmlPullParser xml) {
List<ContributorItem> items = new ArrayList<ContributorItem>();
String title = null;
try {
int eventType = xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
switch (xml.getName()) {
case ContributorTag.NAME:
parseContributorTag(xml, items);
break;
case ContributorsTag.NAME:
title = xml.getAttributeValue(null,
ContributorTag.ATTRIBUTE_TITLE);
try {
if (title != null) {
int resId = mContext.getResources()
.getIdentifier(title, null,
mContext.getPackageName());
if (resId != 0) {
title = mContext.getString(resId);
}
}
} catch (Exception e) {
title = null;
}
break;
default:
break;
}
}
eventType = xml.next();
}
} catch (XmlPullParserException e) {
Log.e(LOG_TAG, e.getMessage(), e);
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
if (title == null || title.isEmpty()) {
title = mContext.getString(R.string.contributor_title);
}
return new ContributorItems(title, items);
}
/**
* Parse the {@code contributor} tag of a contributors XML file.
*
* @param xml
* The {@code XmlPullParser} instance used to read the
* contributors.
* @param cs
* The {@code List} to add a new {@link ContributorItem} instance
* to.
*
* @throws XmlPullParserException
* @throws IOException
*/
private void parseContributorTag(XmlPullParser xml, List<ContributorItem> cs)
throws XmlPullParserException, IOException {
String name = "";
String eMail = "";
String website = "";
String websiteTitle = "";
int eventType = xml.getEventType();
while (!(eventType == XmlPullParser.END_TAG && xml.getName().equals(
ContributorTag.NAME))) {
switch (eventType) {
case XmlPullParser.START_TAG:
switch (xml.getName()) {
case NameTag.NAME:
xml.next();
name = xml.getText();
break;
case EMailTag.NAME:
xml.next();
eMail = xml.getText();
break;
case WebSiteTag.NAME:
websiteTitle = xml.getAttributeValue(null,
WebSiteTag.ATTRIBUTE_TITLE);
xml.next();
website = xml.getText();
break;
default:
break;
}
break;
// case XmlPullParser.TEXT:
// break;
// case XmlPullParser.END_TAG:
// break;
default:
break;
}
eventType = xml.next();
}
if (!name.isEmpty()) {
cs.add(new ContributorItem(name, eMail, website, websiteTitle));
}
}
public static class ContributorItems {
public final String title;
public final List<ContributorItem> items;
public ContributorItems(String title, List<ContributorItem> items) {
this.title = title;
this.items = items;
}
}
/**
* Container used to store information about a release/version.
*/
public static class ContributorItem {
/**
* Name of the contributor
*/
public final String name;
/**
* eMail of the contributor
*/
public final String eMail;
/**
* website of the contributor
*/
public final String website;
/**
* title of website of the contributor
*/
public final String websiteTitle;
ContributorItem(String name, String eMail, String website,
String websiteTitle) {
this.name = name;
this.eMail = eMail;
this.website = website;
if (websiteTitle == null || websiteTitle.isEmpty()) {
this.websiteTitle = this.website;
} else {
this.websiteTitle = websiteTitle;
}
}
public boolean hasWebsite() {
return !this.website.isEmpty();
}
public boolean hasEMail() {
return !this.eMail.isEmpty();
}
}
}