Amazon Gift Cards

Sunday, November 28, 2010

Soft Kitty - Non Android

Once in a while i would post non-android article and here is one of them.

Anyone watch the big bang theory must know this song, if not here is a video


And here is a guitar tab, ahhah
2 or B |--8-5-5--6-3-3--1-3-5-6-8---8-5-5--6-3-3--1-3-1--| (repeat nth times)

:)

Thursday, November 25, 2010

Using library with resources in IntelliJ and Android

As my previous articles indicates, im coding more Android in IntelliJ then eclipse (main reason is that it will let you forget you have a mouse). For those new to IntelliJ, like me, here is how i set up the library in IntelliJ so that both resources and code could be used.


Notes
• Im new to IntelliJ so if im doing something wrong, kindly comment below :)
• As of writing the resources between library cant be handle properly when compiled (you can still do the coding with it) thus we need the help of command line (ant install, adb install/uninstall), on the Bug tracker it was reported to be fine in nightly.

What Do I Need
• As of now you need the EAP of IntelliJ
• Lastest Android SDK
• You need to know how to create a project
• You need to know how to create a library



Instruction
1) Open your project's module settings


2) In Modules, click the add new module (see image below)

3) Select your library if you had previously created one

4) Browse to your libraries path/directory. and select the file with the extension .iml

5) First click on your project (the red circle) then on the dependencies tab, click on add then Module dependencies (it shall have only one module)




Easy Updates 468x60

Command line
So you can code with your favorite IDE and now you want to debug it on the emulator. you could do (please refer to Developing in other IDE (You must have ant in your OS)
cd /path/to/your/project
ant debug


It will then tell you that where the apk is at
debug:
[echo] Running zip align on final apk...
[echo] Debug Package: /path/to/your/project/bin/shootAndLearn-debug.apk


Now you have to install it on the emulator, please refer to Android Debug Bridge page
adb uninstall com.almondmendoza.shootAndLearn
adb install bin/shootAndLearn-debug.apk


adb uninstall com.almondmendoza.shootAndLearn
We have to uninstall the apk from the emulator first before

adb install bin/shootAndLearn-debug.apk
installing it


Conclusion
I know the debugging part is kinda hard right now but the development/coding part is certainly a lot better (got to love the ctrl+w, ctrl+shift+a shortcuts) :)

Sunday, November 21, 2010

Drawing with Canvas in Android, Saving your drawings

Continuing with our Drawing in Canvas series, let us see how to save your drawings
Update:
May 5, 2011 - If saving the image is not that fine, this could come in handy http://stackoverflow.com/questions/5243547/decode-byte-array-to-bitmap-that-has-been-compressed-in-java

Introduction
We draw and saw our drawings are good, now lets save them

Notes
• The files are uploaded in http://goo.gl/ecHpE
• The project was build in IntelliJ and it should be easy to import to Eclipse


What Do I Need

DrawingSurface
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
  private Bitmap mBitmap;
  private ...
  class DrawThread extends  Thread{
    ...
    @Override
    public void run() {
      Canvas canvas = null;
      while (_run){
        try{
          canvas = mSurfaceHolder.lockCanvas(null);
          if(mBitmap == null){
            mBitmap =  Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);;
          }
          final Canvas c = new Canvas (mBitmap);
          c.drawColor(0, PorterDuff.Mode.CLEAR);
          commandManager.executeAll(c);
          canvas.drawBitmap (mBitmap, 0,  0,null);
        } finally {
          mSurfaceHolder.unlockCanvasAndPost(canvas);
        }
      }
    }
  }

  public Bitmap getBitmap(){
    return mBitmap;
  }

  public void surfaceChanged(SurfaceHolder holder, int format, int width,  int height) {
    // TODO Auto-generated method stub
    mBitmap =  Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);;
  }
}

DrawingActivity
public class DrawingActivity extends Activity implements View.OnTouchListener{
  ...
  private static File APP_FILE_PATH = new File("/sdcard/TutorialForAndroidDrawings");

  public void onClick(View view){
    switch (view.getId()){
      ...
      case R.id.saveBtn:
        final Activity currentActivity  = this;
        Handler saveHandler = new Handler(){
          @Override
          public void handleMessage(Message msg) {
            final AlertDialog alertDialog = new AlertDialog.Builder(currentActivity).create();
            alertDialog.setTitle("Saved 1");
            alertDialog.setMessage("Your drawing had been saved :)");
            alertDialog.setButton("OK", new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int which) {
                return;
              }
            });
            alertDialog.show();
          }
        } ;
         new ExportBitmapToFile(this,saveHandler, drawingSurface.getBitmap()).execute();
      break;
    }
  }


  private class ExportBitmapToFile extends AsyncTask {
    private Context mContext;
    private Handler mHandler;
    private Bitmap nBitmap;

    public ExportBitmapToFile(Context context,Handler handler,Bitmap bitmap) {
      mContext = context;
      nBitmap = bitmap;
      mHandler = handler;
    }

    @Override
    protected Boolean doInBackground(Intent... arg0) {
      try {
        if (!APP_FILE_PATH.exists()) {
          APP_FILE_PATH.mkdirs();
        }

        final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
        nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
        out.flush();
        out.close();
        return true;
      }catch (Exception e) {
        e.printStackTrace();
      }
      //mHandler.post(completeRunnable);
      return false;
    }

    @Override
    protected void onPostExecute(Boolean bool) {
      super.onPostExecute(bool);
      if ( bool ){
        mHandler.sendEmptyMessage(1);
      }
    }
  }
}


Quick Explanation
Ads from Amazon:
Explanation
if(mBitmap == null){
  mBitmap = Bitmap.createBitmap (1, 1, Bitmap.Config.ARGB_8888);;
}
final Canvas c = new Canvas (mBitmap);
c.drawColor(0, PorterDuff.Mode.CLEAR);
commandManager.executeAll(c);

This is the core of the saving part, in order for our drawing to be exported we have to create a canvas with bitmap to draw upon to, See Canvas Class for more info.


canvas.drawBitmap (mBitmap, 0, 0,null);
We put our bitmap to our original canvas (the surfaceView's canvas) so that we can see our drawings


public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  mBitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);;
}

We would have to recreate our bitmap when we change the orientation (The drawings will currently be removed)


final Activity currentActivity = this;
Handler saveHandler = new Handler(){ ... }

We cant call the UI tread from another thread so we pass a handler to our AsyncTask, see Using Handler in Android for more information on Handlers

private class ExportBitmapToFile extends AsyncTask {
  private Context mContext;
  private Handler mHandler;
  private Bitmap nBitmap;

  public ExportBitmapToFile(Context context,Handler handler,Bitmap bitmap) {
    mContext = context;
    nBitmap = bitmap;
    mHandler = handler;
  }

  @Override
  protected Boolean doInBackground(Intent... arg0) {
    try {
      if (!APP_FILE_PATH.exists()) {
        APP_FILE_PATH.mkdirs();
      }

      final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
      nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
      out.flush();
      out.close();
      return true;
    }catch (Exception e) {
      e.printStackTrace();
    }
    //mHandler.post(completeRunnable);
    return false;
  }


  @Override
  protected void onPostExecute(Boolean bool) {
    super.onPostExecute(bool);
    if ( bool ){
      mHandler.sendEmptyMessage(1);
    }
  }
}

In order for us to export the bitmap and not hard the UX of our app, we need to create a background thread for it, for more information on AsyncTask see the AsyncTask Class and Painless Threading

final FileOutputStream out = new FileOutputStream(new File(APP_FILE_PATH + "/myAwesomeDrawing.png"));
nBitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();

This is default Java feature, if your now from Java then this just mean that you create a buffer where when we use that in mBitmap.compress the compressed bitmap would be buffered into that FileOutputStream (i hope i was clear in this. haha)


Reference
Most of the stuff here were back in 2009, most of them are in Google Groups.

Wednesday, November 17, 2010

Drawing with Canvas in Android, Undo/Redo with Command Pattern

Update - Nov 21, 2010
Created a series over this topic over at Drawing with Canvas Series, more articles would appear in the future :)

Continuing our Drawing with Canvas in Android, lets implement Undo and Redo with Command Pattern

Introduction
We draw and make mistake, and when we do we may want to undo it and when we undo we might want to redo it. Lets use a common design pattern in dealing with undo (I believe this is a modified command pattern for the command manager but i might be wrong)

Notes
• This is a continuation of Drawing with Canvas in Android Renewed
• The files are uploaded in http://goo.gl/ecHpE
• The project was build in IntelliJ and it should be easy to import to Eclipse


What Do I Need
ICanvasCommand
public interface ICanvasCommand {
  public void draw(Canvas canvas);
  public void undo();
}


DrawingPath
public class DrawingPath implements ICanvasCommand{
  public Path path;
  public Paint paint;

  public void draw(Canvas canvas) {
    canvas.drawPath( path, paint );
  }

  public void undo() {
    //Todo this would be changed later
  }
}


CommandManager
public class CommandManager {
  private List currentStack;
  private List redoStack;

  public  CommandManager(){
    currentStack = Collections.synchronizedList(new ArrayList());
    redoStack = Collections.synchronizedList(new ArrayList());
  }

  public void addCommand(DrawingPath command){
    redoStack.clear();
    currentStack.add(command);
  }

  public void undo (){
    final int length = currentStackLength();
    if ( length > 0) {
      final DrawingPath undoCommand = currentStack.get(  length - 1  );
      currentStack.remove( length - 1 );
      undoCommand.undo();
      redoStack.add( undoCommand );
    }
  }

  public int currentStackLength(){
    final int length = currentStack.toArray().length;
    return length;
  }

  public void executeAll( Canvas canvas){
    if( currentStack != null ){
      synchronized( currentStack ) {
        final Iterator i = currentStack.iterator();
        while ( i.hasNext() ){
          final DrawingPath drawingPath = (DrawingPath) i.next();
          drawingPath.draw( canvas );
        }
      }
    }
  }

  public boolean hasMoreRedo(){
    return  redoStack.toArray().length > 0;
  }

  public boolean hasMoreUndo(){
    return  currentStack.toArray().length > 0;
  }

  public void redo(){
    final int length = redoStack.toArray().length;
    if ( length > 0) {
      final DrawingPath redoCommand = redoStack.get(  length - 1  );
      redoStack.remove( length - 1 );
      currentStack.add( redoCommand );
    }
  }
}


DrawingSurface
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
  private CommandManager commandManager;

  public DrawingSurface(Context context, AttributeSet attrs) {
    ...
    commandManager = new CommandManager();
    ...
  }

  class DrawThread extends  Thread{
    ...
    @Override
    public void run() {
      Canvas canvas = null;
      while (_run){
        try{
          canvas = mSurfaceHolder.lockCanvas(null);
          canvas.drawColor(0, PorterDuff.Mode.CLEAR);
          commandManager.executeAll(canvas);
        ....
  }

  public void addDrawingPath (DrawingPath drawingPath){
    commandManager.addCommand(drawingPath);
  }

  public boolean hasMoreRedo(){
    return commandManager.hasMoreRedo();
  }

  public void redo(){
    commandManager.redo();
  }

  public void undo(){
    commandManager.undo();
  }

  public boolean hasMoreUndo(){
    return commandManager.hasMoreUndo();
  }
}


DrawingActivity
See the source should be simple enough to be understood :)



Ads from Amazon:
Explanation
public interface ICanvasCommand {
  public void draw(Canvas canvas);
  public void undo();
}

In undo command pattern based we need need 2 methods, one is execute and the other is undo (we wont use undo currently)


public class DrawingPath implements ICanvasCommand{
  public Path path;
  public Paint paint;
  public void draw(Canvas canvas) {
    canvas.drawPath( path, paint );
  }
  public void undo() {
    //Todo this would be changed later
  }
}

Let us now implement our interface, the code should be easy to understand. canvas.drawPath( path, paint) would draw our path with the object's paint into our class.


currentStack = Collections.synchronizedList(new ArrayList());
redoStack = Collections.synchronizedList(new ArrayList());

We create 2 List (Its actually a Stack but seems Stack cannot be thread-safe and I dont freakin understand why there are a lot of Array variation in Java, its so messy), one called currentStack for the current paths, another is redoStack where we put the undo commands so we could do redos.


public void addCommand(DrawingPath command){
  redoStack.clear();
  currentStack.add(command);
}

Everytime we add a new command, we should clear the redoStack


Ads from Amazon:
public void undo (){
  final int length = currentStackLength();
  if ( length > 0) {
    final DrawingPath undoCommand = currentStack.get( length - 1 );
    currentStack.remove( length - 1 );
    undoCommand.undo();
    redoStack.add( undoCommand );
  }
}

When we do an undo, we pop the last command from the currentStack and push it to our redoStack. As you can see we implemented undoCommand.undo() we did this incase in the future we have some logics added to our undo function


public void executeAll( Canvas canvas){
  if( currentStack != null ){
    synchronized( currentStack ) {
      final Iterator i = currentStack.iterator();
      while ( i.hasNext() ){
        final DrawingPath drawingPath = (DrawingPath) i.next();
        drawingPath.draw( canvas );
      }
    }
  }
}

We loop through our currentStack and do the execute part (.draw) from our command pattern.


canvas = mSurfaceHolder.lockCanvas(null);
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
commandManager.executeAll(canvas);

In order for our undo/redo to work we have to somehow clear the canvas and drawColor(0, PorterDuff.Mode.CLEAR) helps us with that


Some of the codes are, i believe, logical thus i skip the explanation on them.

Saturday, November 13, 2010

Drawing with Canvas in Android Renewed

Update - Nov 21, 2010
Created a series over this topic over at Drawing with Canvas Series, more articles would appear in the future :)

Back one year ago, i blogged about Drawing with Canvas in Android, at that time i was creating a drawing app that never get released (lazy me). Now im planning to redo everything and share my learnings as i go on (I'm still doing the Shoot and Learn on the side). From the simple tutorial on how to draw canvas in android, lets explode this area further.

Introduction
Based on the Drawing with Canvas in Android lets put the SurfaceView into a separate class (you should know how to use custom view), create a DrawingPath class which would contain our Path and Paint object, and create a DrawingActivity where our touch events, buttons to change colors are located.

Notes
• The package i would use through this tutorial is com.almondmendoza.drawings
• The individual source are located at http://goo.gl/5GulF while the full ones is at https://sites.google.com/site/tutorialsformobileprogramming/android-tutorials/android-files
• In this tutorial we wont handle clearing up the images, undos/redos and screen rotations (all in future articles :) )


What Do I Need
Drawing Activity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ....
>
    <com.almondmendoza.drawings.DrawingSurface
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/drawingSurface"
    />
    <LinearLayout
            .....
    >
    ......
    </LinearLayout>
</RelativeLayout>


Main Activity
Intent drawIntent = new Intent(this, DrawingActivity.class);
startActivity( drawIntent);


DrawingPath.java
public class DrawingPath {
  public Path path;
  public Paint paint;
}


Drawing Activity
public class DrawingActivity extends Activity implements View.OnTouchListener{
  private DrawingSurface drawingSurface;
  private DrawingPath currentDrawingPath;
  private Paint currentPaint;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.drawing_activity);
    setCurrentPaint();
    drawingSurface = (DrawingSurface) findViewById(R.id.drawingSurface);
    drawingSurface.setOnTouchListener(this);
  }

  private void setCurrentPaint(){
    currentPaint = new Paint();
    currentPaint.setDither(true);
    currentPaint.setColor(0xFFFFFF00);
    currentPaint.setStyle(Paint.Style.STROKE);
    currentPaint.setStrokeJoin(Paint.Join.ROUND);
    currentPaint.setStrokeCap(Paint.Cap.ROUND);
    currentPaint.setStrokeWidth(3);
  }

  public boolean onTouch(View view, MotionEvent motionEvent) {
    if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
      currentDrawingPath = new DrawingPath();
      currentDrawingPath.paint = currentPaint;
      currentDrawingPath.path = new Path();
      currentDrawingPath.path.moveTo(motionEvent.getX(), motionEvent.getY());
      currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
    }else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
      currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
    }else if(motionEvent.getAction() == MotionEvent.ACTION_UP){
      currentDrawingPath.path.lineTo(motionEvent.getX(), motionEvent.getY());
      drawingSurface.addDrawingPath(currentDrawingPath);
    }
    return true;
  }

  public void onClick(View view){
    switch (view.getId()){
      case R.id.colorRedBtn:
        currentPaint = new Paint();
        currentPaint.setDither(true);
        currentPaint.setColor(0xFFFF0000);
        currentPaint.setStyle(Paint.Style.STROKE);
        currentPaint.setStrokeJoin(Paint.Join.ROUND);
        currentPaint.setStrokeCap(Paint.Cap.ROUND);
        currentPaint.setStrokeWidth(3);
      break;
      case R.id.colorBlueBtn:
        currentPaint = new Paint();
        currentPaint.setDither(true);
        currentPaint.setColor(0xFF00FF00);
        currentPaint.setStyle(Paint.Style.STROKE);
        currentPaint.setStrokeJoin(Paint.Join.ROUND);
        currentPaint.setStrokeCap(Paint.Cap.ROUND);
        currentPaint.setStrokeWidth(3);
      break;
      case R.id.colorGreenBtn:
        currentPaint = new Paint();
        currentPaint.setDither(true);
        currentPaint.setColor(0xFF0000FF);
        currentPaint.setStyle(Paint.Style.STROKE);
        currentPaint.setStrokeJoin(Paint.Join.ROUND);
        currentPaint.setStrokeCap(Paint.Cap.ROUND);
        currentPaint.setStrokeWidth(3);
      break;
    }
  }
}



Drawing Surface
public class DrawingSurface extends SurfaceView implements SurfaceHolder.Callback {
  private Boolean _run;
  protected DrawThread thread;

  public DrawingSurface(Context context, AttributeSet attrs) {
...
  }

  public void addDrawingPath (DrawingPath drawingPath){
    thread.addDrawingPath(drawingPath);
  }

  class DrawThread extends  Thread{
    private SurfaceHolder mSurfaceHolder;
    private List mDrawingPaths;
    public DrawThread(SurfaceHolder surfaceHolder){
      mSurfaceHolder = surfaceHolder;
      mDrawingPaths = Collections.synchronizedList(new ArrayList());
    }

...

    public void addDrawingPath(DrawingPath drawingPath){
      mDrawingPaths.add( drawingPath );
    }

    @Override
    public void run() {
      Canvas canvas = null;
      while (_run){
        try{
          canvas = mSurfaceHolder.lockCanvas(null);
          synchronized(mDrawingPaths) {
            Iterator i = mDrawingPaths.iterator();
            while (i.hasNext()){
              final DrawingPath drawingPath = (DrawingPath) i.next();
              canvas.drawPath(drawingPath.path, drawingPath.paint);
            }
          }
        } finally {
          mSurfaceHolder.unlockCanvasAndPost(canvas);
        }
      }
    }
  }
....
}






Explanation
<com.almondmendoza.drawings.DrawingSurface
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:id="@+id/drawingSurface"
/>

We start off by using our custom view in our xml, its as simple as creating a class and using it on the layout.


public class DrawingPath {
  public Path path;
  public Paint paint;
}

From Drawing with Canvas in Android we learn to put an ArrayList of Path which have a single color, for us to have different colors on different path, we create a class which would be an List in our DrawThread later on.


private Paint currentPaint;
....
private void setCurrentPaint(){
  currentPaint = new Paint();
  currentPaint.setDither(true);
  currentPaint.setColor(0xFFFFFF00);
  currentPaint.setStyle(Paint.Style.STROKE);
  currentPaint.setStrokeJoin(Paint.Join.ROUND);
  currentPaint.setStrokeCap(Paint.Cap.ROUND);
  currentPaint.setStrokeWidth(3);
}

Here we create a Paint that would serve as our current paint, and set the currentPaint at the start of our activity.


  public void onClick(View view){
  switch (view.getId()){
    case R.id.colorRedBtn:
      currentPaint = new Paint();
      currentPaint.setDither(true);
      currentPaint.setColor(0xFFFF0000);
...
    break;
    case R.id.colorBlueBtn:
      currentPaint = new Paint();
      currentPaint.setDither(true);
      currentPaint.setColor(0xFF00FF00);
...

This is how we change the currentPaint's color, since we use the Paint object, you could use any of its property/methods thus making every Path beautiful :)


  public boolean onTouch(View view, MotionEvent motionEvent) {
  if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
    currentDrawingPath = new DrawingPath();
    currentDrawingPath.paint = currentPaint;
    currentDrawingPath.path = new Path();
    ....
    drawingSurface.addDrawingPath(currentDrawingPath);
  }
  return true;
}

In the previous tutorial, we bind our onTouch on the surfaceView directly, now we bind it in our activity, this way we could control the logic of the touches better (where you would apply your own shared touch library) and you could do things that doesnt harm the SurfaceView/Canvas.


public DrawThread(SurfaceHolder surfaceHolder){
  mSurfaceHolder = surfaceHolder;
  mDrawingPaths = Collections.synchronizedList(new ArrayList());
}

As stated earlier we would create a List of DrawingPath, but in able to manipulate it while our thread is running we need to make sure that its thread-safe thus we call Collections.synchronizedList(new ArrayList()); (I'm not sure about this since i never used threads in other language besides Javascript's webworker)


  public void addDrawingPath (DrawingPath drawingPath){
    thread.addDrawingPath(drawingPath);
  }

From our activity we would pass a DrawingPath to our surfaceView which we would then pass it to our thread, we do this so that our thread would not be accessible to the outside world of our DrawingSurface, which i believe is better for we can inject logics that would deal with our SurfaceView before passing it to our thread.


public void addDrawingPath(DrawingPath drawingPath){
  mDrawingPaths.add( drawingPath );
}

From our DrawingSurface to our DrawThread, we pass the a DrawingPath and add it to our list


synchronized(mDrawingPaths) {
  Iterator i = mDrawingPaths.iterator();
  while (i.hasNext()){
    final DrawingPath drawingPath = (DrawingPath) i.next();
    canvas.drawPath(drawingPath.path, drawingPath.paint);
  }
}

Here we use the Iterator class to iterate over our DrawingPath list and draw our Path on the canvas.

Conclusion
Now we learn how to better put our codes and make sure it can be scale up (im a webdeveloper, ahha) in the future.

Hope this helps :)