Saturday, 17 October 2009

Friday, 16 October 2009

Growing Your Site

Pro Blogger has a link to a talk given by Digg's Kevin Rose in London. The talk is on how to grow your site to a million unique visitors.



Here were my take aways:

  • Keep things simple. More features are not always a good thing. Often they are looking for what they can remove from their site.

  • Build and release. Don't think you understand your users. Release new features, see how it works out with the users. Then iterate.



He also had some ideas about building community via Podcasting and parties and such. Not everyone is a Podcaster methinks. Also, it seems like the launch party thing is really only gonna work in Silicon Valley. Probably not a great help if you live in Northern Wyoming, for example.



In regard to other things Digg, I was listening to a 3 week old This Week in Tech pod cast. Kevin stated that with 40 million unique visitors a month Digg has reached the limits of MySQL. They are planning to move their back end to the open source Cassandra database.



I think that is a nice little factoid to know. Once you hit 20 or 30 million uniques, you may have to start weighing your database options.A nice problem to have. :)

ListView and ListActivity, with Layout Animation

In the article Layout Animation, list_layout_controller is included in layout, main.xml. In the last article ListView and ListActivity, ListView is implemented as ListActivity, without XML file for layout. How can apply the LayoutAnimationController.

In this article, the same visual effect of Layout Animation will be applied on ListView and ListActivity.



- Follow the last exercise, ListView and ListActivity.

- Same as Layout Animation, create the folder /res/anim and add the files list_layout_controller.xml and scale.xml.

list_layout_controller.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="50%"
android:animation="@anim/scale" />


scale.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="0.1"
android:toXScale="1"
android:fromYScale="0.1"
android:toYScale="1.0"
android:duration="2000"
android:pivotX="10%"
android:pivotY="10%"
android:startOffset="100" />
</set>


- Modify AndroidListActivity.java to add two lines of code in onCreate()
 @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, COUNTRIES));
getListView().setTextFilterEnabled(true);

LayoutAnimationController controller
= AnimationUtils.loadLayoutAnimation(
this, R.anim.list_layout_controller);
getListView().setLayoutAnimation(controller);

}

That's!


ListView and ListActivity

A ListView is a View that shows items in a vertically scrolling list. The items are acquired from a ListAdapter.

In this exercise, I will show how to implement a ListView using ListActivity. The main activity (AndroidListView) call the ListActivity(AndroidListActivity) by startActivityForResult. AndroidListActivity have a ListView only, the selected item will be passed back to AndroidListView by means of Bundle.



_ Create a new Android Application named AndroidListView

- Modify main.xml to have a Button to start the ListActivity with ListView, and have a TextView to show the result from ListActivity.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/selectCountryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Select Country"
/>
<TextView
android:id="@+id/myCountry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>


- Modify AndroidListView.java
package com.exercise.AndroidListView;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class AndroidListView extends Activity {

TextView MyCountry;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

MyCountry = (TextView)findViewById(R.id.myCountry);
Button SelectCountryButton = (Button)findViewById(R.id.selectCountryButton);
SelectCountryButton.setOnClickListener(SelectCountryButtonOnClickListener);
}

private Button.OnClickListener SelectCountryButtonOnClickListener =
new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setClass(AndroidListView.this, AndroidListActivity.class);

startActivityForResult(intent, 0);
}
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==0)
{
switch (resultCode)
{ case RESULT_OK:
MyCountry.setText(data.getStringExtra("country"));
break;
case RESULT_CANCELED:
break;

}

}
}


}


- Create AndroidListActivity extends ListActivity

Right click com.exercise.AndroidListView, the package In the Project Windows on the left. Select New > Class >

Click Browser beside Superclass, select ListActivity. (May be you have to clear the content inside "Choose a type" box to make it appear in the "Matching items" list.

Type AndroidListActivity in the Name



Click Finish

- Modify AndroidListActivity
package com.exercise.AndroidListView;

import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class AndroidListActivity extends ListActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, COUNTRIES));
getListView().setTextFilterEnabled(true);
}

static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa",
"Andorra", "Angola", "Anguilla", "Antarctica",
"Antigua and Barbuda", "Argentina", "Armenia", "Aruba",
"Australia", "Austria", "Azerbaijan", "Bahrain",
"Bangladesh", "Barbados", "Belarus", "Belgium", "Belize",
"Benin", "Bermuda", "Bhutan", "Bolivia",
"Bosnia and Herzegovina", "Botswana", "Bouvet Island",
"Brazil", "British Indian Ocean Territory",
"British Virgin Islands", "Brunei", "Bulgaria",
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia",
"Cameroon", "Canada", "Cape Verde", "Cayman Islands",
"Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia",
"Comoros", "Congo", "Cook Islands", "Costa Rica", "Croatia",
"Cuba", "Cyprus", "Czech Republic",
"Democratic Republic of the Congo", "Denmark", "Djibouti",
"Dominica", "Dominican Republic", "East Timor", "Ecuador",
"Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands",
"Fiji", "Finland", "Former Yugoslav Republic of Macedonia",
"France", "French Guiana", "French Polynesia",
"French Southern Territories", "Gabon", "Georgia", "Germany",
"Ghana", "Gibraltar", "Greece", "Greenland", "Grenada",
"Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
"Guyana", "Haiti", "Heard Island and McDonald Islands",
"Honduras", "Hong Kong", "Hungary", "Iceland", "India",
"Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy",
"Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati",
"Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho",
"Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
"Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali",
"Malta", "Marshall Islands", "Martinique", "Mauritania",
"Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
"Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique",
"Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands",
"Netherlands Antilles", "New Caledonia", "New Zealand",
"Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island",
"North Korea", "Northern Marianas", "Norway", "Oman", "Pakistan",
"Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
"Philippines", "Pitcairn Islands", "Poland", "Portugal",
"Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda",
"Sqo Tome and Principe", "Saint Helena", "Saint Kitts and Nevis",
"Saint Lucia", "Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines", "Samoa", "San Marino",
"Saudi Arabia", "Senegal", "Seychelles", "Sierra Leone",
"Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia",
"South Africa", "South Georgia and the South Sandwich Islands",
"South Korea", "Spain", "Sri Lanka", "Sudan", "Suriname",
"Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland",
"Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand",
"The Bahamas", "The Gambia", "Togo", "Tokelau", "Tonga",
"Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
"Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"United States", "United States Minor Outlying Islands",
"Uruguay", "Uzbekistan", "Vanuatu", "Vatican City", "Venezuela",
"Vietnam", "Wallis and Futuna", "Western Sahara", "Yemen",
"Yugoslavia", "Zambia", "Zimbabwe"
};

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);

Intent intent = new Intent();
Bundle bundle = new Bundle();

bundle.putString("country", l.getItemAtPosition(position).toString());
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish();
}

}


Notice that we don't need to load a layout (at least, not in this case, because we're using the whole screen for our list). Instead, we just call setListAdapter() (which automatically adds a ListView to the ListActivity), and provide it with an ArrayAdapter that binds a simple_list_item_1 layout item to each entry in the COUNTRIES array (added next). The next line of code adds a text filter to the ListView, so that when the user begins typing, the list will filter the entire view to display only the items that match the entry.

- Modify AndroidMainfest.xml to include the AndroidListActivity.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exercise.AndroidListView"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidListView"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AndroidListActivity"></activity>
</application>
<uses-sdk android:minSdkVersion="3" />

</manifest>


Actually, it's same as Google example Hello, ListView, with some extra; such as how the inter-action between main activity and the ListActivity.

Download the files.

Thursday, 15 October 2009

Layout Animation

Layout Animation can be used to add visual effects on any controls derived from ViewGroup, such as ListView. ListView is a view that shows items in a vertically scrolling list. The items come from the ListAdapter associated with this view.

In this article, I will have a example to show how to implement a simple Layout Animation.



Create a Android Application named AndroidLayoutAnimation.

- Create a new folder named /anim under /res

- Create two xml file under /res/anim to handle the animation
list_layout_controller.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="50%"
android:animation="@anim/scale" />


scale.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="0.1"
android:toXScale="1"
android:fromYScale="0.1"
android:toYScale="1.0"
android:duration="2000"
android:pivotX="10%"
android:pivotY="10%"
android:startOffset="100" />
</set>


- Modify main.xml to have a ListView and Button.
<?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"
>
<ListView
android:id="@+id/myListView"
android:persistentDrawingCache="animation|scrolling"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/list_layout_controller" />
/>
<Button
android:id="@+id/myRestartButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Restart"
/>
</LinearLayout>


- Modify AndroidLayoutAnimation.java to setContentView() using listlayout.xml, and SetupListView(). In order to show the effect, a button is used to restart the animation.
package com.exercise.AndroidLayoutAnimation;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

public class AndroidLayoutAnimation extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

loadScreen();
}

private Button.OnClickListener MyRestartButtonOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
loadScreen();
}
};

private void loadScreen(){
setContentView(R.layout.main);
SetupListView();

Button MyRestartButton = (Button)findViewById(R.id.myRestartButton);
MyRestartButton.setOnClickListener(MyRestartButtonOnClickListener);
}

private void SetupListView()
{
String[] listItems = new String[] {
"Hello!",
"It's a Demo to use Layout Animation",
"Is it Great?",
"android-er.blogspot.com"
};

ArrayAdapter<String> listItemAdapter
= new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
listItems);

ListView lv = (ListView)this.findViewById(R.id.myListView);
lv.setAdapter(listItemAdapter);
}
}


Download the files.

Tuesday, 13 October 2009

Animation background, using animation-list and AnimationDrawable

In the article Simulate Animation, using Runnable Thread, I described how to simulate animation using Runnable Thread. For sure, it's not a good practice. In this article I show how to implement animation using animation-list and AnimationDrawable.


The animation can be started and stopped by the two button, Start and Stop.

- Save the four graphics, used to form the animation of a rotating arrow, in the /res/drawable/ folder.



- Create a file named arrow_animation.xml in /res/drawable
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:drawable="@drawable/arrow_01" android:duration="100" />
<item android:drawable="@drawable/arrow_02" android:duration="100" />
<item android:drawable="@drawable/arrow_03" android:duration="100" />
<item android:drawable="@drawable/arrow_04" android:duration="100" />
</animation-list>


- Modify main.xml to add two Button to Start and Stop animation
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/myStartButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"
/>
<Button
android:id="@+id/myStopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"
/>
<ImageView
android:id="@+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>


- Modify the Java code as:
package com.exercise.AndroidAnimationDrawable;

import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class AndroidAnimationDrawable extends Activity {

AnimationDrawable AniFrame;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Button MyStartButton = (Button)findViewById(R.id.myStartButton);
MyStartButton.setOnClickListener(MyStartButtonOnClickListener);
Button MyStopButton = (Button)findViewById(R.id.myStopButton);
MyStopButton.setOnClickListener(MyStopButtonOnClickListener);

ImageView MyImageView = (ImageView)findViewById(R.id.myImageView);
MyImageView.setBackgroundResource(R.drawable.arrow_animation);
AniFrame = (AnimationDrawable) MyImageView.getBackground();
}

Button.OnClickListener MyStartButtonOnClickListener =
new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AniFrame.start();
}
};

Button.OnClickListener MyStopButtonOnClickListener =
new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AniFrame.stop();
}
};

}


----------------------------------------
In the book of Professional Android Application Development (Wrox Programmer to Programmer) by Reto Meier, it's stated that:

At the time of going to print, there is a bug in the AnimationDrawable class. Currently, AnimationDrawable resources are not properly loaded until some time after an Activity�s onCreate method has completed. Current work-arounds use timers to force a delay before loading a frame-by-frame resource.



I have the same problem; if AniFrame.start() is called inside onCreate() immediately, the animation cannot start. That's why I add the buttons to start and stop the operation.
----------------------------------------

Android Twitter Client, post updated status to Twitter from Android.

It's a Twitter Client post updated status using Twitter API.



There are three EditText in the application, for user name, password (hiden by password dots) and updated status. Submit to Twitter by pressing of the Submit button. If successful, a Toast with "OK" will be shown, otherwise "ERROR" will be shown.



- Before start; base64 is needed in the application, refer to the article Use base64 in Android to download and include it in the Build Path of the project.

- Modify AndroidManifest.xml, Allows Android applications to open network sockets: "android.permission.INTERNET".

- Modify main.xml to have the input EditText for user name, password and updated status.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="User Name and Password"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="User Name:"
/>
<EditText
android:id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Password:"
/>
<EditText
android:id="@+id/password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="What are you doing?"
/>
<EditText
android:id="@+id/whatareyoudoing"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/mySubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Submit"
/>
</LinearLayout>

main.xml can be downloaded here.

- Modify the java as:
package com.exercise.AndroidTwitterClient;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import org.apache.commons.codec.binary.Base64;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AndroidTwitterClient extends Activity {

EditText MyUsername;
EditText MyPassword;
EditText MyStatus;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Button MySubmitButton = (Button)findViewById(R.id.mySubmit);
MySubmitButton.setOnClickListener(MySubmitButtonOnClickListener);
MyUsername = (EditText)findViewById(R.id.username);
MyPassword = (EditText)findViewById(R.id.password);
MyStatus = (EditText)findViewById(R.id.whatareyoudoing);
}

private Button.OnClickListener MySubmitButtonOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub

if (SendTwitter(MyUsername.getText().toString(),
MyPassword.getText().toString(),
MyStatus.getText().toString())){
Toast.makeText(AndroidTwitterClient.this,
"OK", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(AndroidTwitterClient.this,
"ERROR", Toast.LENGTH_SHORT).show();
}

}

};

public static boolean SendTwitter(String twitteruser,
String twitterpass, String msg) {

boolean result = true;

try {

//String twitteruser = "android_er";
//String twitterpass = "pass_word";
URLConnection connection = null;
String credentials = twitteruser + ":" + twitterpass;

String encodecredentials =
new String(Base64.encodeBase64(credentials.getBytes()));

URL url = new URL("http://twitter.com/statuses/update.xml");
String encodedData = URLEncoder.encode(msg, "UTF-8");

connection = url.openConnection();

connection.setRequestProperty( "Authorization",
"Basic " + encodecredentials);
connection.setDoOutput(true);

OutputStreamWriter out = new
OutputStreamWriter(connection.getOutputStream());
out.write("status=" + encodedData);
out.close();

BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));

while (in.readLine() != null) {
}
in.close();
} catch (Exception e) {
result = false;
}

return result;

}
}

AndroidTwitterClient.java can be downloaded here.