Showing posts with label reusable. Show all posts
Showing posts with label reusable. Show all posts

Thursday, October 10, 2013

Android Almanac - Code Snippet Repo

Hi there! This section i will totatlly dedicate to android dev´s out there! It is as little Android Almanac containing common code snippets for the different purposes and tasks. I will be growing permanently. So if you miss something, just leave a constructive comment and i will try my best to accomplish that ok. Hope you like it.

How to detect device's 3G, wifi or internet connection

private boolean isConnectedToInternet(Context ctx) {

        NetworkInfo info = (NetworkInfo) ((ConnectivityManager) ctx
                .getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();

        if (info == null || !info.isConnected()) {
            return false;
        }
        if (info.isRoaming()) {
            // here is the roaming option you can change it if you want to
            // disable internet while roaming, just return false
            return false;
        }
        return true;
    }

Easy way to give feedback to users while pressing ImageView

final ImageView eraseBtn = (ImageView)findViewById(R.id.eraseBtn);
         eraseBtn.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    eraseBtn.setColorFilter(getResources().getColor(R.color.translucent_black));
                    eraseBtn.invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    eraseBtn.setColorFilter(null);
                    eraseBtn.invalidate();
                    break;
                default:
                    break;
                }
                return false;
            }
        });
Define the translucent black color like this in your strings.xml file:
< ? xml version="1.0" encoding="utf-8" ? >
< resources >
     ....
    < color name="translucent_black" >#51000000< / color>
     ....
< / resources>

How to compute degrees from MouseEvent (x,y coordinates)

In some cases, we need to compute the degrees from a give coordinate. Like from an event. Here is a simple way how to do it:

    private float getDegrees(MotionEvent event) {
        double radians = Math.atan2(event.getY(1) - event.getY(0), event.getX(1) - event.getX(0));
        return (float) Math.toDegrees(radians);
    }

Listening to orientation changes onSensorChanged()

In some cases, we need to implement SensorEventListener and implement onSensorChanded. While listening to it, i lost many yours looking for a simple approach which allows me to simple decide if my fone is lying on the side or if it stands. A very nice and simple way is to listen to its angles like that:

... code omitted ...

public void onSensorChanged(SensorEvent event) {
        float pitch = event.values[1];
        float roll = event.values[2];
        if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
            if (pitch < -45 && pitch > -135) {
                onLandscapePhoneStands();
            } else if (pitch > 45 && pitch < 135) {
                onLandscapePhoneOnHead();
            } else if (roll > 45) {
                onLandscapePhoneLyingOnTheLeftSide();
            } else if (roll < -45) {
                onLandscapePhoneLyingOnTheRightSide();
            }
        } else if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            if (pitch < -45 && pitch > -135) {
                onPhoneStands();
            } else if (pitch > 45 && pitch < 135) {
                onPhoneOnHead();
            } else if (roll > 45) {
                onPhoneLyingOnTheLeftSide();
            } else if (roll < -45) {
                onPhoneLyingOnTheRightSide();
            }
        }
    }

... code omitted ...

How to remove padding from AlertDialog on Android

I had a task to solve, which was very tricky. I needed to display an alert dialog, which presents an image (using the whole space of the dialog) without any buttons. Just a fullscreen picture on it. The problem was, that on the top side of the dialog, was always a border line remaining. No matter what i did or set in the layout.xml. I lost many hours searching for a proper solution. At the end the solution was as simple as i thought, but very tricky. All you need to do, is to call the following piece of code after calling the show() method on your dialogBuilder or on your dialog itself.

... code ommited...

dialogBuilder.show(); 

ViewGroup parent = (ViewGroup)view.getParent();

parent.setPadding(0, 0, 0, 0);

... code ommited ...

Getting the current display size

That´s a very common task for diffent purposes. Here a snippet for it:

DisplayMetrics metrics = getResources().getDisplayMetrics();
  int displayWidth = metrics.widthPixels;
  int displayHeight = metrics.heightPixels;

Rounded Corners by Layouts

Sometimes you'll need to be a little more sofisticated with your layout. One task i had, was to round the corners from my fragment layout, setting stroke type, color and radius. This may also be helpfull to you. In the folder drawable define a shape.xml called: rounded_layout_corners.xml with the content bellow and set it to the android:background attribute of your layout in the xml file.:


    
    
    
    


Rounded Corners by Layouts without bottom line

More examples with rounded corners but this way hidding the bottom line. For this kind of layout you'll need to create the drawable folder for portrait and drawable-land for landscape mode.

Here's the portrait layout i called rounded_layout_corner.xml
 
    
    
     
     
    
    
     
 
And here's the landscape layout i called also rounded_layout_corner.xml but put it in the drawable-land folder.
 
     
    
     
     
    
    
     
 

Supporting Multiple Screen Sizes

That´s a very common task. Here the most important infos you need to know.

drawable-ldpi (120 dpi, Low density screen) - 36px x 36px
drawable-mdpi (160 dpi, Medium density screen) - 48px x 48px
drawable-hdpi (240 dpi, High density screen) - 72px x 72px
drawable-xhdpi (320 dpi, Extra-high density screen) - 96px x 96px
drawable-xxhdpi or drawable-480dpi (480 dpi, XHDPI density screen) - 144px x 144px

Reacting to different orientations

That´s another very common task. Here one option you may like it:

private boolean onPhoneMovement() {
  boolean isPhoneLyingOnTheSide = false;
  DisplayMetrics metrics = getResources().getDisplayMetrics();
  int displayWidth = metrics.widthPixels;
  int displayHeight = metrics.heightPixels;
  switch (getWindowManager().getDefaultDisplay().getRotation()) {
  case Surface.ROTATION_0:
  case Surface.ROTATION_180: {
   onPhoneStands(displayHeight);
   isPhoneLyingOnTheSide = false;
   break;
  }
  case Surface.ROTATION_90:
  case Surface.ROTATION_270: {
   onPhoneLiesOnTheSide(displayWidth);
   isPhoneLyingOnTheSide = true;
   break;
  }
  }
  return isPhoneLyingOnTheSide;
 }

Setting Width and Height to Layouts

That can be very usefull in some cases:

private void onPhoneLiesOnTheSide(int displayWidth) {
  this.toReplace = (LinearLayout) findViewById(R.id.toReplace);
  this.toReplace.getLayoutParams().height = this.buttonBackground.getHeight();
  this.toReplace.getLayoutParams().width = displayWidth - this.buttonBackground.getWidth();
 }

 private void onPhoneStands(int displayHeight) {
  this.toReplace = (LinearLayout) findViewById(R.id.toReplace);
  this.toReplace.getLayoutParams().height = displayHeight - this.buttonBackground.getHeight();
  this.toReplace.getLayoutParams().width = this.buttonBackground.getWidth();
 }

Fragment Replacement and Animation (Slide In and Slide Out)

Attention: If you've never worked with fragments, so please read my previous post about it first. dynamically fragment replacement android This snippet could be a differencial in your app. create a folder anim and anim-land in the folder res. Then put those xml-files in it. In folder anim create a xml file called slide_in.xml


    


The in the same folder anim create a xml file called slide_out.xml


    


The in the folder anim-land create a xml file called slide_in.xml


    


And last but no least in the folder anim-land create a xml file called slide_out.xml


    



Then try this here:
 private void hideFragmen(final Fragment framgmentToShow, boolean isPhoneLyingOnTheSide) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  transaction.setCustomAnimations(R.anim.slide_out, R.anim.slide_in);
  transaction.replace(R.id.toReplace, framgmentToShow);
  transaction.hide(framgmentToShow);
  transaction.commit();
 }

 private void showFragment(final Fragment framgmentToShow, boolean isPhoneLyingOnTheSide) {
  FragmentTransaction transaction = getFragmentManager().beginTransaction();
  transaction.setCustomAnimations(R.anim.slide_out, R.anim.slide_in);
  transaction.replace(R.id.toReplace, framgmentToShow);
  transaction.show(framgmentToShow);
  transaction.commit();
 }

Load/Decode Bitmap from Resource with BitmapFactory

You'll need this line several times while developing with Android.

  Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);

Rotate icons depending on phone Orientation

That´s a nice one. Small, fine and native. Because buttons are also views you can use this i a very generic way.

private void setIconRotation(int degrees) {
  for (View view : viewsToRotate) {
   view.setRotation(degrees);
  }
}

Fix/fixing a layout on the right side of your phone

That's also a good one. No matter what kind of the screen orientation you have. Just fix(post it) your layout in the right corner of your phone.

public void handleRotationChange(int rotation) {
  switch (rotation) {
  case Surface.ROTATION_0:
  case Surface.ROTATION_180: {
   // ***********************************************************
   // rotate toolbar (fix it on the right)
   // ***********************************************************
   this.layoutToolbar.setOrientation(LinearLayout.HORIZONTAL);
   this.layoutToolbar.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
   LayoutParams params = (LayoutParams) this.layoutToolbar.getLayoutParams();
   params.gravity = Gravity.BOTTOM;
   params.width = LayoutParams.MATCH_PARENT;
   params.height = LayoutParams.WRAP_CONTENT;
   // ***********************************************************
   // rotate icons
   // ***********************************************************
   setIconRotation(0);
  }
   break;

  case Surface.ROTATION_90:
  case Surface.ROTATION_270: {
   // ***********************************************************
   // rotate toolbar (fix it on the bottom)
   // ***********************************************************
   this.layoutToolbar.setOrientation(LinearLayout.VERTICAL);
   this.layoutToolbar.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
   LayoutParams params = (LayoutParams) this.layoutToolbar.getLayoutParams();
   params.gravity = Gravity.RIGHT;
   params.width = LayoutParams.WRAP_CONTENT;
   params.height = LayoutParams.MATCH_PARENT;
   // ***********************************************************
   // rotate icons
   // ***********************************************************
   setIconRotation(0);
  }
   break;
  }
 }

Showing/show simple info message on the screen

private void showMessage(String msg) {
  Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
 }

GPS distance calculation from one point to another


public double calculeDistanciaEmMetrosComoDouble(double lat1, double lng1, double lat2, double lng2) {
  return calculeDistancia(lat1, lng1, lat2, lng2);
 }

 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância com o suffix "m" no final*/
 public String calculeDistanciaEmMetros(double lat1, double lng1, double lat2, double lng2) {
  double distanciaCalculada = calculeDistancia(lat1, lng1, lat2, lng2);
  return definaQuantosNumerosAposVirgula(2, distanciaCalculada) + " m";
 }
 
 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância com o suffix "km" no final*/
 public String calculeDistanciaEmKilometros(double lat1, double lng1, double lat2, double lng2) {
  double distanciaCalculada = calculeDistancia(lat1, lng1, lat2, lng2);
  double kilometros = distanciaCalculada / 1000;
  return definaQuantosNumerosAposVirgula(2, kilometros) + " km";
 }
 
 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância em miles (1.609km) com o suffix "miles" no final*/
 public String calculeDistanciaEmMiles(double lat1, double lng1, double lat2, double lng2) {
  double distanciaCalculada = calculeDistancia(lat1, lng1, lat2, lng2);
  double miles = distanciaCalculada / 1609.344;
  return definaQuantosNumerosAposVirgula(2, miles) + " miles";
 }
 
 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância em yards (0.9144m) com o suffix "yards" no final*/
 public String calculeDistanciaEmYards(double lat1, double lng1, double lat2, double lng2) {
  double distanciaCalculada = calculeDistancia(lat1, lng1, lat2, lng2);
  double yards = distanciaCalculada / 0.9144;
  return definaQuantosNumerosAposVirgula(2, yards) + " yards";
 }
 
 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância em feets (0.3048m) com o suffix "feets" no final*/
 public String calculeDistanciaEmFeets(double lat1, double lng1, double lat2, double lng2) {
  double distanciaCalculada = calculeDistancia(lat1, lng1, lat2, lng2);
  double feets = distanciaCalculada / 0.3048;
  return definaQuantosNumerosAposVirgula(2, feets) + " feets";
 }
 
 /** calcula a distância de um ponto ao outro (longitude, latitude) retornando a distância em metros"*/
 private double calculeDistancia(double lat1, double lng1, double lat2, double lng2) {
  double earthRadius = 3958.75;
  double dLat = Math.toRadians(lat2 - lat1);
  double dLng = Math.toRadians(lng2 - lng1);
  double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
    + Math.cos(Math.toRadians(lat1))
    * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng / 2)
    * Math.sin(dLng / 2);
  double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  double dist = earthRadius * c;
  int meterConversion = 1609;
  double result = (dist * meterConversion);
  return result;

 }
 
 private String definaQuantosNumerosAposVirgula(int qnt, double amount){
  NumberFormat df = DecimalFormat.getInstance();
  df.setMinimumFractionDigits(2);
  df.setMaximumFractionDigits(2);
  return df.format(amount);
 }

Hide Application Titlebar


public void hideApplicationTitleBar() {
  requestWindowFeature(Window.FEATURE_NO_TITLE);
 }

Set Application Orientation


setRequestedOrientation(defineActivityOrientation());
public int defineActivityOrientation() {
  return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 }

GPS Locatition Manager, LastKnownLocation, Wifi, Network


protected LocationManager locationManager;
protected String provider;
protected Geocoder coder;
private void initGeoLocation() {
  this.addressFinder = new AddressFinder(this, this.autoCompleteSearch);
  this.locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
  showMessageOnGpsDeactivated();
  // Define the criteria how to select the locatioin provider -> use default
  this.provider = locationManager.getBestProvider(new Criteria(), false);
  this.coder = new Geocoder(this);
 }
public boolean showMessageOnGpsDeactivated() {
  boolean isGpsDeactivated = false;
  if (!isGpsProviderEnabled() || !isNetworkProviderEnabled()) {
   showModalGpsActivateDialog();
   isGpsDeactivated = true;
  }
  return isGpsDeactivated;
 }
private boolean isGpsProviderEnabled() {
  return this.locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
 }

 private boolean isNetworkProviderEnabled() {
  return this.locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
 }
public boolean isOnline() {
  boolean isConnected = false;
  ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo netInfo = cm.getActiveNetworkInfo();
  if (netInfo != null && netInfo.isConnectedOrConnecting()) {
   isConnected = true;
  }
  return isConnected;
 }
public boolean isWiFiOnline() {
  boolean isConnected = false;
  ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
  NetworkInfo mWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
  if (mWifi != null && mWifi.isConnectedOrConnecting()) {
   isConnected = true;
  }
  return isConnected;
 }
public void showModalGpsActivateDialog() {

  LayoutInflater inflater = getLayoutInflater();
  View view = inflater.inflate(R.layout.layout_toast, (ViewGroup) findViewById(R.id.layout_toast_gps));

  new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(getResources().getText(R.string.toast_gps_dialog_title))
    .setPositiveButton(getResources().getText(R.string.toast_gps_dialog_button_text), new DialogInterface.OnClickListener() {
     @Override
     public void onClick(DialogInterface dialog, int which) {
      navigateTo(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
     }

    }).setView(view).setCancelable(false).show();
 }
public Location getLastKnownLocation() {
  Location location = null;
  if (isOnline()) {
   if (!showMessageOnGpsDeactivated()) {
    if (isLocationAvailable()) {
     location = this.locationManager.getLastKnownLocation(this.provider);
    } else {
     showMessage(this, getResources().getString(R.string.toast_gps_dialog_no_gps_available));
    }
   }
  } else {
   showModalNoNetworkDialog();
  }
  return location;
 }

 public boolean isLocationAvailable() {
  this.locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
  this.provider = locationManager.getBestProvider(new Criteria(), false);
  this.coder = new Geocoder(this);
  return this.locationManager.getLastKnownLocation(this.provider) != null;
 }

Navigation, Intent, Google Play Store, Webpage


/** Use this method to get passed values over Intent.SetExtras(...) */
 public String getPassedUserInputSelection(String key) {
  return (String) getIntent().getExtras().get(key);
 }

 /** Use this method to navigate from one activity to another passing values to the target activity */
 public void navigateToPassingValues(Context fromActivity, Class toActivityClass, String bundleKey, Bundle bundle) {
  Intent activityToStart = createActivityToStart(fromActivity, toActivityClass);
  activityToStart.putExtra(bundleKey, bundle);
  startActivity(activityToStart);
 }

 /** Use this method to navigate from one activity to another passing values to the target activity */
 public void navigateToPassingValues(Context fromActivity, Class toActivityClass, String key, String value) {
  Intent activityToStart = createActivityToStart(fromActivity, toActivityClass);
  activityToStart.putExtra(key, value);
  startActivity(activityToStart);
 }

 /** Use this method to navigate from one activity to another */
 public void navigateTo(Context fromActivity, Class toActivityClass) {
  startActivity(createActivityToStart(fromActivity, toActivityClass));
 }

 /** Use this method to navigate directly to a given intent */
 public void navigateTo(Intent intent) {
  startActivity(intent);
 }

 private Intent createActivityToStart(Context fromActivity, Class toActivityClass) {
  return new Intent(fromActivity, toActivityClass);
 }

 /** Use this method to open the google play store from this app */
 public void navigateToGooglePlayStore() {
  final String appName = "com.treslines.onibuspe";
  try {
   startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appName)));
  } catch (android.content.ActivityNotFoundException anfe) {
   startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=" + appName)));
  }
 }

 public void navigateToTreslines() {
  final String webpage = "http://www.treslines.com/index.html";
  try {
   startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(webpage)));
  } catch (android.content.ActivityNotFoundException anfe) {
   // If anything goes wrong, don't disturb the user experience. just don't open the webpage
  }
 }
 
 public void navigateToWebpage(String address) {
  final String webpage = address;
  try {
   startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(webpage)));
  } catch (android.content.ActivityNotFoundException anfe) {
   // If anything goes wrong, don't disturb the user experience. just don't open the webpage
  }
 }

 /** Use this method to display messages to the user */
 public void showMessage(Context context, String msg) {
  Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
  toast.setGravity(Gravity.CENTER, 0, 0);
  toast.show();
 }

AutoComplete, MultiAutoCompleteTextView


/** Use this method to create a custom autocomplete with NoTokenizer and no suggestions flags */
 public void createAutoComplete(MultiAutoCompleteTextView autoComplete, String[] contentToShow) {
  autoComplete.setAdapter(new ArrayAdapter(this, android.R.layout.simple_dropdown_item_1line, contentToShow));
  autoComplete.setTokenizer(getTokenizerForMultiAutoCompleteTextView());
  autoComplete.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
 }

 /** Use this method to create a custom Tokenizer with no comma(,) termination */
 public NoTokenizer getTokenizerForMultiAutoCompleteTextView() {
  return new NoTokenizer();
 }

 private class NoTokenizer extends MultiAutoCompleteTextView.CommaTokenizer {

  @Override
  public int findTokenEnd(CharSequence text, int cursor) {
   return super.findTokenEnd(text, cursor);
  }

  @Override
  public int findTokenStart(CharSequence text, int cursor) {
   return super.findTokenStart(text, cursor);
  }

  @Override
  public CharSequence terminateToken(CharSequence text) {
   CharSequence terminateToken = super.terminateToken(text);
   terminateToken = terminateToken.toString().replace(" ,", "").replace(", ", "");
   return terminateToken;
  }

 }

Creating array from string.xml


public String[] createLinhasFromStringXml() {
  return getResources().getStringArray(R.array.linha_array);
 }

Inflater to put into your abstracted class


public View inflateContentView(int layoutId) {
  LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  return inflater.inflate(layoutId, null);
 }

DB, database date parser


import android.annotation.SuppressLint;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@SuppressLint("SimpleDateFormat")
public class DbDateParser {
 private static final String format = "yyyy-MM-dd";

 public static Date stringToDate(String data) {
  Date dDate = null;
  SimpleDateFormat formatter = new SimpleDateFormat(format);
  try {
   dDate = formatter.parse(data);
  } catch (ParseException e) {
  }
  return dDate;
 }

 public static String dateToString(Date data) {
  return dateToString(data, format);
 }

 public static String dateToString(Date data, String format) {
  SimpleDateFormat formatter = new SimpleDateFormat(format);
  return formatter.format(data);
 }

 public static int getHour(Date data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("HH");
   return Integer.parseInt(formatter.format(data));
  } catch (Exception ex) {
   return -1;
  }
 }

 public static int getMinute(Date data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("mm");
   return Integer.parseInt(formatter.format(data));
  } catch (Exception ex) {
   return -1;
  }
 }

 public static int getDay(Date data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("dd");
   return Integer.parseInt(formatter.format(data));
  } catch (Exception ex) {
   return -1;
  }
 }

 public static int getMonth(Date data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("MM");
   return Integer.parseInt(formatter.format(data));
  } catch (Exception ex) {
   return -1;
  }
 }

 public static int getYear(Date data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("yyyy");
   return Integer.parseInt(formatter.format(data));
  } catch (Exception ex) {
   return -1;
  }
 }

 public static Date getFullHour_String2Date(String data) {
  try {
   SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
   return formatter.parse(data);
  } catch (Exception ex) {
   return new Date();
  }
 }
}

Ormlite, SQLite, DatabaseHelper


import java.sql.SQLException;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;
import com.treslines.entity.EmpresaEntity;
import com.treslines.entity.ItinerarioEntity;
import com.treslines.entity.LinhaEntity;
import com.treslines.entity.ParadaEntity;
import com.treslines.entity.ReferenciaEntity;
import com.treslines.entity.RotaEntity;
import com.treslines.entity.RoteiroEntity;

public class DatabaseHelper extends OrmLiteSqliteOpenHelper {

 private static final String DATABASE_NAME = "databasename.db";
 private static final int DATABASE_VERSION = 1;

 public DatabaseHelper(Context context) {
  super(context, DATABASE_NAME, null, DATABASE_VERSION);
 }

 @Override
 public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
  try {
   Log.i("DB", "onCreate: create tables if not exists");
   TableUtils.createTableIfNotExists(connectionSource, LinhaEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, ReferenciaEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, RoteiroEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, RotaEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, EmpresaEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, ItinerarioEntity.class);
   TableUtils.createTableIfNotExists(connectionSource, ParadaEntity.class);
  } catch (SQLException e) {
   Log.e(DatabaseHelper.class.getSimpleName(), "Can't create database", e);
   throw new RuntimeException(e);
  }
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
  try {
   // delete all tables
   Log.i("DB", "onUpgrade: drop old tables and create new tables");
   TableUtils.dropTable(connectionSource, ParadaEntity.class, true);
   TableUtils.dropTable(connectionSource, ItinerarioEntity.class, true);
   TableUtils.dropTable(connectionSource, EmpresaEntity.class, true);
   TableUtils.dropTable(connectionSource, RotaEntity.class, true);
   TableUtils.dropTable(connectionSource, RoteiroEntity.class, true);
   TableUtils.dropTable(connectionSource, ReferenciaEntity.class, true);
   TableUtils.dropTable(connectionSource, LinhaEntity.class, true);
   // create new tables
   onCreate(db, connectionSource);
  } catch (SQLException e) {
   Log.e(DatabaseHelper.class.getSimpleName(), "Can't drop databases", e);
   throw new RuntimeException(e);
  }
 }

 @Override
 public void close() {
  super.close();
 }
}

import com.j256.ormlite.field.DatabaseField;

// ORMLite download addresses
// ormlite-android-4.45: //www.java2s.com/Code/Jar/o/Downloadormliteandroid445jar.htm
// ormlite-core-4.45: http://sourceforge.net/projects/ormlite/files/

public abstract class AbstractEntity {

 @DatabaseField(columnName="_id", generatedId = true)
 private Integer id;
 @DatabaseField(columnName="timestamp")
 private String timestamp;
 
 // ormlite require default constructor
 public AbstractEntity() {
  super();
 }
 
 public AbstractEntity(Integer id, String timestamp) {
  setId(id);
  setTimestamp(timestamp);
 }

 public Integer getId() {return id;}
 public void setId(Integer id) {this.id = id;}
 public String getTimestamp() {return timestamp;}
 public void setTimestamp(String timestamp) {this.timestamp = timestamp;}

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((id == null) ? 0 : id.hashCode());
  result = prime * result + ((timestamp == null) ? 0 : timestamp.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  AbstractEntity other = (AbstractEntity) obj;
  if (id == null) {
   if (other.id != null)
    return false;
  } else if (!id.equals(other.id))
   return false;
  if (timestamp == null) {
   if (other.timestamp != null)
    return false;
  } else if (!timestamp.equals(other.timestamp))
   return false;
  return true;
 } 
}

import java.util.ArrayList;
import java.util.Collection;

import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;

@DatabaseTable(tableName="empresa")
public class EmpresaEntity extends AbstractEntity implements Comparable{

 @DatabaseField(columnName="empresa_nome")
 private String nome;
 @DatabaseField(columnName="empresa_abreviacao")
 private String abreviacao;
 @DatabaseField(columnName="empresa_codigo")
 private String codigo;
 @ForeignCollectionField(columnName="empresa_linhas")
 private Collection linhas = new ArrayList();
 
 // ormlite require default constructor
 public EmpresaEntity(){
  super();
 }
 
 public EmpresaEntity(String codigo,String abreviacao,String nome, Collection linhas){
  setCodigo(codigo);
  setAbreviacao(abreviacao);
  setNome(nome);
  setLinhas(linhas);
 }
 
 public EmpresaEntity(String codigo,String abreviacao,String nome){
  setCodigo(codigo);
  setAbreviacao(abreviacao);
  setNome(nome);
 }
 
 public Collection getLinhas() {return linhas;}
 public void setLinhas(Collection linhas) {this.linhas = linhas;}
 public String getNome() {return nome;}
 public void setNome(String nome) {this.nome = nome;}
 public String getAbreviacao() {return abreviacao;}
 public void setAbreviacao(String abreviacao) {this.abreviacao = abreviacao;}
 public String getCodigo() {return codigo;}
 public void setCodigo(String codigo) {this.codigo = codigo;}

 @Override
 public int compareTo(EmpresaEntity another) {
  if (equals(another)) {
   return 0;
  }
  return -1;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = super.hashCode();
  result = prime * result + ((abreviacao == null) ? 0 : abreviacao.hashCode());
  result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
  result = prime * result + ((linhas == null) ? 0 : linhas.hashCode());
  result = prime * result + ((nome == null) ? 0 : nome.hashCode());
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (!super.equals(obj))
   return false;
  if (getClass() != obj.getClass())
   return false;
  EmpresaEntity other = (EmpresaEntity) obj;
  if (abreviacao == null) {
   if (other.abreviacao != null)
    return false;
  } else if (!abreviacao.equals(other.abreviacao))
   return false;
  if (codigo == null) {
   if (other.codigo != null)
    return false;
  } else if (!codigo.equals(other.codigo))
   return false;
  if (linhas == null) {
   if (other.linhas != null)
    return false;
  } else if (!linhas.equals(other.linhas))
   return false;
  if (nome == null) {
   if (other.nome != null)
    return false;
  } else if (!nome.equals(other.nome))
   return false;
  return true;
 }
}

😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇

Be sure to read, it will change your life!
Show your work by Austin Kleonhttps://amzn.to/34NVmwx

This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practiceshttps://amzn.to/30WQSm2

Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv

This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp

Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy

😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆

Sunday, June 9, 2013

Translation - Multilingual/Multilanguage Database Design

Hi there! Today i wanna share something very useful to you while developing a multilingual / multilanguage database (DB). While outthere is a lot of code and approches, none of them take the time to point out the details you need to know, to really understand the concept and how you may optimize it. In this post i'm trying to explain it and to show my solution to you in a very simple, grafical way. It may exists even better approches out there, but I think this is a good one.

Understanding the concept before otimization

first of all lets understand the concept. The image below is the best way to visualize my thoughts. so lets take a look of it:


Ok, let's point out what ist good and not so good in this approach.
+ you have all translations files in one place (better maintanance)
+ with every new language you may have or need, you don't need to change your entity tables (flexible, extenpandable)
- not very readable for the DB user and queries/inserts are not trivial
- I do not know the ranges of each table (for example, if i want to print out a specific table for the translator in my DB it may be very difficult at first sight)


How can i optimize this approach?

Well there is a way to do that. For example one of it is to define an insert schema. It will turn this approach into a very readable and enables everybody to search for a specific table range without big efforts. The second image below is the best way to explain what i mean. So lets take a look of it:





Have you figured out the trick? The idea here is to work with conventions.  The insert schema could be something like:

IdRowToTranslate_TableName_ColumnPropertyName_IdRowTranslationReference_TableName

in this concrete example:
1_PRODUCT_NAME_1_TRANSLATION_REFERENCE 
1_PRODUCT_DESCRIPTION_2_TRANSLATION_REFERENCE  

2_PRODUCT_NAME_3_TRANSLATION_REFERENCE 
2_PRODUCT_DESCRIPTION_4_TRANSLATION_REFERENCE
and so on...
 
Once defined the insert and its respective query commands, it turns the model into a much more readable and understandable approach. This way we are now also able to seach by ranges.


Update: 

Hi there! a few days ago,  i had an interesting contatct with Alessandro. A Software Engineer looking also for a multilingual / multilanguage database solution. He had a very good approach. We have discussed PROS and CONS and because i think, that his solution is even more elegant than mine, i asked him to post it here to complement this post and share his solution with us. Here are both solutions (mine and alessandro's solution), so you can directly compare and have an ideia how to do it. Thanks Alessandro for sharing it with us.






That's all. hope you like it.

😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇

Be sure to read, it will change your life!
Show your work by Austin Kleonhttps://amzn.to/34NVmwx

This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practiceshttps://amzn.to/30WQSm2

Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv

This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp

Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy

😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆

Saturday, May 25, 2013

Using SQLite in your Android applications

Hi there!

Currently i'm developing some android apps which needs to access a SQLite database and use basic CRUD (create, read, update, delete) methods.  

I had some difficulties at the beginning and because I think that out there are a lot of other developers that would be happy to have a practical, reusable, well UML-documented solution, I decided to share my experience and solution with you. (this is just one solution of millions) I was looking for a design where the responsibilities were cleary separeted, making it easy to understand it without beeing to complex. If you have never worked with SQLite, you'll learn here how to instanciate and insert data into SQLite as well how to retrieve data from it and so on... We will end up with something like this at the end of this post:


Explaning the details of the UML-diagram first

I'm a convinced clean coder, so can't develop without UML-digrams. It's like the old saying: A picture says more then a thousand words. And in my point of view it is also a better documentation then tousend of lines of comments inside your code. (But i'm not saying that you don't should comment, when needed and helpful)

This tiny framework is showing to you how the design could be. The main classes here are DAO (Data Access Object), DMO (Data Manipulation Object), Table and the enum DbConfig

DbConfig defines all database's configuration central in one location, which makes it simple to change something in case you need. The inner enums defines all the needed things associated with the tables you may need. It is responsible for configuration tasks.

Table is abstract. All tables you may need extends from it making the implementation very simple. It is responsible for defining and creating tables.

DMO is also abstract and contais all manipulation methods you may need. It is up to you to extend it in the way you want.

Entity is a abstract class you may define to encapsulate common properties of the tables you may have in your application. It is responsible to hold the data readed from the database.

DAO is that class which accesses the database and populates the entities you've defined. Its responsibility is to access, read and return the entity you require.

ClientTest here is just a JunitTest representing your Activity when it uses the DAO.


Looking inside the code of DMO and Table




I think the code is a better teller then words. For this reason lets take a look inside of it to understand the details. We will beginn step by step and the logical way we would start, when developing. Lets assume we know what we want. What's the first step? We define the tables. So lets start with the DbConfig and its inner classes.

Enum DbConfig and its inner classes


/**
 * Defines all database associated information in a central, reusable, extensible way. (database, tables, columns, configuration etc.).<br/>
 * 
 * @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a>
 * 
 */
public enum DbConfig {

    DATABASE_TEST, TABLE_VIDEO, TABLE_FOTO, TABLE_AUDIO, TABLE_TEXTO;

    public static final int dbDefaultVersion = 1;
    public static final CursorFactory dbDefaultCursorFactory = null;

    /** defines in a central way the table column names from the table {@link TableFotoConfig} */
    public enum TableFotoConfig {

        ID, NOME, FOTO, TIMESTAMP;

        public static String generateCreateTableStatement() {
            final String c0 = NOME.name() + TEXT + COMMA;
            final String c1 = FOTO.name() + BLOB + COMMA;
            final String c2 = TIMESTAMP.name() + TEXT;
            final String creteStatement = c0 + c1 + c2;
            return creteStatement;
        }
    }

    /** defines in a central way the table column names from the table {@link TableVideoConfig} */
    public enum TableVideoConfig {

        ID, NOME, VIDEO, TIMESTAMP;
        public static String generateCreateTableStatement() {
            final String c0 = NOME.name() + TEXT + COMMA;
            final String c1 = VIDEO.name() + BLOB + COMMA;
            final String c2 = TIMESTAMP.name() + TEXT;
            final String creteStatement = c0 + c1 + c2;
            return creteStatement;
        }
    }

    /** defines in a central way the table column names from the table {@link TableAudioConfig} */
    public enum TableAudioConfig {

        ID, NOME, AUDIO, TIMESTAMP;

        public static String generateCreateTableStatement() {
            final String c0 = NOME.name() + BLOB + COMMA;
            final String c1 = AUDIO.name() + BLOB + COMMA;
            final String c2 = TIMESTAMP.name() + TEXT;
            final String creteStatement = c0 + c1 + c2;
            return creteStatement;
        }
    }

    /** defines in a central way the table column names from the table {@link TableTextoConfig} */
    public enum TableTextoConfig {

        ID, TEXTO, TIMESTAMP;

        public static String generateCreateTableStatement() {
            final String c0 = TEXTO.name() + TEXT + COMMA;
            final String c1 = TIMESTAMP.name() + TEXT;
            final String creteStatement = c0 + c1;
            return creteStatement;
        }
    }

    /** use it to separate values while creating SQL statements */
    public static final String COMMA = ",";
    /** use it to save it as a null value */
    public static final String NULL = " NULL ";
    /** use it to save integers, primary keys */
    public static final String INTEGER = " INTEGER ";
    /** use it to save doubles, floats */
    public static final String REAL = " REAL ";
    /** use it to save text, varchar, char */
    public static final String TEXT = " TEXT ";
    /** use it to save fotos, videos, audio, data etc. */
    public static final String BLOB = " BLOB ";

} 

Now lets take a look inside of the abstract class Table. All tables we need will extend from it.

Abstract Class Table


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public abstract class Table extends SQLiteOpenHelper {

    public Table(Context appContext) {
        super(appContext, DbConfig.DATABASE_TEST.name(), DbConfig.dbDefaultCursorFactory, DbConfig.dbDefaultVersion);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        final String sqlCmdStart = "CREATE TABLE ";
        final String primaryKeyCmd = " (ID integer primary key autoincrement, ";
        final String sqlCmdEnd = ");";
        final String createQuery = sqlCmdStart + defineTableNameToCreate() + primaryKeyCmd + defineTableColumnsToCreate() + sqlCmdEnd;
        db.execSQL(createQuery);
    }

    public abstract String defineTableColumnsToCreate();

    public abstract String defineTableNameToCreate();

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // NOP
    }
} 

In my case i designed 4 classes(tables) for a proof of concept i had. Text, Audio, Video and Audio. Lets see how they look like:

Class TextTable


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class TextoTable extends Table {

    public TextoTable(Context appContext) {
        super(appContext);
    }

    @Override
    public String defineTableColumnsToCreate() {
        return TableTextoConfig.generateCreateTableStatement();

    }

    @Override
    public String defineTableNameToCreate() {
        return DbConfig.TABLE_TEXTO.name();
    }
} 

Class VideoTable


 /** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class VideoTable extends Table {

    public VideoTable(Context appContext) {
        super(appContext);
    }

    @Override
    public String defineTableColumnsToCreate() {
        return TableVideoConfig.generateCreateTableStatement();

    }

    @Override
    public String defineTableNameToCreate() {
        return DbConfig.TABLE_VIDEO.name();
    }
}

Class AudioTable


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class AudioTable extends Table {

    public AudioTable(Context appContext) {
        super(appContext);
    }

    @Override
    public String defineTableColumnsToCreate() {
        return TableAudioConfig.generateCreateTableStatement();

    }

    @Override
    public String defineTableNameToCreate() {
        return DbConfig.TABLE_AUDIO.name();
    }
} 

Class FotoTable


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class FotoTable extends Table {

    public FotoTable(Context appContext) {
        super(appContext);
    }

    @Override
    public String defineTableColumnsToCreate() {
        return TableFotoConfig.generateCreateTableStatement();
    }

    @Override
    public String defineTableNameToCreate() {
        return DbConfig.TABLE_FOTO.name();
    }
} 

OK done! we have our tables now. Lets see what DMO offers to us. This is the class which contains more logic inside. It is a very important class in this approach. It encapsulates all methods we may need. It is best to understand how SQLite works and what we are able to do with.

Abstract Class DMO


**
 * This Data Manipulation Object (DMO) offers CRUD operations (create, read, update, delete) and whatever you may need.
 * 
 * @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a>
 */
public abstract class DMO {

    private SQLiteDatabase db;

    public DMO(Context appContext, Table tableToOpen) throws NullPointerException {
        garantConnection(appContext, tableToOpen);
        openTable(tableToOpen);
    }

    public void openTable(Table tableToOpen) {
        if (this.db == null || !this.db.isOpen()) {
            this.db = tableToOpen.getWritableDatabase();
        }
    }

    public void close() {
        if (this.db != null) {
            this.db.close();
        }
    }

    /** Defines the name of this database in the concrete implementation of this class */
    public abstract String defineDatabaseNameToCreate();

    public int insert(String tableName, ContentValues rowToCreate) {

        db.insert(tableName, null, rowToCreate);
        String[] columnsToShow = null;
        String selection = null;
        String[] selectionArgs = null;
        String groupBy = null;
        String having = null;
        String orderBy = "ID DESC LIMIT 1";
        Cursor query = db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
        query.moveToFirst();
        int indexId = query.getColumnIndex("ID");
        int id = query.getInt(indexId);
        close();
        return id;
    }

    public void update(String tableName, int rowIdToUpdate, ContentValues rowToUpdate) {

        final String whereClause = "ID=" + rowIdToUpdate;
        db.update(tableName, rowToUpdate, whereClause, null);
        close();
    }

    public void dropTable(String tableName) {

        String sql = "DROP TABLE IF EXISTS " + tableName;
        db.execSQL(sql);
        close();
    }

    public boolean dropDatabase(Context appContext, String databaseName) {
        return appContext.deleteDatabase(databaseName);
    }

    public void deleteById(String tableName, int id) {

        String whereClause = "ID=" + id;
        String[] whereArgs = null;
        db.delete(tableName, whereClause, whereArgs);
        close();
    }

    public Cursor selectAll(String tableName, String[] columnNamesToShow) {

        String[] columnsToShow = columnNamesToShow;
        String selection = null;
        String[] selectionArgs = null;
        String groupBy = null;
        String having = null;
        String orderBy = null;
        return db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
    }

    public Cursor selectAllOrderBy(String tableName, String[] columnNamesToShow, String columnNameToOrderBy) {

        String[] columnsToShow = columnNamesToShow;
        String selection = null;
        String[] selectionArgs = null;
        String groupBy = null;
        String having = null;
        String orderBy = columnNameToOrderBy;
        return db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
    }

    public Cursor selectAllDistinct(String tableName, String[] columnNamesToShow, String columnNameToDistinct) {

        String[] columnsToShow = columnNamesToShow;
        String selection = null;
        String[] selectionArgs = null;
        String groupBy = columnNameToDistinct;
        String having = null;
        String orderBy = columnNameToDistinct;
        return db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
    }

    public Cursor selectRowById(String tableName, int id) {

        String[] columnsToShow = null;
        String selection = "ID=" + id;
        String[] selectionArgs = null;
        String groupBy = null;
        String having = null;
        String orderBy = null;
        return db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
    }

    public Cursor getDifferences(String tableName, String timestamp) {

        String[] columnsToShow = null;
        String selection = "TIMESTAMP = '" + timestamp + "'";
        String[] selectionArgs = null;
        String groupBy = null;
        String having = null;
        String orderBy = null;
        return db.query(tableName, columnsToShow, selection, selectionArgs, groupBy, having, orderBy);
    }

    private void garantConnection(Context appContext, Table tableToOpen) {
        if (appContext == null || tableToOpen == null) {
            throw new NullPointerException("appContext and tableToOpen can't be set to null");
        }
    }

} 

 
The next class is the implementation of DMO. In my case i called it Database. In there i define some specific settings acc. to my needs like database name.

Class Database


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class Database extends DMO {

    public Database(Context appContext, Table tableToOpen) {
        super(appContext, tableToOpen);
    }

    @Override
    public String defineDatabaseNameToCreate() {
        return DbConfig.DATABASE_TEST.name();
    }
} 

Ok greate we are done for this part. Now lets take a look into the DAO. It will use the DMO to access the database and manipulate it. In this class you'll learn how to retrieve data from SQLlite. It is a good example if you are new on it.

Looking inside the code of the DAO and Entities

So we are almost done. Before we can look inside of the DAO, we need to create our entities to represent the tables we have. The UML-diagram from it is very simple and looks like that:

As I sad in the introduction, we don't want to repeat ourself right? Remember "clean code" ;-) So in order to reuse the common properties of my tables, i decided to create the abstract class Entity. Here is the code of it. (it is a simple bean)

Abstract Class Entity


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public abstract class Entity {

    private String name;
    private byte[] stream;
    private String timeStamp;

    public String getName() {
        return this.name;
    }

    public byte[] getStream() {
        return this.stream;
    }

    public String getTimeStamp() {
        return this.timeStamp;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setStream(byte[] stream) {
        this.stream = stream;
    }

    public void setTimeStamp(String timeStamp) {
        this.timeStamp = timeStamp;
    }
} 

Good! In this simple example my concrete classes of entity are very simple. I has an empty body. For this reason i will show only one of it.

Class VideoEntity


/** @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a> */
public class VideoEntity extends Entity {
    // concrete entity
} 

Finally le's take a look inside of the DAO. That's also a very interesting class you should take a look inside of it.

Class DAO.


/**
 * This data access object (DAO) is responsible to read and setup database entities<br/>
 * 
 * @author Ricardo Ferreira <a href="http://www.treslines.com">www.treslines.com</a>
 */
public class DAO {

    private DMO connection;

    public FotoEntity readFotoEntity(Activity activity) {
        FotoEntity fotoEntity = new FotoEntity();
        connection = new Database(activity, new FotoTable(activity));
        final Cursor result = connection.selectAll(DbConfig.TABLE_FOTO.name(), null);
        fotoEntity.setName(result.getString(result.getColumnIndex(DbConfig.TableFotoConfig.NOME.name())));
        fotoEntity.setStream(result.getBlob(result.getColumnIndex(DbConfig.TableFotoConfig.FOTO.name())));
        fotoEntity.setTimeStamp(result.getString(result.getColumnIndex(DbConfig.TableFotoConfig.TIMESTAMP.name())));
        connection.close();
        return fotoEntity;
    }

    public AudioEntity readAudioEntity(Activity activity) {
        AudioEntity audioEntity = new AudioEntity();
        connection = new Database(activity, new AudioTable(activity));
        final Cursor result = connection.selectAll(DbConfig.TABLE_FOTO.name(), null);
        audioEntity.setName(result.getString(result.getColumnIndex(DbConfig.TableAudioConfig.NOME.name())));
        audioEntity.setStream(result.getBlob(result.getColumnIndex(DbConfig.TableAudioConfig.AUDIO.name())));
        audioEntity.setTimeStamp(result.getString(result.getColumnIndex(DbConfig.TableAudioConfig.TIMESTAMP.name())));
        connection.close();
        return audioEntity;
    }

    public VideoEntity readVideoEntity(Activity activity) {
        VideoEntity videoEntity = new VideoEntity();
        connection = new Database(activity, new VideoTable(activity));
        final Cursor result = connection.selectAll(DbConfig.TABLE_FOTO.name(), null);
        videoEntity.setName(result.getString(result.getColumnIndex(DbConfig.TableVideoConfig.NOME.name())));
        videoEntity.setStream(result.getBlob(result.getColumnIndex(DbConfig.TableVideoConfig.VIDEO.name())));
        videoEntity.setTimeStamp(result.getString(result.getColumnIndex(DbConfig.TableVideoConfig.TIMESTAMP.name())));
        connection.close();
        return videoEntity;
    }

    public TextoEntity readTextoEntity(Activity activity) {
        TextoEntity textoEntity = new TextoEntity();
        connection = new Database(activity, new TextoTable(activity));
        final Cursor result = connection.selectAll(DbConfig.TABLE_FOTO.name(), null);
        textoEntity.setName(result.getString(result.getColumnIndex(DbConfig.TableTextoConfig.TEXTO.name())));
        textoEntity.setTimeStamp(result.getString(result.getColumnIndex(DbConfig.TableTextoConfig.TIMESTAMP.name())));
        connection.close();
        return textoEntity;
    }
} 

And last but not least, the test of it. The class which explains how all objects interacts together. (That's a Junit test using the library Roboelectric that i've explained in a post here called: Testing Android Apps with Junit (no more slow emulator) )

Class ClientTest


@RunWith(RobolectricTestRunner.class)
public class ClientTest {

    private Activity activity = new Activity();
    private DMO connection;
    private DAO dao = new DAO();

    @Test
    public void databaseConnection() {
        ContentValues fotoRowToCreate = new ContentValues();
        fotoRowToCreate.put(DbConfig.TableFotoConfig.NOME.name(), "foto nome");
        fotoRowToCreate.put(DbConfig.TableFotoConfig.FOTO.name(), new byte[] { 1, 2, 3 });
        fotoRowToCreate.put(DbConfig.TableFotoConfig.TIMESTAMP.name(), "foto nome");
        connection = new Database(activity, new FotoTable(activity));
        final int newRowIndex = connection.insert(DbConfig.TABLE_FOTO.name(), fotoRowToCreate);
        // assert the new inserted row index or query everything if you want...

        ContentValues textoRowToCreate = new ContentValues();
        fotoRowToCreate.put(DbConfig.TableTextoConfig.TEXTO.name(), "foto nome");
        fotoRowToCreate.put(DbConfig.TableTextoConfig.TIMESTAMP.name(), "foto nome");
        connection = new Database(activity, new TextoTable(activity));
        connection.insert(DbConfig.TABLE_TEXTO.name(), textoRowToCreate);

        ContentValues audioRowToCreate = new ContentValues();
        fotoRowToCreate.put(DbConfig.TableAudioConfig.NOME.name(), "foto nome");
        fotoRowToCreate.put(DbConfig.TableAudioConfig.AUDIO.name(), new byte[] { 1, 2, 3 });
        fotoRowToCreate.put(DbConfig.TableAudioConfig.TIMESTAMP.name(), "foto nome");
        connection = new Database(activity, new AudioTable(activity));
        connection.insert(DbConfig.TABLE_AUDIO.name(), audioRowToCreate);

        ContentValues videoRowToCreate = new ContentValues();
        fotoRowToCreate.put(DbConfig.TableVideoConfig.NOME.name(), "foto nome");
        fotoRowToCreate.put(DbConfig.TableVideoConfig.VIDEO.name(), new byte[] { 1, 2, 3 });
        fotoRowToCreate.put(DbConfig.TableVideoConfig.TIMESTAMP.name(), "foto nome");
        connection = new Database(activity, new VideoTable(activity));
        connection.insert(DbConfig.TABLE_VIDEO.name(), videoRowToCreate);

        // cleanUp db, because this is a test and we don't want to keep tests in the db
        connection.dropDatabase(activity, DbConfig.DATABASE_TEST.name());
    }

    @Test
    public void dao() {
        FotoEntity fotoEntity = dao.readFotoEntity(activity);
        // assert whatever you want...
        AudioEntity audioEntity = dao.readAudioEntity(activity);
        VideoEntity videoEntity = dao.readVideoEntity(activity);
        TextoEntity textoEntity = dao.readTextoEntity(activity);
    }
} 

So that was all. You have now an example from A-Z. Hope you like it. Happy coding! ;-)


😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇

Be sure to read, it will change your life!
Show your work by Austin Kleonhttps://amzn.to/34NVmwx

This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practiceshttps://amzn.to/30WQSm2

Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv

This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp

Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy

😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆

Saturday, March 17, 2012

Making beans/data reusable and simultaneously reducing class size to only one method

Hi there! Today i wanna share an idea with you (generic bean/Data)

If there is one thing a hate is to write beans, getter and setters again, again and again. I was searching for a solution on how to make beans reusable and classes more readable, smaller and cleaner. A typical or common situation is when you need domain objects. I've caught myself constantly writting the "same  boring code" again and again. So i decided to search for a solution, which could help me saving time. I had an idea, which i would like to share with you. I've called it: "GenericBean".

IMPORTANT: After listening to the feedbacks from other developers I decided to let this post alive just to show to you, why this approach is not recommend and shall not be used. Stay with POJO's. 

Let's say we have a database table called "address". This address table may contain following attributes:
  • ID
  • FIRST_NAME
  • SECOND_NAME
  • STREET
  • HOUSE_NUMBER
  • ZIP_CODE
  • LAND
Let's say now, we need a domain class called: SimpleDomainAddress. The normal way would be to do something like this:

public class SimpleDomainAddress {
 
 private double addressId;
 private String firstName;
 private String secondName;
 private String street;
 private int houseNumber;
 private String zipCode;
 private String land;


 public double getAddressId() {
  return this.addressId;
 }

 public void setAddressId( double addressId ) {
  this.addressId = addressId;
 }

 public String getFirstName() {
  return this.firstName;
 }

 public void setFirstName( String firstName ) {
  this.firstName = firstName;
 }

 public String getSecondName() {
  return this.secondName;
 }

 public void setSecondName( String secondName ) {
  this.secondName = secondName;
 }

 public String getStreet() {
  return this.street;
 }

 public void setStreet( String street ) {

  this.street = street;
 }

 public int getHouseNumber() {
  return this.houseNumber;
 }

 public void setHouseNumber( int houseNumber ) {
  this.houseNumber = houseNumber;
 }

 public String getZipCode() {
  return this.zipCode;
 }

 public void setZipCode( String zipCode ) {
  this.zipCode = zipCode;
 }

 public String getLand() {
  return this.land;
 }

 public void setLand( String land ) {
  this.land = land;
 }
}

So this is the way everybody would do i think. Now if you have another domain object you'll do that again and so on generating a lot of unnescessary code lines in my point of view.

Let us hold on to what we do here:
  • We always define new variables
  • We always define new methods
  • We should have added "theoretically" some comments to it. 
The following UML diagram visualizes GenericBean approach:
 

Null Object
First of all we define a NULL object that could be written like this:

public class Null {
 // NullObject
}

GenericBean
Then we write the generic bean. This bean could be bigger then the example here. This is only a show case to visualize the idea behind it. I will intentionally violate some code conventions by writting variables very short and beginning with an underscore following by a number. I'll explain later why I am doing this way.

public class GenericBean<A, B, C, D, E, F, G, H, I, J> {
 private A _0;
 private B _1;
 private C _2;
 private D _3;
 private E _4;
 private F _5;
 private G _6;
 private H _7;
 private I _8;
 private J _9;

 public A get_0() {
  return this._0;
 }
 
 public void set_0( A _0 ) {
  this._0 = _0;
 }

 public B get_1() {
  return this._1;
 }

 public void set_1( B _1 ) {
  this._1 = _1;
 }

 public C get_2() {
  return this._2;
 }

 public void set_2( C _2 ) {
  this._2 = _2;
 }

 public D get_3() {
  return this._3;
 }

 public void set_3( D _3 ) {
  this._3 = _3;
 }

 public E get_4() {
  return this._4;
 }

 public void set_4( E _4 ) {
  this._4 = _4;
 }

 public F get_5() {
  return this._5;
 }

 public void set_5( F _5 ) {
  this._5 = _5;
 }

 public G get_6() {
  return this._6;
 }

 public void set_6( G _6 ) {
  this._6 = _6;
 }

 public H get_7() {
  return this._7;
 }

 public void set_7( H _7 ) {
  this._7 = _7;
 }

 public I get_8() {
  return this._8;
 }

 public void set_8( I _8 ) {
  this._8 = _8;
 }

 public J get_9() {
  return this._9;
 }

 public void set_9( J _9 ) {
  this._9 = _9;
 }
}

Shriking to only one method
Ok, at this point we have no profit of it right? Let's write know the same class SimpleDomainAddress again, but now using the GenericBean and Null Object.

public class SimpleDomainAddress {
 
 private GenericBean<Double, String, String, String, Integer, String, String, Null, Null, Null> genericBean;

 public SimpleDomainAddress() {
/** 
Database table: Id, Firstname, Secondname, Street, HouseNumber, Zipcode, Land
(The last 3 entries doesn't exit in the database, so we set our generic bean to Null)
*/
  this.genericBean = new GenericBean<Double, String, String, String, Integer, String, String, Null, Null, Null>();
 }

 /**
  * This generic bean represents the database table. 
  * See bellow how to get and set the values from it.
  * <ul>
  * <li>get_0 <b>return</b> AddressId</li>
  * <li>get_1 <b>return</b> Firstname</li>
  * <li>get_2 <b>return</b> Secondname</li>
  * <li>get_3 <b>return</b> Street</li>
  * <li>get_4 <b>return</b> HouseNumber</li>
  * <li>get_5 <b>return</b> Zipcode</li>
  * <li>get_6 <b>return</b> Land</li>
  * <li>get_7 Null (not used)</li>
  * <li>get_8 Null (not used)</li>
  * <li>get_9 Null (not used)</li>
  * </ul>
  */
 public GenericBean<Double, String, String, String, Integer, String, String, Null, Null, Null> getGenericBean() {
  return this.genericBean;
 }
}

Good side effects:
  • With this approach, the class is shrinking to less than a few lines of code.
  • A good side effect is the comment that is gaining in importance and now is no longer redundant.
  • If we notice that the database needs to be expanded, we only need to replace a NULL entry with the new value and we're done. 
  • Easy to learn (this example says all), simple, reusable.
  • We always know which entry is the first and the last in the database (get_1 & get_6 in this example)
The reason why i wrote the variables like "_0", _1" and so on is that this way, when i type "get" or "set" in my IDE, i'll get the methods in the same order(sequence) as i defined the database fields in my constructor as you can see above. This make the usage in association with the comment more intuitive and powerful.

😱👇 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👇

Be sure to read, it will change your life!
Show your work by Austin Kleonhttps://amzn.to/34NVmwx

This book is a must read - it will put you in another level! (Expert)
Agile Software Development, Principles, Patterns, and Practiceshttps://amzn.to/30WQSm2

Write cleaner code and stand out!
Clean Code - A Handbook of Agile Software Craftsmanship: https://amzn.to/33RvaSv

This book is very practical, straightforward and to the point! Worth every penny!
Kotlin for Android App Development (Developer's Library): https://amzn.to/33VZ6gp

Needless to say, these are top right?
Apple AirPods Pro: https://amzn.to/2GOICxy

😱👆 PROMOTIONAL DISCOUNT: BOOKS AND IPODS PRO 😱👆