Friday, June 22, 2012

Sax Parser example in Android

There are 2 ways to parse an xml in android. SAX an DOM. The DOM parser loads the whole document into memory before it can work with it, which can be slow and uses up a lot more memory - the benefit is that you're not writing as much code. So in mobile environment we are not using commonly this method for parsing xml.But in  SAX , it goes through each element and attribute one at a time, and you can pick and choose which one you want added into memory, but you do need to write a lot more code.  So this one is used in android for parsing. Here find an simple example for implementing this.
 
public static Login parse(String xml) throws Exception {
 LoginHandler handler = new LoginHandler();
 Xml.parse(xml ,handler);
 return handler.tempItem;
}

public static class LoginHandler extends DefaultHandler {

  private static final String RESPONSE = "response";
  private static final String STATUS = "result";
  private static final String CSRF = "_csrf";
  private static final String USER_TOKEN = "user_token"; 

  Login tempItem;
  private StringBuilder builder;

  @Override
  public void characters(char[] ch, int start, int length)
    throws SAXException {
   super.characters(ch, start, length);
   builder.append(ch, start, length);
  }

                // taking upto </tag> position.
  @Override
  public void endElement(String uri, String localName, String qName)
    throws SAXException {
   super.endElement(uri, localName, qName);
   if (this.tempItem != null) {
    if (localName.equalsIgnoreCase(STATUS)) {
      tempItem.setStatus(builder.toString().trim());
    }
   }
    builder.setLength(0);
  }

  @Override
  public void error(SAXParseException e) throws SAXException {
   super.error(e);
  }

  @Override
  public void fatalError(SAXParseException e) throws SAXException {
   super.fatalError(e);
  }

  @Override
  public void startDocument() throws SAXException {
   super.startDocument();
   builder = new StringBuilder();
  }
                // taking start position <response _csrf="456465" user_token="4557897">
  @Override
  public void startElement(String uri, String localName, String qName,
    Attributes attributes) throws SAXException {
   builder.setLength(0);
   super.startElement(uri, localName, qName, attributes);
   if (localName.equalsIgnoreCase(RESPONSE)) {
       tempItem = new Login();
                            // taking attributes value    
                            tempItem.setCsrf(attributes.getValue(CSRF));
                            tempItem.setUser_token(attributes.getValue(USER_TOKEN));
   }
  }

 }

public class Login {

 private String csrf = "";
 public String getUser_token() {
  return user_token;
 }

 public void setUser_token(String user_token) {
  this.user_token = user_token;
 }

 private String status = "";
 private String user_token = "";

 public String getCsrf() {
  return csrf;
 }

 public void setCsrf(String csrf) {
  this.csrf = csrf;
 }

 public String getStatus() {
  return status;
 }

 public void setStatus(String status) {
  this.status = status;
 }

}

Monday, June 18, 2012

Implementing multiple links in same textview android

Sometimes we need to create a textview with 2 separate links or we need to show different colors in different parts of the same textview. In a normal textview we cant implement it directly.  In order to attain this we using spannable textviews.  In this we splitting text in to different parts and implement the different changes. Take an example below.


 
 Textview mCommentTxt = (TextView) findViewById(R.id.contentTxt);
 MovementMethod m = holder.mCommentTxt.getMovementMethod();
 if ((m == null) || !(m instanceof LinkMovementMethod)) {
     holder.mCommentTxt.setMovementMethod(LinkMovementMethod.getInstance());
 }

 String content = String .format("##%s## checked-in $$%s$$", "username" ,"values");
 mCommentTxt.setText(content);
 CharSequence txt = holder.mCommentTxt.getText();
  String bgColor = "#E6E6E6"; 
txt = setSpanBetweenTokens(txt, "##", new   ForegroundColorSpan(Color.parseColor(bgColor)),
    new BackgroundColorSpan(Color.parseColor(bgColor)), new ClickableSpan() {
 @Override
 public void onClick(View v) {
  // Onclick listner
 }
 @Override
 public void updateDrawState(TextPaint ds) {
     ds.setColor(Color.parseColor("#4866a3"));
     ds.setUnderlineText(false);// set to false to remove underline 
     ds.setTypeface(Typeface.DEFAULT_BOLD);
 }
 });
 txt = setSpanBetweenTokens(txt, "$$", new ForegroundColorSpan(Color.WHITE ),      new BackgroundColorSpan(Color.parseColor(bgColor)),new ClickableSpan() {
 @Override
 public void onClick(View v) {
         // Onclick listener  
 }
 @Override
        public void updateDrawState(TextPaint ds) {
           ds.setColor(Color.parseColor("#4866a3"));
           ds.setUnderlineText(false); // set to false to remove underline
           ds.setTypeface(Typeface.DEFAULT_BOLD);
        }
 });
    
 mCommentTxt.setText( txt );
 
public static CharSequence setSpanBetweenTokens(CharSequence text, String token, CharacterStyle... cs) {
 int tokenLen = token.length();
 int start = text.toString().indexOf(token) + tokenLen;
 int end = text.toString().indexOf(token, start);
 if (start > -1 && end > -1) {
         SpannableStringBuilder ssb = new SpannableStringBuilder(text);
  for (CharacterStyle c : cs){
   ssb.setSpan(c, start, end, 0);
  }
         ssb.delete(end, end + tokenLen);
         ssb.delete(start - tokenLen, start);
         text = ssb;
 }
        return text;
}

Sunday, June 3, 2012

Pull to refresh in Android

“Pull To Refresh” is a UI gesture made popular by the Twitter for Iphone app. It allows a user to refresh a list by pulling down and releasing from the top of the list. IOS developers have had library support for this feature for some time now. Over in Android-land, only the official Twitter app has managed to implement this gesture. Long-promised to be open-sourced, Twitter’s solution is still secret and Android developers have no official means to implement this increasingly demanded feature.


Its not my own library. I don't know who is written this library.    But i think it will help u more than when compared to other libraries.   Here they implemented via frame layout and a list view. dragging and releasing part has done inside the frame layout. Check an example below.
 

 

            
            
        
       
    
In xml file ....
 
package com.example.helpers;

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Scroller;
import android.widget.TextView;

import com.example.R;

public class PullToRefresh extends FrameLayout implements
  GestureDetector.OnGestureListener {
 public static final int STATE_CLOSE = 1;
 public static final int STATE_OPEN = 2;
 public static final int STATE_OPEN_MAX = 4;
 public static final int STATE_OPEN_MAX_RELEASE = 5;
 public static final int STATE_OPEN_RELEASE = 3;
 public static final int STATE_UPDATE = 6;
 public static final int STATE_UPDATE_SCROLL = 7;
 private final int MAXHEIGHT = 100;
// private final String TAG = "PullToRefresh";

 private ImageView mArrow;
 private String mDate;
 private GestureDetector mDetector;
 private Flinger mFlinger;
 private boolean mIsAutoScroller;
 private int mPading;
 private ProgressBar mProgressBar;
 private int mState;
 private TextView mTitle;
 private FrameLayout mUpdateContent;

 private UpdateHandle mUpdateHandle;

 private RotateAnimation mFlipAnimation;
 private RotateAnimation mReverseFlipAnimation;
 private Context ctx;

 public PullToRefresh(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  this.ctx = context;
  addUpdateBar();
  init();
 }

 public PullToRefresh(Context context, AttributeSet attrs) {
  super(context, attrs);
  this.ctx = context;
  addUpdateBar();
  init();
 }

 public PullToRefresh(Context context) {
  super(context);
  this.ctx = context;
  addUpdateBar();
  init();
 }

 // private void init() {
 // MAX_LENGHT = getResources().getDimensionPixelSize(
 // R.dimen.updatebar_height);
 // setDrawingCacheEnabled(true);
 // // setBackgroundDrawable(null);
 // setClipChildren(true);
 // this.mDetector.setIsLongpressEnabled(false);
 // }
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  
  boolean bool1 = this.mIsAutoScroller;
  GestureDetector localGestureDetector = this.mDetector;

  localGestureDetector.onTouchEvent(ev);
  switch (ev.getAction()) {
  case MotionEvent.ACTION_MOVE:
   int i1 = getChildAt(1).getTop();
   if (i1 != 0) {
    updateView();
   }

   break;
  case MotionEvent.ACTION_UP:

   if (this.mState == STATE_OPEN) {
    this.mState = STATE_OPEN_RELEASE;
   }
   if (this.mState == STATE_OPEN_MAX) {
    this.mState = STATE_OPEN_MAX_RELEASE;
   }
  
   release();
   break;
  }
  if (mState != STATE_UPDATE) {
   bool1 = super.dispatchTouchEvent(ev);
  }

  int i1 = getChildAt(1).getTop();
  if (i1 != 0) {
   ev.setAction(3);
   super.dispatchTouchEvent(ev);
   updateView();
  }
  return bool1;
 }

 private void init() {
  GestureDetector localGestureDetector = new GestureDetector(this);
  this.mDetector = localGestureDetector;
  Flinger localFlinger = new Flinger();
  this.mFlinger = localFlinger;
  this.mState = 1;
  setDrawingCacheEnabled(true);
  setClipChildren(true);
  this.mDetector.setIsLongpressEnabled(false);
 }

 private void updateView() {
  View localView1 = getChildAt(0);
  View localView2 = getChildAt(1);
  if (this.mDate == null)
   this.mDate = "";

  switch (this.mState) {
  case STATE_CLOSE:
   if (localView1.getVisibility() != View.INVISIBLE)
    localView1.setVisibility(View.INVISIBLE);
  case STATE_OPEN:
  case STATE_OPEN_RELEASE:
   // STATE_OPEN
   int m = localView2.getTop();
   int n = -this.mPading - m;

   localView2.offsetTopAndBottom(n);
   if (localView1.getVisibility() != 0)
    localView1.setVisibility(View.VISIBLE);
   int i1 = localView1.getTop();// 相对于父窗�的顶部大�
   int i2 = -MAXHEIGHT;
   int i3 = this.mPading;
   int i4 = i2 - i3 - i1;
   localView1.offsetTopAndBottom(i4);
   TextView localTextView1 = this.mTitle;
   String str1 = ctx.getText(R.string.update_when_release_the_finger).toString();
   StringBuilder localStringBuilder1 = new StringBuilder(str1)
     .append("\n");
   // String str2 = this.mDate;
   // String str3 = str2;
   localStringBuilder1.append(this.mDate);
   localTextView1.setText(localStringBuilder1.toString());
   this.mProgressBar.setVisibility(View.INVISIBLE);
   this.mArrow.setVisibility(View.VISIBLE);

   break;
  case STATE_OPEN_MAX_RELEASE:
  case STATE_OPEN_MAX:
   // STATE_OPEN_MAX
   int i5 = localView2.getTop();
   int i6 = -this.mPading - i5;
   localView2.offsetTopAndBottom(i6);
   if (localView1.getVisibility() != View.VISIBLE)
    localView1.setVisibility(View.VISIBLE);
   int i7 = localView1.getTop();
   int i8 = -MAXHEIGHT;
   int i9 = this.mPading;
   int i10 = i8 - i9 - i7;
   localView1.offsetTopAndBottom(i10);
   TextView localTextView2 = this.mTitle;
   String str4 = ctx.getText(R.string.update_when_release_the_finger).toString();// release_update:
   StringBuilder localStringBuilder2 = new StringBuilder(str4).append("\n");

   localStringBuilder2.append(this.mDate);
   localTextView2.setText(localStringBuilder2.toString());

   this.mProgressBar.setVisibility(View.INVISIBLE);
   this.mArrow.setVisibility(View.VISIBLE);

   break;
  case STATE_UPDATE:
   // STATE_UPDATE
   int i11 = localView2.getTop();
   int i12 = -this.mPading - i11;
   localView2.offsetTopAndBottom(i12);
   int i13 = localView1.getTop();
   if (this.mProgressBar.getVisibility() != View.VISIBLE)
    this.mProgressBar.setVisibility(View.VISIBLE);
   if (this.mArrow.getVisibility() != View.INVISIBLE)
    this.mArrow.setVisibility(View.INVISIBLE);
   TextView localTextView3 = this.mTitle;
   String str7 = ctx.getString(R.string.updating).toString();// doing_update:加载中...
   StringBuilder localStringBuilder3 = new StringBuilder(str7)
     .append("\n");

   localStringBuilder3.append(this.mDate);
   localTextView3.setText(localStringBuilder3.toString());
   int i14 = -MAXHEIGHT;
   int i15 = this.mPading;
   int i16 = i14 - i15 - i13;
   localView1.offsetTopAndBottom(i16);
   if (localView1.getVisibility() != 0)
    localView1.setVisibility(0);
   this.mProgressBar.setVisibility(View.VISIBLE);
   this.mArrow.setVisibility(View.GONE);
   mArrow.clearAnimation();
   break;
  }
  invalidate();
 }

 private void addUpdateBar() {
  Context localContext1 = getContext();
  mFlipAnimation = new RotateAnimation(0, -180,
    Animation.RELATIVE_TO_SELF, 0.5f,
    Animation.RELATIVE_TO_SELF, 0.5f);
  mFlipAnimation.setInterpolator(new LinearInterpolator());
  mFlipAnimation.setDuration(200);
  mFlipAnimation.setFillAfter(true);
  mReverseFlipAnimation = new RotateAnimation(-180, 0,
    Animation.RELATIVE_TO_SELF, 0.5f,
    Animation.RELATIVE_TO_SELF, 0.5f);
  mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
  mReverseFlipAnimation.setDuration(200);
  mReverseFlipAnimation.setFillAfter(true);

  View localView = LayoutInflater.from(localContext1).inflate(
    R.layout.vw_update_bar, null);
  localView.setVisibility(4);
  addView(localView);

  this.mArrow = new ImageView(localContext1);

  FrameLayout.LayoutParams localLayoutParams1 = new FrameLayout.LayoutParams(
    android.view.ViewGroup.LayoutParams.FILL_PARENT,
    android.view.ViewGroup.LayoutParams.FILL_PARENT);
  ImageView localImageView2 = this.mArrow;
  ImageView.ScaleType localScaleType = ImageView.ScaleType.FIT_CENTER;
  localImageView2.setScaleType(localScaleType);
  this.mArrow.setLayoutParams(localLayoutParams1);
  this.mArrow.setImageResource(R.drawable.arrow_down);

  this.mUpdateContent = (FrameLayout) getChildAt(0).findViewById(
    R.id.iv_content);
  FrameLayout localFrameLayout2 = this.mUpdateContent;
  localFrameLayout2.addView(this.mArrow);
  FrameLayout.LayoutParams localLayoutParams2 = new FrameLayout.LayoutParams(
    android.view.ViewGroup.LayoutParams.FILL_PARENT,
    android.view.ViewGroup.LayoutParams.FILL_PARENT);
  localLayoutParams2.gravity = Gravity.CENTER_VERTICAL;
  this.mProgressBar = new ProgressBar(localContext1);
  int i = getResources().getDimensionPixelSize(R.dimen.progress_padding);
  this.mProgressBar.setPadding(i, i, i, i);
  this.mProgressBar.setLayoutParams(localLayoutParams2);

  this.mUpdateContent.addView(mProgressBar);
  this.mTitle = (TextView) findViewById(R.id.tv_title);
 }

 @Override
 protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2,
   int paramInt3, int paramInt4) {
  View localView1 = getChildAt(0);
  int i = -MAXHEIGHT;
  int j = this.mPading;
  int k = i - j;
  int l = getMeasuredWidth();
  int i1 = -this.mPading;
  localView1.layout(0, k, l, i1);

  View localView2 = getChildAt(1);
  int i2 = -this.mPading;
  int i3 = getMeasuredWidth();
  int i4 = getMeasuredHeight();
  int i5 = this.mPading;
  int i6 = i4 - i5;
  localView2.layout(0, i2, i3, i6);
 }

 public void endUpdate(String paramString) {
  this.mDate = paramString;
  if (this.mPading != 0) {
   this.mState = STATE_CLOSE;
   scrollToClose();
  }
 }

 @Override
 public boolean onDown(MotionEvent e) {
  return false;
 }

 @Override
 public void onShowPress(MotionEvent e) {

 }

 @Override
 public boolean onSingleTapUp(MotionEvent e) {
  return false;
 }

 @Override
 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
   float distanceY) {
  @SuppressWarnings("rawtypes")
  AdapterView localAdapterView = (AdapterView) getChildAt(1);
  int k = localAdapterView.getCount();
  if (k == 0) {
   return false;
  }
  k = localAdapterView.getFirstVisiblePosition();// 获�第一个显示项目的position
  if (k == 0) {
   int t = localAdapterView.getChildAt(0).getTop();
   if (t != 0) {
    return false;
   } else {

    this.mPading = (int) (this.mPading + distanceY / 2);

    if (this.mPading > 0)
     this.mPading = 0;
    // if (distanceY < 0) {

    if (Math.abs(this.mPading) <= MAXHEIGHT) {
     this.mState = STATE_OPEN;

    } else {
     this.mState = STATE_OPEN_MAX;

    }

    // }
    updateView();

   }
  }
  return false;
 }

 @Override
 public void onLongPress(MotionEvent e) {

 }

 @Override
 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
   float velocityY) {
  return false;
 }

 @SuppressWarnings("unused")
 private int getScrollRange() {
  int scrollRange = 0;
  if (getChildCount() > 0) {
   View child = getChildAt(0);
   scrollRange = Math.max(0,child.getHeight() - (getHeight() - this.getBottomPaddingOffset() - this .getTopPaddingOffset()));
  }
  return scrollRange;
 }

 private boolean release() {
  int tempStatus = STATE_OPEN_MAX_RELEASE;
  int i = this.mPading;
  if (i >= 0) {
   return true;
  }
  int j = this.mState;
  switch (j) {
  case STATE_OPEN_RELEASE:
   int k = Math.abs(this.mPading);
   int i1 = MAXHEIGHT;
   if (k < i1) {
    tempStatus = STATE_OPEN_MAX_RELEASE;
    this.mState = tempStatus;
   }
   scrollToClose();
   break;
  case STATE_OPEN_MAX_RELEASE:

   this.mState = tempStatus;
   scrollToUpdate();
   break;
  }
  return false;
 }

 private void scrollToClose() {
  Flinger localFlinger = this.mFlinger;
  int i = -this.mPading;
  localFlinger.startUsingDistance(i, 300);
 }

 private void scrollToUpdate() {
  Flinger localFlinger = this.mFlinger;

  int k = -this.mPading - MAXHEIGHT;
  localFlinger.startUsingDistance(k, 300);
 }

 class Flinger implements Runnable {
  private int mLastFlingX;
  private Scroller mScroller;

  public Flinger() {
   Context localContext = PullToRefresh.this.getContext();
   Scroller localScroller = new Scroller(localContext);
   this.mScroller = localScroller;
  }

  private void startCommon() {
   PullToRefresh.this.removeCallbacks(this);
  }

  @Override
  public void run() {
   boolean bool1 = Math.abs(mPading) != MAXHEIGHT;
   Scroller localScroller = this.mScroller;
   boolean bool2 = localScroller.computeScrollOffset();
   int i = localScroller.getCurrX();
   int j = this.mLastFlingX - i;
   PullToRefresh localPullDownView = PullToRefresh.this;

   localPullDownView.move(j, bool1);
   PullToRefresh.this.updateView();
   if (bool2) {
    this.mLastFlingX = i;
    PullToRefresh.this.post(this);
   } else {
    PullToRefresh.this.mIsAutoScroller = bool1;
    PullToRefresh.this.removeCallbacks(this);
   }
  }

  public void startUsingDistance(int paramInt1, int paramInt2) {
   int i = 0;
   if (paramInt1 == 0)
    --paramInt1;
   startCommon();
   this.mLastFlingX = i;
   Scroller localScroller = this.mScroller;

   localScroller.startScroll(i, 0, -paramInt1, 0, paramInt2);
   PullToRefresh.this.mIsAutoScroller = true;
   PullToRefresh.this.post(this);
  }
 }

 /**
  * 
  * 
  * @param f
  * @param bool1
  */
 public void move(float f, boolean bool1) {
  if (this.mState != STATE_CLOSE) {
   if (!bool1) {
    // 刷新
    if (mState == STATE_OPEN_MAX_RELEASE) {
     this.mState = STATE_UPDATE;
     if (mUpdateHandle != null) {
      mUpdateHandle.onUpdate();
     }
    }
   }
   if (this.mState == STATE_OPEN_MAX_RELEASE
     || this.mState == STATE_OPEN_RELEASE) {
    this.mPading += f;

   }
  } else {
   if (mIsAutoScroller) {
    this.mPading += f;
   }
  }

 }

 public abstract interface UpdateHandle {
  public abstract void onUpdate();
 }

 public void setUpdateDate(String paramString) {
  this.mDate = paramString;
 }

 public void setUpdateHandle(UpdateHandle paramUpdateHandle) {
  this.mUpdateHandle = paramUpdateHandle;
 }

 // public void update()
 // {
 // int i = -MAXHEIGHT;
 // this.mPading = i;
 // this.mState = 7;
 // }

 public void updateWithoutOffset() {
  this.mState = STATE_UPDATE_SCROLL;
  invalidate();
 }

}
Values for the strings.
Refresh
    Updating ...
    Last Updated : 
    Release to update..
vw_update_bar layout xml.



    

        
        

        
        
    

    
    


dimension.xml used



 120px
 120px
 20px
 10px
 
 

Update activity class as follows.
package com.example

public class Home extends Activity implements PullToRefresh.UpdateHandle {
private PullToRefresh refresh;
@Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
                refresh = (PullToRefresh) findViewById(R.id.pullDownView1);
  refresh.setUpdateHandle(this);
                // .....
        }
//...
        @Override
 public void onUpdate() {
  // Update call .. do the need ful for updating.
 }
//....
 private final Handler handler = new Handler(Looper.getMainLooper()) {
  @Override
  public void handleMessage(Message msg) {
   refresh.endUpdate(getString(R.string.pulltorefreshhandle_text) + getTime());
  }
 };


}
After adding adapter values means child items to the list view then call this method to finish update.
Message msg = handler.obtainMessage();
handler.sendMessage(msg);

Thursday, May 31, 2012

Locale Change Receiver Android

Some times we need to know when the locale changing in our phone. We are localizing our app to be able to easily switch between languages. Everything works nicely except for some cached values that are actually in navigation. We have two options:

1. completely restart app on language change - in this case I need a notification and force restart

2. reload the activity - in this case it will require to replace string values and rebuild custom navigation state.

In both cases we need a way to find out when language switches.
In order to find out that we can use Broadcast receiver for this intent. We can use 'android.intent.action.LOCALE_CHANGED ' intent action for the same....



        
            
                
            
        
Create a receiver from Android Manifest.xml From the Receiver we will get the change in locale intent.
 
public class LocaleChangeReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  Log.i("Locale", "Changed");
 }
}

Monday, May 21, 2012

How to hide media files from a folder Android

Sometimes we need to hide the media files from a folder . In order to attain this create a nomedia file on that folder. Name of the file signaling the media scanner to ignore media in the containing directory and its subdirectories. Developers should use this to avoid application graphics showing up in the Gallery and likewise prevent application sounds and music from showing up in the Music app. The media files only get disappererd only when after reboot of that phone.

file= new File(path);
if (!file.exists()){
   try {
      file.createNewFile();
   } catch (IOException e) {
       e.printStackTrace();
   }
}

Thursday, April 19, 2012

Loading Large Bitmaps Efficiently in Android

Bitmaps can quickly consume application's available memory budget leading to an application crash due to the dreaded exception:
java.lang.OutofMemoryError: bitmap size exceeds VM budget
  So working with limited memory, ideally we only want to load a lower resolution version of image in memory.The lower resolution version should match the size of the UI component that displays it.For this we have to consider some factors.

  • Estimated memory usage of loading the full image in memory.
  • Amount of memory you are willing to commit to loading this image given any other memory requirements of your application.
  • Dimensions of the target ImageView or UI component that the image is to be loaded into.
  • Screen size and density of the current device.
Here is another simple example for sampling the images. 

public static 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;
}

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

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

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

// for loading an image with size 100 *100 

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));


Reference : http://developer.android.com/training/displaying-bitmaps/index.html

Wednesday, April 11, 2012

Image resize in Android

Some times we need to  reduce the size of images retrieved from camera down to  a few smaller sizes or situations like  OutOfMemoryError due to  bitmap size  , images that showing in a list view or grid view  etc.  Then we resize the picture for reducing the memory usage .  Here is an simple example for doing this. 
/**
  * This function is used to read the original image file from a given path
  * and then scale it and save it to the application's private scope.
  * 
  * @param sourceImageFilePath Complete path name for the file to be opened.
  * @param desiredWidth The desired image width for this image.
  * @param context To identify the context's application package for writing a private image file.
  * @param destinationImageName The file name of the image to be saved within the applications private scope.
  * @return The scaled bitmap image after saved to the destination file.
  */
 
 public static Bitmap saveGalleryImageBitmap(String sourceImageFilePath, int desiredWidth, Context context, String destinationImageName, int rotate) {
  Bitmap scaledBitmap = null;
  Bitmap rotatedBitmap = null;
  try {
   // Get the source image's dimensions
   BitmapFactory.Options options = new BitmapFactory.Options();
   options.inJustDecodeBounds = true;
   BitmapFactory.decodeFile(sourceImageFilePath, options);
 
   int srcWidth = options.outWidth;
 
   // Only scale if the source is big enough. This code is just trying to fit a image into a certain width.
   if (desiredWidth > srcWidth)
    desiredWidth = srcWidth;
 
   // Calculate the correct inSampleSize/scale value. This helps reduce
   // memory use. It should be a power of 2
   // from:
   // http://stackoverflow.com/questions/477572/android-strange-out-of-memory-issue/823966#823966
   int inSampleSize = 1;
   while (srcWidth / 2 > desiredWidth) {
    srcWidth /= 2;
    inSampleSize *= 2;
   }
 
   float desiredScale = (float) desiredWidth / srcWidth;
 
   // Decode with inSampleSize
   options.inJustDecodeBounds = false;
   options.inDither = false;
   options.inSampleSize = inSampleSize;
   options.inScaled = false;
   options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
   Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(sourceImageFilePath, options);
 
   // Resize
   Matrix matrix = new Matrix();
   matrix.postScale(desiredScale, desiredScale);
   scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0, sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
   sampledSrcBitmap = null;
 
   // Save
   FileOutputStream out = null;
   try {
    out = context.openFileOutput(destinationImageName, Context.MODE_PRIVATE);
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   }
   
   Matrix rotateMatrix = new Matrix();
   rotateMatrix.postRotate(rotate); // For rotating the image.
   rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), rotateMatrix, true);
   scaledBitmap = null;
   
   rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);   
  } catch (OutOfMemoryError e) {
   scaledBitmap = null;
   e.printStackTrace();
  } catch (Exception e) {
   scaledBitmap = null;
   e.printStackTrace();
  }
  return rotatedBitmap;
 }

Tuesday, April 3, 2012

GPS Location in Android Devices

In order to get started with the location capabilities of the Android API, the first step is to take reference of the LocationManager class, which provides access to the system location services. This is done via the getSystemService of our activity (actually it inherits it from the Context parent class). Then, we request updates of the device's location using the methodrequestLocationUpdates. In that method, we provide the name of the preferred location provider (in our case GPS), the minimum time interval for notifications (in milliseconds), the minimum distance interval for notifications (in meters) and finally a class implementing the LocationListener interface. That interface declares methods for handling changes in the user's location as well as changes in the location provider's status. All the above can be translated into code as below:



package com.example.android.lbs;

import android.app.Activity;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class GPSActivity extends Activity {
    
    private static final long MINIMUM_DISTANCE_CHANGE_FOR_UPDATES = 1; // in Meters
    private static final long MINIMUM_TIME_BETWEEN_UPDATES = 1000; // in Milliseconds
    
    protected LocationManager locationManager;
    
    protected Button retrieveLocationButton;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        retrieveLocationButton = new Button(this);
 retrieveLocationButton.setText("GPS");
 addContentView(retrieveLocationButton, new LayoutParams(android.view.ViewGroup.LayoutParams.WRAP_CONTENT ,                             android.view.ViewGroup.LayoutParams.FILL_PARENT));

        
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        
        locationManager.requestLocationUpdates(
                LocationManager.GPS_PROVIDER, 
                MINIMUM_TIME_BETWEEN_UPDATES, 
                MINIMUM_DISTANCE_CHANGE_FOR_UPDATES,
                new MyLocationListener()
        );
        
    retrieveLocationButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                showCurrentLocation();
            }
    });        
        
    }    

    protected void showCurrentLocation() {

        Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

        if (location != null) {
            String message = String.format(
                    "Current Location \n Longitude: %1$s \n Latitude: %2$s",
                    location.getLongitude(), location.getLatitude()
            );
            Toast.makeText(LbsGeocodingActivity.this, message,
                    Toast.LENGTH_LONG).show();
        }

    }   

    private class MyLocationListener implements LocationListener {

        public void onLocationChanged(Location location) {
            String message = String.format(
                    "New Location \n Longitude: %1$s \n Latitude: %2$s",
                    location.getLongitude(), location.getLatitude()
            );
            Toast.makeText(LbsGeocodingActivity.this, message, Toast.LENGTH_LONG).show();
        }

        public void onStatusChanged(String s, int i, Bundle b) {
            Toast.makeText(LbsGeocodingActivity.this, "Provider status changed",
                    Toast.LENGTH_LONG).show();
        }

        public void onProviderDisabled(String s) {
            Toast.makeText(LbsGeocodingActivity.this,
                    "Provider disabled by the user. GPS turned off",
                    Toast.LENGTH_LONG).show();
        }

        public void onProviderEnabled(String s) {
            Toast.makeText(LbsGeocodingActivity.this,
                    "Provider enabled by the user. GPS turned on",
                    Toast.LENGTH_LONG).show();
        }

    }
    
}
In order to be able to run the above code, the necessary permissions have to be granted. These are:
1. ACCESS_FINE_LOCATION
2. ACCESS_MOCK_LOCATION
3. ACCESS_COARSE_LOCATION
Include those in the AndroidManifest.xml file, which will be as follows:

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />



Read more: http://www.javacodegeeks.com/2010/09/android-location-based-services.html

Monday, March 26, 2012

Implementing Broadcast Receiver in Android

broadcast receiver is a component that responds to system-wide broadcast announcements. Many broadcasts originate from the system—for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture was captured. Applications can also initiate broadcasts—for example, to let other applications know that some data has been downloaded to the device and is available for them to use. Although broadcast receivers don't display a user interface, they may create a status bar notification to alert the user when a broadcast event occurs. More commonly, though, a broadcast receiver is just a "gateway" to other components and is intended to do a very minimal amount of work. For instance, it might initiate a service to perform some work based on the event.

               We can simply implement broadcast receivers in applications .If you don't need to send broadcasts across applications, consider using this class with LocalBroadcastManager instead of the more general facilities described below. This will give you a much more efficient implementation (no cross-process communication needed) and allow you to avoid thinking about any security issues related to other applications being able to receive or send your broadcasts. 


Here a simple example for registering and sending a broadcast.



For receiving a broadcast
@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 IntentFilter filter = new IntentFilter("LOGOUT_BROADCAST");
     registerReceiver(logoutReceiver, filter);

}

private BroadcastReceiver logoutReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context arg0, Intent arg1) {
                      // to do 
  }
};
@Override
 protected void onDestroy() {
  super.onDestroy();
  unregisterReceiver(logoutReceiver);
 }
For sending a broadcast
Intent intent = new Intent("LOGOUT_BROADCAST",null);
context.sendBroadcast(intent);

Wednesday, March 21, 2012

ImageView zooming in Android

The common way to show a big image was enable the  user to zoom in, zoom out and pan  that image . A simple method is load the image in a web view. it will automatically handle the zooming. Also there are so many other ways to do the same. Please go through the following links.

 1. https://github.com/MikeOrtiz/TouchImageView 

 2. https://github.com/a85/WebComicViewer/blob/master/src/com/rickreation/ui/ZoomableImageView.java 

 3. http://code.google.com/p/android-multitouch-controller/

Monday, March 19, 2012

Formatting currency value in Edittext

When we work with the currency   we need to use ',' separator in appropriate places  .  For example 1500 as 1,500. For formatting the currency value, we can use NumberFormat Class . Also we need to add a textchange listener to that edit text. Format the value while we get a call in afterTextChanged() method in addTextChangedListener . A simple example below

mDishPrice.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
 }

 @Override
 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
 }

 @Override
 public void afterTextChanged(Editable s) {
  /***
   * No need to continue the function if there is nothing to
   * format
   ***/
  if (s.length() == 0){
   return;
                }

  /*** Now the number of digits in price is limited to 8 ***/
  String value = s.toString().replaceAll(",", "");
  if (value.length() > 8) {
   value = value.substring(0, 8);
  }
  String formattedPrice = getFormatedCurrency(value);
  if (!(formattedPrice.equalsIgnoreCase(s.toString()))) {
   /***
    * The below given line will call the function recursively
    * and will ends at this if block condition
    ***/
   mDishPrice.setText(formattedPrice);
   mDishPrice.setSelection(mDishPrice.length());
  }
 }
});
/**
 * 
 * @param value not formated amount
 * @return Formated string of amount (##,##,##,###).
 */
public static String getFormatedCurrency(String value) {
 try {
  NumberFormat formatter = new DecimalFormat("##,##,##,###");
  return formatter.format(Double.parseDouble(value));
 } catch (Exception e) {
  e.printStackTrace();
 }
 return "";
}

Wednesday, March 14, 2012

Keyboard raising issue when tabview at bottom of Android

There is an issue when placing a edittext in a tabview at bottom. when we tap on edit box the soft keyboard pops up and it move the whole view to upward (means the bottom tab also). When  we want to show the keyboard over the tabs at bottom itself. To solve this issue you may need to adjust the windowSoftInputMode in your manifest. This controls how the screen shifts when the soft keyboard is shown. This page has more info on the various input modes.   We can solve it as follows.


In AndroidManifest.xml


<activity
             android:name="TabActivity" android:windowSoftInputMode="adjustPan" >  
</activity>

Sunday, March 11, 2012

Injecting a javascript for getting html of a webpage in Android

To get the source code of html page, we can use javascript . For this first we have to enable the javascript interface of the web view. Then provide a custom webview client for that view. We will get a onPage finished call after loading a url. And in this method we can inject a javascript to get a html. An example below

WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true);
settings.setSavePassword(false);
webview.addJavascriptInterface(new MyJavaScriptInterface(), "HTMLOUT");
webview.setWebViewClient(new WebViewClient() {
 @Override
 public boolean shouldOverrideUrlLoading(WebView view, String url) {
  view.loadUrl(url);
  return true;
 }
 public void onPageFinished(WebView view, String url) {
  Log.i("Web view", "Finished loading URL: " + url);
//  killProgressDialog();
  webview.loadUrl("javascript:window.HTMLOUT.showHTML(document.getElementById('oauth_pin').innerHTML)");
 }

 @Override
 public void onPageStarted(WebView view, String url, Bitmap favicon) {
//  showProgressDialog();
  super.onPageStarted(view, url, favicon);
 }
 @Override
 public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
//  killProgressDialog();
  Toast.makeText(getBaseContext(),"Loading error", Toast.LENGTH_SHORT).show();
 }
});


class MyJavaScriptInterface {
 public void showHTML(String html) {
                Log.i("HTML" ,html);                         
        }
}

Wednesday, March 7, 2012

Leaked window Exception When using Progress Dialog in Android

Views have a reference to their parent Context (taken from constructor argument). If you leave an Activity without destroying Dialogs and other dynamically created Views, they still hold this reference to your Activity (if you created with this as Context: like new ProgressDialog(this)), so it cannot be collected by the GC, causing a memory leak. To solve this issue , dismiss method of the dialog should be called in on destroy method of that activity.
@Override
protected void onDestroy() {
    runOnUiThread(new Runnable() {
       @Override
       public void run() {
        if (mDialog.isShowing()) {
          mDialog.dismiss();
        }
       }
    });
    super.onDestroy();
 }

Tuesday, March 6, 2012

Converting an inputStream to String in utf-8

Sometimes we need to check the response of the http request by converting the stream to a string.  A simple method with which  we can do it in UTF-8 format  is  follows.
public static String readStream(InputStream in) throws Exception {

  if(in == null){
     return "";
  }

  int ch;
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  while ((ch = in.read()) != -1) {
   bos.write(ch);
  }
  in.close();
  return new String(bos.toByteArray(), "UTF-8");

}

Monday, March 5, 2012

Add images to gallery dynamically in Android


To add an image into the  android gallery we can use   MediaScannerConnection  class. The system scans the SD card when it is mounted to find any new image (and other) files.So sometimes we need to restart the phone to get updated. To solve this   we can  programmatically add a file,  using this class.

public void saveImageBitmap(byte[] sourceImageData, Context context) {
  Bitmap scaledBitmap = null;
  try {
   ContentValues image = new ContentValues();
   image.put(Media.DISPLAY_NAME, "sample_" + System.currentTimeMillis());
   image.put(Media.MIME_TYPE, "image/jpeg");
   Uri uri = context.getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, image); 
   Bitmap bitmap = BitmapFactory.decodeByteArray(sourceImageData, 0, sourceImageData.length);
   Bitmap rotateBitmap = null;
   Matrix matrix = new Matrix();
   rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
 
   OutputStream ops = context.getContentResolver().openOutputStream(uri);
   rotateBitmap.compress(Bitmap.CompressFormat.JPEG, 90, ops);
   ops.close();
   Cursor c = context.getContentResolver().query(uri, null, null, null, null);
   c.moveToFirst();
   String imagePath = c.getString(1);
   c.close();
   SingleScanMediaFile mScanner = new SingleScanMediaFile(context, imagePath);
          mScanner.onScanCompleted(imagePath, Uri.fromFile(new File(imagePath)));
  } catch (Exception e) {
   e.printStackTrace();
        }
  
} 


 public static class SingleScanMediaFile implements MediaScannerConnectionClient {
     private MediaScannerConnection mMediaScanner;
     private String uri; 
    SingleScanMediaFile (Context c, String uri) {
         this.uri = uri;
         mMediaScanner = new MediaScannerConnection(c, this);
         mMediaScanner.connect();
    } 
   
   @Override public void onMediaScannerConnected() {
         Log.i("Sample", "MEDIA SCANNER CONNECTED");
         try { 
              mMediaScanner.scanFile(uri, null);
         } catch (Exception e) {
             e.printStackTrace();
             mMediaScanner.disconnect(); 
         }
   }
  @Override public void onScanCompleted(String path, Uri uri) {
        Log.i("sample", "MEDIA SCANING COMPLETED");
        mMediaScanner.disconnect(); 
   } 
 }   

Escaping single quotes when using SQLite in Android

A verycommon problem while using sqlite and content providers are single quotes in arguements of query. Eventhough  we are not bothered about the same, it may lead to some problems  while executing the query. A simple approach to solve this issue is to use  content values and  selectionArgs.   Some examples are given below.


String sql = "select COUNT(*) FROM  table name WHERE parameter=?"; // parameter - column name
 mDB.rawQuery(sql , new String[]{param value} );

ContentValues values = new ContentValues();
values.put("categoryId", category.getCategoryId());
mDB.insert(table name , "NULL", values);


ContentValues values = new ContentValues();
values.put("registeredDate", regDate);
mDB.update(table name, values, "postId=?", new String[]{postId});

Saturday, March 3, 2012

Android : ListView inside a ScrollView

If there is a requirement  to place listview inside a scrollview , we cant implement it directly because of their vertical scroll property . To achieve this , after calling adpater.notifyDataSetChanged() , find the width and height of the listview  and invoke  requestLayout function. By doing this the performance of the list view become poor because we are changing listview as a normal linearlayout. It becomes a normal view group and child.




public static void setListViewHeightBasedOnChildren(ListView listView) {
  ListAdapter listAdapter = listView.getAdapter();
  if (listAdapter == null) {
   // pre-condition
   return;
  }

  int totalHeight = 0;
  int desiredWidth = MeasureSpec.makeMeasureSpec(listView.getWidth(),
    MeasureSpec.AT_MOST);
  for (int i = 0; i < listAdapter.getCount(); i++) {
   View listItem = listAdapter.getView(i, null, listView);
   listItem.measure(desiredWidth, MeasureSpec.UNSPECIFIED);
   totalHeight += listItem.getMeasuredHeight();
  }

  ViewGroup.LayoutParams params = listView.getLayoutParams();
  params.height = totalHeight
    + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
  listView.setLayoutParams(params);
  listView.requestLayout();
 }

Friday, March 2, 2012

UTF-8 Encoding of parameters in POST request

We should consider  encoding of  the parameters  which we sent on  http request.. Otherwise when a parameter  such as in japaneese language then request may not be a successful one .In an http post request we can do it as below.

ArrayList<namevaluepair> nameValuePairs = new ArrayList<namevaluepair>();
nameValuePairs.add(new BasicNameValuePair("parameter","parameter value"));
URL url = new URL(" url string ");

AbstractHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url.toURI());
httpPost.addHeader("Date", dateCurrent);
int nResponse = 0;
HttpResponse response = null;
try {
         httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs , "UTF-8"));
         response = httpClient.execute(httpPost);
        nResponse = response.getStatusLine().getStatusCode();
        if (nResponse == HttpURLConnection.HTTP_OK) {
          InputStream instream = response.getEntity().getContent();
                              // Process the stream
        }else if (nResponse == HttpURLConnection.HTTP_UNAUTHORIZED) {
        String res = readStream(response.getEntity().getContent());
        Log.i("Response", res);
                            // Process the error stream.
       }
   }  catch (Exception e) {
 e.printStackTrace();
}
return null;
}

Progress Dialog issue when clicking default search button in Android

When we are using a Non-cancelable progress dialog , There a chance to dismiss when a  when a user click in default search button of Anroid phone.. To prevent this , Override keylistner of the progress dialog and consume that event.
For example :

mDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
  @Override
  public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
   if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() == 0) {
    return true; // Pretend we processed it
   }
  return false; // Any other keys are still processed as normal
  }
});