Friday, March 23, 2012

Java: A Practical use of Interfaces

On the last post, I used an interface to help call an overrided method when some event is triggered (such as the Back button).

So to jot things down, here is what an interface seems to me.

Button: Overriding onClick example

Okay, so you have an interface like this:

public interface IClass {
      public void methodOne();
      public String methodTwo ( int i );
}

This is some interface class, and if you haven't worked with interfaces much, then you'll probably be like, "This has no use to me." Anyways, lets try to make some module...hmmm...lets try a Button class.

public class MyButton {
   IClass listener;
   MyButton() {}
   public void onClick() { listener.methodOne(); }
   public void setOnClickListener(IClass listener) { this.listener = listener; }
}

Now, here's the situation. You created the MyButton in some bigger context class, but you can't override it. For example, in Android, you call in your components statically like this:

(MyButton) findViewById(R.id.mybutton);

This doesn't allow you to override methods like this:

new MyButton () {
   @Override
   public void onClick() { //do something }
};

So in times like these, you ask how am I going to customize that onClick method? That's what setOnClickListener is for. You pass in a new Interface into the method like this:

setOnClickListener(new IClass() { 
@Override
methodOne() { // do stuff  }
};

Now since the onClick() function is defined to use the listener's method, it will now run the methodOne that you overrode. So in a way, it's like passing in a method as a parameter.
BUT!!! I just realized....couldn't I have done it with a normal class? Yes. I believe so. So next question is...What is the advantage of using an interface than a class in this situation? Or what is the advantage of implementing an interface that forces you to override all it's methods?

Uses of Interfaces

From what I read, if you app is constantly on update mode, use interfaces since you can implement interfaces as many times as you want like:

public class MyClass implements Interface1, Interface2, Interface3

Or you can extend an interface with multiple interfaces like:

public interface Interface1 extends Interface2, Interface3

Type Casting these babies

Another usage is to connect two different typed objects together by casting. Say you have two class that implements some interface:

public class Ball implements Relatable
public class Car implements Relatable

Where the interface Relatable coded like this:

public interface Relatable {
      public boolean isSofterThan(Relatable otherObject);
}

Now, if you wanted to make the method isSofterThan work with just the two classes (no interface), you'd have to make specific methods for each class like this:
(Reminder: For the following examples, interfaces is not implemented)
For Ball class:
public boolean isSofterThan(Car object);

For Car class:
public boolean isSofterThan(Ball object);

Now let's say that a class called Plushie comes along, then the Ball and Car class would have to add:
public boolean isSofterThan(Plushie object);

The point here is that you'd have to keep producing more of the same method with different signatures. Now this is where the interface comes in. Assume that each class has a variable called softness. So in each class that now implements Relatable, you can define the isSofterThan method like:

@Override 
public boolean isSofterThan(Relatable otherObject) {
    return (this.softness > otherObject.softness) ? true : false;
}

Now that this method is defined in the Ball, Car and Plushie classes. Lets try to use this in actual code.

Ball ball = new Ball();
Car car = new Car();
Plushie plushie = new Plushie();

ball.softness = 5;
car.softness = 2;
plushie.softness =  99999999999999999999;

Relatable rBall = (Relatable)ball;
Relatable rCar = (Relatable)car;
Relatable rPlushie = (Relatable)plushie;

if (rBall.isSofterThan(rCar)) println("Ball is softer than car!"); 
else println("Car is softer than Ball");

if (rPlushie.isSofterThan(rCar) println("OVER 9000000!");


Okay so I will disclaim that I haven't really tested this out. But the main thing to look at is that I type casted two different objects into one similar object, which is displayed in hot, red text. I learned this thing from:

http://docs.oracle.com/javase/tutorial/java/IandI/interfaceAsType.html

That's it for now... Remember, I'm a programming noob. So help me, don't bash me. :)

Android: Do something after Soft Keyboard closes

Heya, I got this method from one of the stack overflow threads, but forgot to save the link. But I'm just posting the code here for reference as usual.

So this involves making a custom EditText and listener interface.

Interface code:

public interface ImeBackListener {
 public abstract void onImeBack(SearchEditText ctrl, String text);
}


Now apply this when making your custom EditText:

public class SearchEditText extends EditText{

 private ImeBackListener mOnImeBack;
 
 public SearchEditText(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }
 
 @Override
 public boolean onKeyPreIme(int keycode, KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP)
   if (mOnImeBack != null) mOnImeBack.onImeBack(this,  this.getText().toString());
  return super.dispatchKeyEvent(event);
 }
 
 public void setOnImeBackListener(ImeBackListener listener) {
  mOnImeBack = listener;
 }

}

So when you call this in the code (statically or by creating a new object), just call setOnImeBackListener (new ImeBackListener() { // your code });

"your code" would be whatever you want to change in the activity.

Tuesday, March 20, 2012

Java: File Renaming

In a project, sometimes you get a bunch of files that need to be renamed. They either have same prefix/suffix and it would be a pain to rename them one by one. Being a beginner programmer, I felt obliged to build a quick "script" instead of download an app. So here's the code:

import java.io.File;
import java.util.*;
import java.io.IOException;

class Renamer {
 public static void main(String[] args) throws Exception {
  String path = ".";

  String files;
  File folder = new File(path);
  File[] listOfFiles = folder.listFiles();

  for (File f: listOfFiles) {
   f.renameTo(new File(f.getName().replaceAll("_mdpi","")));
  }
 }

}

Using Textpad's ctrl+1 and ctrl+2 shortcuts for run and compile, the task becomes easy., but I'm sure that using one liners in other script proggies are faster. I need get around to learning Python or something. Hmmmm....

Android: Timer/TimerTask/Hander/ stopping AsyncTask

Here's a common implementation that I used.

The Handler and TimerTask that is randomly within my activity class.

private Handler mHandler = new Handler();
 private class MyTimerTask extends TimerTask {
  @Override
  public void run() {
   mHandler.post(new Runnable() {
    @Override
    public void run() {
     Log.i("MyTimerTask", "Cancelling Search Task");
     progressBarLayout.setVisibility(LinearLayout.INVISIBLE);
     if (!stask.isCancelled()) stask.cancel(true);
     toast = Toast.makeText(mContext,"Could not find any locations", Toast.LENGTH_LONG);
     toast.show();
    }
   });
  }
  
 }


I have a Timer global variable in the AsyncTask class, so then I instantiate the timer and run it like this within the Async doInBackground method:

timer = new Timer();
timer.schedule(new MyTimerTask(), SEARCH_TIME_OUT);

In the post execute method, I cancelled the timer. Not sure if the timer will still be running after the AsyncTask is done, so I cancelled it just in case.

In the doInBackground, I could've handled all the code in one section without creating any classes in the activity. So it would've looked like this:

final Handler mHandler = new Handler();
TimerTask mTimerTask = new TimerTask() {
   public void run() {
      mHandler.post(new Runnable() {
          //handle cancelling here
   });
};
mTimer = new Timer();
mTimer.schedule(mTimerTask, TIME_OUT);

Hmmm, this seems better to use. I'll switch that in my code later.

Sunday, March 18, 2012

Android: ListView, Checkable, ArrayAdapter.....FUN STUFF!!!

EDIT: I needed the list to resize based on the quanity of items. It's rather hard to do it with a ListView. So I actually skipped using the adapter and just went with building rows on a vertical LinearLayout. 

I starting to feel lazy again, so I just copy and pasted the CustomDropDown class and the necessary xml files at the bottom. But here are some notes about the code:

1. Create a CustomDropDown that extends Button. After all, the Spinner is just a button that opens a Dialog. You can copy the whole code at the bottom of this post.

- You might see something like isButtonAutoGen or something like that, setTextOnSelectedItems. These are basically related to showing the selected contents on the DropDown Button. If you look at the image below where the dropDown has text that says "Click to open dialog," then imagine that text is replaced by the values in the list. These values are separated by commas and ellipsizes if the content is too long.

- The method, createList, is where it populates the values into the drop down.

2. So now you just need the layouts for these rows and the dialog itself. I will post these at the bottom of the post as well. Animations and styles are included as well.

3. To use, make a new CustomDropDown. Populate data by passing in a text array or text resource into setAdapterFromResources. If you look at the image below, you will see a "Done" button. You may have to manual give it's clickListener the property to close the dialog.

Other things:  You can set the multichoice mode and singlechoice mode. Both modes will use the checkbox drawable, since I haven't gotten around to using a radio button for the singlechoice mode.

I hope that the code below works for you. I haven't really tested it out by copy and pasting. Anyways, farewell.

Intro:
Here is what I was trying to do with these playful things:

Goal:

Create a spinner that opens a dialog from the bottom and stops about half way. The picture shows a Single-Choice mode, but it should be able to convert to Multi-Choice mode where it shows check-boxes instead of radio buttons. Also, the contents inside the ListView must be capable of modification.

Things that need to be created:

- anim, layout, and style XMLs
- adapter, checkable, and button implementations






I'm pretty tired right now...I'll add more to this section. In the mean time, you can refer to this:
http://stackoverflow.com/questions/4842349/android-listview-with-radiobutton-checkbox-in-singlechoice-mode-and-a-custom-row

The link above helped me on implementing Checkable. This is what binds the View of a row in a ListView to have a checkable state.

Another thing if you use the Single-Choice mode for the list, using the AdapterView.OnItemSelectedListener does not work here. Instead, use AdapterView.OnItemClickListner. This means if you want another component to be affected by the dropdown at the beginning of the activity, you would have to manually set the state of the component. Then the listener will take care of the rest afterwards.

When access data in the ListView, such as the checkbox value, all ways use the "Item Checked" methods. The Item Selected just does not work at all. So use those methods for both Singe and Multi-modes.

Anyways, time to flee......

Full CustomDropDown code:
public class CustomDropDown extends Button {

 private Context context;
 private Dialog dialog;
 private boolean isButtonTextAutoGen;
 private CheckBox[] checkBox;
 private TextView[] textView;
 private int listSize;
 private ListLayout layout;
 
 private final int TEXT_SIZE = 16;

 public CustomDropDown(Context context, AttributeSet attrs) {
  super(context, attrs);
  setBackgroundDrawable(getResources().getDrawable(android.R.drawable.btn_dropdown));
  setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL);
  this.setEllipsize(TextUtils.TruncateAt.END);
  this.setSingleLine(true);
  this.context = context;
  this.isButtonTextAutoGen = true;
 }
 
 // Adapter methods
 
 public void setAdapterFromResources(int array_res) {
  CharSequence[] txt = context.getResources().getTextArray(array_res);
  setAdapterFromTextArray(txt);  
 }
 
 public void setAdapterFromTextArray(CharSequence[] txt) {
  
  // Initialize dialog
  this.dialog = new Dialog(context,R.style.Popup_Dialog) {
   @Override
   public void onDetachedFromWindow() {
    if (isButtonTextAutoGen) setTextOnButtonToSelectedItems();
   }
  };
  this.dialog.setContentView(R.layout.popup_list_dialog); 
  this.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    dialog.show(); 
   }
  });

  createList(txt);
 }
 
 private void createList(CharSequence[] text) {
  
  this.listSize = text.length;
  textView = new TextView[listSize];
  checkBox = new CheckBox[listSize];
  
  layout = (ListLayout) dialog.findViewById(R.id.listlayout);
  
  for (int i=0;i<listSize;i++) {
   View row = LayoutInflater.from(getContext()).inflate(R.layout.single_line_checkbox_item, null);
   textView[i] = (TextView) row.findViewById(R.id.text);
   checkBox[i] = (CheckBox) row.findViewById(R.id.checkbox);
   
   textView[i].setText(text[i].toString());
   textView[i].setTextSize(TypedValue.COMPLEX_UNIT_PX,TEXT_SIZE);
   
   layout.addView(row);
   
   if (i < (listSize-1)) {
    LinearLayout divider = new LinearLayout(getContext());
    divider.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1));
    divider.setBackgroundColor(Color.argb(160, 160, 160, 160));
    
    layout.addView(divider);
   }
  }
  
  layout.setChoiceMode(ListLayout.CHOICE_MODE_MULTIPLE);
  
 }
 
 // Getters/setters
 public Dialog getDialog()     { return this.dialog; }
 
 public void setBoolStringToCheckBoxes(String s) {
  if (s!=null) 
  {
   for (int i=0;i<s.length();i++) {
    if (s.charAt(i)=='1') checkBox[i].setChecked(true);
    else checkBox[i].setChecked(false);
   }
   
  }
 }
 
 public String getCheckBoxValuesToString() {
  StringBuilder sb = new StringBuilder(); 
  for (int i=0;i<listSize;i++) {
   if (checkBox[i].isChecked()) sb.append("1");
   else sb.append("0");
  }
  return sb.toString();
 }
 
 public boolean isNoneChecked() {
  for (int i=0;i<listSize;i++) 
   if (checkBox[i].isChecked()) return false;
  return true;
 }
 
 public boolean isCheckedAt(int index) {
  return checkBox[index].isChecked();
 }
 
 public int getSize() { return listSize; }
 
 public void setTextAt(int index, String text) {
  textView[index].setText(text);
 }
 
 public String getTextAt(int index) { 
  return textView[index].getText().toString();
 }
 
 public CheckBox[] getCheckBoxArray() { return checkBox; }
 
 public void singleChoiceListenerMethod(CheckBox chbx) {
  for (int i=0;i<listSize;i++) {
   final int current = i;
   for (CheckBox b: checkBox) {
    if (b==chbx) b.setChecked(true);
    else b.setChecked(false);
   }
  }
 }
 
 public void setChoiceMode(int mode) {
  setChoiceMode(mode,0);
 }
 
 public void setChoiceMode(int mode, int startingPos) {
  if (mode==ListView.CHOICE_MODE_SINGLE) {
   
   ((TextView) dialog.findViewById(R.id.select_option_text)).setText("Select one");
   
   // set up listeners to act like single mode
   for (int i=0;i<listSize;i++) {
    final int current = i;
    checkBox[i].setOnClickListener(new View.OnClickListener() {     
     @Override
     public void onClick(View v) {      
      for (CheckBox b: checkBox) {
       if (b==checkBox[current]) b.setChecked(true);
       else b.setChecked(false);
      }
     }
    });
   }
   // check if none is check at start, set the first value to true
   
//   for (int i=0;i<listSize;i++) {
//    if (checkBox[i].isChecked()) return;
//   }
//   checkBox[startingPos].setChecked(true);
  }
 }
 
 public int getSelectedItemPosition() {
  for (int i=0;i<listSize;i++) {
   if (checkBox[0].isChecked()) return i;
  }
  return -1;
 }
 
 public void setTextOnButtonToSelectedItems() {
  StringBuilder sb = new StringBuilder();
  for (int i=0;i<listSize;i++) {
   if (checkBox[i].isChecked()) sb.append(getTextAt(i)).append(", ");
  }
  if (sb.length()!=0) {
   this.setText(sb.toString().substring(0, sb.length()-2));
  } else {
   this.setText("--Select--");
  }
 }
 
 public void setIsButtonTextAutoGen(boolean b) { this.isButtonTextAutoGen = b; }

}

popup_list_dialog.xml
<?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="wrap_content"
    android:orientation="vertical"
    android:background="@color/gray"
    android:layout_gravity="bottom"
    android:padding="5dp"
    >
 <RelativeLayout 
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     >
     <TextView
         android:id="@+id/select_option_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_centerVertical="true"
         android:text="Select one or more"
         android:textColor="@color/black"
         />
     <Button        
        android:id="@+id/btn_done"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="  Done  " 
        android:layout_alignParentRight="true"    
        />
 </RelativeLayout>
    
    <com.att.planitgreen.component.ListLayout 
        android:id="@+id/listlayout"        
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"   
        android:orientation="vertical"     
        android:background="#666666"
        android:padding="5dp"
        />
</LinearLayout>

single_line_checkbox_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
   >
    <TextView
        android:id="@+id/text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toLeftOf="@+id/checkbox"
        android:text="text"
        />
    <CheckBox 
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        />

</RelativeLayout>

styles.xml
<style name="Popup_Dialog">
        <item name="android:windowNoTitle">true</item>  
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowContentOverlay">@null</item>  
        <item name="android:windowAnimationStyle">@style/Animations.PopupDialog</item>
        
    </style>

<style name="Animations" parent="@android:Animation"/>  
     <style name="Animations.PopupDialog">
         <item name="android:windowEnterAnimation">@anim/slide_in_bottom</item>  
            <item name="android:windowExitAnimation">@anim/slide_out_down</item>
     </style>

slide_in_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
 <translate android:fromYDelta="100%" android:toYDelta="0"
            android:duration="@android:integer/config_mediumAnimTime"/>
</set>

slide_out_down.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromYDelta="0"
    android:toYDelta="100%p"
    android:duration="@android:integer/config_mediumAnimTime" 
    
    />

Saturday, March 17, 2012

Suicidal Bunnies!!!

I love how you slow down for them and they run to your car as if they are going to run your over. Damn clowns!

Android: Sharing Facebook, Twitter, E-mail

Okay, there are tutorials out there just as much as Justin Bieber fans...hmmm...is that a little or a lot?? Anyway, here we gooo!!!

Sharing E-mail
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, mTitle);
intent.putExtra(android.content.Intent.EXTRA_TEXT, mMessage);
startActivity(Intent.createChooser(intent, "Title of E-mail Chooser Dialog"));

Sharing Twitter
Use "https://twitter.com/intent/tweet?text="
You can append a string right after. So far appending this formats things correctly:
URLEncoder.encode(mMessage,"UTF-8")

So your string looks like:
"https://twitter.com/intent/tweet?text="+URLEncoder.encode(mMessage,"UTF-8")

Sharing Facebook
Then most annoying sharing method if you plan to set a pre-made text in your app.

- Download the SDK and extract the folder you need (One with project files :\ )
- Create existing project with the Facebook stuff.
- Make sure it's a library. Go to Project Properties->Android, and check mark isLibrary
- Now add the library to your current project.
- Now you need to make a facebook activity. So create FacebookActivity!!!

- To create a preview message, implement it yourself. You can use a dialog or something. But you can directly post your message to your wall. Here's the code:

facebook.request("me");
String response = facebook.request("me/feed", parameters, "POST");

- There's a lot more code in the activity, but you can just visit here:
http://www.integratingstuff.com/2010/10/14/integrating-facebook-into-an-android-application/

Happy days!!!!