Android 平添书签(二)

Android 添加书签(二)
运行结果:
Android 平添书签(二)

Bookmarker.java:
package com.iaiai;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageButton;
import android.widget.ListView;

/**
 * 
 * <p>
 * Title: Bookmarker.java
 * </p>
 * <p>
 * E-Mail: 176291935@qq.com
 * </p>
 * <p>
 * QQ: 176291935
 * </p>
 * <p>
 * Http: iaiai.iteye.com
 * </p>
 * <p>
 * Create time: 2011-8-29
 * </p>
 * 
 * @author 丸子
 * @version 0.0.1
 */
public class Bookmarker extends Activity implements OnItemClickListener,
		OnClickListener {
	
	/** Called when the activity is first created. */
	private ListView mListView;
	private BookmarkAdapter bookmarkAdapter;
	private ImageButton upButton;
	private ImageButton downButton;
	private ImageButton deleteButton;
	private ImageButton launchButton;
	private ImageButton topButton;
	private ImageButton bottomButton;
	private static final int DIALOG_YES_NO_MESSAGE = 1;
	private static final int DIALOG_WELCOME = 2;
	private int currentPos = -1;

	private static final String PREFS_NAME = "BookmarkerPrefs";
	private static final String LAUNCHED_KEY = "LAUNCHED";
	private static final int MENU_ABOUT = 0;
	private static final int MENU_ADD = 1;

	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		setContentView(R.layout.main);
		
		ContentValues inputValue = new ContentValues();  
        inputValue.put(android.provider.Browser.BookmarkColumns.BOOKMARK, 1);  
        inputValue.put(android.provider.Browser.BookmarkColumns.TITLE, "丸子");  
        inputValue.put(android.provider.Browser.BookmarkColumns.URL, "http://iaiai.iteye.com");                            
        ContentResolver cr = getContentResolver();  
        Uri uri = cr.insert(android.provider.Browser.BOOKMARKS_URI, inputValue);

		bookmarkAdapter = new BookmarkAdapter(this, "");// new
														// ArrayAdapter<Bookmark>(this,
														// android.R.layout.simple_list_item_single_choice,bookmarks);
		mListView = ((ListView) findViewById(R.id.mylistview));
		mListView.setAdapter(bookmarkAdapter);
		mListView.setOnItemClickListener(this);

		upButton = (ImageButton) findViewById(R.id.upbutton);
		downButton = (ImageButton) findViewById(R.id.downbutton);
		deleteButton = (ImageButton) findViewById(R.id.deletebutton);
		launchButton = (ImageButton) findViewById(R.id.launchbutton);
		topButton = (ImageButton) findViewById(R.id.topbutton);
		bottomButton = (ImageButton) findViewById(R.id.bottombutton);

		upButton.setOnClickListener(this);
		downButton.setOnClickListener(this);
		deleteButton.setOnClickListener(this);
		launchButton.setOnClickListener(this);
		topButton.setOnClickListener(this);
		bottomButton.setOnClickListener(this);

		// if we have some bookmarks select the first and start everything
		if (bookmarkAdapter.getCount() > 0) {
			bookmarkAdapter.initialiseAllRows();
			currentPos = 0;
			bookmarkAdapter.setSelected(currentPos);
		}

		// Make sure the welcome message only appears on first launch
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
		if (settings != null) {
			boolean launchedPreviously = settings.getBoolean(LAUNCHED_KEY,
					false);
			if (!launchedPreviously) {
				showDialog(DIALOG_WELCOME);
				SharedPreferences.Editor editor = settings.edit();
				editor.putBoolean(LAUNCHED_KEY, true);
				editor.commit();
			}
		}
	}

	/**
	 * Invoked during init to give the Activity a chance to set up its Menu.
	 * 
	 * @param menu
	 *            the Menu to which entries may be added
	 * @return true
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		menu.add(0, MENU_ADD, 0, "Add New Bookmark").setIcon(
				android.R.drawable.ic_menu_info_details);
		menu.add(0, MENU_ABOUT, 1, R.string.menu_about).setIcon(
				android.R.drawable.ic_menu_info_details);

		return true;
	}

	/**
	 * Invoked when the user selects an item from the Menu.
	 * 
	 * @param item
	 *            the Menu entry which was selected
	 * @return true if the Menu item was legit (and we consumed it), false
	 *         otherwise
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == MENU_ABOUT) {
			showDialog(DIALOG_WELCOME);
			return true;
		} else if (item.getItemId() == MENU_ADD) {
			Browser.saveBookmark(this, "New Bookmark", "http://");
			bookmarkAdapter.notifyDataSetChanged();
			return true;
		} else
			return false;
	}

	public void onItemClick(AdapterView<?> arg0, View view, int position,
			long id) {
		currentPos = position;
		bookmarkAdapter.setSelected(position);
	}

	public void onClick(View v) {

		if (v == upButton && currentPos > 0) {
			bookmarkAdapter.moveItemUp(currentPos);
			currentPos--;
		} else if (v == downButton && currentPos >= 0
				&& currentPos < bookmarkAdapter.getCount() - 1) {
			bookmarkAdapter.moveItemDown(currentPos);
			currentPos++;
		} else if (v == deleteButton) {
			showDialog(DIALOG_YES_NO_MESSAGE);
		} else if (v == topButton) {
			currentPos = bookmarkAdapter.moveToTop(currentPos);
		} else if (v == bottomButton) {
			currentPos = bookmarkAdapter.moveToBottom(currentPos);
		} else if (v == launchButton) {
			String url = bookmarkAdapter.getUrl(currentPos);
			if (url != null) {
				Intent i = new Intent("android.intent.action.VIEW",
						Uri.parse(url));
				startActivity(i);
			}
		}

	}

	protected Dialog onCreateDialog(int id) {
		if (id == DIALOG_YES_NO_MESSAGE) {
			return new AlertDialog.Builder(Bookmarker.this)
					.setIcon(android.R.drawable.ic_dialog_alert)
					.setTitle("Confirm Delete")
					.setMessage(
							"Are you sure you want to delete this bookmark?")
					.setPositiveButton("OK",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									bookmarkAdapter.deleteRow(currentPos);
								}
							})
					.setNegativeButton("Cancel",
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									// do nothing
								}
							}).create();
		} else if (id == DIALOG_WELCOME) {
			String message = getString(R.string.welcome);
			if (bookmarkAdapter.getCount() < 1) {
				message += getString(R.string.nomarks);
			}

			AlertDialog dialog = new AlertDialog.Builder(Bookmarker.this)
					.create();// new AlertDialog(Bookmarker.this);
			dialog.setTitle("Welcome");
			dialog.setMessage(message);
			dialog.setButton("OK", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int whichButton) {
					if (bookmarkAdapter.getCount() < 1) {
						finish();
					}
				}
			});

			dialog.setCancelable(true);
			return dialog;
		} else
			return null;
	}
}


BookmarkItem.java:
package com.iaiai;

import android.content.Context;
import android.graphics.Bitmap;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * 
 * <p>
 * Title: BookmarkItem.java
 * </p>
 * <p>
 * E-Mail: 176291935@qq.com
 * </p>
 * <p>
 * QQ: 176291935
 * </p>
 * <p>
 * Http: iaiai.iteye.com
 * </p>
 * <p>
 * Create time: 2011-8-29
 * </p>
 * 
 * @author 丸子
 * @version 0.0.1
 */
class BookmarkItem extends RelativeLayout {

	private TextView mTextView; // title
	private TextView mUrlText; // url
	private ImageView mImageView; // favicon
	private LayoutInflater mFactory;
	private RelativeLayout mBookmarkItem;

	/**
	 * Instantiate a bookmark item, including a default favicon.
	 * 
	 * @param context
	 *            The application context for the item.
	 */
	BookmarkItem(Context context) {
		super(context);

		mFactory = LayoutInflater.from(context);
		mFactory.inflate(R.layout.bookmark_item, this);
		mTextView = (TextView) findViewById(R.id.title);
		mUrlText = (TextView) findViewById(R.id.url);
		mImageView = (ImageView) findViewById(R.id.favicon);
		mBookmarkItem = (RelativeLayout) findViewById(R.id.bookmarkitem);
	}

	public void setSelected() {
		mBookmarkItem.setBackgroundColor(0xFFFF6633);
		mUrlText.setTextColor(0xFF000000);
	}

	public void setUnselected() {
		mBookmarkItem.setBackgroundColor(0xFF000000);
		mUrlText.setTextColor(0xFFAAAAAA);
	}

	/**
	 * Copy this BookmarkItem to item.
	 * 
	 * @param item
	 *            BookmarkItem to receive the info from this BookmarkItem.
	 */
	/* package */void copyTo(BookmarkItem item) {
		item.mTextView.setText(mTextView.getText());
		item.mUrlText.setText(mUrlText.getText());
		item.mImageView.setImageDrawable(mImageView.getDrawable());
	}

	/**
	 * Return the name assigned to this bookmark item.
	 */
	/* package */CharSequence getName() {
		return mTextView.getText();
	}

	/**
	 * Return the TextView which holds the name of this bookmark item.
	 */
	/* package */TextView getNameTextView() {
		return mTextView;
	}

	/**
	 * Set the favicon for this item.
	 * 
	 * @param b
	 *            The new bitmap for this item. If it is null, will use the
	 *            default.
	 */
	/* package */void setFavicon(Bitmap b) {
		if (b != null) {
			mImageView.setImageBitmap(b);
		} else {
			mImageView.setImageResource(R.drawable.app_web_browser_sm);
		}
	}

	/**
	 * Set the new name for the bookmark item.
	 * 
	 * @param name
	 *            The new name for the bookmark item.
	 */
	/* package */void setName(String name) {
		mTextView.setText(name);
	}

	/**
	 * Set the new url for the bookmark item.
	 * 
	 * @param url
	 *            The new url for the bookmark item.
	 */
	/* package */void setUrl(String url) {
		mUrlText.setText(url);
	}
}


BookmarkAdapter.java:
package com.iaiai;

import java.io.ByteArrayOutputStream;
import java.util.Date;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Browser;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebIconDatabase;
import android.webkit.WebIconDatabase.IconListener;
import android.widget.BaseAdapter;

/**
 * 
 * <p>
 * Title: BookmarkAdapter.java
 * </p>
 * <p>
 * E-Mail: 176291935@qq.com
 * </p>
 * <p>
 * QQ: 176291935
 * </p>
 * <p>
 * Http: iaiai.iteye.com
 * </p>
 * <p>
 * Create time: 2011-8-29
 * </p>
 * 
 * @author 丸子
 * @version 0.0.1
 */
class BookmarkAdapter extends BaseAdapter {

	private Cursor mCursor;
	private int mCount;
	private ContentResolver mContentResolver;
	private ChangeObserver mChangeObserver;
	private DataSetObserver mDataSetObserver;
	private boolean mDataValid;
	private Bookmarker mBookmarker;

	// Implementation of WebIconDatabase.IconListener
	private class IconReceiver implements IconListener {
		public void onReceivedIcon(String url, Bitmap icon) {
			updateBookmarkFavicon(mContentResolver, url, icon);
		}
	}

	// Instance of IconReceiver
	private final IconReceiver mIconReceiver = new IconReceiver();

	/**
	 * Create a new BrowserBookmarksAdapter.
	 * 
	 * @param b
	 *            BrowserBookmarksPage that instantiated this. Necessary so it
	 *            will adjust its focus appropriately after a search.
	 */
	public BookmarkAdapter(Bookmarker b, String curPage) {
		this(b, curPage, false);
	}

	/**
	 * Create a new BrowserBookmarksAdapter.
	 * 
	 * @param b
	 *            BrowserBookmarksPage that instantiated this. Necessary so it
	 *            will adjust its focus appropriately after a search.
	 */
	public BookmarkAdapter(Bookmarker b, String curPage, boolean createShortcut) {
		mDataValid = false;
		mBookmarker = b;
		mContentResolver = b.getContentResolver();
		mChangeObserver = new ChangeObserver();
		mDataSetObserver = new MyDataSetObserver();
		// FIXME: Should have a default sort order that the user selects.
		search();
		// FIXME: This requires another query of the database after the
		// initial search(null). Can we optimize this?
		Browser.requestAllIcons(mContentResolver,
				Browser.BookmarkColumns.FAVICON + " is NULL AND "
						+ Browser.BookmarkColumns.BOOKMARK + " == 1",
				mIconReceiver);
	}

	public void initialiseAllRows() {
		int processed = 0;

		// ensures each row has a created time
		while (processed < mCount) {
			for (int i = 0; i < mCount; i++) {
				Bundle thisRow = getRow(i, true);
				Long thisCreateTime = thisRow
						.getLong(Browser.BookmarkColumns.CREATED);
				if (thisCreateTime == null || thisCreateTime < 900) {
					// get createTime of row above and subtract 1000;
					long createTime = (mCount - i) * 1000;
					if (i > 0) {
						Bundle prevRow = getRow(i, true);
						Long prevCreateTime = prevRow
								.getLong(Browser.BookmarkColumns.CREATED);
						if (prevCreateTime != null && prevCreateTime > 1001) {
							createTime = prevCreateTime - 1000;
						}
					}

					thisRow.putLong(Browser.BookmarkColumns.CREATED, createTime);
					updateRow(thisRow, true);
					break;// underlying dataset has updated so quit the loop and
							// start again
				}
				processed = i + 1;
			}
		}
	}

	/**
	 * Return a hashmap with one row's Title, Url, and favicon.
	 * 
	 * @param position
	 *            Position in the list.
	 * @return Bundle Stores title, url of row position, favicon, and id for the
	 *         url. Return a blank map if position is out of range.
	 */
	public Bundle getRow(int position, boolean includeCreateTime) {
		Bundle map = new Bundle();
		if (position < 0 || position >= mCount) {
			return map;
		}
		mCursor.moveToPosition(position);
		String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
		map.putString(Browser.BookmarkColumns.TITLE,
				mCursor.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX));
		map.putString(Browser.BookmarkColumns.URL, url);
		byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX);
		if (data != null) {
			map.putByteArray("FAV_BYTES", data);
			map.putParcelable(Browser.BookmarkColumns.FAVICON,
					BitmapFactory.decodeByteArray(data, 0, data.length));
		}

		if (includeCreateTime && createdColumnIndex != -1) {
			Long createTime = mCursor.getLong(createdColumnIndex);
			if (createTime != null && createTime != 0) {
				map.putLong(Browser.BookmarkColumns.CREATED, createTime);
			}
		}

		map.putInt("id", mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));

		return map;
	}

	/**
	 * Update a row in the database with new information. Requeries the database
	 * if the information has changed.
	 * 
	 * @param map
	 *            Bundle storing id, title and url of new information
	 */
	public void updateRow(Bundle map, boolean includeCreateTime) {

		// Find the record
		int id = map.getInt("id");
		int position = -1;
		for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor
				.moveToNext()) {
			if (mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX) == id) {
				position = mCursor.getPosition();
				break;
			}
		}
		if (position < 0) {
			return;
		}

		mCursor.moveToPosition(position);
		ContentValues values = new ContentValues();
		String title = map.getString(Browser.BookmarkColumns.TITLE);
		if (!title.equals(mCursor
				.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX))) {
			values.put(Browser.BookmarkColumns.TITLE, title);
		}
		String url = map.getString(Browser.BookmarkColumns.URL);
		if (!url.equals(mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX))) {
			values.put(Browser.BookmarkColumns.URL, url);
		}

		byte[] favicon = map.getByteArray("FAV_BYTES");
		if (favicon != null) {
			values.put(Browser.BookmarkColumns.FAVICON, favicon);
		}

		if (includeCreateTime) {
			Long createTime = map.getLong(Browser.BookmarkColumns.CREATED);
			if (createTime != null)
				values.put(Browser.BookmarkColumns.CREATED, createTime);
		}

		if (values.size() > 0
				&& mContentResolver.update(Browser.BOOKMARKS_URI, values,
						"_id = " + id, null) != -1) {
			refreshList();
		}
	}

	/**
	 * Delete a row from the database. Requeries the database. Does nothing if
	 * the provided position is out of range.
	 * 
	 * @param position
	 *            Position in the list.
	 */
	public void deleteRow(int position) {
		if (position < 0 || position >= getCount()) {
			return;
		}
		mCursor.moveToPosition(position);
		String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
		WebIconDatabase.getInstance().releaseIconForPageUrl(url);
		Uri uri = ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
				mCursor.getInt(Browser.HISTORY_PROJECTION_ID_INDEX));
		int numVisits = mCursor.getInt(Browser.HISTORY_PROJECTION_VISITS_INDEX);
		if (0 == numVisits) {
			mContentResolver.delete(uri, null, null);
		} else {
			// It is no longer a bookmark, but it is still a visited site.
			ContentValues values = new ContentValues();
			values.put(Browser.BookmarkColumns.BOOKMARK, 0);
			mContentResolver.update(uri, values, null, null);
		}
		refreshList();
	}

	/**
	 * Refresh list to recognize a change in the database.
	 */
	public void refreshList() {
		searchInternal();
	}

	/**
	 * Search the database for bookmarks that match the input string.
	 * 
	 * @param like
	 *            String to use to search the database. Strings with spaces are
	 *            treated as having multiple search terms using the OR operator.
	 *            Search both the title and url.
	 */
	public void search() {
		searchInternal();
	}

	/**
	 * Update the bookmark's favicon.
	 * 
	 * @param cr
	 *            The ContentResolver to use.
	 * @param url
	 *            The url of the bookmark to update.
	 * @param favicon
	 *            The favicon bitmap to write to the db.
	 */
	/* package */static void updateBookmarkFavicon(ContentResolver cr,
			String url, Bitmap favicon) {
		if (url == null || favicon == null) {
			return;
		}
		// Strip the query.
		int query = url.indexOf('?');
		String noQuery = url;
		if (query != -1) {
			noQuery = url.substring(0, query);
		}
		url = noQuery + '?';
		// Use noQuery to search for the base url (i.e. if the url is
		// http://www.yahoo.com/?rs=1, search for http://www.yahoo.com)
		// Use url to match the base url with other queries (i.e. if the url is
		// http://www.google.com/m, search for
		// http://www.google.com/m?some_query)
		final String[] selArgs = new String[] { noQuery, url };
		final String where = "(" + Browser.BookmarkColumns.URL + " == ? OR "
				+ Browser.BookmarkColumns.URL + " GLOB ? || '*') AND "
				+ Browser.BookmarkColumns.BOOKMARK + " == 1";
		final String[] projection = new String[] { Browser.BookmarkColumns._ID };
		final Cursor c = cr.query(Browser.BOOKMARKS_URI, projection, where,
				selArgs, null);
		boolean succeed = c.moveToFirst();
		ContentValues values = null;
		while (succeed) {
			if (values == null) {
				final ByteArrayOutputStream os = new ByteArrayOutputStream();
				favicon.compress(Bitmap.CompressFormat.PNG, 100, os);
				values = new ContentValues();
				values.put(Browser.BookmarkColumns.FAVICON, os.toByteArray());
			}
			cr.update(
					ContentUris.withAppendedId(Browser.BOOKMARKS_URI,
							c.getInt(0)), values, null, null);
			succeed = c.moveToNext();
		}
		c.close();
	}

	/**
	 * Internal function used in search, sort, and refreshList.
	 */
	private int createdColumnIndex = -1;

	private void searchInternal() {
		if (mCursor != null) {
			mCursor.unregisterContentObserver(mChangeObserver);
			mCursor.unregisterDataSetObserver(mDataSetObserver);
			mCursor.deactivate();
		}

		// need to add the created date column to the query
		String[] columns = new String[Browser.HISTORY_PROJECTION.length + 1];
		System.arraycopy(Browser.HISTORY_PROJECTION, 0, columns, 0,
				Browser.HISTORY_PROJECTION.length);
		createdColumnIndex = columns.length - 1;
		columns[createdColumnIndex] = Browser.BookmarkColumns.CREATED;

		String whereClause = Browser.BookmarkColumns.BOOKMARK + " == 1";
		String orderBy = Browser.BookmarkColumns.CREATED + " DESC";
		String[] selectionArgs = null;
		mCursor = mContentResolver.query(Browser.BOOKMARKS_URI, columns,
				whereClause, selectionArgs, orderBy);
		mCursor.registerContentObserver(mChangeObserver);
		mCursor.registerDataSetObserver(mDataSetObserver);

		mDataValid = true;
		notifyDataSetChanged();

		mCount = mCursor.getCount();
	}

	/**
	 * How many items should be displayed in the list.
	 * 
	 * @return Count of items.
	 */
	public int getCount() {
		if (mDataValid) {
			return mCount;
		} else {
			return 0;
		}
	}

	public boolean areAllItemsEnabled() {
		return true;
	}

	public boolean isEnabled(int position) {
		return true;
	}

	/**
	 * Get the data associated with the specified position in the list.
	 * 
	 * @param position
	 *            Index of the item whose data we want.
	 * @return The data at the specified position.
	 */
	public Object getItem(int position) {
		return null;
	}

	/**
	 * Get the row id associated with the specified position in the list.
	 * 
	 * @param position
	 *            Index of the item whose row id we want.
	 * @return The id of the item at the specified position.
	 */
	public long getItemId(int position) {
		return position;
	}

	int mCurrentSelection = -1;

	public void setSelected(int position) {
		mCurrentSelection = position;
		notifyDataSetInvalidated();
	}

	/**
	 * Get a View that displays the data at the specified position in the list.
	 * 
	 * @param position
	 *            Index of the item whose view we want.
	 * @return A View corresponding to the data at the specified position.
	 */
	public View getView(int position, View convertView, ViewGroup parent) {
		if (!mDataValid) {
			throw new IllegalStateException(
					"this should only be called when the cursor is valid");
		}
		if (position < 0 || position > mCount) {
			throw new AssertionError(
					"BrowserBookmarksAdapter tried to get a view out of range");
		}

		if (convertView == null) {
			convertView = new BookmarkItem(mBookmarker);
		}
		bind((BookmarkItem) convertView, position);
		if (position == mCurrentSelection) {
			((BookmarkItem) convertView).setSelected();
		} else {
			((BookmarkItem) convertView).setUnselected();
		}

		return convertView;
	}

	/**
	 * Return the title for this item in the list.
	 */
	public String getTitle(int position) {
		return getString(Browser.HISTORY_PROJECTION_TITLE_INDEX, position);
	}

	/**
	 * Return the Url for this item in the list.
	 */
	public String getUrl(int position) {
		return getString(Browser.HISTORY_PROJECTION_URL_INDEX, position);
	}

	/**
	 * Private helper function to return the title or url.
	 */
	private String getString(int cursorIndex, int position) {
		if (position < 0 || position > mCount) {
			return "";
		}
		mCursor.moveToPosition(position);
		return mCursor.getString(cursorIndex);
	}

	private void bind(BookmarkItem b, int position) {
		mCursor.moveToPosition(position);

		String title = mCursor
				.getString(Browser.HISTORY_PROJECTION_TITLE_INDEX);
		b.setName(title);
		String url = mCursor.getString(Browser.HISTORY_PROJECTION_URL_INDEX);
		b.setUrl(url);
		byte[] data = mCursor.getBlob(Browser.HISTORY_PROJECTION_FAVICON_INDEX);
		if (data != null) {
			b.setFavicon(BitmapFactory.decodeByteArray(data, 0, data.length));
		} else {
			b.setFavicon(null);
		}
	}

	private class ChangeObserver extends ContentObserver {
		public ChangeObserver() {
			super(new Handler());
		}

		@Override
		public boolean deliverSelfNotifications() {
			return true;
		}

		@Override
		public void onChange(boolean selfChange) {
			refreshList();
		}
	}

	private class MyDataSetObserver extends DataSetObserver {
		@Override
		public void onChanged() {
			mDataValid = true;
			notifyDataSetChanged();
		}

		@Override
		public void onInvalidated() {
			mDataValid = false;
			notifyDataSetInvalidated();
		}
	}

	// returns new position
	public int moveItemUp(int position) {
		if (position > 0) {
			Bundle rowOnTop = getRow(position - 1, false);
			Bundle rowUnder = getRow(position, false);

			int upId = rowOnTop.getInt("id");
			rowOnTop.putInt("id", rowUnder.getInt("id"));
			rowUnder.putInt("id", upId);

			mCurrentSelection--;

			updateRow(rowOnTop, false);
			updateRow(rowUnder, false);

			return mCurrentSelection;
		} else
			return position;
	}

	// returns new position
	public int moveItemDown(int position) {
		if (position < mCount - 1) {
			Bundle rowOnTop = getRow(position, false);
			Bundle rowUnder = getRow(position + 1, false);

			int upId = rowOnTop.getInt("id");
			rowOnTop.putInt("id", rowUnder.getInt("id"));
			rowUnder.putInt("id", upId);

			mCurrentSelection++;
			updateRow(rowOnTop, false);
			updateRow(rowUnder, false);

			return mCurrentSelection;
		} else
			return position;
	}

	// returns new position
	public int moveToTop(int position) {
		if (position > 0) {
			// get the row
			Bundle rowToMove = getRow(position, false);
			// set its created time to now
			rowToMove.putLong(Browser.BookmarkColumns.CREATED,
					new Date().getTime());
			// update the row
			updateRow(rowToMove, true);

			mCurrentSelection = 0;
			return mCurrentSelection;
		} else
			return position;
	}

	// returns new post
	public int moveToBottom(int position) {
		if (position < mCount - 1) {
			// get the time the last row in the list was created
			Bundle lastRow = getRow(mCount - 1, true);
			Long createTime = lastRow.getLong(Browser.BookmarkColumns.CREATED);

			long newCreateTime = (createTime == null || createTime == 0) ? 0
					: createTime - 1;
			// set the create time of our row to older than the last on the list
			Bundle rowToMove = getRow(position, false);
			rowToMove.putLong(Browser.BookmarkColumns.CREATED, newCreateTime);
			updateRow(rowToMove, true);

			mCurrentSelection = mCount - 1;
			return mCurrentSelection;
		} else
			return position;
	}

	public String launchUrlOfItem(int position) {
		try {
			return getRow(position, false).getString(
					Browser.BookmarkColumns.URL);
		} catch (Exception e) {
			return null;
		}
	}
}


bookmark_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="wrap_content"
	android:paddingLeft="10dip" android:paddingTop="5dip"
	android:paddingBottom="5dip" android:id="@+id/bookmarkitem">
	<ImageView android:id="@+id/favicon" android:layout_width="20dip"
		android:layout_height="20dip" android:focusable="false"
		android:padding="2dip" android:layout_marginTop="4dip"
		android:layout_marginRight="6dip" android:layout_alignParentTop="true"
		android:background="@drawable/fav_icn_background" android:src="@drawable/app_web_browser_sm"
		android:layout_alignParentLeft="true" />
	<TextView android:id="@+id/title" android:textAppearance="?android:attr/textAppearanceMedium"
		android:maxLines="1" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_toRightOf="@+id/favicon"
		android:ellipsize="end" />
	<TextView android:id="@+id/url" android:textAppearance="?android:attr/textAppearanceSmall"
		android:maxLines="1" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_below="@+id/title"
		android:layout_toRightOf="@+id/favicon" android:ellipsize="end" />
</RelativeLayout>


main.xml:
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout 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:layout_width="fill_parent"
		android:layout_height="wrap_content" />
	<ImageView android:id="@+id/imageview" android:layout_width="70px"
		android:layout_height="427px" android:layout_x="250px"
		android:layout_y="0px" android:background="#66FFFFFF">
	</ImageView>
	<ImageButton android:id="@+id/topbutton"
		android:layout_width="64px" android:layout_height="60px" android:src="@drawable/top"
		android:layout_x="252px" android:layout_y="5px" android:scaleType="fitCenter"></ImageButton>
	<ImageButton android:id="@+id/upbutton"
		android:layout_width="64px" android:layout_height="60px" android:src="@drawable/up"
		android:layout_x="252px" android:layout_y="70px" android:scaleType="fitCenter"></ImageButton>
	<ImageButton android:id="@+id/downbutton" android:src="@drawable/down"
		android:layout_width="64px" android:layout_height="60px"
		android:layout_x="252px" android:layout_y="135px" android:scaleType="fitCenter"></ImageButton>
	<ImageButton android:id="@+id/bottombutton" android:src="@drawable/bottom"
		android:layout_width="64px" android:layout_height="60px"
		android:layout_x="252px" android:layout_y="200px" android:scaleType="fitCenter"></ImageButton>
	<ImageButton android:id="@+id/launchbutton" android:src="@drawable/launch"
		android:layout_width="64px" android:layout_height="60px"
		android:layout_x="252px" android:layout_y="305px" android:scaleType="fitCenter"></ImageButton>
	<ImageButton android:id="@+id/deletebutton"
		android:layout_width="64px" android:layout_height="60px" android:src="@drawable/trash"
		android:textSize="12sp" android:layout_x="252px" android:layout_y="370px">
	</ImageButton>
</AbsoluteLayout>


strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">丸子</string>
<string name="welcome">Welcome to Bookmarker.\n\nBookmarker is a very simple app which allows you to reorder and delete the bookmarks used in the G1\'s web browser.\nSimple.</string>
<string name="nomarks">\n\nYou don\'t have any bookmarks at the moment, Bookmarker will now close.</string>
<string name="menu_about">About</string>
</resources>


下面是用到的一些图片:
Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)Android 平添书签(二)
1 楼 573842281 2012-02-17  
博主很是强啊,值得学习!
2 楼 maoxy 2012-05-08  
获取书签图标的时候,得到的只是个LOGO,但是浏览器里面显示的图片确实网站的缩略图,这个是怎么弄的?