Saturday, July 31, 2010

Colors in AlertDialog

I wanted to use colors in AlertDialog and I found this post. It work great although the solution there is not full (there are features of AlertDialog that will not work). One issue with that post - the code & XML there can not be easily copied.

Here's that code again, with some changes/improvements:
* Renamed the class name to ColorAlertDialog
* ColorAlertDialog.Builder does not inherits AlertDialog.Builder. This caused some confusion (you could call function but they didn't do anything).
* Added functionality to ColorAlertDialog.Builder
* Fixed force-close on Android 1.5

Class (I've renamed the class):

import java.util.ArrayList;
import java.util.Iterator;

import android.app.AlertDialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.content.res.Resources.Theme;
import android.graphics.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;

public class ColorAlertDialog extends AlertDialog {
private static int NONE = -1;
private int tint = NONE;

/**
* @param context
* @param theme
*/
protected ColorAlertDialog(Context context) {
super(context);
init();
}

/**
* @param context
* @param theme
*/
protected ColorAlertDialog(Context context, int theme) {
super(context, theme);
init();
}

/**
*
*/
private void init() {
final Theme theme = getContext().getTheme();
final TypedArray attrs = theme.obtainStyledAttributes(new int[] { android.R.attr.tint });
tint = attrs.getColor(0, NONE);
}

/* (non-Javadoc)
* @see android.app.Dialog#show()
*/
@Override
public void show() {
super.show();
setTint(tint);
}

/**
* @param tint
*/
public void setTint(int tint) {
this.tint = tint;

if ( tint != NONE ) {
Iterator vi = iterator(android.R.id.content);
while ( vi.hasNext() ) {
tintView(vi.next(), tint);
}
}
}

/**
* Set the {@link tint} color for the {@link View}
*
* @param v the {@link View} to change the tint
* @param tint color tint
*/
private static void tintView(final View v, final int tint) {
if ( v != null ) {
final Mode mode = Mode.SRC_ATOP;
if ( v instanceof ImageView ) {
final Drawable d = ((ImageView)v).getDrawable();
if ( d != null ) {
try {
d.mutate().setColorFilter(tint, mode);
} catch (Exception ex) {
// Patch for Android 1.5
}
}
}
else {
final Drawable d = v.getBackground();
if ( d != null ) {
d.setColorFilter(tint, mode);
}
}
}
}

/**
* @param button
*/
public void setCancelButton(Button button) {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cancel();
}
});
}

/**
* @param button
*/
public void setPositiveButton(Button button) {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
}

/**
* Return a {@link ChildrenIterator} starting at the {@link ViewGroup}
* identified by the specified resource id.
*
* @param res resource id of the {@link ViewGroup} to start the iteration
* @return iterator
*/
public Iterator iterator(int res) {
final ViewGroup vg = (ViewGroup)findViewById(res);
return new ChildrenIterator(vg);
}

public static class Builder {
private ColorAlertDialog dialog;

public Builder(Context context) {
dialog = new ColorAlertDialog(context);
}

public Builder(Context context, int theme) {
dialog = new ColorAlertDialog(context, theme);
}

public ColorAlertDialog create() {
return dialog;
}

public Builder setMessage(CharSequence message) {
dialog.setMessage(message);
return this;
}

public Builder setTitle(CharSequence title) {
dialog.setTitle(title);
return this;
}

public Builder setPositiveButton(int resId, OnClickListener listener) {
return setPositiveButton(dialog.getContext().getString(resId), listener);
}

public Builder setPositiveButton(CharSequence text, OnClickListener listener) {
dialog.setButton(BUTTON_POSITIVE, text, listener);
return this;
}

public Builder setNegativeButton(int resId, OnClickListener listener) {
return setNegativeButton(dialog.getContext().getString(resId), listener);
}

public Builder setNegativeButton(CharSequence text, OnClickListener listener) {
dialog.setButton(BUTTON_NEGATIVE, text, listener);
return this;
}

public Builder setIcon(int iconId) {
dialog.setIcon(iconId);
return this;
}

public Builder setCancelable(boolean flag) {
dialog.setCancelable(flag);
return this;
}

public ColorAlertDialog show() {
dialog.show();
return dialog;
}
}

public class ChildrenIterator implements Iterator {
ArrayList list;
private int i;

public ChildrenIterator(ViewGroup vg) {
super();

if ( vg == null )
throw new RuntimeException("ChildrenIterator needs a ViewGroup != null to find its children");

init();
findChildrenAndAddToList(vg, list);
}

private void init() {
list = new ArrayList();
i = 0;
}

@Override
public boolean hasNext() {
return ( i < list.size() );
}

@Override
public V next() {
return list.get(i++);
}

@Override
public void remove() {
list.remove(i);
}

@SuppressWarnings("unchecked")
private void findChildrenAndAddToList(final ViewGroup root, final ArrayList list) {
for (int i=0; i < root.getChildCount(); i++) {
V v = (V)root.getChildAt(i);
list.add(v);
if ( v instanceof ViewGroup )
findChildrenAndAddToList((ViewGroup)v, list);

}
}
}
}


And the styles (put in an XML file under the 'res/values' folder):











#88FF0000
#880000FF
#88FFFF00
#88995f86
#aaffbf00
#88ff33cc
#0000

Sunday, July 25, 2010

Notes on Android SQLite bulk insert

There's no real bulk insert in SQLite I'm aware of to date. I've tried various ways to insert data fast into Android's SQLite DB, and here are my notes:
1. Use transactions. (e.g. db.beginTransaction, etc..).
2. Use compiled statement: db.compileStatement(insertStatementString), it's much faster than db.insert().

So, in short:

db.beginTransaction();
try {
string sql = "insert into products (id) values (?)";
SQLiteStatement insert = db.compileStatement(sql);

for (.....) {
insert.bindLong(1, 12345);
insert.executeInsert();
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}

Tuesday, July 20, 2010

ASP.NET error: Object Expected / ScriptResource.axd

What this error means in plain English: JavaScript error.

The following case is how I solved a specific error in ASP.NET. It might give you ideas how to solve similar issues of ("Object Expected" with ScriptResource.axd). The easiest way to solve it is to use Firefox & firebug to trace down the specific JavaScript line which cause the problem.

In the scenario bellow a resource (JavaScript) could not be loaded by the browser because the clock on the computer was set back in time. This caused this problem in another JavaScript (missing definition) which caused the error message to pop-up.

Step by step how I traced down the problem:
1. When running an ASP.NET site with Menu item I got the "Object expected" error with the following information: "ScriptResource.axd". When I clicked the detailed information it gave me the URL of the resource which cause the problem.

Bottom line:
*
This error is a result of an unspecified JavaScript Error which accrued during running of a JavaScript file embedded in an ASP.NET project.
* Verify you can open the JavaScript URL using the browser.
* Debug the page using Firebug (on Firefox), see (2)

2. When opening the page in Firefox with Firebug and setting the Firebug "console" to "enabled" (with "Show JavaScript errors") it caught the following exception:
"WebForm_GetElementByTagName is not defined"

The function "WebForm_GetElementByTagName" is declared in an embedded resource called: "WebForms.js", which is in many cases the first Java script included in an HTML generated by an ASP.NET page.

Searching about this error online yielded only the following result:
http://forums.asp.net/p/1439101/3253854.aspx

Buttom line:
* Try to open in the browser each and every JavaScript include files (.AXD) in the page.

3. The error I got when I've tried to open the first ".AXD" JavaScript include file was - Parameter: "utmDate" out of range/out of date - which basically pointed me to check the computer date, since I've taken it back in time.

Setting the date forward again fixed my problem.

Sunday, July 18, 2010

Combo box / Drop down list box in Android

As usual doing something simple in Android is frustrating - this time: a simple Combo (or DropDownList). The little and important tweak - calling "setDropDownViewResource" to set the style of the items in the dialog box.

XML:
<spinner id="@+id/spinner" layout_width="wrap_content" android:layout_height="wrap_content" />

Code to add the data:

Spinner spinner = (Spinner)this.findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_spinner_item,
new String[] { "1", "2", "3" });
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);


A common task is to populate a spinner using a string array defined in a resource file. Here's a function which do just that:

public static void populateSpinnerWithArray(Spinner spinner, int stringArrayResId) {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
spinner.getContext(),
android.R.layout.simple_spinner_item,
spinner.getContext().getResources().getStringArray(stringArrayResId));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
}

App crash when setting layout_weight without layout_width

While playing with the layout on Android I encountered the following weird problem: when using "android:layout_weight" attribute for an element without "android:layout_width" attribute it works just fine in the emulator but on the real phone it crash (Nexus One, Android 2.2).

Since using "android:layout_width" gives me unwanted results I've tried to work around this issue and found the following solution:
android:layout_width="0dip"

Thursday, July 8, 2010

Displaying a simple message box in Android

There are 2 options to display a pop-up in Android (both are not blocking):
Toast - display a short balloon pop-up that will disappear after few seconds
AlertDialog - display a pop-up window with buttons.

Toast:

Toast.makeText(this, "Hello World", Toast.LENGTH_SHORT).show();


AlertDialog:

AlertDialog ad = new AlertDialog.Builder(this).create();
ad.setCancelable(false); // This blocks the 'BACK' button
ad.setMessage("Hello World");
ad.setButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
ad.show();


Note: it's better to use "this.getString(R.string.ok)" and get the string from a resource file.

Sunday, July 4, 2010

Android Debug.startMethodTracing() failed

With the following exception: "file open failed".

Reason: No SD card write permission was given to the application (Android 1.6+)
Solution: Make sure you add the permission to the Manifest file -

Friday, July 2, 2010

Setting Android volume programmatically to maximum

I searched a bit how to the volume of an Android application to maximum, and I saw few post regarding people asking about MediaPlayer.setValue function.

It's not the right function for setting the volume (see the help).
To set the volume to maximum (or any relative value):

// Get the AudioManager
AudioManager audioManager =
(AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
// Set the volume of played media to maximum.
audioManager.setStreamVolume (
AudioManager.STREAM_MUSIC,
audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
0);