Thursday 6 February 2014

Android: Custom EditText (TextArea) with custom Background

In this example I will show how to create custom background with custom EditText  (TextArea). The final result you can see in the picture.
The background is downloaded from background textures. Text area is surrounded with black color and in the bottom is a counter how many characters user still can enter. 
When the user hits SHOW button the Toast displays entered text.






import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @author Peter.Gostincar
 *
 */
public class MainFragment extends Fragment {

 private int mMaxCount;
 private EditText mText;
 private Button mButtonShowText;
 private TextView mCounter;
 private String mFinalText;

 @Override
 public void onCreate(Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  mMaxCount = Integer.parseInt(getString(R.string.max_value));
 }


 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
  View v = inflater.inflate(R.layout.fragment_main, parent, false);
  mText = (EditText) v.findViewById(R.id.custom_text_view);
  mCounter = (TextView) v.findViewById(R.id.counter);
  mButtonShowText = (Button) v.findViewById(R.id.button_show);
  setListeners();
  return v;
 }


 /**
  * 
  */
 private void setListeners() {
  mText.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) {
    int strLenght = s.toString().length();
    int available = mMaxCount - strLenght;
    setAvailableSpace(available);
    if(available < 0){
     s.delete(strLenght-1, strLenght);
    }
    mFinalText = s.toString();
   }
  });

  mButtonShowText.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    Toast.makeText(getActivity(), mFinalText, Toast.LENGTH_SHORT).show();
   }
  });
 }

 protected void setAvailableSpace(int available) {
  mCounter.setText(available + "");
 }
}



We put this file (border.xml) into res/drawable  folder
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >
    <solid android:color="#ffffff" />

    <stroke
        android:width="1dp"
        android:color="#000000" />

</shape>



We put this file (repeating.xml) into res/drawable  folder
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:src="@drawable/nistri" 
    android:tileMode="repeat"/>
Nistri is the name of the image copied into drawable folder.



We put this file (fragment_main.xml) into res/layout folder
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/repeating"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="15dp" >

        <EditText
            android:id="@+id/custom_text_view"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:background="@drawable/border"
            android:gravity="top"
            android:hint="Enter text"
            android:padding="2dp" />

        <TextView
            android:id="@+id/counter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/custom_text_view"
            android:layout_alignRight="@id/custom_text_view"
            android:padding="5dp"
            android:text="@string/max_value"
            android:textColor="@color/counter" />
    </RelativeLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp" >

        <Button
            android:id="@+id/button_show"
            android:layout_centerHorizontal="true"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="SHOW" />
    </RelativeLayout>

</LinearLayout>

Friday 31 January 2014

Android : Properly rotated image (Landscape or Portrait)

I've been struggling for a few hours, how to rotate properly image after it's been taken trough Android app (Camera Intent).
I assume that this code example will be in some help for you also.
In the picture we can see one photo made in portrait mode and one in landscape mode. The both pictures are properly rotated, which is impossible with out additional fixing.




package com.example.testtakingandrotatingimage;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;

/**
 * @author Peter.Gostincar
 *
 */
public class MainFragment extends Fragment {

 
 private Uri mPictureFileUri;
 private static final int REQUEST_PHOTO = 0;
 private LinearLayout imageHolder;


 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState){
  View v = inflater.inflate(R.layout.fragment_main, parent, false);

  imageHolder = (LinearLayout) v.findViewById(R.id.image_holder);

  Button c = (Button) v.findViewById(R.id.take_image);
  c.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View arg0) {
    takePicture();
   }
  });
 
  return v;
 }


 @Override
 public void onActivityResult(int requestCode, int resultCode, Intent data){
  if(resultCode != Activity.RESULT_OK)
   return;
  if(requestCode == REQUEST_PHOTO){
   Bitmap image = rotateImage(mPictureFileUri);//rotate if necessary 
   showImageOnLayout(image);//show
  }
 }

 
 /**
  * 
  */
 protected void takePicture() {
  mPictureFileUri = Helpers.getGlobalPictureFileUri(getActivity());
  //is intent available & is mPictureFileUri created (is there space...)
  if((mPictureFileUri != null) && Helpers.
    isIntentAvailable(MediaStore.ACTION_IMAGE_CAPTURE, getActivity())){

   // create Intent to take a picture and return control to the calling application
   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   intent.putExtra(MediaStore.EXTRA_OUTPUT, mPictureFileUri);
   startActivityForResult(intent, REQUEST_PHOTO);
  }
  else {
   Toast.makeText(getActivity(), "something wrong under the hood", 
     Toast.LENGTH_SHORT).show();
  }
 }
 

 /**
  * 
  * @param image
  */
 private void showImageOnLayout(Bitmap image) {
  ImageView tmpImage = new ImageView(getActivity());
  tmpImage.setImageBitmap(image);
  tmpImage.setPadding(10, 10, 10, 10);

  ((ViewGroup) imageHolder).addView(tmpImage);
 }


 /**
  * here is the magic
  * @param uri
  * @return
  */
 private Bitmap rotateImage(Uri uri) {
  Bitmap image = Helpers.decodeSampledBitmapFromResource(getResources(), 
    uri.getPath(), 200, 200);
  try {
   Matrix matrix = new Matrix();

   ExifInterface exifInterface = new ExifInterface(uri.getPath());
   int rotation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
     ExifInterface.ORIENTATION_NORMAL);
   switch(rotation){
    case ExifInterface.ORIENTATION_NORMAL:{
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_90:{
     matrix.postRotate(90);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), 
       matrix, true);
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_180:{
     matrix.postRotate(180);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(),
       matrix, true);
     break;
    }
    case ExifInterface.ORIENTATION_ROTATE_270:{
     matrix.postRotate(270);
     image = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), 
       matrix, true);
     break;
    }
   }
  } catch (Exception e) {
   // TODO: handle exception
  }
  return image;
 }
}


class Helpers {
 
 private static final String MY_APPLICATION = "somepath";

 /**
  * 
  * @param mAppContext
  * @return
  */
 public static Uri getGlobalPictureFileUri(FragmentActivity mAppContext) {
  //To be safe, you should check that the SDCard is mounted
  // using Environment.getExternalStorageState() before doing this.

  if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
   return null;

  File mediaStorageDir = new File(mAppContext.getExternalFilesDir(
    Environment.DIRECTORY_PICTURES) , MY_APPLICATION);

  if(mediaStorageDir.toString().equals(MY_APPLICATION))
   mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
     Environment.DIRECTORY_PICTURES), MY_APPLICATION);

  // This location works best if you want the created images to be shared
  // between applications and persist after your app has been uninstalled.
  // Create the storage directory if it does not exist
  if (! mediaStorageDir.exists()){
   if (! mediaStorageDir.mkdirs()){
    return null;
   }
  }
  // Create a media file name
  String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
  File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+
    timeStamp + ".jpg");

  return Uri.fromFile(mediaFile);
 }


 /**
  * 
  * @param action
  * @param mAppContext
  * @return
  */
 public static boolean isIntentAvailable(String action, FragmentActivity mAppContext) {
  final PackageManager packageManager = mAppContext.getPackageManager();
  final Intent intent = new Intent(action);
  List list =
    packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
  return list.size() > 0;
 }
 
 
 /**
  * 
  * @param res
  * @param pathName
  * @param reqWidth
  * @param reqHeight
  * @return
  */
 public static Bitmap decodeSampledBitmapFromResource(Resources res, String pathName,
   int reqWidth, int reqHeight) {
  // First decode with inJustDecodeBounds=true to check dimensions
  final BitmapFactory.Options options = new BitmapFactory.Options();
  options.inJustDecodeBounds = true;
  BitmapFactory.decodeFile(pathName, options);
  // Calculate inSampleSize
  options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
  // Decode bitmap with inSampleSize set
  options.inJustDecodeBounds = false;
  Bitmap bitmap = BitmapFactory.decodeFile(pathName, options);
  return bitmap;
 }

 
 /**
  * 
  * @param options
  * @param reqWidth
  * @param reqHeight
  * @return
  */
 private 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) {

   final int halfHeight = height / 2;
   final int halfWidth = width / 2;

   // Calculate the largest inSampleSize value that is a power of 2 and keeps both
   // height and width larger than the requested height and width.
   while ((halfHeight / inSampleSize) > reqHeight
     && (halfWidth / inSampleSize) > reqWidth) {
    inSampleSize *= 2;
   }
  }
  return inSampleSize;
 }
}