Page 1 of 1

Adding a Bug Reporter / Suggestion Box To Your Android App

#1 H3R3T1C  Icon User is offline

  • Android Expert
  • member icon

Reputation: 276
  • View blog
  • Posts: 757
  • Joined: 30-March 07

Posted 26 August 2011 - 11:27 AM

*
POPULAR

Hello! This tutorial will show you how to create bug reporting and user suggestion reporting system for your app!
Before we begin make sure you have the following installed:
Android SDK
Eclipse ~ Java
Google Chrome Browser
Google Web Toolkit & App Engine installed Via Eclipse plugin

Other Requirements:
Google Account with App Engine account (only needed if going to use Google App engine for host).

Ok now that that is done we can start with the fun stuff!

First off we need to crate a new Android project. For this project I just named it Android Bug Report. For the build target just select Android 1.6. There is no specific reason I pick Android 1.6. For the package name you can use whatever you want. Make sure Create Activity is checked and put in Main for it. Your screen should look like the following:
Posted Image

Once you hit ok your new project should be created. Before we go any further we need to add a few things to AndroidManifiest.xml. Open up AndroidManifest.xml click the application tab and select .Main from application nodes. In the 'Attributes for Main' go down to config changes and type in orientation. Then go to the Permissions tab, click add and select Uses Permission. From the pull down menu select 'android.permission.INTERNET'. Click the save button and now close the manifest.

Sense this is not a tutorial on GUI I'm not going to explain the XML layouts for this project so you can just copy paste them into your project!
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" android:background="#C0C0C0">
    <Spinner android:id="@+id/spinner1" android:layout_marginLeft="5dip" android:layout_height="wrap_content" android:layout_marginRight="5dip" android:layout_width="fill_parent"></Spinner>
    <EditText android:lines="1" android:id="@+id/editText1" android:layout_width="fill_parent" android:layout_marginLeft="10dip" android:layout_height="wrap_content" android:inputType="textEmailAddress" android:layout_marginRight="10dip" android:hint="Your Email (optional)"></EditText>
    <TextView android:layout_gravity="center_horizontal" android:textAppearance="?android:attr/textAppearanceMedium" android:id="@+id/TextView02" android:layout_width="wrap_content" android:textColor="#000" android:text="Report info" android:layout_height="wrap_content"></TextView>
    <ScrollView android:id="@+id/scrollView2" android:fillViewport="true" android:layout_width="fill_parent" android:layout_weight="1" android:layout_height="50dip">
        <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" android:id="@+id/editText2" android:inputType="textMultiLine" android:gravity="top"></EditText>
    </ScrollView>
    <TextView android:layout_gravity="right" android:id="@+id/textView2" android:layout_width="wrap_content" android:textColor="#000" android:text="0 of 500 characters" android:layout_height="wrap_content" android:layout_marginRight="5dip"></TextView>
    <LinearLayout android:id="@+id/linearLayout2" android:layout_height="wrap_content" android:layout_width="fill_parent">
        <Button android:layout_gravity="bottom|left" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Send"></Button>
        <Button android:layout_gravity="bottom" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Cancel"></Button>
    </LinearLayout>
    
</LinearLayout>



In your project replace the code in your main.xml under res -> layout with the above code. Your main.xml should now look like the following:
Posted Image

Now its time to code some of the app its self. Open up Main.java. For right now in Main.java we are going to create 3 methods like so:
private void initonclick()
{

}
private void initSpinner()
{

}
private void initTextListener()
{

}


Before we fill in the code for each of these methods in your onCreate method call each of these methods like so:
public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initSpinner();
        initTextListener();
        initonclick();
    }



In the private void initonclick() add this code:
onclickListener click = new onclickListener(){

			@Override
			public void onclick(View v) {
				switch(v.getId())
				{
					case R.id.button1:// send button
					{
						break;
					}
					case R.id.button2:// cancel button
					{
						finish();
						break;
					}
				}
				
			}};
			findViewById(R.id.button1).setonclickListener(click);
			findViewById(R.id.button2).setonclickListener(click);


The purpose of the above code is to attach an onclickListener to each of the buttons!

Next in the private void initSpinner() method add this code:
String[] data = {"Bug Report","Suggestion"};
    	Spinner s= (Spinner)findViewById(R.id.spinner1);
    	ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item, data);
    	adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    	s.setAdapter(adapter);


The purpose of the above code is to add the options to our spinner and attach the adapter!

Before we fill in the next method we need to a global variable that will be used by the next method. Above onCreate add this:
// This is the maximum characters limit of our report text box!
	private static final int MAX_CHAR_LIM = 500;



Next in the private void initTextListener() method add this code:
final EditText et = (EditText)findViewById(R.id.editText2);
    	final TextView tv = (TextView)findViewById(R.id.textView2);
    	et.addTextChangedListener(new TextWatcher(){

			@Override
			public void afterTextChanged(Editable arg0) {
				
				if(arg0.length()>MAX_CHAR_LIM)
				{
					char[]ch = new char[MAX_CHAR_LIM];
					arg0.getChars(0, MAX_CHAR_LIM-1, ch, 0);
					StringBuilder sb = new StringBuilder();
					for(char c:ch)
						sb.append(c);
					et.setText(sb.toString());
					et.setSelection(MAX_CHAR_LIM-1);
					Toast.makeText(Main.this, "Max charcter count of "+MAX_CHAR_LIM+" reached!", Toast.LENGTH_SHORT).show();
				}
				tv.setText(et.getText().length()+" of "+MAX_CHAR_LIM+" characters");
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
				// Not used!
			}
			@Override
			public void onTextChanged(CharSequence s, int start, int before,
					int count) {
				// Not Used!
			}});



The purpose of the above code is to make sure that the info text box only hold as much text was wanted!
We are done working on the Android part for now (we will add the rest of the code once we set up the server!) so now need to create a new Web Application Project! Click the blue circle with a g in the middle of it in the Eclipse tool bar and select 'New Web Application Project'. For project name put 'Android Bug Report Server'. For the package you can put com.web.h3r3t1c.bugreport. Leave all the other options the same and hit Finish. The new project should now be created!
We need to make sure that project works so click the run button to run the project. A Development Mode tab should of shown up in Eclipse. One http://127.0.0.1:888...=127.0.0.1:9997 is shown in that tab right click it and select Open With -> Add a Browser. For name put Chrome and for location find your Google Chrome browser and hit ok. The project should now load in Google Chrome Browser. If everything went well then it should of loaded in the chrome browser but if not do whatever it tells you to fix it! Go ahead and close the browser as we do not need it open anymore!

In the in Android Bug Report Server project expand the war folder then expand the WEB-INF folder. Open up Web.xml.
Replace all the data with this :
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  
  <!-- Servlets -->
  <servlet>
    <servlet-name>submitPage</servlet-name>
    <jsp-file>submit.jsp</jsp-file>

  </servlet>
  
  <servlet-mapping>
    <servlet-name>submitPage</servlet-name>
    <url-pattern>/submitPage/*</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>submit</servlet-name>
    <servlet-class>com.web.h3r3t1c.bugreport.server.SubmitServlet</servlet-class>

  </servlet>
  
  <servlet-mapping>
    <servlet-name>submit</servlet-name>
    <url-pattern>/submit</url-pattern>
  </servlet-mapping>
  
  
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>submit.html</welcome-file>
  </welcome-file-list>

</web-app>



Hit save and close it.
Now create a new File called submit.html by right clicking war New -> File.
Open up submit.html and put this code in it:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

<form action="/submit" method="post">
    <input type = "hidden" name = "type" value ="-1"></input>
    <input type = "hidden" name = "email" value = "test@test.com"></input>
    <input type = "hidden" name = "data" value = "empty"></input>
    <input type = "hidden" name = "uuid" value = "000"></input>
    <input type = "hidden" name = "app" value = "unknown"></input>
    <div><input type="submit" value="Submit test data" /></div>
  </form>
</body>
</html>

The purpose of the above code is so we can test out the submit part of our sever!

Next expand src and expand each of the package and delete all of the *.java files as they will not be needed!
Next create a new java file under the com.web.h3r3t1c.bugreport.server package called PMF and put this code in it:
package com.web.h3r3t1c.bugreport.server;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
    private static final PersistenceManagerFactory pmfInstance =
        JDOHelper.getPersistenceManagerFactory("transactions-optional");

    private PMF() {}

    public static PersistenceManagerFactory get() {
        return pmfInstance;
    }
}


The above code creates a single instance of PersistenceManager that will be needed later!

Next under the same package create a new Java file called ReportData and put this code in it:
package com.web.h3r3t1c.bugreport.server;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;

@PersistenceCapable
public class ReportData {

	@PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
	
	@Persistent
	private String type;
	@Persistent
	private String email;
	@Persistent
	private String data;
	@Persistent
	private String uuid;
	@Persistent
	private String app;
	
	public ReportData(String t,String e,String d,String u,String a)
	{
		type = t;
		email = e;
		data = d;
		uuid = u;
		app = a;
	}
	
	public void setType(String type) {
		this.type = type;
	}
	public String getType() {
		return type;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getEmail() {
		return email;
	}
	public void setUuid(String uuid) {
		this.uuid = uuid;
	}
	public String getUuid() {
		return uuid;
	}
	public void setData(String data) {
		this.data = data;
	}
	public String getData() {
		return data;
	}
	public void setApp(String app) {
		this.app = app;
	}
	public String getApp() {
		return app;
	}

	public void setKey(Key key) {
		this.key = key;
	}

	public Key getKey() {
		return key;
	}
	
}


The purpose of the above code is to create a class that will store our data that the users is submitting so we can view it later!

Next we need to create another java class under the same package called SubmitServlet. Put the following code in SubmitServlet:

package com.web.h3r3t1c.bugreport.server;

import java.io.IOException;

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

public class SubmitServlet extends HttpServlet{

	public void doPost(HttpServletRequest req, HttpServletResponse resp)
    throws IOException {
		String type = req.getParameter("type");
		String email = req.getParameter("email");
		String data = req.getParameter("data");
		String uuid = req.getParameter("uuid");
		String app = req.getParameter("app");
		ReportData report = new ReportData(type,email,data,uuid,app);
		PersistenceManager pm = PMF.get().getPersistenceManager();
		resp.setContentType("text/xml");
        try {
            pm.makePersistent(report);
        } 
        catch(Exception e)
        {
        	resp.getWriter().println("<status>error</status>");
        	pm.close();
        	return;
        }finally {
            pm.close();
        }
		resp.getWriter().println("<status>ok</status>");
	}
}


Save everything and hit the run button. When the Link shows up in the Development Mode tab open up your Google Chrome browser and enter this url: http://127.0.0.1:8888/ . You should see a button that says 'Submit test data'. When you click it you should now see <status>ok</status>. If it does then everything is working ok and we can continue but if not go back and see if messed something up.

Now we need to create an application in App Engine online by going to https://appengine.google.com , logging in and clicking Create Application. Fill out all the required info and hit Create Application. Now go back to Eclipse and click the App Engine button in the tool bar. In the Deploy Project to Google App Engine click App engine project settings...
For the Application ID enter the id you just created. Click ok and youll be taking back to the deploy window. Hit Cancel because we are not yet read to deploy. In the toolbar hit the button thats a red toolbox with a g on it. In the windows that appears hit the add button and select the first option. Hit Ok then hit Apply then hit compile. It should now compile!
One more thing we need to do before we can deploy is open up Android_Bug_Report_Server.gwt.xml which is under src -> com.web.h3r3t1c.bugreport. When you open it up should look exaclty like the following but if it does not copy paste this code in it!
<?xml version="1.0" encoding="UTF-8"?>
<module rename-to='android_bug_report_server'>
  <!-- Inherit the core Web Toolkit stuff.                        -->
  <inherits name='com.google.gwt.user.User'/>

  <!-- Inherit the default GWT style sheet.  You can change       -->
  <!-- the theme of your GWT application by uncommenting          -->
  <!-- any one of the following lines.                            -->
  <inherits name='com.google.gwt.user.theme.clean.Clean'/>
  <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
  <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->

  <!-- Other module inherits                                      -->

  <!-- Specify the app entry point class.                         -->
	<entry-point class='com.web.h3r3t1c.bugreport.client.Android_Bug_Report_Server'/>
  <!-- Specify the paths for translatable code                    -->
  <source path='client'/>
  <source path='shared'/>

</module>


Now under src -> com.web.h3r3t1c.bugreport.client create a new java file called Android_Bug_Report_Server and in it put this code:
package com.web.h3r3t1c.bugreport.client;

import com.google.gwt.core.client.EntryPoint;


/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Android_Bug_Report_Server implements EntryPoint {

	@Override
	public void onModuleLoad() {
		// TODO Auto-generated method stub
		
	}
	
	
}



This is needed because the app needs an entry point for the client. This will be used later on! Now open up the deploy window again and deploy the app! Once its done go to https://appengine.google.com/ and you should see 1 under current versions for you app. Click on it and it should take your apps page on app engine. Hit the submit test data button. you should see <status>ok</status> if everything is working ok!

Now we need to add the submit function to our Android part of our project. Reopen up the android project if you have closed it and then open up Main.java. As you have noticed before one of the fields in our post submit was uuid. Were are going to use a one time randomly generated Unique User Identification to identify each users install of the app with the bug reporter. In Main.java create a new method called private String generateUUID() and fill it in with the following code:
return UUID.randomUUID().toString();



The next method requires a new global variable so add private String uuid; to your globals.

Next create another method called private void initPrefs() and put the following code in it and dont forget to make a call to it from the onCreate method:
SharedPreferences mPrefs;
    	Context mContext = this.getApplicationContext();
        mPrefs = mContext.getSharedPreferences("userPrefs", 0);
        uuid = mPrefs.getString("uuid", "na");
        if(uuid.equalsIgnoreCase("na"))
        {
        	SharedPreferences.Editor edit = mPrefs.edit();
        	String s = generateUUID();
            edit.putString("uuid", s);
            edit.commit();
            uuid = s;
        }


The above will see if we already created a uuid for this user and if not then we will generate one for them!

Next we need to create a new class called PostMethodThread. In the same package that you have Main.java create a new file called PostMethodThread.java and put this code in it(Read the comments in the code for complete understanding of the code!):
package com.app.h3r3t1c.bugreport;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.widget.Toast;

public class PostMethodThread extends AsyncTask<String,Void,String>{

	private final WeakReference<ProgressDialog>prj; // this is the progress dialog that is shown while the post is taking place
	private static final String url = "http://h3r3t1cdemobugreport.appspot.com/submit";// this is the url of the SubmitServlet on our server
	
	public PostMethodThread(ProgressDialog p)
	{
		prj  = new WeakReference<ProgressDialog>(p);
	}
	/**
	 * This is done on the Task thread and is non-blocking!
	 */
	@Override
	protected String doInBackground(String... data) {
		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(url);
		List<NameValuePair> value=new ArrayList<NameValuePair>(); 
		value.add(new BasicNameValuePair("type",data[0]));
		value.add(new BasicNameValuePair("email",data[1])); 
		value.add(new BasicNameValuePair("data",data[2])); 
		value.add(new BasicNameValuePair("uuid",data[3])); 
		value.add(new BasicNameValuePair("app",data[4])); 
		UrlEncodedFormEntity entity = null;
		try {
			entity = new UrlEncodedFormEntity(value);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		post.setEntity(entity); 
		try {
			HttpResponse res = client.execute(post);
			InputStream bodyStream = res.getEntity().getContent();
			ByteArrayOutputStream outStream=null;
			outStream=new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int length;
            while((length=bodyStream.read(buffer))>0){
             outStream.write(buffer,0,length);
            }
            String body=new String(outStream.toByteArray(),"UTF-8");
            bodyStream.close();
            outStream.close();
            return body;
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * To be implemented latert
	 */
	@Override
	protected void onCancelled() {
		
	}
	/**
	 * This is run on the UI thread and is blocking!
	 */
	@Override
	protected void onPostExecute(String result) {
		ProgressDialog p = prj.get();
		if(p!=null&&p.isShowing())
			p.dismiss();
		if(result == null||result.equalsIgnoreCase("<status>error</status>"))
		{
			Toast.makeText(p.getContext(), "An error occured while submitting data!", Toast.LENGTH_LONG).show();
			return;
		}
		Toast.makeText(p.getContext(), "Submission successful", Toast.LENGTH_LONG).show();
	}
	
}


Important: Make sure that you put your URL in for the url variable!

Now go back to your Main.java and add three new globals:
private static final String APP_NAME = "Bug_Report_For_Tutorial";
private PostMethodThread post;
private ProgressDialog prj;



Next we need to crate two new method called private void submitData() and private ProgressDialog initProgressDialog(). submitData() is where we will make our calls to the PostMethodThread and initProgressDialog is where we will create our progress dialog!
Fill in the method initProgressDialog() with this code:
ProgressDialog p = new ProgressDialog(this);
    	p.setIndeterminate(true);
    	p.setTitle("Submitting Data!");
    	p.setCancelable(false);
    	p.setMessage("Submitting data! Please Wait...");
    	return p;



Fill in submitData() with this code:
    	String data = ((EditText)findViewById(R.id.editText2)).getText().toString();
    	String email = ((EditText)findViewById(R.id.editText1)).getText().toString();
    	int selected = ((Spinner)findViewById(R.id.spinner1)).getSelectedItemPosition();
    	String type = null;
    	if(data.length()==0)
    	{
    		Toast.makeText(this, "", Toast.LENGTH_LONG).show();
    		return;
    	}
    	if(email.length()==0)
    	{
    		email = "na";
    	}
    	if(selected==0)
    	{
    		type = "Bug Report";
    	}
    	else if(selected == 1)
    	{
    		type  = "Suggestion";
    	}
    	prj = initProgressDialog();
    	prj.show();
    	post = new PostMethodThread(prj);
    	post.execute(type,email,data,uuid,APP_NAME);



Now go back to the initonclick() method and under case R.id.button1:// send button
make a call to submitData() like so:
case R.id.button1:// send button
{
	submitData();
	break;
}



The android part of the tutorial is now complete so lets move back to the server part.

In src -> com.web.h3r3t1c.bugreport.shared create a new java file called Report and put this code in it:
package com.web.h3r3t1c.bugreport.shared;

import java.io.Serializable;


public class Report implements Serializable{
	
	private String type;

	private String email;

	private String data;

	private String uuid;

	private String app;
	public Report(){}
	public Report(String t,String e,String d,String u,String a)
	{
		type = t;
		email = e;
		data = d;
		uuid = u;
		app = a;
	}
	
	public void setType(String type) {
		this.type = type;
	}
	public String getType() {
		return type;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getEmail() {
		return email;
	}
	public void setUuid(String uuid) {
		this.uuid = uuid;
	}
	public String getUuid() {
		return uuid;
	}
	public void setData(String data) {
		this.data = data;
	}
	public String getData() {
		return data;
	}
	public void setApp(String app) {
		this.app = app;
	}
	public String getApp() {
		return app;
	}
}


This class will be used to get Report data from the server to where we can view it on a web page!

Next create a new java class called ReportDataStore in the same package as Report. This class will be used transfer a List of reports from the server to the report data page!
Code for ReportDataStore:
package com.web.h3r3t1c.bugreport.shared;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class ReportDataStore implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -8622969897264243873L;
	private List<Report>data;
	
	public ReportDataStore()
	{
		data = new ArrayList<Report>();
	}
	public void add(Report r)
	{
		data.add(r);
	}
	public List<Report> getData()
	{
		return data;
	}
}




Next open up Android_Bug_Report_Server.html under war and replace its code with this:
<!doctype html>
<!-- The DOCTYPE declaration above will set the    -->
<!-- browser's rendering engine into               -->
<!-- "Standards Mode". Replacing this declaration  -->
<!-- with a "Quirks Mode" doctype may lead to some -->
<!-- differences in layout.                        -->

<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

    <!--                                                               -->
    <!-- Consider inlining CSS to reduce the number of requested files -->
    <!--                                                               -->
    <link type="text/css" rel="stylesheet" href="Android_Bug_Report_Server.css">

    <!--                                           -->
    <!-- Any title is fine                         -->
    <!--                                           -->
    <title>Report Data Page</title>
    
    <!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" src="android_bug_report_server/android_bug_report_server.nocache.js"></script>
  </head>

  <!--                                           -->
  <!-- The body can have arbitrary html, or      -->
  <!-- you can leave the body empty if you want  -->
  <!-- to create a completely dynamic UI.        -->
  <!--                                           -->
  <body>

    <!-- OPTIONAL: include this if you want history support -->
    <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
    
    <!-- RECOMMENDED if your web app will not function without Javascript enabled -->
    <noscript>
      <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
        Your web browser must have Javascript enabled
        in order for this application to display correctly.
      </div>
    </noscript>

    <h1>Report Data</h1>

    <table align="center">
      <tr>
        <td id="main"></td>
      </tr>
    </table>
  </body>
</html>


This is basically a cleanup of the code!

Now we need to create 3 new files. Under src -> com.web.h3r3t1c.bugreport.client create ReportService and ReportServiceAsync. Then under src -> com.web.h3r3t1c.bugreport.server create ReportServiceImpl.
Code for ReportService:
package com.web.h3r3t1c.bugreport.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import com.web.h3r3t1c.bugreport.shared.ReportDataStore;

@RemoteServiceRelativePath("report")
public interface ReportService extends RemoteService{
	
	ReportDataStore getReportData();
}


Code for ReportServiceAsync:
package com.web.h3r3t1c.bugreport.client;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.web.h3r3t1c.bugreport.shared.ReportDataStore;

public interface ReportServiceAsync {

	void getReportData(AsyncCallback<ReportDataStore> callback);


}


code for ReportServiceImpl:
package com.web.h3r3t1c.bugreport.server;

import java.util.List;

import javax.jdo.PersistenceManager;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.web.h3r3t1c.bugreport.client.ReportService;
import com.web.h3r3t1c.bugreport.shared.Report;
import com.web.h3r3t1c.bugreport.shared.ReportDataStore;

public class ReportServiceImpl extends RemoteServiceServlet implements ReportService{

	@Override
	public ReportDataStore getReportData() {
		ReportDataStore data = new ReportDataStore();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		javax.jdo.Query query = pm.newQuery(ReportData.class);
		List<ReportData>in = (List<ReportData>) query.execute();
		for(ReportData r:in)
		{
			data.add(new Report(r.getType(),r.getEmail(),r.getData(),r.getUuid(),r.getApp()));
		}
		pm.close();
		return data;
	}

	
}



The purpose of these three classes is to retrieve the ReportData off the server!

Next open up Android_Bug_Report_Server.java under src -> com.web.h3r3t1c.bugreport.client and replace its code with this code:
package com.web.h3r3t1c.bugreport.client;

import java.util.List;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.web.h3r3t1c.bugreport.shared.Report;
import com.web.h3r3t1c.bugreport.shared.ReportDataStore;


/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Android_Bug_Report_Server implements EntryPoint {

	private Image img;
	@Override
	public void onModuleLoad() {
		
		initLoading();
		ReportServiceAsync r = GWT.create(ReportService.class);
		r.getReportData(new AsyncCallback<ReportDataStore>(){

			@Override
			public void onFailure(Throwable caught) {
				Label l = new Label("An error has occured!<br>Please try again in a few seconds!"+caught.getMessage());
				RootPanel root = RootPanel.get("main");
				root.add(l);
			}

			@Override
			public void onSuccess(ReportDataStore result) {
				loadUI(result.getData());
			}
			
		});
	}
	private void initLoading()
	{
		img = new Image("img/loading7.gif");
		img.setSize("100px", "100px");
		RootPanel.get("main").add(img);
	}
	private void loadUI(List<Report>data)
	{
		
	    CellTable<Report>table = new CellTable<Report>();
	    TextColumn<Report> type = new TextColumn<Report>(){

			@Override
			public String getValue(Report o) {
				// TODO Auto-generated method stub
				return o.getType();
			}};
	    table.addColumn(type, "Report Type");
	    TextColumn<Report> email = new TextColumn<Report>(){

			@Override
			public String getValue(Report o) {
				// TODO Auto-generated method stub
				return o.getEmail();
			}};
	    table.addColumn(email, "Email");
	    TextColumn<Report> d = new TextColumn<Report>(){

			@Override
			public String getValue(Report o) {
				String s = o.getData();
				
				return s;
			}};
	    table.addColumn(d, "Report Data");
	    TextColumn<Report> uuid = new TextColumn<Report>(){

			@Override
			public String getValue(Report o) {
				// TODO Auto-generated method stub
				return o.getUuid();
			}};
	    table.addColumn(uuid, "UUID");
	    TextColumn<Report> app = new TextColumn<Report>(){

			@Override
			public String getValue(Report o) {
				// TODO Auto-generated method stub
				return o.getApp();
			}};
	    table.addColumn(app, "App");
	    
	    
	    ListDataProvider<Report>dp = new ListDataProvider<Report>();
	    dp.addDataDisplay(table);
	    dp.setList(data);
	    
		RootPanel root = RootPanel.get("main");
		root.remove(img);
		root.add(table);
	}
	
	
}


This is the entry point / GUI for displaying our report data!

Next under war we need to make a new folder called img and place the following image in it:
Posted Image
Make sure you name the image loading7.gif

Now open up web.xml under war -> WEB-INF and replace its code with this code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  
  <!-- Servlets -->
 
 <servlet>
    <servlet-name>reportService</servlet-name>
    <servlet-class>com.web.h3r3t1c.bugreport.server.ReportServiceImpl</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>reportService</servlet-name>
    <url-pattern>/android_bug_report_server/report</url-pattern>
  </servlet-mapping>
  
  <servlet>
    <servlet-name>submit</servlet-name>
    <servlet-class>com.web.h3r3t1c.bugreport.server.SubmitServlet</servlet-class>

  </servlet>
  
  <servlet-mapping>
    <servlet-name>submit</servlet-name>
    <url-pattern>/submit</url-pattern>
  </servlet-mapping>
  
  
  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>Android_Bug_Report_Server.html</welcome-file>
  </welcome-file-list>

</web-app>


This code is cleaned up abit from the last time we modified it!

Now that all this is done redeploy your app to app engine. Once its been deployed to appengine you can now view it live on appengine at <Application ID>.appspot.com
Here is what the bug report page looks like:
Posted Image
http://i.imgur.com/gGsj5.png

Closing Notes:
• This tutorial was meant to a very basic into on how to do this. You can extend it to make it more advanced like adding a password login for viewing the report data or adding a delete button to delete fixed bugs.
• To delete reports you can go into your app dashboard by going to appengine.google.com -> Clicking the app id -> Then selecting Datastore View on the left panel.

Android Source:
http://www.sendspace.com/pro/dl/y8id0u
Server Source:
http://www.sendspace.com/pro/dl/lwy6cl

This post has been edited by H3R3T1C: 29 August 2011 - 01:16 PM


Is This A Good Question/Topic? 5
  • +

Replies To: Adding a Bug Reporter / Suggestion Box To Your Android App

#2 Dogstopper  Icon User is online

  • The Ninjaducky
  • member icon



Reputation: 2870
  • View blog
  • Posts: 11,025
  • Joined: 15-July 08

Posted 26 August 2011 - 12:55 PM

Great tutorial! It really shows how related Android devices are with things like the web these days! Great demonstration!

Also, as a note to the readers:
There is currently a bug on the forums which cause onclick to be all lowercased. If you receive errors because of this, simply change it to standard Java naming conventions (in this case, 'c' is capitalized)
Was This Post Helpful? 0
  • +
  • -

#3 stackoverflow  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 164
  • View blog
  • Posts: 545
  • Joined: 06-July 11

Posted 27 August 2011 - 04:45 PM

I like this tutorial-- it's a bit advanced but truly useful for any serious application. :)
Was This Post Helpful? 0
  • +
  • -

#4 malas  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 16-August 14

Posted 16 August 2014 - 07:27 AM

thanks for your good tutorial.

i just have problem here

onclickListener click = new onclickListener(){

			@Override
			public void onclick(View v) {
				switch(v.getId())
				{
					case R.id.button1:// send button
					{
						break;
					}
					case R.id.button2:// cancel button
					{
						finish();
						break;
					}
				}
				
			}};
			findViewById(R.id.button1).setonclickListener(click);
			findViewById(R.id.button2).setonclickListener(click);


error is :
The type new View.onclickListener(){} must implement the inherited abstract method View.onclickListener.onclick(View)

what should I do ?

thanks.
Was This Post Helpful? 0
  • +
  • -

#5 macosxnerd101  Icon User is online

  • Self-Trained Economist
  • member icon




Reputation: 10461
  • View blog
  • Posts: 38,765
  • Joined: 27-December 08

Posted 16 August 2014 - 01:45 PM

Quote

The type new View.onclickListener(){} must implement the inherited abstract method View.onclickListener.onclick(View)

Implement the onclick() method.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1