Wednesday, February 3, 2010

how to use JExcel with spring 3 on google app engine

 

First, i tried org.apache.poi. It works in local but throw exception when i upload it to google app engine. Then i have to to JExcel, it works well till now.

step 1: setup your spring environment for your google app engine, please refer my previous article for this.

step 2: download jexcelapi, what i used is jexcelapi_2_6_12. Then copy jxl.jar into war/WEB-INF/lib/ and add it to build path.

step 3: configure web.xml to add spring configuration as below

    <servlet>
<servlet-name>web-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>web-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>



step 4: create web-dispatcher-servlet.xml which is required by spring looks like below:



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>

<context:component-scan base-package="com.zpylxapp.controller.html" />

<bean id="viewResolver"
class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="views" />
</bean>
</beans>



step 5: create views.properties file under your “src” directory and add following configuration



xl.(class)=com.zpylxapp.excel.HomePage



step 6: create com.zpylxapp.excel.HomePage view class



package com.zpylxapp.excel;

import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jxl.*;
import jxl.write.Label;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import org.springframework.web.servlet.view.document.AbstractJExcelView;
public class HomePage extends AbstractJExcelView {

protected void buildExcelDocument(Map model, WritableWorkbook wb,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
WritableSheet sheet = wb.createSheet("Spring", 0);
sheet.addCell(new Label(0, 0, "Spring-Excel jexcel"));
List words = (List) model.get("wordList");
for (int i = 0; i < words.size(); i++) {
sheet.addCell(new Label(2 + i, 0, (String) words.get(i)));
}
}
}

step 7: create your controller use “return new ModelAndView("xl", map);” as return

@Controller
@RequestMapping("/inventory")
public class InventoryController {

@RequestMapping(value = "/{item}", method = RequestMethod.GET)
public ModelAndView getxls(Inventory item) {

Map map = new HashMap();
List wordList = new ArrayList();
wordList.add("hello");
wordList.add("world");
map.put("wordList", wordList);
return new ModelAndView("xl", map);
}
}



step 8: done! try it in your google app engine.

Monday, February 1, 2010

Iphone NSDate and NSDateComponents

 

http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendars.html#//apple_ref/doc/uid/TP40003470-SW1

1. use components:fromDate:toDate:options: to determine the temporal difference between two dates in units other than seconds (which you could calculate with the NSDate method timeIntervalSinceDate

NSDate *startDate = ...; 
NSDate *endDate = ...;

NSCalendar *gregorian = [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar];

NSUInteger unitFlags = NSMonthCalendarUnit | NSDayCalendarUnit;

NSDateComponents *components = [gregorian components:unitFlags
fromDate:startDate
toDate:endDate options:0];
NSInteger months = [components month];
NSInteger days = [components day];




2. use the dateByAddingComponents:toDate:options: method to add components of a date (such as hours or months) to an existing date



NSDate *today = [[NSDate alloc] init]; 
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setHour:1];
[offsetComponents setMinutes:30];
// Calculate when, according to Tom Lehrer, World War III will end
NSDate *endOfWorldWar3 = [gregorian dateByAddingComponents:comps toDate:today options:0];

Thursday, January 14, 2010

Goolge app engine +JPA+ java + spring + REST + JSON + Iphone

 

I have been inching to try out google app engine and its capabilities with "app engine + java + BlazeDS + Flex", it works well on my personal website. But "remote object data accessing" is not suitable for client platform like Iphone and Android. I happend find a great topic talking about "app engine + java + spring + REST + JSON", it does help me with my Planned Iphone preject.

The end product will be spring mvc app running in google app engine handling GET, POST, PUT and DELETE http requests with JSON objects in and out utilizing JDO and seesions. And you can access it from you Iphone App.
So here you have it. Step by step.

Part One: Once you are finished with this article, you will be able to implement REST services in Google Apps Engine using JAVA, Spring 3.0 and JSON.

  1. Eclipse with google App engine. I am using Eclipse, which is my favorite IDE. There is a lot of article about how to install Google App Engine on eclipse, and google also have great help doc here: http://code.google.com/eclipse/docs/download.html. I believe you can get it done easily and finally you will got something like:

  2. Create a new google Web Application Project: File -> new -> Web Application Project (google), and named it like "gae-json-iphone"
  3. Add Spring Support.

    Go Download the latest version of Spring 3 from http://www.springsource.org/download. For this tutorial we have downloaded spring-framework-3.0.0.RELEASE-with-docs.zip, which contains the documentation also. unzip it into a folder where you keep your java libs. Copy the following Jars into war\WEB-INF\llib directory:
    1.   org.springframework.asm-3.0.0.RELEASE.jar
    2.   org.springframework.beans-3.0.0.RELEASE.jar
    3.   org.springframework.context-3.0.0.RELEASE.jar
    4.   org.springframework.core-3.0.0.RELEASE.jar
    5.   org.springframework.expression-3.0.0.RELEASE.jar
    6.   org.springframework.oxm-3.0.0.RELEASE.jar
    7.   org.springframework.web-3.0.0.RELEASE.jar
    8.   org.springframework.web.servlet-3.0.0.RELEASE.jar :
    Also grab your favorite version of log4j.jar and commons-logging-1.1.1.jar. Remember to rename commons-logging-1.1.1.jar to something like commons-logging-1.1.jar, google app engine replaces this jar with its own version with crippled packages, by providing different name we keep both versions and make spring happy. see more why . Once you copied the jars, open project preferences and add those jars to the build library path:

  4. configuring spring.
    The final version should look like this:
    <?xml version="1.0" encoding="utf-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

    version="2.5">
    <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/log4j.properties</param-value>
    </context-param>
    <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

    <servlet>
    <servlet-name>rest-json</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
    <servlet-name>rest-json</servlet-name>
    <url-pattern>/api/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    </web-app>



  5. Add JSON support. -- Jackson JSON.Grab the library from here and put jackson-core-1.4.jar and jackson-mapper-1.4.jar file into projects WEB-INF/lib directory


  6. Configure Spring

    To configure Spring servlet we need to create servletname-servlet.xml file for spring bean configuration. Our file has to be named rest-json-servlet.xml.



    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd"
    >

    <!--
    tell String to scan “com.zpylxapp.iphone” package for any bean
    annotations
    -->
    <context:component-scan base-package="com.zpylxapp.iphone" />

    <!--
    define message converter to convert messages sent to the server into
    beans using MappingJacksonHttpMessageConverter
    -->
    <bean
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
    <list>
    <ref bean="jsonHttpMessageConverter" />
    </list>
    </property>
    </bean>

    <bean id="jsonHttpMessageConverter"
    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />

    <!--
    tells spring to render messages sent from the server to the client
    using JsonView
    -->
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="mediaTypes">
    <map>
    <entry key="json" value="application/json" />
    </map>
    </property>
    <property name="defaultContentType" value="application/json" />
    <property name="defaultViews">
    <list>
    <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
    </list>
    </property>
    </bean>

    </beans>




Part Two: Using JPA with App Engine. GAE support both JDO and JPA. I use JPA here. see detail of GAE JPA here.




  1. Creating the persistence.xml File. The JPA interface needs a configuration file named persistence.xml . You can create this file in /src/META-INF/persistence.xml :

    <?xml version="1.0" encoding="UTF-8" ?> 
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">

    <persistence-unit name="transactions-optional">
    <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
    <properties>
    <property name="datanucleus.NontransactionalRead" value="true"/>
    <property name="datanucleus.NontransactionalWrite" value="true"/>
    <property name="datanucleus.ConnectionURL" value="appengine"/>
    </properties>
    </persistence-unit>

    </persistence>



  2. Getting an EntityManager Instance.

    An app interacts with JPA using an instance of the EntityManager class. You get this instance by instantiating and calling a method on an instance of the EntityManagerFactory class. The factory uses the JPA configuration (identified by the name "transactions-optional") to create EntityManager instances.



    Because an EntityManagerFactory instance takes time to initialize, it's a good idea to reuse a single instance as much as possible. An easy way to do this is to create a singleton wrapper class with a static instance, as follows:



    EMF.java



    import javax.persistence.EntityManagerFactory; 
    import javax.persistence.Persistence;

    public final class EMF {
    private static final EntityManagerFactory emfInstance =
    Persistence.createEntityManagerFactory("transactions-optional");

    private EMF() {}

    public static EntityManagerFactory get() {
    return emfInstance;
    }
    }



  3. User bean to support permissions.
    package com.zpylxapp.iphone;

    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;

    @Entity
    public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String email;
    private String name;
    private String password;


    public long getId() {
    return id;
    }
    public void setId(long id) {
    this.id = id;
    }
    public String getEmail() {
    return email;
    }
    public void setEmail(String email) {
    this.email = email;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public void setPassword(String password) {
    this.password = password;
    }
    public String getPassword() {
    return password;
    }
    }



  4. Implement Login Controller. (for POST request “/login”)
    package com.zpylxapp.iphone;

    import java.io.IOException;
    import java.util.List;

    import javax.persistence.EntityManager;
    import javax.servlet.http.HttpServletRequest;

    import org.apache.log4j.Logger;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;

    import com.zpylxapp.jpa.*;

    @Controller
    @RequestMapping("/login")
    public class LoginController {

    private static Logger logger = Logger.getLogger( LoginController.class );

    @SuppressWarnings("unchecked")
    @ModelAttribute("user")
    @RequestMapping(value = "/", method = RequestMethod.POST)
    public User login( @RequestBody User user, HttpServletRequest request ) throws IOException {
    logger.debug(user);

    // Hardcoded for demo website
    if( user.getName().equalsIgnoreCase("user1") && user.getPassword().equals("password1") ) {
    user.setId(1001);
    user.setEmail("fake@email.com");
    //set user session to check if user login or not.Even it is RESTful, we still need "Session"
    request.getSession(true).setAttribute( Constants.SESSION_USER , user);
    return user;
    }

    EntityManager em = EMF.get().createEntityManager();
    List<User> users;
    try {
    javax.persistence.Query query = em.createQuery("select p from User p ");
    users = (List<User>)query.getResultList();
    if(users.size()>0)
    return users.get(0);
    } finally {
    em.close();
    }
    return null;
    }
    }



  5. Add a preHandle interceptor, to check only if the user is present in the session. If no user is present and user is not trying to log in, send back an error. lets code our interceptor. I named it SessionInerceptor:
    package com.zpylxapp.iphone;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

    public class SessionInterceptor extends HandlerInterceptorAdapter {
    public boolean preHandle(HttpServletRequest request,
    HttpServletResponse response, Object handler) throws Exception {
    User user = (User) request.getSession().getAttribute(
    Constants.SESSION_USER);
    String uri = request.getRequestURI();
    String base = request.getContextPath();
    uri = uri.substring(base.length());
    if (user == null && !uri.startsWith("/api/login")) {
    request.getSession().invalidate();
    response.setContentType("application/json");
    response.getWriter().write("{\"error\":\"SESSION-EXPIRED\"}");
    return false;
    }
    return true;
    }

    }



    Now lets add this interceptor to the Spring MVC stack by adding these 5 lines to our rest-json-flex-servlet.xml file.



    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
    <bean class="com.lureto.rjf.SessionInterceptor"/>
    </property>
    </bean>



    One more thing – we need to enable session in google app engine. Add this line to your appengine-web.xml.



    <sessions-enabled>true</sessions-enabled>



    sessions are backed up by the datastore, you should be able to see the session data using the Data Viewer in the Admin Console.



    work on java part is already done. Let us move to Iphone client.





Part Three: Using JPA with App Engine.




  1. To use JSON in Iphone, you need third part library like json-framework. Download JSON_2.2.2.dmg and open it, then drag the JSON directory into your project.

    Picture_7 




  2. Create a view based iphone project, add button and textfield as following:

     Picture_8



     




  3. Handler to the “login” button is as below. which will get user/password from user input and encapsulate them in a JSON post request. If the user is invalid, give an alert window. If it is valid, the response is a NSDictionary type. It will be display in another TableView based page.
    -(IBAction)sendLogin:(id)sender{

    //prepare json data
    NSString *username = tfUsername.text;
    NSString *password = tfPassword.text;

    NSDictionary *data = [[NSDictionary alloc] initWithObjectsAndKeys:
    username, USERNAMEKEY,
    password, PASSWORDKEY,
    nil];
    SBJsonWriter *json = [SBJsonWriter alloc];
    NSString *jsonString = [json stringWithObject:data];
    NSLog(jsonString);

    [data release];
    [json release];

    //create/send http post request
    NSData *postData = [jsonString dataUsingEncoding:NSASCIIStringEncoding];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:@"http://zpylxrealtors.appspot.com/api/login/"]];
    [request setHTTPMethod:@"POST"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:postData];

    NSURLResponse *urlResponse;
    NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:nil];
    [request release];
    NSString *jsonData = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    NSLog(jsonData);

    //parse response json data
    // Create a dictionary from the JSON string
    @try {
    NSDictionary *response = [jsonData JSONValue];
    id user = [response objectForKey:@"user"];
    if(user != nil && [user isKindOfClass:[NSDictionary class]]){
    //go to next view
    UserListViewController *nextview = [[UserListViewController alloc] initWithStyle:UITableViewStyleGrouped];
    nextview.user = user;
    [self presentModalViewController:nextview animated:YES];
    [nextview release];
    }
    else{
    //alert
    UIAlertView *myAlert = [[UIAlertView alloc] initWithTitle:@"Error"
    message:@"invalid user/password!" delegate:nil
    cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
    [myAlert show];
    [myAlert release];

    }
    }
    @catch (NSException * e) {
    //nothing to do here, just provent to crash when google location search does not work
    NSLog(@"NSException: ....");
    }
    @finally {
    [jsonData release];
    }
    }



  4. delegation code for the table view:
    @interface UserListViewController : UITableViewController {
    NSDictionary *user;
    }
    @property(nonatomic, retain) NSDictionary *user;


    ……………

    #pragma mark Table view methods


    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
    }



    // Customize the number of rows in the table view.
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 3;
    }



    // Customize the appearance of table view cells.
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:CellIdentifier] autorelease];
    }

    // Set up the cell...
    if(indexPath.row == 0){
    cell.textLabel.text = @"name";
    cell.detailTextLabel.text = [user objectForKey:@"name"];
    }
    else if(indexPath.row == 1){
    cell.textLabel.text = @"id";
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", [user objectForKey:@"id"]];
    }
    else{
    cell.textLabel.text = @"email";
    cell.detailTextLabel.text = [user objectForKey:@"email"];
    }
    return cell;
    }



  5. result page:

    Picture_9 Picture_10



     



Sunday, January 10, 2010

using xmpp in google app engine

 

 

To enable the XMPP service for a Java app, edit the appengine-web.xml file and include the following lines:

  <inbound-services>      <service>xmpp_message</service>    </inbound-services>


With the XMPP service enabled, when App Engine receives a chat message for the app, it makes an HTTP POST request to the following URL path:



/_ah/xmpp/message/chat/



To handle incoming messages, you simply create a request handler that accepts POST requests at this URL path.



package com.zpylxapp.xmpp;

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.xmpp.JID;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.MessageBuilder;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

@SuppressWarnings("serial")
public class XMPPReceiverServlet extends HttpServlet {

public void doPost(HttpServletRequest req, HttpServletResponse res)
throws IOException {
XMPPService xmpp = XMPPServiceFactory.getXMPPService();
Message message = xmpp.parseMessage(req);

JID fromJid = message.getFromJid();
String body = message.getBody();

System.out.println(body + fromJid);

// Create a response
String response = "echo: " + body;
System.out.println(fromJid.getId() + " <-- JEliza: " + response);
// Send out response
message = new MessageBuilder().withRecipientJids(fromJid).withBody(
response).build();
xmpp.sendMessage(message);
}

}



To map this servlet to the XMPP URL path, put the following section in your web.xml file, inside the <web-app> element:



    <servlet>
<servlet-name>xmppreceiver</servlet-name>
<servlet-class>com.zpylxapp.xmpp.XMPPReceiverServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>xmppreceiver</servlet-name>
<url-pattern>/_ah/xmpp/message/chat/</url-pattern>
</servlet-mapping>

Thursday, December 24, 2009

Android Basics - Dialogs and Floating Activities

original link: http://www.androidcompetencycenter.com/tag/alertdialogbuilder/

As we know android provide Activity class for developing application screens. But many a time application needs to show Dialog boxes or floating screen to do simple tasks likes taking input from user or ask for confirmation etc.

In android Dialogs can be created in following 2 ways:

1. Creating a dialog with the help of android Dialog class or its subclass like AlertDialog.
2. Using dialog theme for the activity.

Using android Dialog class:
Let see how we can create a dialog with the help of Dialog class. To define a dialog the dialog class has to extend the android Dialog class.


class MyDialog extends Dialog {
/**
* @param context
*/
public MyDialog(Context context) {
super(context);
}
}

Define a layout for our dialog. Here is the xml file for of the layout that asks for user’s name.


android:id=”@+id/widget28″
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:orientation=”vertical”
xmlns:android =”http://schemas.android.com/apk/res/android”
>
android:id=”@+id/nameMessage”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”Enter Name:”
>

android:id=”@+id/nameEditText”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:textSize=”18sp”
>

android:id=”@+id/buttonLayout”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_gravity=”center_horizontal”
>
android:id=”@+id/okButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”OK”
>

android:id=”@+id/cancelButton”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”Cancel”
>




Use the above layout for our dialog

class MyDialog extends Dialog {
….

/**
* @see android.app.Dialog#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(”TestApp”, “Dialog created”);
setContentView(R.layout.mydialog);
}
}

Now the dialog can be shown by calling the show method like this

…
MyDialog dialog = new MyDialog(context);
dialog.show();
…

Event handling for the Dialog controls are same as in case of the activity. Modify the dialog code to handle the onclick event on the ok and cancel button.

class MyDialog extends Dialog implements OnClickListener {

private Button okButton;

private Button cancelButton;

private EditText nameEditText;

protected void onCreate(Bundle savedInstanceState) {
okButton = (Button) findViewById(R.id.okButton);
cancelButton = (Button) findViewById(R.id.cancelButton);

nameEditText = (EditText) findViewById(R.id.nameEditText);

okButton.setOnClickListener(this);
cancelButton.setOnClickListener(this);
}

public void onClick(View view) {
switch (view.getId()) {
case R.id.okButton:
dismiss();
break;
case R.id.cancelButton:
cancel();
break;
}
}
}

To close the dialog the dismiss() method can be called. The dialog can call the dismiss method itself or some other code can also close the dialog by calling dismiss().

The dialog also supports cancel. Canceling means the dialog action is canceled and does not need to perform any operation. Dialog can be canceled by calling cancel() method. Canceling the dialog also dismisses the dialog.

When user clicks the phones BACK button the dialog gets canceled. If you do not want to cancel the dialog on BACK button you can set cancelable to false as

setCancelable(false);

Note that the cancel() method call till be able to cancel the dialog (which is the desirable functionality in most cases). The dialog’s cancel and dismiss events can be listened with the help of OnCancelListener and OnDismissListener.

Returning information from dialog:
Now our dialog can take the name from the user. We need to pass that name to the calling activity. Dialog class does not provide any direct method for returning values. But our own Listener can be created as follows:


public interface MyDialogListener {

public void onOkClick(String name); // User name is provided here.

public void onCancelClick();
}

The dialog’s constructor has to be modified to take the object of the listener:

public MyDialog(Context context, MyDialogListener listener) {
super(context);
this.listener = listener;
}

Now the calling activity needs to provide the object of the class that implements the MyDialogListener which will gets called on ok or cancel button clicks.

Now we need update the onclick method to return the user name.

public void onClick(View view) {
switch (view.getId()) {
case R.id.okButton:
listener.onOkClick(nameEditText.getText().toString()); // returning the user’s name.
dismiss();
break;
case R.id.cancelButton:
cancel();
break;
}
}

Using AlertDialog:

AlertDialog is the subclass of the Dialog. It by default provide 3 buttons and text message. The buttons can be made visible as required. Following code creates an AlertDialog that ask user a question and provide Yes, No option.

AlertDialog dialog = new AlertDialog.Builder(context).create();

dialog.setMessage(”Do you play cricket?”);
dialog.setButton(”Yes”, myOnClickListener);
dialog.setButton2(”No”, myOnClickListener);

dialog.show();

The onClick method code for the button listener myOnClickListener will be like this:

public void onClick(DialogInterface dialog, int i) {
switch (i) {
case AlertDialog.BUTTON1:
/* Button1 is clicked. Do something */
break;
case AlertDialog.BUTTON2:
/* Button2 is clicked. Do something */
break;
}
}

AlertDialog.Builder:
AlertDialog has a nested class called ‘Builder’. Builder class provides facility to add multichoice or single choice lists. The class also provides methods to set the appropriate Adaptors for the lists, set event handlers for the list events etc. The Builder button calls the Button1, Button2, Button3 as PositiveButton, NeutralButton, NegativeButton.

Here is an example of dialog box with Multichoice list

new AlertDialog.Builder(context)
.setIcon(R.drawable.icon)
.setTitle(R.string.alert_dialog_multi_choice)
.setMultiChoiceItems(R.array.select_dialog_items,
new boolean[]{false, true, false, true, false},
new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int whichButton, boolean isChecked) {
/* Something on click of the check box */
}
})
.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {

/* User clicked Yes so do some stuff */
}
})
.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {

/* User clicked No so do some stuff */
}
})
.create();

Activity Managed Dialog:

Android also provide facility to create dialogs. Activity created dialog can be managed by Activity methods like showDialog(), onCreateDialog(), onPrepareDialog(), dismissDialog(), removeDialog().
The onCreateDialog create the dialog that needs to be shown, for example

/**
* @see android.app.Activity#onCreateDialog(int)
*/
@Override
protected Dialog onCreateDialog(int id) {
return new AlertDialog.Builder(this).setMessage(”How are you?”).setPositiveButton(”Fine”, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {
/* Do something here */
}
}).setNegativeButton(”Not so good”, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int which) {
/* Do something here */
}
}).create();
}

You can create multiple dialog boxes and differentiate them with the id parameter. The dialog can be shown with the help of showDialog(id) method. The onCreateDialog method gets called for the first time when showDialog method is called. For subsequent calls to the showDialog() the dialog is not created but shown directly.
If you need to update the dialog before it is getting shown then you can do that in onPrepareDialog() method. The methods get called just before the dialog is shown to the user.
To close the dialog you can call dismissDialog() method. Generally you will dismiss the dialogs in the click handles of the dialog buttons.
The removeDialog() method will remove the dialog from the activity management and if showDialog is again called for that dialog, the dialog needs to be created.

Using android Dialog theme for activity:
Another simple way to show the dialog is to make the Activity to work as a Dialog (floating activity). This can be done by applying dialog Theme while defining the activity entry in the AndroidManifest.xml


…


The activity will be shown as a dialog as the activity is using ‘Theme.Dialog’ as theme.

Tuesday, December 22, 2009

iphone: tips collection


iPhone: Make your Default.png

A UIPickerView with labels

http://blog.nottoobadsoftware.com/2009/03/a-uipickerview-with-labels/



how to send SMS/Email from an application

You can do this with a short code by way of a SMS/MMS aggregator like OpenMarket or as another person has posted you can use the SMS Gateway but this requires the knowledge of the end users carrier name. If your replying to a SMS on the iPhone I believe you can use the API which in turn would give you the carrier id of the incoming SMS message you would like to reply to.

Launch SMS with New Message Window Open:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/10998-launch-sms-new-message-window-open.html

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"sms:"]]

"sms" - brings up general Text Messages screen

"sms: " - brings up the New Message screen, but with a little blue circle in the To field that it a little confusing and probably needs to be deleted.

"sms:?" - brings up the New Message screen, but with a question mark in a blue circle in the To field

"sms://" - brings up general Text Messages screen

"sms:&" - brings up the New Message screen, but with a ampersand in a blue circle in the To field

"sms:." - brings up the New Message screen, but with a period in a blue circle in the To field




how to invite friends from facebook conenct in iphone app:



You can find info about using FBC on the iPhone here(http://developers.facebook.com/connect.php?tab=iphone)


there are two modes you can use it in. One where your developer secret is in the phone app, if that makes you nervous you can proxy the authentication through your own server. FB developer site has the info along with programmer supported wikis



Facebook Connect 能做什么? 这个是个老外做的,想像了一些商业场景:如果 Amazon/iTunes/iPhone 这些网站/服务/设备实现了Facebook Connect……




how to debug EXC_BAD_ACCESS on iPhone http://www.codza.com/how-to-debug-exc_bad_access-on-iphone


Instruments on Leopard: How to debug those random crashes in your Cocoa app



google geocoding UK postcode issue:


oogle Maps API provides a geocoding feature, for finding the latitude and longitude of places or addresses; but it does not work for UK postcodes. This is thanks to Royal Mail who have a copyright on the data, and are very restrictive with their (expensive) licenses for it.


solution: http://www.tomanthony.co.uk/blog/geocoding-uk-postcodes-with-google-map-api/

if use restFul ajax local search, use this: http://ajax.googleapis.com/ajax/services/search/local?v=1.0&q=xxx&gl=fr

reference: http://code.google.com/apis/ajaxsearch/documentation/

memory leaks detecting:


un the tools within XCode over menu -> run -> start with performance tool ->......


Introduction to Instruments User Guide http://developer.apple.com/iphone/library/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Introduction/Introduction.html


Finding iPhone Memory Leaks: A “Leaks” Tool Tutorial

http://www.mobileorchard.com/find-iphone-memory-leaks-a-leaks-tool-tutorial/


Instruments on Leopard: How to debug those random crashes in your Cocoa app


how to handle & in xml file:

answer: Put the data in a section and you should get what you want.

<name>name>


How to view contents of NSDictionary variable in Xcode debugger?

In the gdb window you can use po to inspect the object.



The difference between initWithString and stringWithString is that stringWithString returns an auto-released pointer. This means that you don't need to release it specifically, since that will be taken care of next time that the auto-release pool cleans up any auto-released pointers. initWithString, on the other hand, returns a pointer with a retain count of 1 - you do need to call release on that pointer, or else it would result in a memory leak.


code for plucking useful bits out of the addressBook:


- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker

shouldContinueAfterSelectingPerson:(ABRecordRef)person {

customer.firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);

customer.lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);

customer.companyName = (NSString *)ABRecordCopyValue(person, kABPersonOrganizationProperty);

if (!customer.firstName.length) {

customer.firstName = @"*";

}

if (!customer.lastName.length) {

customer.lastName = @"*";

}

if (!customer.companyName.length) {

customer.companyName = @"";

}



CFStringRef email, emailLabel, phone, phoneLabel;

ABMutableMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);

NSMutableDictionary *myPhoneDict = [NSMutableDictionary dictionaryWithCapacity:ABMultiValueGetCount(phoneMulti)];

for (CFIndex i = 0; i <>

phoneLabel = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(phoneMulti, i));

phone = ABMultiValueCopyValueAtIndex(phoneMulti, i);

[myPhoneDict setObject:(NSString*)phone forKey:(NSString*)phoneLabel];

CFRelease(phone);

CFRelease(phoneLabel);

}

if ( [myPhoneDict objectForKey:@"mobile"] != nil) {

customer.phone = [myPhoneDict objectForKey:@"mobile"];

} else if ( [myPhoneDict objectForKey:@"home"] != nil) {

customer.phone = [myPhoneDict objectForKey:@"home"];

} else if ( [myPhoneDict objectForKey:@"work"] != nil) {

customer.phone = [myPhoneDict objectForKey:@"work"];

}


ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);

NSMutableDictionary *myEmailDict = [NSMutableDictionary dictionaryWithCapacity:ABMultiValueGetCount(multi)];

for (CFIndex i = 0; i <>

emailLabel = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(multi, i));

email = ABMultiValueCopyValueAtIndex(multi, i);

[myEmailDict setObject:(NSString*)email forKey:(NSString*)emailLabel];

CFRelease(email);

CFRelease(emailLabel);

}

if ([myEmailDict objectForKey:@"home"] != nil) {

customer.email = [myEmailDict objectForKey:@"home"];

} else if ([myEmailDict objectForKey:@"work"] != nil) {

customer.email = [myEmailDict objectForKey:@"work"];

} else if ([myEmailDict objectForKey:@"other"] != nil) {

customer.email = [myEmailDict objectForKey:@"other"];

}

// get the address stuff

ABMultiValueRef streets = ABRecordCopyValue(person, kABPersonAddressProperty);

NSMutableDictionary *myAddressDict = [NSMutableDictionary dictionaryWithCapacity:ABMultiValueGetCount(streets)];

for (CFIndex j = 0; j <>

NSMutableDictionary *myLabelDict = [[NSMutableDictionary alloc] init];

CFDictionaryRef dict = ABMultiValueCopyValueAtIndex(streets, j);

CFStringRef typeTmp = ABMultiValueCopyLabelAtIndex(streets, j);

CFStringRef type = ABAddressBookCopyLocalizedLabel(typeTmp);

NSString *street = [(NSString *)CFDictionaryGetValue(dict, kABPersonAddressStreetKey) copy];

NSString *city = [(NSString *)CFDictionaryGetValue(dict, kABPersonAddressCityKey) copy];

NSString *state = [(NSString *)CFDictionaryGetValue(dict, kABPersonAddressStateKey) copy];

NSString *zip = [(NSString *)CFDictionaryGetValue(dict, kABPersonAddressZIPKey) copy];

NSString *country = [(NSString *)CFDictionaryGetValue(dict, kABPersonAddressCountryKey) copy];

if ((street != nil) && (street.length > 0))

[myLabelDict setObject:street forKey:@"street"];

if ((city != nil) && (city.length > 0))

[myLabelDict setObject:city forKey:@"city"];

if ((state != nil) && (state.length > 0))

[myLabelDict setObject:state forKey:@"state"];

if ((zip != nil) && (zip.length > 0))

[myLabelDict setObject:zip forKey:@"zip"];

if ((country != nil) && (country.length > 0))

[myLabelDict setObject:country forKey:@"country"];

[myAddressDict setObject:myLabelDict forKey:(NSString *)type];

[myLabelDict release];

[street release];

[city release];

[state release];

[zip release];

[country release];

CFRelease(dict);

CFRelease(type);

CFRelease(typeTmp);

}


NSString *theKey;

if ([myAddressDict objectForKey:@"home"] != nil) {

theKey = @"home";

} else if ([myAddressDict objectForKey:@"work"] != nil) {

theKey = @"work";

} else if ([myAddressDict objectForKey:@"other"] != nil) {

theKey = @"other";

}

if ([[[myAddressDict objectForKey:theKey] objectForKey:@"street"] length] > 0) {

customer.street = [[myAddressDict objectForKey:theKey] objectForKey:@"street"];

} else {

customer.street = @"";

}

if ([[[myAddressDict objectForKey:theKey] objectForKey:@"city"] length] > 0) {

customer.city = [[myAddressDict objectForKey:theKey] objectForKey:@"city"];

} else {

customer.city = @"";

}

if ([[[myAddressDict objectForKey:theKey] objectForKey:@"state"] length] > 0) {

customer.state = [[myAddressDict objectForKey:theKey] objectForKey:@"state"];

} else {

customer.state = @"";

}

if ([[[myAddressDict objectForKey:theKey] objectForKey:@"zip"] length] > 0) {

customer.postalCode = [[myAddressDict objectForKey:theKey] objectForKey:@"zip"];

} else {

customer.postalCode = @"";

}

if ([[[myAddressDict objectForKey:theKey] objectForKey:@"country"] length] > 0) {

customer.country = [[myAddressDict objectForKey:theKey] objectForKey:@"country"];

} else {

customer.country = @"";

}

CFRelease(streets);

CFRelease(phoneMulti);

CFRelease(multi);

[customer dehydrate];

[self dismissModalViewControllerAnimated:YES];

return NO;

}