Tuesday 31 July 2012

Contact Me 2012

If you need to contact me for any reason, just add a comment to this post. Please include contact information if you need me to get back to you. I will not post any personal information to this thread.

Monday 30 July 2012

Introducing Google Fiber: The Next Chapter of the Internet



Google Fiber starts with Internet speeds 100 times faster than what most Americans have today. In this video, we see the evolution of the Internet represented in three different stages. It started with Dial-Up, grew with Broadband -- and with Google Fiber, the possibilities are endless. Pre-register now at http://www.google.com/fiber

About the Video:
The cars and model installation were built by hand, and filmed at Agua Dolce airport in Santa Clarita, CA. The installation now resides in the Fiber Space in Kansas City. For more info about the Fiber Space, please visit http://google.com/fiber/fiberspace


JQuery Accordion Style vi Cheat Sheet

JQuery LogoOver the weekend I was able to complete an accordion style vi cheat sheet using JQuery on the Tools section of my web site. It is based on a really good cheat sheet that is linked at the bottom of my page. I just added some style changes, reordered it and made it expand or collapse sections.



If I can find the time, I'll try to make a offline version of the cheat sheet using HTML5.

Introduction to Android App Development for the Kindle Fire


Get Started Fast with Android App Development for Amazon�s Best-Selling Kindle Fire!

Practically overnight, the Amazon Kindle Fire has become the world�s top-selling Android-based tablet. Now, in this electronic-only mini-book, expert Android developers provide an introduction to the basics of Kindle Fire development.

Lauren Darcey and Shane Conder first introduce you to Android and walk you through installing its latest development tools. Next, you�ll learn the essential design principles you need to write Android Kindle Fire apps, discover how Android applications are structured and configured, and walk through incorporating user interfaces and other application resources into your projects.

It�s simply the fastest way to start developing apps for today�s hottest Android tablet!


Get Current Stories/News with DuckDuckGo

Webkit icon
One thing that is not obvious with the DuckDuckGo search engine is how do I get a list of new links on a subject? It is pretty easy. Just add the following to text to the end of your query: sort:date



For example to search for the latest JQuery stories I could submit: jquery sort:date.

Apply LruCache on GridView

The example "Caching Bitmaps with LruCache" provide a example to Apply LruCache on Gallery. This exercise is GridView version using LruCache.

Reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache

GridView with LruCache


To using android.util.LruCache in the code, targetSdkVersion of "13" is needed to be specified in AndroidManifest.xml.

The layout file, refer to the post "GridView loading photos from SD Card".

package com.example.androidgridview;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

public class ImageAdapter extends BaseAdapter {

private Context mContext;
ArrayList<String> itemList = new ArrayList<String>();

public ImageAdapter(Context c) {
mContext = c;
}

void add(String path){
itemList.add(path);
}

@Override
public int getCount() {
return itemList.size();
}

@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}

//Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);

// Use the path as the key to LruCache
final String imageKey = itemList.get(position);
final Bitmap bm = getBitmapFromMemCache(imageKey);

if (bm == null){
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(imageKey);
};

imageView.setImageBitmap(bm);
return imageView;
}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {

Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(

BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{

private final WeakReference<ImageView> imageViewReference;

public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}

@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}

}

ImageAdapter myImageAdapter;

private LruCache<String, Bitmap> mMemoryCache;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myImageAdapter.add(file.getAbsolutePath());
}

// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass
= ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
.getMemoryClass();

// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}

public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mMemoryCache.get(key);
}

}


Download the files.


Sunday 29 July 2012

Factory images of Jelly Bean for Galaxy Nexus and Nexus S released

Factory Images for Nexus Devices updated with Factory images of Jelly Bean for Galaxy Nexus and Nexus S.

Factory Images for Nexus Devices


You will find these files useful if you have used the Android Open-Source Project, flashed custom builds on your device, and wish to return that device to its factory state.

Gallery-like single column GridView

by changing android:numColumns="1", the last exercise of "GridView" can be modified to a Gallery-like single column GridView.

Gallery-like single column GridView


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<GridView
android:id="@+id/gridview"
android:layout_width="130dp"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="1"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
android:background="@android:color/background_dark"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@android:color/background_light">
</LinearLayout>
</LinearLayout>


GridView loading photos from SD Card

Previous exercises described how to implement "Gallery-like HorizontalScrollView" and "Vertical Gallery-like ScrollView". Alternatively, it can be displayed in GridView.

GridView


Add a <GridView> in layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<GridView
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"/>

</LinearLayout>


Main code:
package com.example.androidgridview;

import java.io.File;
import java.util.ArrayList;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends Activity {

public class ImageAdapter extends BaseAdapter {

private Context mContext;
ArrayList<String> itemList = new ArrayList<String>();

public ImageAdapter(Context c) {
mContext = c;
}

void add(String path){
itemList.add(path);
}

@Override
public int getCount() {
return itemList.size();
}

@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}

@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) { // if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}

Bitmap bm = decodeSampledBitmapFromUri(itemList.get(position), 220, 220);

imageView.setImageBitmap(bm);
return imageView;
}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {

Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(

BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

}

ImageAdapter myImageAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

GridView gridview = (GridView) findViewById(R.id.gridview);
myImageAdapter = new ImageAdapter(this);
gridview.setAdapter(myImageAdapter);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myImageAdapter.add(file.getAbsolutePath());
}
}

}


Download the files.

Next:
- Gallery-like single column GridView
- Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method


Wednesday 25 July 2012

Multiple $(document).ready(function() with JQuery

JQuery LogoDid you know you can call JQuery's $(document).ready(function() multiple times? Well yes, you can according to the documentation.



I have playing with templating systems for both Abbey Workshop and Blue Sky Workshop. With those systems, I typically have some JQuery code in the header of the template. Consequently, that code is on every page. But what if you want to add some more JavaScript that does something completely different? Well no problem, just add to the content to the page and add a <script> tag wherever you want it. Pretty cool!

Mac OS X Mountain Lion (10.8) is out



It's here! Mac OS X Mountain Lion is available on the Mac App Store. CNET has a review.A lot more iOS and iCloud integration. So apps like iMessage and Reminder will also be on your desktop, making them a lot more useful.



As always, I will be waiting for the first major patch before I start installing it.

Sea Change for Big Networks?

Internet IconVMWare's $1B purchase of Nicira Networks was big news. Why? That was my question. Remember that new OpenFlow technology for managing large networks? Well Nicira is one of the first companies to build a product based on OpenFlow. This technology is aimed directly at Cisco and Juniper networks. One of the original investors explains.



Now if the big guys are smart, they will quickly embrace this technology and come out with their own products and thusly crush their smaller competitors. It will be interesting to see if that happens.

Monday 23 July 2012

Internet Usage by Operating System

Internet IconI found this interesting so I thought I would share:

Usage share of Operating Systems



For my next project at work, I was wondering what operating system to use for any activities. The original course, which this content is based off of, was written for Solaris. But I can't imagine most potential students would have that. So one way of determining operating system usage is to look at statistics gathered by browsers. So the link shows that data for Wikipedia and some other sites that gather these sort of statistics.



Some observations:

  • Windows usage is still around around 70% with most users on Windows 7 and XP.

  • iPhone plus iPad plus Android share is already over 10%. Smaller than I thought it would be, but if you think world wide usage it makes a lot of sense. Smart phones and tablets are just beginning to penetrate everywhere. And tablets are really gonna take off with 7" form factors and $200 list prices like the Google Nexus 7.

  • Mac OS X combined with Linux is only around 10%.

As another data point, I looked at the stats from one of my web sites (abbeyworkshop.com) to see what the geeks are using during the last year. Windows is still strong at 62%, but Mac OS X plus Linux jumps up to 36%. Way to go geeks!



Anyway, it looks like Windows 7 and XP are still my primary targets. But maybe I should think about Linux in the future.

Saturday 21 July 2012

New Designs for the Blog

The template design has been updated as of this afternoon. Basically, the site is using the Blogger Picture Window template with the following tweeks.





  • Changed the background to a picture I have been using for years. (It has more sky.)

  • Added gray text shadow to the blog title and description. This makes the text pop a little more given the background has similar colors.

  • Updated the Mobile template to use the Mobile version of Picture Window. So the site should look a lot beetter on a phone now.

Let me know if you have any questions or comments. Hopefully things looks a little better.

Vertical Gallery-like ScrollView

With the custom LinearLayout (MyHorizontalLayout.java) in last exercise "Implement custom LinearLayout for Gallery-like HorizontalScrollView", it can be implement vertical Gallery-like ScrollView also.

Vertical Gallery-like ScrollView


Keep both MainActivity.java and MyHorizontalLayout.java of last exercise no change.

Modify the layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<ScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent" >
<com.example.androidhorizontalscrollviewgallery.MyHorizontalLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
/>
</ScrollView>

</LinearLayout>


Related:
- GridView loading photos from SD Card


Friday 20 July 2012

Implement custom LinearLayout for Gallery-like HorizontalScrollView

Last exercise explain the basic to "implement Gallery-like HorizontalScrollView". In this post, we are going to implement our custom LinearLayout for Gallery-like HorizontalScrollView.

Custom LinearLayout for Gallery-like HorizontalScrollView


MyHorizontalLayout.java, our custom LinearLayout for Gallery-like HorizontalScrollView.
package com.example.androidhorizontalscrollviewgallery;
import java.util.ArrayList;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MyHorizontalLayout extends LinearLayout {

Context myContext;
ArrayList<String> itemList = new ArrayList<String>();

public MyHorizontalLayout(Context context) {
super(context);
myContext = context;
}

public MyHorizontalLayout(Context context, AttributeSet attrs) {
super(context, attrs);
myContext = context;
}

public MyHorizontalLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
myContext = context;
}

void add(String path){
int newIdx = itemList.size();
itemList.add(path);
addView(getImageView(newIdx));
}

ImageView getImageView(int i){
Bitmap bm = null;
if (i < itemList.size()){
bm = decodeSampledBitmapFromUri(itemList.get(i), 220, 220);
}

ImageView imageView = new ImageView(myContext);
imageView.setLayoutParams(new LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);

return imageView;
}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(

BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

}


Modify layout to include MyHorizontalLayout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.example.androidhorizontalscrollviewgallery.MyHorizontalLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>

</LinearLayout>


See how simple is the main code:
package com.example.androidhorizontalscrollviewgallery;

import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.widget.Toast;

public class MainActivity extends Activity {

MyHorizontalLayout myHorizontalLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myHorizontalLayout = (MyHorizontalLayout)findViewById(R.id.mygallery);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myHorizontalLayout.add(file.getAbsolutePath());
}
}

}


Download the files.

Next:
- Vertical Gallery-like ScrollView

Related:
- GridView loading photos from SD Card


Implement Gallery-like HorizontalScrollView

As mentioned in the post "Implement Android Gallery widget" - android.widget.Gallery is no longer supported. Other horizontally scrolling widgets include HorizontalScrollView and ViewPager from the support library.

It's a example to implement Gallery-like view using HorizontalScrollView. Please note that in this example, the bitmaps in HorizontalScrollView will not be removed even not in screen. So if too much bitmaps loaded, error of java.lang.OutOfMemoryError will be thrown!

Gallery-like HorizontalScrollView


Add HorizontalScrollView in layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:id="@+id/mygallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
</HorizontalScrollView>

</LinearLayout>


Main Java code:
package com.example.androidhorizontalscrollviewgallery;

import java.io.File;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends Activity {

LinearLayout myGallery;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myGallery = (LinearLayout)findViewById(R.id.mygallery);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myGallery.addView(insertPhoto(file.getAbsolutePath()));
}
}

View insertPhoto(String path){
Bitmap bm = decodeSampledBitmapFromUri(path, 220, 220);

LinearLayout layout = new LinearLayout(getApplicationContext());
layout.setLayoutParams(new LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);

ImageView imageView = new ImageView(getApplicationContext());
imageView.setLayoutParams(new LayoutParams(220, 220));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);

layout.addView(imageView);
return layout;
}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(

BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

}


Download the files.

Next:
- Implement custom LinearLayout for Gallery-like HorizontalScrollView


Power Saver Button for iPad

Since getting an iPad 3 with LTE earlier this year, I have noticed that the battery runs down much faster than my old Wifi only iPad. It seems likely this is caused by the iPad's constantly checking with the cell tower, GPS, and other services. With the iPad in ready mode, the battery runs down about 10% or more per day.



So I was thinking, there must be an app I can use to save battery life. Then it struck me, what about Airplane Mode. The Airplane Mode setting turns off all communication systems on the device (Wifi, 4G, GPS). So last night, I turned on Airplane Mode and left my iPad in standby. This morning, the device only had lost 2% of its charge.



So it seems like to me, the power saver mode is built in already. :)


Aurora Shootings

My thoughts and prayers go out to those affected by the shootings in Aurora last night. It is always very sobering when something like this happens 20 miles from your house.

Thursday 19 July 2012

New Java Training Blog

Duke WavingAfter my previous post, I would be remiss in not pointing out a new blog for my team. The Java training developers at Oracle have setup the Java Training Beat blog.



The blog will include posts on new tutorials and products created by our team. In addition, we will include any other goodies that will help you get to know Java a little bit better. Feel free to bookmark the site and watch for updates.



Of course I will continue to post here with my insights such as they are. :)


Java Command Line Performance Tools

Duke WavingWell most of my friends don't get to see my work. My main gig is creating customer Java training for Oracle. For example, the main Java programming course: Java SE 7 Programming.



Well my current assignment is to create some free content for the Oracle Learning Library. So my first tutorial is out:

Java SE 7: Reviewing JVM Performance Command Line Tools

OLL Entry | Tutorial Link



The tutorial covers some of the built in command line tools included with Java 7. With the tools, you can query running JVMs and find out a great deal of information about them. So if this sounds interesting check it out.

Dialog Animation using windowAnimations

In this exercise, we are going to apply slide-in and slide-out animation on dialog, using windowAnimations.


Create/modify /res/values/styles.xml to add animation style of DialogAnimation, using build-in animation of slide_in_left and slide_out_right.
<resources>

<style name="AppTheme" parent="android:Theme.Light" />

<style name="DialogAnimation">
<item name="android:windowEnterAnimation">@android:anim/slide_in_left</item>
<item name="android:windowExitAnimation">@android:anim/slide_out_right</item>
</style>
</resources>


Implement our dialog layout, /res/layout/dialoglayout.xml.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<Button
android:id="@+id/dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="dismiss"/>

</LinearLayout>


MainActivity.java
package com.example.animationdialog;

import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.app.Activity;
import android.app.Dialog;

public class MainActivity extends Activity {

Button btnOpenDialog;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOpenDialog = (Button)findViewById(R.id.opendialog);
btnOpenDialog.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
openDialog();
}});
}

private void openDialog(){
final Dialog dialog = new Dialog(MainActivity.this);
dialog.setTitle("Animation Dialog");
dialog.setContentView(R.layout.dialoglayout);
dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
Button btnDismiss = (Button)dialog.getWindow().findViewById(R.id.dismiss);

btnDismiss.setOnClickListener(new OnClickListener(){

@Override
public void onClick(View v) {
dialog.dismiss();
}});

dialog.show();
}

}


Main layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
android:id="@+id/opendialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:padding="@dimen/padding_medium"
android:text="Open Dialog"
tools:context=".MainActivity" />

</RelativeLayout>

Download the files.


Android SDK Tools Revision 20.0.1, NDK revision 8b, and ADT 20.0.1 released

Android SDK Tools Revision 20.0.1, full SDK for Android 4.1, released. You can now develop and publish applications against API level 16 using new Jelly Bean APIs. The new update can be downloaded through SDK Manager. Also updated are NDK revision 8b and ADT Plugin 20.0.1.

Update SDK on Eclipse:

Please note that note that the SDK Tools r20.0.1 is designed for use with ADT 20.0.1 and later. To update ADT in Eclipse, click Help -> Check for updates, to update ADT.

After updated ADT, click Window -> Android SDK Manager to install updated components.



Wednesday 18 July 2012

Create your own Evening Newspaper



Ever want to make your own Drudge Report or similar news site? ReadWriteWeb has a story on Mule Design, a web consulting company, that decided to do just that. They wanted to create a daily summary of news that emulated the evening additions published by newspapers. So instead of creating a super busy mess, like most news sites, they created Evening Edition.



Evening Edition is a very simple site with only a handful of stories listed. The excellent design makes the site easy to read. Definitely a concept worth emulating. And heck, anyone could come up with a similar design and start their own evening addition. Which would be kinda cool.



Now I am not convinced this is a huge break-thru that will save all newspapers. However, I think it does highlight the fact that often, less is more. I am struck at how awful most newspaper sites and newspaper apps generally are. The designs are busy with pop overs and pop unders and text everywhere. Just think how much more traffic they could get if they emulated this design.

Tuesday 17 July 2012

Learn a Language while you Surf

Google LogoRan across this video on CNet today. It gives a pointer to this language immersion extension for Chrome.



Essentially it allows the plugin to insert foreign words and phrases into the pages you browse. The context should help you learn the language quickly. Anyway, looks like it could be very cool and well worth sharing.

Implement GestureDetector/OnGestureListener to detect Fling, and ObjectAnimator for ImageView

In this exercise, I will implement GestureDetector with OnGestureListener for a ImageView of icon. Once the user fling on the icon, it will be moved (animated using ObjectAnimator).



package com.example.androidanimatefling;

import android.os.Bundle;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {

TextView info;
ImageView flingObj;
FrameLayout mainScreen;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

info = (TextView)findViewById(R.id.info);
flingObj = (ImageView)findViewById(R.id.flingobject);
mainScreen = (FrameLayout)findViewById(R.id.mainscreen);

final GestureDetector myGesture = new GestureDetector(this, new MyOnGestureListener());

flingObj.setOnTouchListener(new OnTouchListener(){

@Override
public boolean onTouch(View v, MotionEvent event) {
return myGesture.onTouchEvent(event);
}});

flingObj.setClickable(true);

}

class MyOnGestureListener implements OnGestureListener{

int MIN_DIST = 100;

@Override
public boolean onDown(MotionEvent arg0) {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
float e1X = e1.getX();
float e1Y = e1.getY();
float e2X = e2.getX();
float e2Y = e2.getY();
float distX = e2X - e1X;
float distY = e2Y - e1Y;

info.setText(
"e1X e1Y : " + String.valueOf(e1X) + " : " + String.valueOf(e1Y) + "\n" +
"e2X e2Y : " + String.valueOf(e2X) + " : " + String.valueOf(e2Y) + "\n" +
"velocityX : " + String.valueOf(velocityX) + "\n" +
"velocityY : " + String.valueOf(velocityY));

//Get the Y OFfset
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();

int[] location = new int[2];
flingObj.getLocationOnScreen(location);
float orgX = location[0];
float orgY = location[1] - offsetY;

float stopX = orgX + distX;
float stopY = orgY + distY;

if (distX > MIN_DIST) {
//Fling Right
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationX", orgX, stopX);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if(distX < - MIN_DIST){
//Fling Left
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationX", orgX, stopX);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if (distY > MIN_DIST) {
//Fling Down
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationY", orgY, stopY);
flingAnimator.setDuration(1000);
flingAnimator.start();
}else if(distY < - MIN_DIST){
//Fling Up
ObjectAnimator flingAnimator = ObjectAnimator.ofFloat(flingObj, "translationY", orgY, stopY);
flingAnimator.setDuration(1000);
flingAnimator.start();
}

return true;
}

@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub

}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// TODO Auto-generated method stub
return false;
}

@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub

}

@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return false;
}};

}


<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/mainscreen">

<TextView
android:id="@+id/info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

<ImageView
android:id="@+id/flingobject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
android:src="@drawable/ic_launcher"
tools:context=".MainActivity" />

</FrameLayout>


In order to use android.animation.ObjectAnimator, minSdkVersion="11" is needed.

Download the files.

Error of getLocationInWindow() and getLocationOnScreen()

The View class provide the methods to computes the coordinates:

In my experience, the returned x location is correct, but the y location is always error with a fixed offset. The offset various depends on devices and configuration.

To correct it, we can get the offset using the code, after view displayed:
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();


offset of getLocationInWindow() and getLocationOnScreen()


package com.example.androidoffsetgetlocation;

import android.os.Bundle;
import android.app.Activity;
import android.util.DisplayMetrics;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

LinearLayout mainScreen;
ImageView object;
TextView textOnCreate, textOnWindowFocusChanged;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mainScreen = (LinearLayout)findViewById(R.id.mainscreen);
object = (ImageView)findViewById(R.id.object);
textOnCreate = (TextView)findViewById(R.id.textview1);
textOnWindowFocusChanged = (TextView)findViewById(R.id.textview2);

readLocation(textOnCreate, "onCreate()");
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);

readLocation(textOnWindowFocusChanged, "onWindowFocusChanged()");
}

private void readLocation(TextView tv, String status){
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int offsetX = displayMetrics.widthPixels - mainScreen.getMeasuredWidth();
int offsetY = displayMetrics.heightPixels - mainScreen.getMeasuredHeight();

int[] locationInWindow = new int[2];
object.getLocationInWindow(locationInWindow);
int[] locationOnScreen = new int[2];
object.getLocationOnScreen(locationOnScreen);

tv.setText(
"\n" + status +"\n"
+ "getLocationInWindow() - " + locationInWindow[0] + " : " + locationInWindow[1] + "\n"
+ "getLocationOnScreen() - " + locationOnScreen[0] + " : " + locationOnScreen[1] + "\n"
+ "Offset x: y - " + offsetX + " : " + offsetY);

}

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/mainscreen">

<ImageView
android:id="@+id/object"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/textview1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/textview2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>

</LinearLayout>



Monday 16 July 2012

Samsung Laptop Reviews Samsung Series 5 550 Chromebook Wi-Fi

Samsung Laptop Reviews Samsung Series 5 550 Chromebook Wi-Fi - This is a Samsung laptop with a specific brand name Samsung Series 5 550 Chromebook Wi-Fi. samsung laptop reviews is a laptop that has the advantage of features that are very sophisticated. more details can be seen below.

Samsung Laptop Reviews


Samsung Laptop Reviews 2

Samsung Laptop Reviews


Samsung Laptop Reviews 3

Samsung Laptop Reviews


Price: in here
For see more on this laptop you can be found in here
BK99FUW2BRRZ
I hope you are satisfied with the content contains in this blog. thank you for your visit in here.
Samsung Laptop Reviews Samsung Series 5 550 Chromebook Wi-Fi.

Complete list of all new features of Android 4.1 Jelly Bean

Android 4.1, Jelly Bean, is the fastest and smoothest version of Android yet. Jelly Bean improves on the simplicity and beauty of Android 4.0, and introduces a new Google search experience on Android.

  • Everything in Jelly Bean feels fast, fluid, and smooth. Moving between home screens and switching between apps is effortless, like turning pages in a book.
  • Jelly Bean features improved performance throughout the system, including faster orientation changes, faster responses when switching between recent apps, and smoother and more consistent rendering across the system through vsync and triple buffering.
  • Jelly Bean has more reactive and uniform touch responses, and makes your device even more responsive by boosting your device's CPU instantly when you touch the screen, and turns it down when you don't need it to improve battery life.


To read what's new in Android 4.1 Jelly Bean, visit: http://www.android.com/about/jelly-bean/.

Implement grouped CheckBox on Action Menu

Example of Action Menu with grouped CheckBox.

Action Menu with grouped CheckBox


Create /res/menu/activity_main.xml to define action menu.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_settings"
android:title="@string/menu_settings"
android:orderInCategory="100"
android:showAsAction="never" />
<group android:checkableBehavior="single">
<item android:id="@+id/selecta"
android:title="Selection A" android:checked="true"/>
<item android:id="@+id/selectb"
android:title="Selection B" />
<item android:id="@+id/selectc"
android:title="Selection C" />
</group>
</menu>


The checked status will not be updated automatically. We can change it in onOptionsItemSelected() callback method.
package com.example.androidactionbar;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.selecta:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"A Selected",
Toast.LENGTH_LONG).show();
return true;
case R.id.selectb:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"B Selected",
Toast.LENGTH_LONG).show();
return true;
case R.id.selectc:
item.setChecked(true);
Toast.makeText(getApplicationContext(),
"C Selected",
Toast.LENGTH_LONG).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}

}



Sunday 15 July 2012

Everyone Hates Firefox Updates

Firefox LogoWebMonkey posted this last week on some of the discussions at Mozilla around updates. The story has links to the original post by developer Jono DiCarlo as well as some links to the discussions surrounding the post. The gist of the post is this, all the constant updates to Firefox, to be more like Google Chrome, is killing Firefox.



I couldn't agree more. Chrome can do all those updates because they take place in the background and seldom make any changes to the UI. By contrast, Firefox constantly asks you to approve the updates and the changes too often include major changes to the UI.



This is a particular pet peeve of mine. Of course software needs updated, but often these updates occur too frequently. Mac OS X, gets updates too often. Just when I get to the point where I start to enjoy a version, I HAVE to upgrade again.



The latest Ubuntu UI update is really irritating. It is not that the new Unity Heads Up Display (HUD) is bad. I just really liked the old version of the UI. Why are you forcing me to switch when I don't want to?



To me a 3 to 5 year cycle on operating system UI updates make a lot more sense, than yearly or even bi-yearly cycles. Plus, can't someone come up with a way so I can keep some of the features I like? That would be truly awesome.

LruCache with different size

Last exercise show Gallery with cached bitmaps using LruCache.

There is no specific size or formula that suits all applications, it's up to you to analyze your usage and come up with a suitable solution. A cache that is too small causes additional overhead with no benefit, a cache that is too large can once again cause java.lang.OutOfMemory exceptions and leave the rest of your app little memory to work with.

In this exercise, there are two Gallery widgets on screen, both with cached bitmaps using LruCache. The upper one with smaller cache size and the lower one with bigger cache size. There are more than 200 photos in /test/ directory.  The upper small cache size gallery always re-load the cache. The lower one with bigger size of cache can keep bitmaps in cache, but when the cached memory get large, it crash the app!.



package com.example.androidgallery;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

public abstract class CacheGalleryBaseAdapter extends BaseAdapter {

ArrayList<String> GalleryFileList;
Context context;

CacheGalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}

abstract Bitmap abstractGetBitmapFromMemCache(String key);
abstract BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView);

@Override
public int getCount() {
return GalleryFileList.size();
}

@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

ImageView imageView = new ImageView(context);

//---
// Use the path as the key to LruCache
final String imageKey = GalleryFileList.get(position);

final Bitmap bm = abstractGetBitmapFromMemCache(imageKey);
if (bm == null){
BitmapWorkerTask task = abstractStartBitmapWorkerTask(imageView);
task.execute(imageKey);
}

LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);

imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);

layout.addView(imageView);
return layout;

}

public void add(String newitem){
GalleryFileList.add(newitem);
}

}

public class CacheGalleryBaseAdapter_SmallSize extends CacheGalleryBaseAdapter{

CacheGalleryBaseAdapter_SmallSize(Context cont) {
super(cont);
}

@Override
Bitmap abstractGetBitmapFromMemCache(String key) {
return getBitmapFromMemCache_SmallSize(key);
}

@Override
BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView) {
return (new BitmapWorkerTask_SmallSize(imageView));
}

}

public class CacheGalleryBaseAdapter_BigSize extends CacheGalleryBaseAdapter{

CacheGalleryBaseAdapter_BigSize(Context cont) {
super(cont);
}

@Override
Bitmap abstractGetBitmapFromMemCache(String key) {
return getBitmapFromMemCache_BigSize(key);
}

@Override
BitmapWorkerTask abstractStartBitmapWorkerTask(ImageView imageView) {
return(new BitmapWorkerTask_BigSize(imageView));
}

}

CacheGalleryBaseAdapter_SmallSize myFastGallery1BaseAdapter;
CacheGalleryBaseAdapter_BigSize myFastGallery2BaseAdapter;

Gallery myFastGallery1, myFastGallery2;

private LruCache<String, Bitmap> mMemoryCache_SmallSize;
private LruCache<String, Bitmap> mMemoryCache_BigSize;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myFastGallery1 = (Gallery)findViewById(R.id.fastgallery1);
myFastGallery2 = (Gallery)findViewById(R.id.fastgallery2);

myFastGallery1BaseAdapter = new CacheGalleryBaseAdapter_SmallSize(this);
myFastGallery2BaseAdapter = new CacheGalleryBaseAdapter_BigSize(this);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myFastGallery1BaseAdapter.add(file.getPath());
myFastGallery2BaseAdapter.add(file.getPath());
}

myFastGallery1.setAdapter(myFastGallery1BaseAdapter);
myFastGallery2.setAdapter(myFastGallery2BaseAdapter);

// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();

final int cacheSize_SmallSize = 1024 * 1024 * memClass / 12;
mMemoryCache_SmallSize = new LruCache_customSize(cacheSize_SmallSize);

final int cacheSize_BigSize = 1024 * 1024 * memClass;
mMemoryCache_BigSize = new LruCache_customSize(cacheSize_BigSize);

}

class LruCache_customSize extends LruCache<String, Bitmap>{

public LruCache_customSize(int maxSize) {
super(maxSize);
// TODO Auto-generated constructor stub
}

protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}

}

abstract class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{

private final WeakReference<ImageView> imageViewReference;

public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}

}

public Bitmap getBitmapFromMemCache_SmallSize(String key) {
return (Bitmap) mMemoryCache_SmallSize.get(key);
}

public Bitmap getBitmapFromMemCache_BigSize(String key) {
return (Bitmap) mMemoryCache_BigSize.get(key);
}

class BitmapWorkerTask_SmallSize extends BitmapWorkerTask{

public BitmapWorkerTask_SmallSize(ImageView imageView) {
super(imageView);
// TODO Auto-generated constructor stub
}

@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);

if( getBitmapFromMemCache_SmallSize(String.valueOf(params[0])) == null){
mMemoryCache_SmallSize.put(String.valueOf(params[0]), bitmap);
}

return bitmap;
}

}

class BitmapWorkerTask_BigSize extends BitmapWorkerTask{

public BitmapWorkerTask_BigSize(ImageView imageView) {
super(imageView);
// TODO Auto-generated constructor stub
}

@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);

if( getBitmapFromMemCache_BigSize(String.valueOf(params[0])) == null){
mMemoryCache_BigSize.put(String.valueOf(params[0]), bitmap);
}

return bitmap;
}

}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Small Cache Size" />
<Gallery
android:id="@+id/fastgallery1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Big Cache Size" />
<Gallery
android:id="@+id/fastgallery2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

</LinearLayout>


modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".

Download the files.


Saturday 14 July 2012

Caching Bitmaps with LruCache

In the last exercise of "Implement Android Gallery widget with scale-down bitmap", the bitmap will be loaded and scaled every-time getView() of GalleryBaseAdapter called. The side-effect is the gallery widget stop a moment whenever a new a new item come-out.

The LruCache class (also available in the Support Library for use back to API Level 4) is particularly well suited to the task of caching bitmaps, keeping recently referenced objects in a strong referenced LinkedHashMap and evicting the least recently used member before the cache exceeds its designated size. ~ reference: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html#memory-cache.

In this exercise, two Gallery widget were implemented. In the video shown below, the upper one is original gallery, the lower one is gallery with cached bitmaps. It can be noted that the cached gallery need more time to be loaded in first loading, but swipe much more smooth.



Main activity.
package com.example.androidgallery;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.LruCache;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class MainActivity extends Activity {

public class GalleryBaseAdapter extends BaseAdapter {

ArrayList<String> GalleryFileList;
Context context;

GalleryBaseAdapter(Context cont){
context = cont;
GalleryFileList = new ArrayList<String>();
}

@Override
public int getCount() {
return GalleryFileList.size();
}

@Override
public Object getItem(int position) {
return GalleryFileList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

Bitmap bm = decodeSampledBitmapFromUri(GalleryFileList.get(position), 200, 200);
LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);

ImageView imageView = new ImageView(context);
imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);

layout.addView(imageView);
return layout;
}

public void add(String newitem){
GalleryFileList.add(newitem);
}

}

public class CacheGalleryBaseAdapter extends GalleryBaseAdapter{

CacheGalleryBaseAdapter(Context cont) {
super(cont);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

ImageView imageView = new ImageView(context);

//---
// Use the path as the key to LruCache
final String imageKey = GalleryFileList.get(position);

final Bitmap bm = getBitmapFromMemCache(imageKey);
if (bm == null){
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(imageKey);
}

LinearLayout layout = new LinearLayout(context);
layout.setLayoutParams(new Gallery.LayoutParams(250, 250));
layout.setGravity(Gravity.CENTER);

imageView.setLayoutParams(new Gallery.LayoutParams(200, 200));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageBitmap(bm);

layout.addView(imageView);
return layout;

}

}

GalleryBaseAdapter myGalleryBaseAdapter;
CacheGalleryBaseAdapter myCacheGalleryBaseAdapter;
Gallery myPhotoGallery, myFastGallery;

private LruCache<String, Bitmap> mMemoryCache;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myPhotoGallery = (Gallery)findViewById(R.id.photogallery);
myFastGallery = (Gallery)findViewById(R.id.fastgallery);

myGalleryBaseAdapter = new GalleryBaseAdapter(this);
myCacheGalleryBaseAdapter = new CacheGalleryBaseAdapter(this);

String ExternalStorageDirectoryPath = Environment
.getExternalStorageDirectory()
.getAbsolutePath();

String targetPath = ExternalStorageDirectoryPath + "/test/";

File targetDirector = new File(targetPath);

File[] files = targetDirector.listFiles();
for (File file : files){
myGalleryBaseAdapter.add(file.getPath());
myCacheGalleryBaseAdapter.add(file.getPath());
}

myPhotoGallery.setAdapter(myGalleryBaseAdapter);
myFastGallery.setAdapter(myCacheGalleryBaseAdapter);

// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();

// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}

};
}

class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{

private final WeakReference<ImageView> imageViewReference;

public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}

@Override
protected Bitmap doInBackground(String... params) {
final Bitmap bitmap = decodeSampledBitmapFromUri(params[0], 200, 200);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}

@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = (ImageView)imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}

}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}

public Bitmap getBitmapFromMemCache(String key) {
return (Bitmap) mMemoryCache.get(key);
}

public Bitmap decodeSampledBitmapFromUri(String path, int reqWidth, int reqHeight) {
Bitmap bm = null;

// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeFile(path, options);

return bm;
}

public int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}

return inSampleSize;
}

}


Modify the layout to have two gallery.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery without Cache" />
<Gallery
android:id="@+id/photogallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Gallery with Cache" />
<Gallery
android:id="@+id/fastgallery"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

</LinearLayout>


android.util.LruCache is needed in the code, modify AndroidManifest.xml to have minSdkVersion and targetSdkVersion of "13".

Download the files.


Please note that this approach have it's own trade-off: if you request cacheSize too much, it will make your app causing java.lang.OutOfMemory exceptions, if too small, it will cause additional overhead. In my own openion, if you have predictable images (or resources) needed for caching, it's a good choice.

Read more: LruCache with different size

Related:
- Apply LruCache on GridView