Friday, August 31, 2012

Determine light level, Sensor.TYPE_LIGHT.

Example to determine light level using Android light sensor:

Determine light level, Sensor.TYPE_LIGHT.


package com.example.androidsensor;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 TextView textLIGHT_available, textLIGHT_reading;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textLIGHT_available 
         = (TextView)findViewById(R.id.LIGHT_available);
        textLIGHT_reading 
         = (TextView)findViewById(R.id.LIGHT_reading);
     
     SensorManager mySensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
     
     Sensor LightSensor = mySensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
     if(LightSensor != null){
      textLIGHT_available.setText("Sensor.TYPE_LIGHT Available");
      mySensorManager.registerListener(
        LightSensorListener, 
        LightSensor, 
        SensorManager.SENSOR_DELAY_NORMAL);
      
     }else{
      textLIGHT_available.setText("Sensor.TYPE_LIGHT NOT Available");
     }
    }
    
    private final SensorEventListener LightSensorListener
     = new SensorEventListener(){

   @Override
   public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void onSensorChanged(SensorEvent event) {
    if(event.sensor.getType() == Sensor.TYPE_LIGHT){
     textLIGHT_reading.setText("LIGHT: " + event.values[0]);
    }
   }
     
    };

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    
    <TextView
        android:id="@+id/LIGHT_available"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/LIGHT_reading"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>


Related:
- Access temperature sensor, TYPE_TEMPERATURE and TYPE_AMBIENT_TEMPERATURE.

Thursday, August 30, 2012

Access temperature sensor, TYPE_TEMPERATURE and TYPE_AMBIENT_TEMPERATURE.

The temperature sensors (Sensor.TYPE_TEMPERATURE/Sensor.TYPE_AMBIENT_TEMPERATURE) are used to determine temperature of the phone, for internal hardware. They are not available on all device. (It's not available on my HTC One X and HTC Flyer!)

Sensor.TYPE_TEMPERATURE is deprecated, use Sensor.TYPE_AMBIENT_TEMPERATURE instead.

Example:
package com.example.androidsensor;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 TextView textTEMPERATURE_available, textTEMPERATURE_reading;
 TextView textAMBIENT_TEMPERATURE_available, textAMBIENT_TEMPERATURE_reading;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        textTEMPERATURE_available 
         = (TextView)findViewById(R.id.TEMPERATURE_available);
        textTEMPERATURE_reading 
         = (TextView)findViewById(R.id.TEMPERATURE_reading);
     textAMBIENT_TEMPERATURE_available 
      = (TextView)findViewById(R.id.AMBIENT_TEMPERATURE_available);
     textAMBIENT_TEMPERATURE_reading 
      = (TextView)findViewById(R.id.AMBIENT_TEMPERATURE_reading);
     
     SensorManager mySensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
     
     Sensor TemperatureSensor = mySensorManager.getDefaultSensor(Sensor.TYPE_TEMPERATURE);
     if(TemperatureSensor != null){
      textTEMPERATURE_available.setText("Sensor.TYPE_TEMPERATURE Available");
      mySensorManager.registerListener(
        TemperatureSensorListener, 
        TemperatureSensor, 
        SensorManager.SENSOR_DELAY_NORMAL);
      
     }else{
      textTEMPERATURE_available.setText("Sensor.TYPE_TEMPERATURE NOT Available");
     }
     
     Sensor AmbientTemperatureSensor 
      = mySensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
     if(AmbientTemperatureSensor != null){
      textAMBIENT_TEMPERATURE_available.setText("Sensor.TYPE_AMBIENT_TEMPERATURE Available");
      mySensorManager.registerListener(
        AmbientTemperatureSensorListener, 
        AmbientTemperatureSensor, 
        SensorManager.SENSOR_DELAY_NORMAL);
     }else{
      textAMBIENT_TEMPERATURE_available.setText("Sensor.TYPE_AMBIENT_TEMPERATURE NOT Available");
     }
    }
    
    private final SensorEventListener TemperatureSensorListener
     = new SensorEventListener(){

   @Override
   public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void onSensorChanged(SensorEvent event) {
    if(event.sensor.getType() == Sensor.TYPE_TEMPERATURE){
     textTEMPERATURE_reading.setText("TEMPERATURE: " + event.values[0]);
    }
   }
     
    };
    
    private final SensorEventListener AmbientTemperatureSensorListener
     = new SensorEventListener(){

   @Override
   public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // TODO Auto-generated method stub
   
   }

   @Override
   public void onSensorChanged(SensorEvent event) {
    if(event.sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE){
     textAMBIENT_TEMPERATURE_reading.setText("AMBIENT TEMPERATURE: " + event.values[0]);
    }
   }
 
    };

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    
    <TextView
        android:id="@+id/TEMPERATURE_available"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/TEMPERATURE_reading"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/AMBIENT_TEMPERATURE_available"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/AMBIENT_TEMPERATURE_reading"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>


Related:
- Determine light level, Sensor.TYPE_LIGHT.

Read Aperture, Exposure Time, and ISO from Exif

The post "Read EXIF of JPG file" demonstrate how to read Exif from JPG file using ExifInterface. Start from API Level 11, Exif tag of TAG_APERTURE, TAG_EXPOSURE_TIME and TAG_ISO was added in the android.media.ExifInterface class. You can simple modify the code in "Read EXIF of JPG file" to read Aperture, Exposure Time, and ISO from Exif.

Wednesday, August 29, 2012

Samsung Pays $1B to Apple with 30 Truck Loads of 5 Cent Coins? It's NOT TRUE.

A funny story is sweeping the internet. Samsung paid $1.05 billion to Apple by sending 30 trucks containing five-cent coins.

According to PaperBlog, the trucks were sent to Apple's main office in California this morning. Initially, the security of the company prevented the intrusion. However, Apple CEO Tim Cook received a call from the chief executive of Samsung that they have sent the payment for the fine ruled by the jury in the recently concluded patent battle of the two tech giants.

The funny thing is, the story originated from a meme of the popular website 9Gag.com. Thus, the news report is a hoax.

Details: http://au.ibtimes.com/articles/378402/20120829/samsung-pay-apple-1-billion-coins-five.htm

Tuesday, August 28, 2012

How Apple Jury Reached a Verdict

Apple Jury Foreman: Here's How We Reached a Verdict

Aug. 27 (Bloomberg) -- The Apple Vs. Samsung Jury Foreman Vel Hogan discusses his experience throughout the billion dollar patent case. He speaks with Emily Chang on Bloomberg Television's "Bloomberg West." (Source: Bloomberg)


Sunday, August 26, 2012

Implement ListView NOT extends ListActivity

Example to implement ListView NOT extends ListActivity:

Create /res/layout/row.xml to define the layout of rows in ListView.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="25sp" />


Modify the layout to add a ListView.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    <ListView
        android:id="@+id/mylist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


package com.example.androidlistview;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 String[] month ={
   "January", 
   "February", 
   "March", 
   "April",
   "May", 
   "June", 
   "July", 
   "August",
   "September", 
   "October", 
   "November", 
   "December"};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ListView myListView = (ListView)findViewById(R.id.mylist);
        ListAdapter myListAdapter = new ArrayAdapter<String>(
          getApplicationContext(),
          R.layout.row,
          month);
        myListView.setAdapter(myListAdapter);
        myListView.setOnItemClickListener(new OnItemClickListener(){

   @Override
   public void onItemClick(AdapterView<?> parent, View view, int position,
     long id) {
    Toast.makeText(getApplicationContext(), 
      parent.getItemAtPosition(position).toString(), 
      Toast.LENGTH_LONG).show();
   } 
        });
    }

}


Implement ListView NOT extends ListActivity

Saturday, August 25, 2012

New permission for Android 4.1, API Level: 16.

Android 4.1 add the following new permissions:
  • READ_EXTERNAL_STORAGE
    Provides protected read access to external storage. In Android 4.1 by default all applications still have read access. This will be changed in a future release to require that applications explicitly request read access using this permission. If your application already requests write access, it will automatically get read access as well. There is a new developer option to turn on read access restriction, for developers to test their applications against how Android will behave in the future.
  • READ_USER_DICTIONARY
    Allows an application to read the user dictionary. This should only be required by an IME, or a dictionary editor like the Settings app.
  • READ_CALL_LOG
    Allows an application to read the system's call log that contains information about incoming and outgoing calls.
  • WRITE_CALL_LOG
    Allows an application to modify the system's call log stored on your phone
  • WRITE_USER_DICTIONARY
    Allows an application to write to the user's word dictionary.

Details: Android 4.1 APIs | Android Developers : Permissions

Wednesday, August 22, 2012

Determine the best camera preview size

It'a example to determine the best supported camera preview size, to have the biggest preview area.

Determine the best camera preview size


package com.example.androidcamera;

import java.io.IOException;
import java.util.List;

import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
import android.app.Activity;

public class MainActivity extends Activity {
 
 Camera myCamera;
 SurfaceView mySurfaceView;
 SurfaceHolder mySurfaceHolder;
 boolean isPreview;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        isPreview = false;
        mySurfaceView = (SurfaceView)findViewById(R.id.mypreview);
        mySurfaceHolder = mySurfaceView.getHolder();
        mySurfaceHolder.addCallback(mySurfaceCallback);
        
    }
    
    SurfaceHolder.Callback mySurfaceCallback
    = new SurfaceHolder.Callback(){

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width,
    int height) {
   Camera.Parameters myParameters = myCamera.getParameters();
   Camera.Size myBestSize = getBestPreviewSize(width, height, myParameters);
   
   if(myBestSize != null){
    myParameters.setPreviewSize(myBestSize.width, myBestSize.height);
    myCamera.setParameters(myParameters);
    myCamera.startPreview();
    isPreview = true;
    
    Toast.makeText(getApplicationContext(), 
      "Best Size:\n" +
      String.valueOf(myBestSize.width) + " : " + String.valueOf(myBestSize.height), 
      Toast.LENGTH_LONG).show();
   }
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
   try {
    myCamera.setPreviewDisplay(mySurfaceHolder);
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
   
  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
   // TODO Auto-generated method stub
   
  }
     
    };
    
    private Camera.Size getBestPreviewSize(int width, int height, Camera.Parameters parameters){
     Camera.Size bestSize = null;
     List<Camera.Size> sizeList = parameters.getSupportedPreviewSizes();
     
     bestSize = sizeList.get(0);
     
     for(int i = 1; i < sizeList.size(); i++){
      if((sizeList.get(i).width * sizeList.get(i).height) >
        (bestSize.width * bestSize.height)){
       bestSize = sizeList.get(i);
      }
     }

     return bestSize;
    }
    
 @Override
 protected void onResume() {
  super.onResume();
  myCamera = Camera.open();
 }

 @Override
 protected void onPause() {
  
  if(isPreview){
   myCamera.stopPreview();
  }
  
  myCamera.release();
  myCamera = null;
  isPreview = false;
  
  super.onPause();
 }

}


Modify the layout to have a SurfaceView for preview.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <SurfaceView
        android:id="@+id/mypreview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>


Modify AndroidManifest.xml to add permission of "android.permission.CAMERA", and set android:screenOrientation="landscape".
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidcamera"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <uses-permission android:name="android.permission.CAMERA"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" 
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


Download the files.


Tuesday, August 21, 2012

Decompiling Android



Decompiling Android looks at the the reason why Android apps can be decompiled to recover their source code, what it means to Android developers and how you can protect your code from prying eyes. This is also a good way to see how good and bad Android apps are constructed and how to learn from them in building your own apps.

This is becoming an increasingly important topic as the Android marketplace grows and developers are unwittingly releasing the apps with lots of back doors allowing people to potentially obtain credit card information and database logins to back-end systems, as they don’t realize how easy it is to decompile their Android code.       
  • In depth examination of the Java and Android class file structures
  • Tools and techniques for decompiling Android apps
  • Tools and techniques for protecting your Android apps

What you’ll learn

  • How to download an Android app and decompile it into its original Java source and HTML5 and CSS code
  • How to protect your Android apps so that others cannot decompile it
  • To identify potential security threats that currently exist and how to avoid them  
  • What tools are available to decompile and protect Android apps
  • The structure of a Java Classfile and an Android classfile
  • How the standard JVM and the Dalvik JVM differ
  • How to create your own Android decompiler and obfuscator

Who this book is for

This book is for Android developers and their managers. It's also for hackers and hobbyist types who wish to see how Android apps are constructed as a means of learning how to build Android apps.

Table of Contents

  1. Laying the Groundwork
  2. Ghost in the Machine 
  3. Inside the DEX File
  4. Tools of the Trade
  5. Decompiler Design
  6. Decompiler Implementation
  7. Case Studies


Retain fragment instance across Activity re-creation (such as from a configuration change)

Fragment.setRetainInstance (boolean retain) control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
  • onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
  • onCreate(Bundle) will not be called since the fragment is not being re-created.
  • onAttach(Activity) and onActivityCreated(Bundle) will still be called.

Example:
Fragment.setRetainInstance (boolean retain)

In the example, there are two Fragment, MyFragment and MyFragment2. Basically, both Fragments are same, except that MyFragment2 call setRetainInstance(true) in onCreate(). It can be noticed that onCreate() and onDestroy() of MyFragment2 will not be called when orientation change, and the field myState of MyFragment2 will not be cleared.

Main layout, contain two fragments.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="horizontal" >

   <fragment
       class="com.example.androidretaininstance.MyFragment"
       android:id="@+id/myfragment1"
       android:layout_width="0px"
       android:layout_weight="1"
       android:layout_height="match_parent" />
   <fragment
       class="com.example.androidretaininstance.MyFragment2"
       android:id="@+id/myfragment2"
       android:layout_width="0px"
       android:layout_weight="1"
       android:layout_height="match_parent" />

</LinearLayout>


/res/layout/fragmentlayout.xml, the layout of the fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:orientation="vertical" >
   <TextView
       android:id="@+id/myid"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" />
   <ScrollView 
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView
           android:id="@+id/mystatus"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />
   </ScrollView>

</LinearLayout>


MyFragment.java
package com.example.androidretaininstance;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

public class MyFragment extends Fragment {
 
 int myID;
 String myState = "";
 
 TextView textID;
 TextView textStatus;

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
   Bundle savedInstanceState) {
  View myFragmentView = inflater.inflate(R.layout.fragmentlayout, container, false);
  textID = (TextView)myFragmentView.findViewById(R.id.myid);
  textStatus = (TextView)myFragmentView.findViewById(R.id.mystatus);
  
  textID.setText("ID = " + String.valueOf(myID));
  
  updateStatus(myID + ":onCreateView()");
  return myFragmentView;
  
 }

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  myID = getId();
  updateStatus(myID + ":onCreate()");
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
  updateStatus(myID + ":onDestroy()");
 }
 
 private void updateStatus(String st){
  
  if(textStatus == null){
   myState += st + " (delayed)\n";
  }else{
   myState += st +"\n";
   textStatus.setText(myState);
  }
  
  Toast.makeText(getActivity(), st, Toast.LENGTH_LONG).show();
 }
 
}


MyFragment2.java, extends MyFragment, override onCreate() to call setRetainInstance(true).
package com.example.androidretaininstance;

import android.os.Bundle;

public class MyFragment2 extends MyFragment {

 @Override
 public void onCreate(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onCreate(savedInstanceState);
  setRetainInstance(true);
 }
 
}


Download the files.


Monday, August 20, 2012

Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method

As mentioned in the post "Activity will be re-started when screen orientation changed", the old activity will be destroyed when orientation changed. And also mentioned in last exercise "EditText keep no change after orientation changed", the states of some views will be kept. But it's not always true; for example, refer to the exercise in "GridView", the GridView will always reload photos from SD Card when orientation changed.

To maintain the old states, we can overriding the method onRetainNonConfigurationInstance() to return the old activity. Via the returned activity object, we can retrieve the fields of the old activity.

Please notice that the method onRetainNonConfigurationInstance() is deprecated, If you are targeting HONEYCOMB or later, consider instead using a Fragment with Fragment.setRetainInstance(boolean).

Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method


Modify the java code in the exercise "GridView loading photos from SD Card", override the method onRetainNonConfigurationInstance() to return the activity object, this. In orCreate() method, check if old activity exist by calling getLastNonConfigurationInstance(). If the returned object not null, means old activity exist.


 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        GridView gridview = (GridView) findViewById(R.id.gridview);
        
        //Check if old activity exist
        MainActivity oldActivity = (MainActivity)getLastNonConfigurationInstance();
        if(oldActivity == null){
         myImageAdapter = new ImageAdapter(this);
            gridview.setAdapter(myImageAdapter);
            
            String ExternalStorageDirectoryPath = Environment
              .getExternalStorageDirectory()
              .getAbsolutePath();
            
            String targetPath = ExternalStorageDirectoryPath + "/test/";
            
            Toast.makeText(getApplicationContext(), targetPath, Toast.LENGTH_LONG).show();
            File targetDirector = new File(targetPath);
            
            File[] files = targetDirector.listFiles();
            for (File file : files){
             myImageAdapter.add(file.getAbsolutePath());
            } 
            
            // Get memory class of this device, exceeding this amount will throw an
            // OutOfMemory exception.
            final int memClass 
             = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE))
              .getMemoryClass();
            
            // Use 1/8th of the available memory for this memory cache.
            final int cacheSize = 1024 * 1024 * memClass / 8;
            
            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
             
             @Override
             protected int sizeOf(String key, Bitmap bitmap) {
              // The cache size will be measured in bytes rather than number of items.
              return bitmap.getByteCount(); 
             }
            };
        }else{
         Toast.makeText(getApplicationContext(), "oldActivity exist", Toast.LENGTH_LONG).show();
         myImageAdapter = oldActivity.myImageAdapter;
         gridview.setAdapter(myImageAdapter);
         
        }
        
    }
 
 @Override
 public Object onRetainNonConfigurationInstance() {
  // TODO Auto-generated method stub
  return this;
 }


Download the files.


Sunday, August 19, 2012

EditText keep no change after orientation changed

As mentioned in the post "Activity will be re-started when screen orientation changed", onCreate() method will be called when orientation changed, to re-layout the screen. But in case of some views, such as EditText, the Android life-cycle mechanism will restore the states.

orientation changed


Here is a example, there are two EditText in the layout. The first one is assigned with a ID, it will update the TextView once text changed. The second one have no ID assigned. You can notice that the text in the first EditText (with ID) will keep no change after orientation changed, and onTextChanged() method of the TextChangedListener will be called also, to update the TextView.

On the other hand, the second EditText (without ID assigned) will clear to empty when orientation changed.

Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    <TextView
        android:id="@+id/lasttext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />
    <EditText
        android:id="@+id/intext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:context=".MainActivity" />

</LinearLayout>


package com.example.androidorientationchange;

import android.os.Bundle;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {
 
 TextView lastText;
 EditText inText;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        lastText = (TextView)findViewById(R.id.lasttext);
        
     inText = (EditText)findViewById(R.id.intext);
     inText.addTextChangedListener(new TextWatcher(){

   @Override
   public void afterTextChanged(Editable s) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void beforeTextChanged(CharSequence s, int start, int count,
     int after) {
    // TODO Auto-generated method stub
    
   }

   @Override
   public void onTextChanged(CharSequence s, int start, int before,
     int count) {
    // TODO Auto-generated method stub
    
    
    lastText.setText(s);
    
    Toast.makeText(MainActivity.this, 
         "onTextChanged: " + s, Toast.LENGTH_SHORT).show();
   }});
     
     Toast.makeText(MainActivity.this, 
       "onCreate", Toast.LENGTH_LONG).show();
    }

}


Next:
- Retrieve old activity state for configuration change by overriding onRetainNonConfigurationInstance() method

Thursday, August 16, 2012

Android Programming with App Inventor



Here is a great introduction of the re-released MIT App Inventor.

Related Link:
- MIT App Inventor


Wednesday, August 15, 2012

Implement slide top-down vertical animation

The exercise "Implement slide-in and slide-out animation" implement horizontal animation using build in animation xml android.R.anim.slide_in_left and android.R.anim.slide_out_right. Currently, there are no build-in vertical animation xml supported. To implement vertical animation, create it by ourself.



/res/anim/slidedown_in.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fromYDelta="-50%"
    android:toYDelta="0.0"
    android:duration="1000" />


/res/anim/slidedown_out.xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:fromYDelta="0.0"
    android:toYDelta="50%"
    android:duration="1000" />


Layout.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="horizontal"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
<ImageView
   android:id="@+id/image1"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:src="@drawable/ic_launcher"
   android:visibility="invisible"
   />
<ImageView
   android:id="@+id/image2"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:src="@drawable/ic_launcher"
   android:visibility="invisible"
   />
<ImageView
   android:id="@+id/image3"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:src="@drawable/ic_launcher"
   android:visibility="invisible"
   />
</LinearLayout>


Main code.
package com.exercise.androidanimation;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

public class MainActivity extends Activity {
 
 ImageView image1, image2, image3;
 Animation animationSlideDownIn, animationSlideDownOut;
 ImageView curSlidingImage;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        image1 = (ImageView)findViewById(R.id.image1);
        image2 = (ImageView)findViewById(R.id.image2);
        image3 = (ImageView)findViewById(R.id.image3);
        
        animationSlideDownIn = AnimationUtils.loadAnimation(this, R.anim.slidedown_in);
        animationSlideDownOut = AnimationUtils.loadAnimation(this, R.anim.slidedown_out);
        
        animationSlideDownIn.setAnimationListener(animationSlideInListener);
        animationSlideDownOut.setAnimationListener(animationSlideOutListener);

        curSlidingImage = image1;
        image1.startAnimation(animationSlideDownIn);
        image1.setVisibility(View.VISIBLE);
    }

 @Override
 protected void onPause() {
  // TODO Auto-generated method stub
  super.onPause();
  image1.clearAnimation();
  image2.clearAnimation();
  image3.clearAnimation();
 }
 
 AnimationListener animationSlideInListener
 = new AnimationListener(){

  @Override
  public void onAnimationEnd(Animation arg0) {
   // TODO Auto-generated method stub
   if(curSlidingImage == image1){
    image1.startAnimation(animationSlideDownOut); 
   }else if(curSlidingImage == image2){
    image2.startAnimation(animationSlideDownOut); 
   }else if(curSlidingImage == image3){
    image3.startAnimation(animationSlideDownOut); 
   } 
  }

  @Override
  public void onAnimationRepeat(Animation animation) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onAnimationStart(Animation animation) {
   // TODO Auto-generated method stub
   
  }
  
 };
 
 AnimationListener animationSlideOutListener
 = new AnimationListener(){

  @Override
  public void onAnimationEnd(Animation animation) {
   // TODO Auto-generated method stub
   if(curSlidingImage == image1){
    curSlidingImage = image2;
    image2.startAnimation(animationSlideDownIn);
    image1.setVisibility(View.INVISIBLE);
    image2.setVisibility(View.VISIBLE);
    image3.setVisibility(View.INVISIBLE); 
   }else if(curSlidingImage == image2){
    curSlidingImage = image3;
    image3.startAnimation(animationSlideDownIn);
    image1.setVisibility(View.INVISIBLE);
    image2.setVisibility(View.INVISIBLE);
    image3.setVisibility(View.VISIBLE); 
   }else if(curSlidingImage == image3){
    curSlidingImage = image1;
    image1.startAnimation(animationSlideDownIn);
    image1.setVisibility(View.VISIBLE);
    image2.setVisibility(View.INVISIBLE);
    image3.setVisibility(View.INVISIBLE); 
   }
  }

  @Override
  public void onAnimationRepeat(Animation animation) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onAnimationStart(Animation animation) {
   // TODO Auto-generated method stub
   
  }
  
 };

}



Download the files.

Monday, August 13, 2012

Preview build of ADT 21 and of the SDK Tools, r21 released



Preview 1 is mostly a bugfix release. It contains a number of bug fixes in various areas that were new in ADT 20, such as the new template infrastructure. Whereas 20.0.1, 20.0.2 and the upcoming 20.0.3 contained a small number of absolutely critical fixes, ADT 21 contains a much larger number of general bug fixes and infrastructure improvements.

Details: http://tools.android.com/download/adt-21-preview

Sunday, August 5, 2012

Share with photo using ShareActionProvider

Last exercise demonstrate how to "send email with photo by starting activity with Intent of ACTION_SEND". In this exercise, we will do the same thing using ShareActionProvider.

ShareActionProvider was introduced since API Level 14. This is a provider for a share action. It is responsible for creating views that enable data sharing and also to show a sub menu with sharing activities if the hosting item is placed on the overflow menu.

Share with photo using ShareActionProvider


Modify /res/menu/activity_main.xml to define the android:actionProviderClass attribute for the corresponding in your menu resource file.

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/menu_item_share"
        android:showAsAction="ifRoom"
        android:title="Share"
        android:actionProviderClass="android.widget.ShareActionProvider" />
</menu>


In order for ShareActionProvider to function, you must provide it a share intent. Find the corresponding MenuItem while inflating your menu resource in your Activity or Fragment. Next, call MenuItem.getActionProvider() to retreive an instance of ShareActionProvider. Use setShareIntent() to update the share intent associated with that action item. ~ refer to onCreateOptionsMenu(Menu menu) in the code.

When you need to update the content to be shared (ex. EditText changed, photo attached), call setShareIntent(shareIntent) again to update the intent. ~ refer setShareIntent(Intent shareIntent) in the code.
package com.example.androidsharephoto;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ShareActionProvider;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 private ShareActionProvider myShareActionProvider;
 
 EditText edittextEmailAddress;
    EditText edittextEmailSubject;
    EditText edittextEmailText;
    TextView textImagePath;
    Button buttonSelectImage;
    
    final int RQS_LOADIMAGE = 0;
    
    Uri imageUri = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        edittextEmailAddress = (EditText)findViewById(R.id.email_address);
        edittextEmailSubject = (EditText)findViewById(R.id.email_subject);
        edittextEmailText = (EditText)findViewById(R.id.email_text);
        edittextEmailAddress.addTextChangedListener(commonTextWatcher);
        edittextEmailSubject.addTextChangedListener(commonTextWatcher);
        edittextEmailText.addTextChangedListener(commonTextWatcher);
        
        textImagePath = (TextView)findViewById(R.id.imagepath);
        
        buttonSelectImage = (Button)findViewById(R.id.selectimage);
        buttonSelectImage.setOnClickListener(buttonSelectImageOnClickListener);
    }
    
    TextWatcher commonTextWatcher
    = new TextWatcher(){

  @Override
  public void afterTextChanged(Editable s) {
   // TODO Auto-generated method stub
   setShareIntent(createShareIntent());
  }

  @Override
  public void beforeTextChanged(CharSequence s, int start, int count,
    int after) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onTextChanged(CharSequence s, int start, int before,
    int count) {
   // TODO Auto-generated method stub
   
  }};
    
    OnClickListener buttonSelectImageOnClickListener
    = new OnClickListener(){

  @Override
  public void onClick(View arg0) {
   Intent intent = new Intent(Intent.ACTION_PICK,
     android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
   startActivityForResult(intent, RQS_LOADIMAGE);
  }};
  
 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // TODO Auto-generated method stub
  super.onActivityResult(requestCode, resultCode, data);
   
  if (resultCode == RESULT_OK){
   switch(requestCode){
   case RQS_LOADIMAGE:
    imageUri = data.getData();
    textImagePath.setText(imageUri.toString());
    setShareIntent(createShareIntent());
    break; 
   }  
  }
 }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        
        MenuItem item = menu.findItem(R.id.menu_item_share);
        myShareActionProvider = (ShareActionProvider)item.getActionProvider();
        myShareActionProvider.setShareHistoryFileName(
          ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
        myShareActionProvider.setShareIntent(createShareIntent());
        return true;
    }
    
    

 private Intent createShareIntent() {
     String emailAddress = edittextEmailAddress.getText().toString();
  String emailSubject = edittextEmailSubject.getText().toString();
  String emailText = edittextEmailText.getText().toString();
  String emailAddressList[] = {emailAddress};
  
  Intent shareIntent = new Intent(Intent.ACTION_SEND); 
  
  shareIntent.putExtra(Intent.EXTRA_EMAIL, emailAddressList);  
  shareIntent.putExtra(Intent.EXTRA_SUBJECT, emailSubject); 
  shareIntent.putExtra(Intent.EXTRA_TEXT, emailText); 
  
  if(imageUri != null){
   shareIntent.putExtra(Intent.EXTRA_STREAM, imageUri);
   shareIntent.setType("image/png");
  }else{
   shareIntent.setType("plain/text");
  }
  
     return shareIntent; 
    }
 
 private void setShareIntent(Intent shareIntent) {
  if (myShareActionProvider != null) {
   myShareActionProvider.setShareIntent(shareIntent); 
  } 
 }

    
}


Layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="@string/hello_world"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter email address:"
   />
<EditText 
   android:id="@+id/email_address"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:inputType="textEmailAddress"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter email Subject:"
   />
<EditText 
   android:id="@+id/email_subject"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:inputType="textEmailSubject"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter Text:"
   />
<EditText 
   android:id="@+id/email_text"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   />
<TextView
    android:id="@+id/imagepath"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
<Button
    android:id="@+id/selectimage"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Select image"
    />
</LinearLayout>


Download the files.


Saturday, August 4, 2012

Send email with Image by starting activity using Intent of ACTION_SEND

In the previous posts "Send email using Intent.ACTION_SEND" and "Select Image using Android build-in Gallery" demonstrate how to send text email and load image by starting activity using Intent. In this exercise, both function are work together to send email with photos attached.

Send email with Image by starting activity using Intent of ACTION_SEND


To attach image in email intent, call putExtra() to attach the image uri, and call setType() to set type of "image/png".

package com.exercise.AndroidEMail;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {
 
 EditText edittextEmailAddress;
    EditText edittextEmailSubject;
    EditText edittextEmailText;
    TextView textImagePath;
    Button buttonSelectImage;
    Button buttonSendEmail_intent;
    
    final int RQS_LOADIMAGE = 0;
    final int RQS_SENDEMAIL = 1;
    
    Uri imageUri = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        edittextEmailAddress = (EditText)findViewById(R.id.email_address);
        edittextEmailSubject = (EditText)findViewById(R.id.email_subject);
        edittextEmailText = (EditText)findViewById(R.id.email_text);
        textImagePath = (TextView)findViewById(R.id.imagepath);
        buttonSelectImage = (Button)findViewById(R.id.selectimage);
        buttonSendEmail_intent = (Button)findViewById(R.id.sendemail_intent);
        
        buttonSelectImage.setOnClickListener(buttonSelectImageOnClickListener);
        buttonSendEmail_intent.setOnClickListener(buttonSendEmail_intentOnClickListener);
    }

    OnClickListener buttonSelectImageOnClickListener
    = new OnClickListener(){

  @Override
  public void onClick(View arg0) {
   Intent intent = new Intent(Intent.ACTION_PICK,
     android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
   startActivityForResult(intent, RQS_LOADIMAGE);
  }};
  
 OnClickListener buttonSendEmail_intentOnClickListener
 = new OnClickListener(){
  
  @Override
  public void onClick(View arg0) {
   String emailAddress = edittextEmailAddress.getText().toString();
   String emailSubject = edittextEmailSubject.getText().toString();
   String emailText = edittextEmailText.getText().toString();
   String emailAddressList[] = {emailAddress};
   
   Intent intent = new Intent(Intent.ACTION_SEND); 
   
   intent.putExtra(Intent.EXTRA_EMAIL, emailAddressList);  
   intent.putExtra(Intent.EXTRA_SUBJECT, emailSubject); 
   intent.putExtra(Intent.EXTRA_TEXT, emailText); 
   
   if(imageUri != null){
    intent.putExtra(Intent.EXTRA_STREAM, imageUri);
    intent.setType("image/png");
   }else{
    intent.setType("plain/text");
   }
   
   startActivity(Intent.createChooser(intent, "Choice App to send email:"));
    
  }};

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  // TODO Auto-generated method stub
  super.onActivityResult(requestCode, resultCode, data);
  
  if (resultCode == RESULT_OK){
   switch(requestCode){
   case RQS_LOADIMAGE:
    imageUri = data.getData();
    textImagePath.setText(imageUri.toString());
    break;
   case RQS_SENDEMAIL:
    
    break;
   }
    
  }
 }

    
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="vertical"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="@string/hello_world"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter email address:"
   />
<EditText 
   android:id="@+id/email_address"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:inputType="textEmailAddress"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter email Subject:"
   />
<EditText 
   android:id="@+id/email_subject"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:inputType="textEmailSubject"
   />
<TextView 
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text="Enter Text:"
   />
<EditText 
   android:id="@+id/email_text"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   />
<TextView
    android:id="@+id/imagepath"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    />
<Button
    android:id="@+id/selectimage"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Select image"
    />
<Button 
   android:id="@+id/sendemail_intent"
   android:layout_width="fill_parent"
   android:layout_height="wrap_content"
   android:text=" Send email using Intent.ACTION_SEND "
   />
</LinearLayout>


Download the files.


Related:
- Share with photo using ShareActionProvider
- Start activity to send multi images attached, with action of ACTION_SEND_MULTIPLE

Thursday, August 2, 2012

Create custom background for Views

This exercise demonstrate how to create custom background, which can be applied on various views such as TextView, Button, ImageButton...etc.

custom background

Create /res/drawable/mybackground.xml to define our custom background.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_pressed="true" >
        <shape>
            <gradient
                android:startColor="#FF0000"
                android:endColor="#FF00FF"
                android:angle="180" />
            <stroke
                android:width="3dp"
                android:color="@android:color/background_dark" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="50dp"
                android:top="10dp"
                android:right="50dp"
                android:bottom="10dp" />
        </shape>
    </item>

    <item android:state_focused="true" >
        <shape>
            <gradient
                android:startColor="@android:color/background_light"
                android:endColor="@android:color/background_dark"
                android:angle="180" />
            <stroke
                android:width="3dp"
                android:color="@android:color/background_dark" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="50dp"
                android:top="10dp"
                android:right="50dp"
                android:bottom="10dp" />
        </shape>
    </item>

    <item>        
        <shape>
            <gradient
                android:startColor="@android:color/background_light"
                android:endColor="@android:color/background_dark"
                android:angle="180" />
            <stroke
                android:width="3dp"
                android:color="@android:color/background_dark" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="50dp"
                android:top="10dp"
                android:right="50dp"
                android:bottom="10dp" />
        </shape>
    </item>
</selector>


Use the custom background in layout file.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, it's a TextView"
        android:clickable="true"
        android:background="@drawable/mybackground"
        android:layout_margin="5dp" />
    
 <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, it's a Button"
        android:background="@drawable/mybackground"
        android:layout_margin="5dp" />
        
    <ImageButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher"
        android:background="@drawable/mybackground"
        android:layout_margin="5dp" />

</LinearLayout>


Download the files.

Related:
- Create custom Button using StateListDrawable


Wednesday, August 1, 2012

Create device artwork easily with Device Art Generator

Device artwork generated with Device Art Generator
With Dalvik Debug Monitor Server (DDMS) in Android SDK Tools (or the new screen capture feature on Android 4), it's easy to capture your current screen. How to...if you want create device artwork like the one in right?


With Device Art Generator, you can create device artwork with your own screen easily. The device art generator allows you to quickly wrap your app screenshots in real device artwork. This provides better visual context for your app screenshots on your web site or in other promotional materials.

Simple steps to generate device artwork using Device Art Generator:
  • Drag a screenshot from your desktop onto a device shown in screen.
  • Customize the generated image and drag the generated image to your desktop to save.



Create custom Button using StateListDrawable

android.graphics.drawable.StateListDrawable lets you assign a number of graphic images to a single Drawable and swap out the visible item by a string ID value.

It can be defined in an XML file with the <selector> element. Each state Drawable is defined in a nested <item> element. For more information, see the guide to Drawable Resources.


it's a simple exercise create custom Button using StateListDrawable.

custom Button using StateListDrawable


Prepare your custom button images of default, focused and pressed in /res/drawable/ folder.

Create /res/drawable/mybutton.xml to define each state Drawable.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
        android:state_pressed="true"
        android:drawable="@drawable/button_pressed" />
    <item 
        android:state_focused="true"
        android:drawable="@drawable/button_focused" />
    <item
        android:drawable="@drawable/button" />
</selector>


Assign the custom button in layout using android:background="@drawable/mybutton".
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:background="@drawable/mybutton" />

</RelativeLayout>


Download the files.


Related:
- Create custom background for Views


Google Play Developer Program Policy (“Content Policy”) Updated



The Google Play Developer Program Policy (“Content Policy”) updated. Improvements include the addition of clarifying language for existing policies, support for new Google Play features and content, as well as the introduction of a new section addressing ad behavior in apps.

Visit and familiarize yourself with the above policies. If you find any existing applications in your catalog to be in non-compliance, Google ask you to remedy and republish the application within 30 calendar days of this notification. After this time period, applications discovered to be in violation may be subject to removal from Google Play. Any newly published applications must adhere to the latest version of the Content Policy for Google Play.

Source: Google Play for Developers - Google Play Developer Program Policy (“Content Policy”) Update.