/*
* Copyright © 2008, 2010, Oracle and/or its affiliates. All rights reserved
*/
package com.sun.me.web.sample.local;
import com.sun.lwuit.Button;
import com.sun.lwuit.ComboBox;
import com.sun.lwuit.Command;
import com.sun.lwuit.Component;
import com.sun.lwuit.Container;
import com.sun.lwuit.Dialog;
import com.sun.lwuit.events.DataChangedListener;
import com.sun.lwuit.events.SelectionListener;
import java.io.IOException;
import com.sun.lwuit.Display;
import com.sun.lwuit.Font;
import com.sun.lwuit.Form;
import com.sun.lwuit.Graphics;
import com.sun.lwuit.Image;
import com.sun.lwuit.Label;
import com.sun.lwuit.List;
import com.sun.lwuit.TextField;
import com.sun.lwuit.animations.CommonTransitions;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.io.ConnectionRequest;
import com.sun.lwuit.io.NetworkManager;
import com.sun.lwuit.io.services.ImageDownloadService;
import com.sun.lwuit.io.ui.SliderBridge;
import com.sun.lwuit.io.util.JSONParser;
import com.sun.lwuit.layouts.BorderLayout;
import com.sun.lwuit.layouts.BoxLayout;
import com.sun.lwuit.layouts.FlowLayout;
import com.sun.lwuit.list.DefaultListCellRenderer;
import com.sun.lwuit.list.ListModel;
import com.sun.lwuit.plaf.Border;
import com.sun.lwuit.plaf.Style;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
/**
* Implements the local business search UI of the makeover demo
*
* @author Shai Almog
*/
public class LocalApp {
/* Demo mode is activiated if network connection fails */
private boolean demoMode = false;
private static final int DEFAULT_MAP_ZOOM = 6;
private int zoom = DEFAULT_MAP_ZOOM;
/* TODO: Get own APP ID for Yahoo Local and Maps APIs */
private static final String APPID = "VS2gtQrV34ElS4obpTabGJ0lxYxDjwPzrjgaj_xTo.VbdnpA24586Jul4oDCXpO3UVN7";
private static final String LOCAL_BASE = "http://local.yahooapis.com/LocalSearchService/V2/localSearch";
private static final String MAP_BASE = "http://api.local.yahoo.com/MapsService/V1/mapImage";
static final Object LOADING_MARKER = new Object();
private boolean started;
private Command exitCommand = new Command("Exit") {
public void actionPerformed(ActionEvent ev) {
Display.getInstance().exitApplication();
}
};
private Command defaultThemeCommand = new Command("Default Theme") {
public void actionPerformed(ActionEvent ev) {
setTheme("/theme.res");
}
};
private Command javaThemeCommand = new Command("LWUIT Theme") {
public void actionPerformed(ActionEvent ev) {
setTheme("/LWUITtheme.res");
}
};
private void setTheme(String name) {
try {
com.sun.lwuit.util.Resources res = com.sun.lwuit.util.Resources.open(name);
com.sun.lwuit.plaf.UIManager.getInstance().setThemeProps(res.getTheme(res.getThemeResourceNames()[0]));
Display.getInstance().getCurrent().refreshTheme();
} catch(java.io.IOException err) {
err.printStackTrace();
}
}
private static Image getImage(String name) {
try {
return Image.createImage(name);
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
public void startApp() {
// distinguish between start and resume from pause
if(!started) {
started = true;
// show your LWUIT form here e.g.: new MyForm().show();
// this is a good place to set your default theme using
// the UIManager class e.g.:
try {
com.sun.lwuit.util.Resources res = com.sun.lwuit.util.Resources.open("/theme.res");
com.sun.lwuit.plaf.UIManager.getInstance().setThemeProps(res.getTheme(res.getThemeResourceNames()[0]));
} catch(java.io.IOException err) {
err.printStackTrace();
}
showMainForm();
}
}
private void showMainForm() {
Form mainForm = createForm("Local Search");
mainForm.setTransitionInAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, false, 400));
mainForm.setTransitionOutAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 400));
mainForm.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
mainForm.addComponent(new Label("search for:"));
final TextField searchFor = new TextField("coffee", 50);
mainForm.addComponent(searchFor);
mainForm.addComponent(new Label("location:"));
final TextField location = new TextField("95054", 50);
mainForm.addComponent(location);
mainForm.addComponent(new Label("street:"));
final TextField street = new TextField(50);
mainForm.addComponent(street);
mainForm.addComponent(new Label("sort results by:"));
final ComboBox sortResults = new ComboBox(new String[] {"Distance", "Title", "Rating", "Relevance"});
mainForm.addComponent(sortResults);
mainForm.addCommand(exitCommand);
mainForm.addCommand(defaultThemeCommand);
mainForm.addCommand(javaThemeCommand);
Command searchCommand = new Command("Search") {
public void actionPerformed(ActionEvent ev) {
showSearchResultForm(searchFor.getText(), location.getText(), street.getText(), (String) sortResults.getSelectedItem());
}
};
Button searchButton = new Button(searchCommand);
Container searchContainer = new Container(new BorderLayout());
searchContainer.addComponent(BorderLayout.EAST, searchButton);
mainForm.addComponent(searchContainer);
mainForm.addCommand(searchCommand);
mainForm.show();
}
private void exception(Exception ex) {
ex.printStackTrace();
Dialog.show("Error", "Error connecting to search service - Turning on DEMO MODE", "OK", null);
demoMode = true;
showMainForm();
}
private void showSearchResultForm(String searchFor, String location, String street, String sortOrder) {
final Form resultForm = createForm("result list");
resultForm.setScrollable(false);
resultForm.setLayout(new BorderLayout());
InfiniteProgressIndicator tempIndicator = null;
try {
tempIndicator = new InfiniteProgressIndicator(Image.createImage("/wait-circle.png"));
} catch (IOException ex) {
tempIndicator = null;
ex.printStackTrace();
}
final InfiniteProgressIndicator indicator = tempIndicator;
final List resultList = new List(new LocalResultModel(searchFor, location, sortOrder, street)) {
public boolean animate() {
boolean val = super.animate();
// return true of animate only if there is data loading, this saves battery and CPU
if(indicator.animate()) {
int index = getSelectedIndex();
index = Math.max(0, index - 4);
ListModel model = getModel();
int dest = Math.min(index + 4, model.getSize());
for(int iter = index ; iter < dest ; iter++) {
if(model.getItemAt(index) == LOADING_MARKER) {
return true;
}
}
}
return val;
}
};
Links pro = new Links();
pro.title = "prototype";
pro.tel = "9999999999";
pro.distance = "9999999";
pro.address = "Long address string";
pro.rating = "5";
resultList.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
showMap(resultForm, resultList.getSelectedItem());
}
});
resultList.setRenderingPrototype(pro);
resultList.setFixedSelection(List.FIXED_NONE_CYCLIC);
resultList.getStyle().setBorder(null);
resultList.setRenderer(new DefaultListCellRenderer(false) {
private Label focus;
private Container selected;
private Label firstLine;
private Label secondLine;
private boolean loading;
{
selected = new Container(new BoxLayout(BoxLayout.Y_AXIS));
firstLine = new Label("First Line");
secondLine = new Label("Second Line");
int iconWidth = 20;
firstLine.getStyle().setMargin(LEFT, iconWidth);
secondLine.getStyle().setMargin(LEFT, iconWidth);
selected.addComponent(firstLine);
selected.addComponent(secondLine);
}
public Component getListCellRendererComponent(List list, Object value, int index, boolean isSelected) {
if(value == null || value == LOADING_MARKER) {
loading = true;
if(isSelected) {
firstLine.setText("Loading...");
secondLine.setText("Loading...");
return selected;
}
return indicator;
}
loading = false;
if(isSelected) {
int listSelectionColor = list.getStyle().getFgColor();
firstLine.getStyle().setFgColor(listSelectionColor);
secondLine.getStyle().setFgColor(listSelectionColor);
firstLine.getStyle().setBgTransparency(0);
secondLine.getStyle().setBgTransparency(0);
Links l = (Links)value;
firstLine.setText(l.address + " " + l.tel);
secondLine.setText(l.distance + " miles " + ("".equals(l.rating) ? "" : ", " + l.rating + "*"));
return selected;
}
super.getListCellRendererComponent(list, ((Links)value).title, index, isSelected);
return this;
}
public void paint(Graphics g) {
if(loading) {
indicator.setX(getX());
indicator.setY(getY());
indicator.setWidth(getWidth());
indicator.setHeight(getHeight());
indicator.paint(g);
} else {
super.paint(g);
}
}
public Component getListFocusComponent(List list) {
if(focus == null) {
try {
focus = new Label(Image.createImage("/svgSelectionMarker.png"));
focus.getStyle().setBgTransparency(0);
} catch (IOException ex1) {
ex1.printStackTrace();
}
}
return focus;
}
});
resultForm.addComponent(BorderLayout.CENTER, resultList);
resultForm.addCommand(new Command("Map") {
public void actionPerformed(ActionEvent ev) {
showMap(resultForm, resultList.getSelectedItem());
}
});
resultForm.addCommand(new Command("Details") {
public void actionPerformed(ActionEvent ev) {
showDetails(resultForm, resultList.getSelectedItem());
}
});
Command backCommand = new Command("Back") {
public void actionPerformed(ActionEvent ev) {
showMainForm();
}
};
resultForm.setBackCommand(backCommand);
resultForm.addCommand(backCommand);
resultForm.addCommand(exitCommand);
resultForm.show();
}
private Label createSubLabel(String text, int fgColor, Font f) {
Label l = new Label(text);
Style s = l.getStyle();
s.setFgColor(fgColor);
s.setFont(f);
return l;
}
private void showDetails(final Form resultForm, final Object selectedItem) {
if(selectedItem != null && selectedItem instanceof Links) {
Links l = (Links)selectedItem;
int fgColor = resultForm.getStyle().getFgColor();
Font standardFont = resultForm.getStyle().getFont();
Form details = createForm(l.title);
details.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
details.addComponent(createSubLabel("address", fgColor, standardFont));
details.addComponent(new Label(l.address));
details.addComponent(createSubLabel("distance", fgColor, standardFont));
details.addComponent(new Label(l.distance + " mi."));
details.addComponent(createSubLabel("average rating", fgColor, standardFont));
details.addComponent(new Label(l.rating + "*"));
details.addComponent(createSubLabel("telephone", fgColor, standardFont));
details.addComponent(new Label(l.tel));
details.addCommand(exitCommand);
Command backCommand = new Command("Back") {
public void actionPerformed(ActionEvent ev) {
resultForm.show();
}
};
details.setBackCommand(backCommand);
details.addCommand(backCommand);
details.setTransitionInAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, false, 400));
details.setTransitionOutAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 400));
details.show();
}
}
private void showMap(final Form resultForm, final Object selectedItem) {
// might still be downloading the entry...
if(selectedItem instanceof Links) {
final Links link = (Links)selectedItem;
if (link == null) {
return;
}
final Label mapLabel = new Label();
final ImageDownloadService img = new ImageDownloadService("", mapLabel);
final Dialog progress = new Dialog();
ConnectionRequest con = new ConnectionRequest() {
protected void readResponse(InputStream input) throws IOException {
JSONParser p = new JSONParser();
Hashtable results = (Hashtable)p.parse(new InputStreamReader(input)).get("ResultSet");
String url = (String)results.get("Result");
img.setUrl(url);
img.addRequestHeader("Accept", "image/png");
img.setDisposeOnCompletion(progress);
NetworkManager.getInstance().addToQueue(img);
}
};
con.setUrl(MAP_BASE);
con.setPriority(ConnectionRequest.PRIORITY_HIGH);
con.setPost(false);
con.addArgument("output", "json");
con.addArgument("appid", APPID);
con.addArgument("latitude", link.latitude);
con.addArgument("longitude", link.longitude);
con.addArgument("image_height", Integer.toString((int)(Display.getInstance().getDisplayHeight() * 1.5)));
con.addArgument("image_width", Integer.toString((int)(Display.getInstance().getDisplayWidth() * 1.5)));
con.addArgument("zoom", Integer.toString(zoom));
progress.getDialogStyle().setBorder(Border.createRoundBorder(6, 6, 0xe3ef5a));
progress.setTransitionInAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 400));
progress.setTransitionOutAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, false, 400));
((FlowLayout)progress.getLayout()).setValign(Component.CENTER);
progress.addComponent(new Label("Please Wait"));
SliderBridge b = new SliderBridge(new ConnectionRequest[]{ con, img });
progress.addComponent(b);
NetworkManager.getInstance().addToQueue(con);
progress.showPacked(BorderLayout.CENTER, true);
final Form map = createForm("Map");
map.setScrollableX(true);
map.setScrollableY(true);
map.setTransitionInAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, false, 400));
map.setTransitionOutAnimator(CommonTransitions.createFastSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 400));
map.setLayout(new BorderLayout());
map.addComponent(BorderLayout.CENTER, mapLabel);
if(zoom < 9) {
map.addCommand(new Command("Zoom Out") {
public void actionPerformed(ActionEvent ev) {
zoom++;
map.setTransitionOutAnimator(null);
showMap(resultForm, selectedItem);
}
});
}
if(zoom > 1) {
map.addCommand(new Command("Zoom In") {
public void actionPerformed(ActionEvent ev) {
zoom--;
map.setTransitionOutAnimator(null);
showMap(resultForm, selectedItem);
}
});
}
Command backCommand = new Command("Back") {
public void actionPerformed(ActionEvent ev) {
resultForm.show();
}
};
map.addCommand(backCommand);
map.setBackCommand(backCommand);
map.addCommand(exitCommand);
map.show();
} else {
Dialog.show("Please Wait", "Please wait for download to complete", "OK", null);
}
}
protected void pauseApp() {
}
protected void destroyApp(boolean arg0) {
}
private static class Links {
String title;
String business;
String listing;
String map;
String tel;
String latitude;
String longitude;
String rating;
String address;
String distance;
};
private Form createForm(String title) {
Form f = new Form(title);
f.getTitleComponent().setAlignment(Component.LEFT);
f.setMenuCellRenderer(new DefaultListCellRenderer(false));
return f;
}
/**
* A list model that lazily fetches a result over the web if its unavailable
*/
class LocalResultModel implements ListModel {
private Vector cache;
private boolean fetching;
private Vector fetchQueue = new Vector();
private Vector dataListeners = new Vector();
private Vector selectionListeners = new Vector();
private int selectedIndex = 0;
private boolean firstTime = true;
private String searchFor;
private String location;
private String sortOrder;
private String street;
public LocalResultModel(String searchFor, String location, String sortOrder, String street) {
cache = new Vector();
cache.setSize(1);
this.searchFor = searchFor;
this.location = location;
this.sortOrder = sortOrder.toLowerCase();
this.street = street;
}
private void fetch(final int startOffset) {
int count = Math.min(cache.size(), startOffset + 9);
for(int iter = startOffset - 1 ; iter < count ; iter++) {
if(cache.elementAt(iter) == null) {
cache.setElementAt(LOADING_MARKER, iter);
}
}
NetworkManager.getInstance().addToQueue(fetchEntry(startOffset));
}
private ConnectionRequest fetchEntry(final int startOffset) {
ConnectionRequest req = new ConnectionRequest() {
protected void readResponse(InputStream input) throws IOException {
JSONParser p = new JSONParser();
Hashtable result = p.parse(new InputStreamReader(input));
result = (Hashtable)result.get("ResultSet");
if(startOffset == 1) {
// Yahoo now blocks anything over 250 results!
cache.setSize(Math.min(250, ((Double)result.get("totalResultsAvailable")).intValue()));
}
Vector entries = (Vector)result.get("Result");
if(cache.size() < entries.size() + startOffset) {
cache.setSize(entries.size() + startOffset);
}
for(int iter = 0 ; iter < entries.size() ; iter++) {
result = (Hashtable)entries.elementAt(iter);
Links currentLink = new Links();
currentLink.title = (String)result.get("Title");
currentLink.address = (String)result.get("Address");
currentLink.map = (String)result.get("MapUrl");
currentLink.listing = (String)result.get("ClickUrl");
currentLink.business = (String)result.get("BusinessClickUrl");
currentLink.tel = (String)result.get("Phone");
currentLink.latitude = (String)result.get("Latitude");
currentLink.longitude = (String)result.get("Longitude");
currentLink.rating = (String)result.get("AverageRating");
currentLink.distance = (String)result.get("Distance");
cache.setElementAt(currentLink, startOffset + iter - 1);
fireDataChangedEvent(DataChangedListener.CHANGED, startOffset + iter - 1);
}
}
};
req.setPost(false);
req.setUrl(LOCAL_BASE);
req.addArgument("output", "json");
req.addArgument("appid", APPID);
req.addArgument("query", searchFor);
req.addArgument("location", location);
req.addArgument("sort", sortOrder.toLowerCase());
if(street.length() > 0) {
req.addArgument("street", street);
}
req.addArgument("start", Integer.toString(startOffset));
return req;
}
public Object getItemAt(int index) {
Object val = cache.elementAt(index);
if(val == null) {
fetch(index + 1);
return LOADING_MARKER;
}
return val;
}
public int getSize() {
return cache.size();
}
public void setSelectedIndex(int index) {
int oldIndex = selectedIndex;
this.selectedIndex = index;
fireSelectionEvent(oldIndex, selectedIndex);
}
public void addDataChangedListener(DataChangedListener l) {
dataListeners.addElement(l);
}
public void removeDataChangedListener(DataChangedListener l) {
dataListeners.removeElement(l);
}
private void fireDataChangedEvent(final int status, final int index){
if(!Display.getInstance().isEdt()) {
Display.getInstance().callSeriallyAndWait(new Runnable() {
public void run() {
fireDataChangedEvent(status, index);
}
});
return;
}
// we query size with every iteration and avoid an Enumeration since a data
// changed event can remove a listener instance thus break the enum...
for(int iter = 0 ; iter < dataListeners.size() ; iter++) {
DataChangedListener l = (DataChangedListener)dataListeners.elementAt(iter);
l.dataChanged(status, index);
}
}
public void addSelectionListener(SelectionListener l) {
selectionListeners.addElement(l);
}
public void removeSelectionListener(SelectionListener l) {
selectionListeners.removeElement(l);
}
private void fireSelectionEvent(int oldIndex, int newIndex){
Enumeration listenersEnum = selectionListeners.elements();
while(listenersEnum.hasMoreElements()){
SelectionListener l = (SelectionListener)listenersEnum.nextElement();
l.selectionChanged(oldIndex, newIndex);
}
}
public void addItem(Object item) {
}
public void removeItem(int index) {
}
public int getSelectedIndex() {
return selectedIndex;
}
}
}