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
  }
});