/**
* Copyright (C) 2010 Marc A. Paradise
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.bbssh.ui.screens;
import java.util.Vector;
import javax.microedition.pim.Contact;
import javax.microedition.pim.ContactList;
import javax.microedition.pim.PIM;
import javax.microedition.pim.PIMException;
import me.regexp.RE;
import me.regexp.RESyntaxException;
import net.rim.blackberry.api.browser.Browser;
import net.rim.blackberry.api.invoke.AddressBookArguments;
import net.rim.blackberry.api.invoke.Invoke;
import net.rim.blackberry.api.invoke.MessageArguments;
import net.rim.blackberry.api.invoke.PhoneArguments;
import net.rim.blackberry.api.mail.Address;
import net.rim.blackberry.api.mail.AddressException;
import net.rim.blackberry.api.mail.Message;
import net.rim.blackberry.api.mail.MessagingException;
import net.rim.device.api.applicationcontrol.ApplicationPermissions;
import net.rim.device.api.i18n.ResourceBundle;
import net.rim.device.api.system.Clipboard;
import net.rim.device.api.ui.ContextMenu;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.ObjectListField;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.MainScreen;
import org.bbssh.BBSSHApp;
import org.bbssh.help.HelpManager;
import org.bbssh.i18n.BBSSHResource;
import org.bbssh.ui.components.MenuWrapper;
import org.bbssh.util.Tools;
/**
* A list of urls. Allows user to view URLs in the buffer, copy them to clipboard, send via email, open email, view web
* pages, etc.
*/
public class URLListScreen extends MainScreen implements BBSSHResource, FieldChangeListener {
// FIXME - this is not correctly matching if a soft line wrap is occurring within the buffer,
// since this will intepret it as a hard wrap and it will only match up to EOL.
// @todo customizable RE matching?
// ((http|ftp|https|ftp|wap)\\:\\/\\/\\S[\\n \t\t])
public static RE urlMatcher =
new RE(
"(https?|ftp|gopher|telnet|file|notes|ms-help)://\\S+([ \\t.]|$)??");
public static RE emailMatcher =
new RE(
"([a-zA-Z0-9_\\-\\.\\+]+)@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.)|(([a-zA-Z0-9\\-]+\\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\\]?)");
public static RE phoneMatcher =
new RE(
"(1\\s*[-\\/\\.]?)?(\\((\\d{3})\\)|(\\d{3}))\\s*[-\\/\\.]?\\s*(\\d{3})\\s*[-\\/\\.]?\\s*(\\d{4})\\s*(([xX]|[eE][xX][tT])\\.?\\s*(\\d+))*");
// @todo: secondary URL matcher?
// [any].[any]? or optional, "loose URL matching" option?
ResourceBundle res = ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
ObjectListField field = new ObjectListField();
public void fieldChanged(Field field, int context) {
// don't care - ideally we would capture when teh Enter key was pressed only,
// but that seems to require a customized class. Will come back to this...
}
/**
* Internal class representing a matching result of some kind.
*/
private static class Match {
private static final int URL = 0;
private static final int EMAIL = 1;
private static final int PHONE = 2;
private static final int INVALID = -1;
private int type;
private String value;
public Match(int type, String value) {
this.type = type;
this.value = value;
}
public String toString() {
return value;
}
}
public URLListScreen(String text) {
super(DEFAULT_CLOSE | DEFAULT_MENU);
setTitle(res.getString(URLLIST_TITLE));
try {
Vector results = new Vector();
addToList(results, text, urlMatcher, Match.URL);
addToList(results, text, emailMatcher, Match.EMAIL);
addToList(results, text, phoneMatcher, Match.PHONE);
Object[] a;
if (results.size() > 0) {
a = Tools.vectorToArray(results);
} else {
a = new Object[] { new Match(Match.INVALID, res.getString(URLLIST_NO_MATCH)) };
}
field.setChangeListener(this);
field.set(a);
field.setSize(a.length);
} catch (RESyntaxException e) {
field.set(new Object[] { new Match(Match.INVALID, e.getMessage()) });
}
add(field);
}
private void addToList(Vector results, String text, RE pattern, int type) {
int x = 0;
while (pattern.match(text, x)) {
String match = pattern.getParen(0).replace('\n', (char) 0).replace('\r', (char) 0).replace('\t', (char) 0);
// // Strip out inappropriate ending characters -- applies mostly to HTML
// while (match.length() > 1 && (match.endsWith("\n") || match.endsWith("\r") || match.
// endsWith(".") || match.endsWith("\t"))) {
// match = match.substring(0, match.length() - 1);
// }
results.addElement(new Match(type, match));
x = pattern.getParenEnd(0) + 1;
}
}
protected void makeMenu(Menu menu, int instance) {
menu.add(HelpManager.getHelpMenu());
super.makeMenu(menu, instance);
Match sel = getCurrentSelection();
if (sel == null) {
return;
}
// Copy is always present.
menu.add(copyToClipboard);
switch (sel.type) {
case Match.URL:
menu.add(openBrowser);
break;
case Match.EMAIL:
menu.add(sendEmail);
menu.add(addContact);
break;
case Match.PHONE:
menu.add(dialNumber);
menu.add(addContact);
break;
}
menu.setDefault(1);
}
protected void makeContextMenu(ContextMenu contextMenu) {
super.makeContextMenu(contextMenu);
Match sel = getCurrentSelection();
if (sel == null) {
return;
}
MenuWrapper wrapper = new MenuWrapper(contextMenu);
// Copy is always present.
wrapper.addItem(copyToClipboard);
switch (sel.type) {
case Match.URL:
wrapper.addItem(openBrowser);
break;
case Match.EMAIL:
wrapper.addItem(addContact);
wrapper.addItem(sendEmail);
sendEmail.setText(res.getString(URLLIST_MENU_SEND_EMAIL) + sel.value);
break;
case Match.PHONE:
wrapper.addItem(addContact);
wrapper.addItem(dialNumber);
dialNumber.setText(res.getString(URLLIST_MENU_DIAL_NUMBER) + sel.value);
break;
}
}
Match getCurrentSelection() {
int sel = field.getSelectedIndex();
if (sel == -1) {
return null;
}
Object o = field.get(field, sel);
if (o == null || !(o instanceof Match)) {
return null;
}
Match val = (Match) o;
if (val.type == Match.INVALID) {
return null;
}
return val;
}
//
private MenuItem sendEmail = new MenuItem(res, URLLIST_MENU_SEND_EMAIL, 2, 10) {
public void run() {
try {
if (!BBSSHApp.inst().requestPermission(ApplicationPermissions.PERMISSION_EMAIL,
BBSSHResource.MSG_PERMISSIONS_MISSING_EMAIL_SCRAPER))
return;
Match m = getCurrentSelection();
if (m == null) {
return;
}
Message msg = new Message();
Address a = new Address(m.value, m.value);
Address[] addresses = { a };
msg.addRecipients(net.rim.blackberry.api.mail.Message.RecipientType.TO, addresses);
Invoke.invokeApplication(Invoke.APP_TYPE_MESSAGES,
new MessageArguments(msg));
} catch (AddressException ex) {
Status.show(ex.getMessage());
} catch (MessagingException ex) {
Status.show(ex.getMessage());
} finally {
close();
}
}
};
private MenuItem copyToClipboard = new MenuItem(res, URLLIST_MENU_COPY, 1, 10) {
public void run() {
Match m = getCurrentSelection();
if (m == null) {
return;
}
Clipboard.getClipboard().put(m.toString());
close();
}
};
private MenuItem openBrowser = new MenuItem(res, URLLIST_MENU_OPEN_BROWSER, 3, 10) {
public void run() {
Match m = getCurrentSelection();
if (m == null) {
return;
}
// The permission required here is the app interaction perm - and we can't even run without it,
// so no need to check it.
Browser.getDefaultSession().displayPage(m.value);
close();
}
};
private MenuItem dialNumber = new MenuItem(res, URLLIST_MENU_DIAL_NUMBER, 4, 10) {
public void run() {
Match m = getCurrentSelection();
if (m == null) {
return;
}
if (!BBSSHApp.inst().requestPermission(ApplicationPermissions.PERMISSION_PHONE,
BBSSHResource.MSG_PERMISSIONS_MISSING_PHONE_SCRAPER))
return;
PhoneArguments call = new PhoneArguments(PhoneArguments.ARG_CALL, m.value);
Invoke.invokeApplication(Invoke.APP_TYPE_PHONE, call);
close();
}
};
private MenuItem addContact = new MenuItem(res, URLLIST_MENU_ADD_CONTACT, 5, 10) {
public void run() {
Match m = getCurrentSelection();
if (m == null) {
return;
}
if (!BBSSHApp.inst().requestPermission(ApplicationPermissions.PERMISSION_PIM,
BBSSHResource.MSG_PERMISSIONS_MISSING_PIM_SCRAPER))
return;
ContactList contacts = null;
try {
contacts = (ContactList) PIM.getInstance().openPIMList(PIM.CONTACT_LIST,
PIM.READ_WRITE);
Contact c = (Contact) contacts.createContact();
if (m.type == Match.EMAIL) {
c.addString(Contact.EMAIL, 0, m.value);
} else if (m.type == Match.PHONE) {
c.addString(Contact.TEL, Contact.ATTR_WORK, m.value);
}
AddressBookArguments arg = new AddressBookArguments(AddressBookArguments.ARG_NEW, c);
Invoke.invokeApplication(Invoke.APP_TYPE_ADDRESSBOOK, arg);
} catch (PIMException e) {
Status.show(e.getMessage());
} finally {
close();
}
}
};
}