Saturday, May 31, 2014

Fungsi Camera dan fungsi ubah wallpaper di Android

Pada postingan ini, saya ingin menjelaskan bagaimana sebuah applikasi dapat mengakses kamera, dan dapat langsung mengubah hasil foto anda dengan satu tombol tanpa harus mensettingnya.

Tahap pertama, pastikan anda membuat 1 buah button yang berguna untuk mengakses kamera, 1 buah Image View untuk menampilkan hasil dari foto yang sudah di submit, dan 1 buah Image button, untuk mengubah wallpaper anda dari hasil gambar yang telah di submit tersebut. Apabila anda ingin mengikuti tahap saya, saya menggunakan code berikut:

       
        <Button
            android:id="@+id/ibTakePict"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Camera" />
        <ImageView
            android:id="@+id/ivReturnedPict"
            android:layout_below="@+id/Camera"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Change Wallpaper" />
        <ImageButton

            android:id="@+id/bSetWall"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@+id/button1"
            android:layout_marginLeft="78dp"
            android:layout_marginTop="59dp"
            android:src="@drawable/abc_ab_bottom_solid_dark_holo" />

Langkah kedua adalah membuat sebuah permission android. Permission ini bertujuan agar Android Manifest.xml mengetahui apabila applikasi tersebut akan menggunakan layanan yang telah di sediakan di android. Dan apakah Android Manifest tersebut?, Android Manifest adalah sebuah tempat penyimpanan yang berisikan gudang informasi android. Lalu apakah saja informasi tersebut?, informasinya adalah:

1. Versi SDK yang di gunakan di applikasi
2. Merubah kode versi aplikasi saat melakukan update aplikasi
3. Memberi akses permission untuk fitur pada aplikasi
4. Menerjemahkan string atau label ke dalam aplikasi yang diperlukan, seperti string icon launcher, app name.
5. Inisialisasi dukungan layar perangkat
6. Dan lain sebagainya (sumber: http://mkhuda.com/open-source/memahami-konfigurasi-androidmanifest-aplikasi-android/)

Nah sekarang waktunya memberikan permission di Android Manifest, agar applikasi dapat menggukan layanan tersebut.

       
    <uses-feature android:name="android.hardware.camera"
                      android:required="true" />


Permission di atas adalah permission untuk kamera, dimana value dari android : required adalah true. Selanjutnya adalah tahap memberikan sebuah permission agar applikasi dapat merubah tampilan dari hasil gambar yang telah kita dapat, dengan cara:

       

    <uses-permission android:name="android.permission.SET_WALLPAPER"/>



Di tahap ini kita sudah tidak berurusan lagi dengan Android Manifest, karena kita sudah memberikan izin kepada applikasi bahwa kita hanya akan menggunakan kamera dan hanya dapat mengakses pengubahan wallpaper.

Untuk tahap berikutnya kita akan bermain di SRC, Apa kegunaan src di android?, src mempunyai fungsi sebagai file file activity yang bersifat java programming. dan file activity bersifat logic yang akan di gunakan oleh layout yang telah di buat. Di tahap ini, saya akan mengekstends sifat dari Activity, dan akan mengimplement View.OnclickListener. Tahap pertama inialisasi semua button yang telah kita buat di layout. Berikut cara penginisialisasiannya pada oncreate:

       
    ImageView iv = (ImageView) findViewById(R.id.ivReturnedPict);
    ImageButton ib = (ImageButton) findViewById(R.id.ibTakePict);
    Button b = (Button) findViewById(R.id.bSetWall);
    b.setOnClickListener(this);
    ib.setOnClickListener(this);


Selanjutnya, saya akan membuat sebuah switch case di onclick, sebelumnya saya akan memberikan penjelasan, apakah onclick itu. OnClick adalah sebuah override dari extends Activity yang berfungsi untuk apabila kita memencet atau mengclick sesuatu pada button atau apapun yang di register di OnClick, dia akan melakukan sebuah validasi sesuai dengan yang di inginkan developer. Oleh karena itu, saya ingin memberitahu applikasi saya apabila button yang di tekan, apa yang akan terjadi, dan apabila button Image apa yang akan terjadi.

       
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId())
        {
              case R.id.bSetWall:
               try {
               getApplicationContext().setWallpaper(bmp);
               } catch (IOException e) {
                     e.printStackTrace();
               }
               break;
               case R.id.ibTakePict:
               i= new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
               startActivityForResult(i, cameraData);
               break;            
               }
        }


 Pada tahap ini, apa bila saya menekan tombol button, applikasi akan menggunakan kamera, apabila anda menyimpan submit gambar tersebut, anda akan kembali ke layar applikasi anda, dan anda dapat mengklik image button untuk mengubah tampilan layar monitor android anda.

Pertanyaan yang lain, apa yang terjadi apa bila saya reject hasil gambar?, tentu applikasi akan memberikan tanda error, karena hasil apapun yang di dapat si applikasi harus tetap mengubah tampilannya, sedangkat gambar yang di submit tidak di simpan sama sekali. Lalu apa solusinya?, kita akan menggunakan override onActivityResult. Apa fungsinya?, onActivityResult mempunyai fungsi untuk melakukan sesuatu logika bedasarkan arahan dari user. Berikut code yang saya buat:

       
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)                   {
         // TODO Auto-generated method stub
         super.onActivityResult(requestCode, resultCode, data);
         if(resultCode==RESULT_OK)
         {
               Bundle extras = data.getExtras();
               bmp = (Bitmap) extras.get("data");
               iv.setImageBitmap(bmp);
         }
    }


Tanpa kita sadari, apabila kita mensubmit gambar, pada override onActivityResult ada 2, yaitu bisa kita submit valuenya adalah RESULT_OK, dan apabila kita mereject valuenya adalah RESULT_CANCELED. Oleh karena itu kita dapat memberikan sebuah logika di sini, bahwa apabila value yang di dapat adalah RESULT_OK, maka ImageView akan merubah tampilan wallpaper, dan apabila yang lain, maka tidak akan terjadi apa apa atau tidak merubah tampilan apa apa. Sekian postingan dari saya, mudah mudahan dapat membantu anda. Selamat mencoba :)

Tuesday, May 27, 2014

Behaviour Driven Development using Cucumber in Java

there's a conversation between a partner and a business analyst (ba)
partner : bro, you need to encrypt your data before you send it to my service
ba : ok, what kind of encryption?
partner : we use AES encryption
ba : ok sir, please give me an example of the original text and what's the correct encryption result.
partner : try this. 'BAHLUL ENTE' should be encrypted as '/0YQIBztk6sqIRiw/Bh3Mw=='
partner : use 'DEFAULT_ENCRYPTOR_KEY_1234' as the encryption key.
ba : are you sure with that one sir?
partner : off course. that's how it should be.

ok, so the partner need an additional feature for the integration, to encrypt the data before it's sent to his service.

lets break it down.
feature: text encryption using AES algorithm
test scenario: we have text 'BAHLUL ENTE', encrypt it with key 'DEFAULT_ENCRYPTOR_KEY_1234' and it should resulted '/0YQIBztk6sqIRiw/Bh3Mw=='.

so, how to make it easy to explain the developer about this?
is there any tools that can be easily understandable for both the partner to check and the developer to code?

it's cucumber.

the main idea is to have a file that contains every requirement and test scenarios (and off course it should be human readable), and there's a unit testing program that reads the file, and runs every single step in the scenario. and assert if the program runs well according to the provision.

ok, where to start?

first, create a text file, for this example name it as encryption.feature, that will be the reference of the scenarios.
 Feature: text encryption using AES algorithm  
 
 Scenario: Encrypting text using AES encryption algorithm  
 Given the encryption algorithmm is "AES"  
 And we set the encryptor key "DEFAULT_ENCRYPTOR_KEY_1234"  
 When we give text "BAHLUL ENTE"  
 Then the result should be "/0YQIBztk6sqIRiw/Bh3Mw==" 
 And the decryption should bring "/0YQIBztk6sqIRiw/Bh3Mw==" back to "BAHLUL ENTE" 

from this point, let's move to developer's point of view.

that file contains Gherkin syntax, which is 'Given', 'When', 'Then', 'And', there is also one more that we don't use here is 'But'. you can see the explanation of each of them here https://github.com/cucumber/cucumber/wiki/Given-When-Then.

how to read it from java?

as usual unit test, we can use junit. also we need a library that 'integrate' cucumber and junit, and also cucumber and java (at first cucumber is developed for ruby language).

if we use maven, we simple add these dependencies:
 <dependency>  
      <groupId>info.cukes</groupId>  
      <artifactId>cucumber-java</artifactId>  
      <version>1.0.0</version>  
      <scope>test</scope>  
 </dependency>  
 <dependency>  
      <groupId>info.cukes</groupId>  
      <artifactId>cucumber-junit</artifactId>  
      <version>1.0.0</version>  
      <scope>test</scope>  
 </dependency>  
 <dependency>  
      <groupId>junit</groupId>  
      <artifactId>junit</artifactId>  
      <version>4.10</version>  
      <scope>test</scope>  
 </dependency>  

then we need to create a junit runner that will run the test. we name it org.mazb.encr.RunTest.java and put it under src/test/java.
 import org.junit.runner.RunWith;  
 import cucumber.junit.Cucumber; 
 
 @RunWith(Cucumber.class)  
 @Cucumber.Options ( format = { "pretty" } )  
 public class RunTest {  

 }  

@Cucumber annotation tells the program to run the Gherkin syntax in the feature file somewhere in the classpath. default location is under src/test/resources and under the same package with the runner (org.mazb.encr), so we put the feature file there.
format = { "pretty" } attribute tells the program to verbose the test in the console.

can we run the test now? yes we can, let's see what happens.
we simply run it with mvn test. and here's what happens:
 Running org.mazb.encr.RunTest  
 
 Feature: text encryption using AES algorithm  
  Scenario: Encrypting text using AES encryption algorithm  
   Given the encryption algorithmm is "AES"  
   And we set the encryptor key "DEFAULT_ENCRYPTOR_KEY_1234"  
   When we give text to encrypt "BAHLUL ENTE"  
   Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="  
   And the decryption should bring "/0YQIBztk6sqIRiw/Bh3Mw==" back to "BAHLUL ENTE"  
 
 You can implement missing steps with the snippets below:  
 @Given("^the encryption algorithmm is \"([^\"]*)\"$")  
 public void the_encryption_algorithmm_is(String arg1) {  
   // Express the Regexp above with the code you wish you had  
   throw new PendingException();  
 } 
 
 @Given("^we set the encryptor key \"([^\"]*)\"$")  
 public void we_set_the_encryptor_key(String arg1) {  
   // Express the Regexp above with the code you wish you had  
   throw new PendingException();  
 } 
 
 @When("^we give text to encrypt \"([^\"]*)\"$")  
 public void we_give_text_to_encrypt(String arg1) {  
   // Express the Regexp above with the code you wish you had  
   throw new PendingException();  
 }  

 @Then("^the encryption result should be \"([^\"]*)\"$")  
 public void the_encryption_result_should_be(String arg1) {  
   // Express the Regexp above with the code you wish you had  
   throw new PendingException();  
 }  

 @Then("^the decryption should bring \"([^\"]*)\" back to \"([^\"]*)\"$")  
 public void the_decryption_should_bring_back_to(String arg1, String arg2) {  
   // Express the Regexp above with the code you wish you had  
   throw new PendingException();  
 }  

it tells us that we haven't define the steps in the scenario. we need to implement every single line under Gherkin keywords into java statements so it can be run programatically.

so we create a java class, under src/test/java, and implement the steps. for example org.mazb.encr.EncryptionStepDefinition.java. we create a method for each step in the feature file, and put the suitable annotation (Given/When/Then) on it. Cucumber will scan the classes under src/test/java that contains the right step based on the feature file.

for example we use this class:
 import static org.junit.Assert.assertTrue;  

 import org.apache.commons.lang.StringUtils;  

 import org.mazb.encr.AesEncryptor;  

 import cucumber.annotation.en.Given;  
 import cucumber.annotation.en.Then;  
 import cucumber.annotation.en.When;  

 public class EncryptionStepDefinition {  

      private AesEncryptor encryptor = new AesEncryptor();  

      @Given("^the encryption algorithmm is \"([^\"]*)\"$")  
      public void encryptionAlgorithm(String algo){  
           encryptor.setAlgorithmName(algo);  
      }  

      @Given("^we set the encryptor key \"([^\"]*)\"$")  
      public void encryptorKey(String encryptorKey){  
           encryptor.setEncryptorKey(encryptorKey);  
      }  

      @When("^we give text to encrypt \"([^\"]*)\"$")  
      public void setText(String text){  
           encryptor.setText(text);  
      }  

      @Then("^the encryption result should be \"([^\"]*)\"$")  
      public void getResult(String result) throws Exception{  
           assertTrue(StringUtils.equals(encryptor.encrypt(), result));  
      }  

      @Then("^the decryption should bring \"([^\"]*)\" back to \"([^\"]*)\"$")  
      public void getDecryptionResult(String encrypted, String original) throws Exception{  
           encryptor.setText(encrypted);  
           assertTrue(StringUtils.equals(encryptor.decrypt(), original));  
      }  
 }  

note: AesEncryptor is the encryption class that we want to test. assuming we already create the class and now want to test it.

all the methods in this class have annotation from cucumber.annotation.en.*. each description inside the annotation must suites the steps defined in feature file, and for the value we're gonna test, we can use regex in the description.

for example,
@Given("^the encryption algorithmm is \"([^\"]*)\"$")
must have the same pattern with
Given the encryption algorithmm is "AES"
in the feature file so cucumber can get it right.

([^\"]*) is a regular expression, the spec can be found else wehere, here's for example: http://www.vogella.com/tutorials/JavaRegularExpressions/article.html. the regex also will be argument to the method, so the string defined with that regex will be accepted as args[0] in the method. see also the last method which has 2 regex (passes 2 arguments).

can we run the test now? yes we can, let's see what happens.
 Running org.mazb.encr.RunTest  
 Feature: text encryption using AES algorithm  

  Scenario: Encrypting text using AES encryption algorithm  
   Given the encryption algorithmm is "AES"  
   And we set the encryptor key "DEFAULT_ENCRYPTOR_KEY_1234"  
   When we give text to encrypt "BAHLUL ENTE"        
   Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="  
    java.lang.AssertionError  
         at org.junit.Assert.fail(Assert.java:92)  
         at org.junit.Assert.assertTrue(Assert.java:43)  
         at org.junit.Assert.assertTrue(Assert.java:54)  
         at org.mazb.encr.EncryptionStepDefinition.getResult(EncryptionStepDefinition.java:33)  
         at ✽.Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="(org/mazb/encr/encryption.feature:7)  

   And the decryption should bring "/0YQIBztk6sqIRiw/Bh3Mw==" back to "BAHLUL ENTE" # EncryptionStepDefinition.getDecryptionResult(String,String)  

 java.lang.AssertionError  
      at org.junit.Assert.fail(Assert.java:92)  
      at org.junit.Assert.assertTrue(Assert.java:43)  
      at org.junit.Assert.assertTrue(Assert.java:54)  
      at org.mazb.encr.EncryptionStepDefinition.getResult(EncryptionStepDefinition.java:33)  
      at ✽.Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="(org/mazb/encr/encryption.feature:7)  

oops! what's going on? seems like there's something wrong with the encryption. cucumber tells us any false assert per step. in this case, the problem exists in the step 'Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="' which means our encryption returns the wrong result from the provision. let's check again the code, and fix it.

after it fixed, let's run the test again
 Running org.mazb.encr.RunTest  
 Feature: text encryption using AES algorithm  

  Scenario: Encrypting text using AES encryption algorithm  
   Given the encryption algorithmm is "AES"  
   And we set the encryptor key "DEFAULT_ENCRYPTOR_KEY_1234"  
   When we give text to encrypt "BAHLUL ENTE"  
   Then the encryption result should be "/0YQIBztk6sqIRiw/Bh3Mw=="  
   And the decryption should bring "/0YQIBztk6sqIRiw/Bh3Mw==" back to "BAHLUL ENTE"  

 [INFO] ------------------------------------------------------------------------  
 [INFO] BUILD SUCCESS  
 [INFO] ------------------------------------------------------------------------  
 [INFO] Total time: 2.821s  
 [INFO] Finished at: Tue May 27 14:24:38 WIT 2014  
 [INFO] Final Memory: 8M/111M  
 [INFO] ------------------------------------------------------------------------  

yup, it succeed.

with cucumber, we (business analyst, developer or even partner) can easily check the test cases. this tool will show its power when working with complex business process. the partner and the analyst can be agree and deal in a same text file, which is feature file, that can be easily implemented by the developer in the unit test.

for better use, cucumber can be used in a continuous integration tool (like jenkins or bamboo or else) and run the test in schedule. any issue in the program can be easily check in the log file or in the ci's administration site.

just try it, have a nice code..
sample sourcecode: https://github.com/mazbergaz/sample_cucumber