Saturday, January 31, 2015

Simple web server provide image download, using org.apache.http.protocol.HttpService

Last post show a "Simple web server using org.apache.http.protocol.HttpService". In this post, I tried to provide image download, by visiting http://<ip address>:8080/image

But FAIL - As shown in the video, sometimes the download will fail. And once fail, the code will block and have to be re-start. Hope anybody can advise.


Modify MainActivity.java
package com.example.androidhttpservice;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;
import android.os.Bundle;
import android.os.Environment;

/*
 * Permission needed:
 * "android.permission.INTERNET"
 * "android.permission.READ_EXTERNAL_STORAGE"
 */

public class MainActivity extends ActionBarActivity {

 HttpServiceThread httpServiceThread;
 
 TextView infoIp;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  infoIp = (TextView) findViewById(R.id.infoip);
  infoIp.setText(getIpAddress() + ":" 
    + HttpServiceThread.HttpServerPORT + "\n");

  httpServiceThread = new HttpServiceThread();
  httpServiceThread.start();
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  httpServiceThread.stopServer();
 }

 private String getIpAddress() {
  String ip = "";
  try {
   Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
     .getNetworkInterfaces();
   while (enumNetworkInterfaces.hasMoreElements()) {
    NetworkInterface networkInterface = enumNetworkInterfaces
      .nextElement();
    Enumeration<InetAddress> enumInetAddress = networkInterface
      .getInetAddresses();
    while (enumInetAddress.hasMoreElements()) {
     InetAddress inetAddress = enumInetAddress.nextElement();

     if (inetAddress.isSiteLocalAddress()) {
      ip += "SiteLocalAddress: "
        + inetAddress.getHostAddress() + "\n";
     }

    }

   }

  } catch (SocketException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   ip += "Something Wrong! " + e.toString() + "\n";
  }

  return ip;
 }

 private class HttpServiceThread extends Thread {

  ServerSocket serverSocket;
  Socket socket;
  HttpService httpService;
  BasicHttpContext basicHttpContext;
  static final int HttpServerPORT = 8080;
  boolean RUNNING = false;

  HttpServiceThread() {
   RUNNING = true;
   startHttpService();
  }

  @Override
  public void run() {

   try {
    serverSocket = new ServerSocket(HttpServerPORT);
    serverSocket.setReuseAddress(true);

    while (RUNNING) {
     socket = serverSocket.accept();
     
     DefaultHttpServerConnection httpServerConnection = new DefaultHttpServerConnection();
     httpServerConnection.bind(socket, new BasicHttpParams());
     httpService.handleRequest(httpServerConnection,
       basicHttpContext);
     httpServerConnection.shutdown();
    }
    serverSocket.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (HttpException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

  private synchronized void startHttpService() {
   BasicHttpProcessor basicHttpProcessor = new BasicHttpProcessor();
   basicHttpContext = new BasicHttpContext();

   basicHttpProcessor.addInterceptor(new ResponseDate());
   basicHttpProcessor.addInterceptor(new ResponseServer());
   basicHttpProcessor.addInterceptor(new ResponseContent());
   basicHttpProcessor.addInterceptor(new ResponseConnControl());

   httpService = new HttpService(basicHttpProcessor,
     new DefaultConnectionReuseStrategy(),
     new DefaultHttpResponseFactory());

   HttpRequestHandlerRegistry registry = new HttpRequestHandlerRegistry();
   registry.register("/", new HomeCommandHandler());
   registry.register("/image", new ImageCommandHandler());
   httpService.setHandlerResolver(registry);
  }

  public synchronized void stopServer() {
   RUNNING = false;
   if (serverSocket != null) {
    try {
     serverSocket.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  }

  class HomeCommandHandler implements HttpRequestHandler {

   @Override
   public void handle(HttpRequest request, HttpResponse response,
     HttpContext httpContext) throws HttpException, IOException {

    HttpEntity httpEntity = new EntityTemplate(
      new ContentProducer() {

       public void writeTo(final OutputStream outstream)
         throws IOException {

        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
          outstream, "UTF-8");
        String response = "<html><head></head><body><h1>Hello HttpService, from Android-er<h1></body></html>";

        outputStreamWriter.write(response);
        outputStreamWriter.flush();
       }
      });
    response.setHeader("Content-Type", "text/html");
    response.setEntity(httpEntity);
   }

  }
  
  class ImageCommandHandler implements HttpRequestHandler {

   @Override
   public void handle(HttpRequest request, HttpResponse response,
     HttpContext context) throws HttpException, IOException {

    File file = new File(
      Environment.getExternalStorageDirectory(), 
      "android-er_sketch.jpg");

                FileEntity fileEntity = new FileEntity(file, "image/jpg");
                response.setHeader("Content-Type", "application/force-download");
                response.setHeader("Content-Disposition","attachment; filename=image.jpg");
    response.setHeader("Content-Type", "image/jpg");
    response.setEntity(fileEntity);
   }
   
  }

 }

}

Layout file, activity_main.xml, refer to last post.

Permission of "android.permission.INTERNET" and "android.permission.READ_EXTERNAL_STORAGE" is needed in AndroidManifest.xml.

download filesDownload the files.

Friday, January 30, 2015

Simple web server using org.apache.http.protocol.HttpService

It is a example of simple web server, using org.apache.http.protocol.HttpService. org.apache.http.protocol.HttpServiceis a server side HTTP protocol handler based on the classic (blocking) I/O model.


MainActivity.java
package com.example.androidhttpservice;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;

import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentProducer;
import org.apache.http.entity.EntityTemplate;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.HttpService;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;

import android.support.v7.app.ActionBarActivity;
import android.widget.TextView;
import android.os.Bundle;

/*
 * Permission needed:
 * "android.permission.INTERNET"
 */

public class MainActivity extends ActionBarActivity {

 HttpServiceThread httpServiceThread;
 
 TextView infoIp;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  infoIp = (TextView) findViewById(R.id.infoip);
  infoIp.setText(getIpAddress() + ":" 
    + HttpServiceThread.HttpServerPORT + "\n");

  httpServiceThread = new HttpServiceThread();
  httpServiceThread.start();
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  httpServiceThread.stopServer();
 }

 private String getIpAddress() {
  String ip = "";
  try {
   Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
     .getNetworkInterfaces();
   while (enumNetworkInterfaces.hasMoreElements()) {
    NetworkInterface networkInterface = enumNetworkInterfaces
      .nextElement();
    Enumeration<InetAddress> enumInetAddress = networkInterface
      .getInetAddresses();
    while (enumInetAddress.hasMoreElements()) {
     InetAddress inetAddress = enumInetAddress.nextElement();

     if (inetAddress.isSiteLocalAddress()) {
      ip += "SiteLocalAddress: "
        + inetAddress.getHostAddress() + "\n";
     }

    }

   }

  } catch (SocketException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   ip += "Something Wrong! " + e.toString() + "\n";
  }

  return ip;
 }

 private class HttpServiceThread extends Thread {

  ServerSocket serverSocket;
  Socket socket;
  HttpService httpService;
  BasicHttpContext basicHttpContext;
  static final int HttpServerPORT = 8080;
  boolean RUNNING = false;

  HttpServiceThread() {
   RUNNING = true;
   startHttpService();
  }

  @Override
  public void run() {

   try {
    serverSocket = new ServerSocket(HttpServerPORT);
    serverSocket.setReuseAddress(true);

    while (RUNNING) {
     socket = serverSocket.accept();
     DefaultHttpServerConnection httpServerConnection = new DefaultHttpServerConnection();
     httpServerConnection.bind(socket, new BasicHttpParams());
     httpService.handleRequest(httpServerConnection,
       basicHttpContext);
     httpServerConnection.shutdown();
    }
    serverSocket.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (HttpException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }

  private synchronized void startHttpService() {
   BasicHttpProcessor basicHttpProcessor = new BasicHttpProcessor();
   basicHttpContext = new BasicHttpContext();

   basicHttpProcessor.addInterceptor(new ResponseDate());
   basicHttpProcessor.addInterceptor(new ResponseServer());
   basicHttpProcessor.addInterceptor(new ResponseContent());
   basicHttpProcessor.addInterceptor(new ResponseConnControl());

   httpService = new HttpService(basicHttpProcessor,
     new DefaultConnectionReuseStrategy(),
     new DefaultHttpResponseFactory());

   HttpRequestHandlerRegistry registry = new HttpRequestHandlerRegistry();
   registry.register("/", new HomeCommandHandler());
   httpService.setHandlerResolver(registry);
  }

  public synchronized void stopServer() {
   RUNNING = false;
   if (serverSocket != null) {
    try {
     serverSocket.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
  }

  class HomeCommandHandler implements HttpRequestHandler {

   @Override
   public void handle(HttpRequest request, HttpResponse response,
     HttpContext httpContext) throws HttpException, IOException {

    HttpEntity httpEntity = new EntityTemplate(
      new ContentProducer() {

       public void writeTo(final OutputStream outstream)
         throws IOException {

        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
          outstream, "UTF-8");
        String response = "<html><head></head><body><h1>Hello HttpService, from Android-er<h1></body></html>";

        outputStreamWriter.write(response);
        outputStreamWriter.flush();
       }
      });
    response.setHeader("Content-Type", "text/html");
    response.setEntity(httpEntity);
   }

  }

 }

}

activity_main.xml
<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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidhttpservice.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/infoip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

download filesDownload the files.

FreeFlight 3 updated V3.2.14

FreeFlight 3, the official control app to pilot the Parrot Drones, updated V3.2.14.

What's New
Help menu shortcut
Major bug fixes
Academy:
Keep track of all your sessions and share your photos, videos and navigation data with other members of the Parrot Cloud.
Bebop:
New indicators in HUD: distance, speed, altitude
Rolling Spider :
Improve range
Decrease long distance lag
- Please note that the Rolling Spider firmware update should be done directly from our website : www.parrot.com/support/, updated firmware version 1.97.0 is available now.


Monday, January 26, 2015

How to show line number in Android Studio


To show line number in Android Studio:
File Menu > Settings > expand Editor > Appearance
Check the box of Show line numbers


Transfer image between Android devices, via Socket

The post "File transfer via Socket, between Android devices" show how to send text file between Android devices using Socket. In this example, we are going to send image file of "png" via Socket, with ObjectOutputStream in server side and ObjectInputStream in client side.


The png file is fixed "android-er_sketch_1000.png" at ExternalStorageDirectory in server, and received file in client will be save as "test.png" at ExternalStorageDirectory.

android-er_sketch_1000.png
Here I list the MainActivity of both server and client. For the layout files, refer to the example of "File transfer via Socket, between Android devices".

Server side MainActivity.java
package com.example.androidsocketfiletransferserver;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Enumeration;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.os.Environment;
import android.widget.TextView;
import android.widget.Toast;

/*
 * Permission needed:
 * <uses-permission android:name="android.permission.INTERNET"/>
 * <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 */

public class MainActivity extends ActionBarActivity {

 TextView infoIp, infoPort;

 static final int SocketServerPORT = 8080;
 ServerSocket serverSocket;
 
 ServerSocketThread serverSocketThread;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  infoIp = (TextView) findViewById(R.id.infoip);
  infoPort = (TextView) findViewById(R.id.infoport);

  infoIp.setText(getIpAddress());
  
  serverSocketThread = new ServerSocketThread();
  serverSocketThread.start();
 }

 @Override
 protected void onDestroy() {
  super.onDestroy();
  
  if (serverSocket != null) {
   try {
    serverSocket.close();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
 }

 private String getIpAddress() {
  String ip = "";
  try {
   Enumeration<NetworkInterface> enumNetworkInterfaces = NetworkInterface
     .getNetworkInterfaces();
   while (enumNetworkInterfaces.hasMoreElements()) {
    NetworkInterface networkInterface = enumNetworkInterfaces
      .nextElement();
    Enumeration<InetAddress> enumInetAddress = networkInterface
      .getInetAddresses();
    while (enumInetAddress.hasMoreElements()) {
     InetAddress inetAddress = enumInetAddress.nextElement();

     if (inetAddress.isSiteLocalAddress()) {
      ip += "SiteLocalAddress: "
        + inetAddress.getHostAddress() + "\n";
     }

    }

   }

  } catch (SocketException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
   ip += "Something Wrong! " + e.toString() + "\n";
  }

  return ip;
 }
 
 public class ServerSocketThread extends Thread {

  @Override
  public void run() {
   Socket socket = null;
   
   try {
    serverSocket = new ServerSocket(SocketServerPORT);
    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      infoPort.setText("I'm waiting here: " 
       + serverSocket.getLocalPort());
     }});
    
    while (true) {
     socket = serverSocket.accept();
     FileTxThread fileTxThread = new FileTxThread(socket);
     fileTxThread.start();
    }
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } finally {
    if (socket != null) {
     try {
      socket.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }

 }
 
 public class FileTxThread extends Thread {
  Socket socket;
  
  FileTxThread(Socket socket){
   this.socket= socket;
  }

  @Override
  public void run() {
   File file = new File(
     Environment.getExternalStorageDirectory(), 
     "android-er_sketch_1000.png");
   
   byte[] bytes = new byte[(int) file.length()];
   BufferedInputStream bis;
   try {
    bis = new BufferedInputStream(new FileInputStream(file));
    bis.read(bytes, 0, bytes.length);
    
    ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); 
    oos.writeObject(bytes);
    oos.flush();
    
    socket.close();
    
    final String sentMsg = "File sent to: " + socket.getInetAddress();
    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      Toast.makeText(MainActivity.this, 
        sentMsg, 
        Toast.LENGTH_LONG).show();
     }});
    
   } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   } finally {
    try {
     socket.close();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   
  }
 }
}

download filesDownload the files in Server side.


Client side MainActivity.java
package com.example.androidsocketfiletransferclient;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Bundle;
import android.os.Environment;

/*
 * Permission needed:
 * <uses-permission android:name="android.permission.INTERNET"/>
 * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 */

public class MainActivity extends ActionBarActivity {

 EditText editTextAddress;
 Button buttonConnect;
 TextView textPort;

 static final int SocketServerPORT = 8080;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  editTextAddress = (EditText) findViewById(R.id.address);
  textPort = (TextView) findViewById(R.id.port);
  textPort.setText("port: " + SocketServerPORT);
  buttonConnect = (Button) findViewById(R.id.connect);
  
  buttonConnect.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    ClientRxThread clientRxThread = 
     new ClientRxThread(
      editTextAddress.getText().toString(), 
      SocketServerPORT);
    
    clientRxThread.start();
   }});
 }

 private class ClientRxThread extends Thread {
  String dstAddress;
  int dstPort;

  ClientRxThread(String address, int port) {
   dstAddress = address;
   dstPort = port;
  }

  @Override
  public void run() {
   Socket socket = null;
   
   try {
    socket = new Socket(dstAddress, dstPort);
    
    File file = new File(
      Environment.getExternalStorageDirectory(), 
      "test.png");

    ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
    byte[] bytes;
    FileOutputStream fos = null;
    try {
     bytes = (byte[])ois.readObject();
     fos = new FileOutputStream(file);
     fos.write(bytes);
    } catch (ClassNotFoundException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    } finally {
     if(fos!=null){
      fos.close();
     }
     
    }

       socket.close();
       
       MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      Toast.makeText(MainActivity.this, 
        "Finished", 
        Toast.LENGTH_LONG).show();
     }});
    
   } catch (IOException e) {

    e.printStackTrace();
    
    final String eMsg = "Something wrong: " + e.getMessage();
    MainActivity.this.runOnUiThread(new Runnable() {

     @Override
     public void run() {
      Toast.makeText(MainActivity.this, 
        eMsg, 
        Toast.LENGTH_LONG).show();
     }});
    
   } finally {
    if(socket != null){
     try {
      socket.close();
     } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }

}

download filesDownload the files in client side.


More example of Android Network programming
- Bi-directional communication between Client and Server, using ServerSocket, Socket, DataInputStream and DataOutputStream

How to fix "Found 2 versions of android-support-v4.jar in the dependency list"

Recently after updated SDK on Android-Eclipse, tried to import my old example, and have the following error reported:

Found 2 versions of android-support-v4.jar in the dependency list,
but not all the versions are identical (check is based on SHA-1 only at this time).
...

My solution is to delete the file android-support-v4.jar in libs folder of my project.

Saturday, January 24, 2015

Install ADT (Android Developer Tools) on Eclipse

Android offers a custom plugin for the Eclipse IDE, called Android Development Tools (ADT). This plugin provides a powerful, integrated environment in which to develop Android apps. It extends the capabilities of Eclipse to let you quickly set up new Android projects, build an app UI, debug your app, and export signed (or unsigned) app packages (APKs) for distribution.

Note: Be aware that Android Studio is now the official IDE for Android, so you should migrate to Android Studio to receive all the latest IDE updates. 

You should install the ADT plugin only if you already have an Eclipse installation that you want to continue using.

~ referenec: http://developer.android.com/sdk/installing/installing-adt.html


Download Eclipse:
It's very straightforward, visit http://www.eclipse.org/downloads/, download Eclipse IDE for Java Developers (Eclipse 3.7.2 or greater), unpack the downloaded file, and move the unpacked folder to any location you want.


Install ADT (Android Developer Tools) on Eclipse:


  1. Start Eclipse, then select Help > Install New Software.
  2. Click Add, in the top-right corner.
  3. In the Add Repository dialog that appears, enter "ADT Plugin" for the Name and the following URL for the Location:
    https://dl-ssl.google.com/android/eclipse/
    Note: The Android Developer Tools update site requires a secure connection. Make sure the update site URL you enter starts with HTTPS.
  4. Click OK.
  5. In the Available Software dialog, select the checkbox next to Developer Tools and click Next.
  6. In the next window, you'll see a list of the tools to be downloaded. Click Next.
  7. Read and accept the license agreements, then click Finish.
    If you get a security warning saying that the authenticity or validity of the software can't be established, click OK.
  8. When the installation completes, restart Eclipse.
reference: http://developer.android.com/sdk/installing/installing-adt.html



Friday, January 23, 2015

Update Android Studio

To Update Android Studio, click Help on menu, Check for Update...


This video show how to update installed Android Studio 1.0.1 to 1.0.2.

Install Android Studio on Ubuntu Linux


To install Android Studio IDE 1.0.1 on Ubuntu, visit https://developer.android.com/sdk/index.html, download Android Studio for Linux.


Follow the steps in https://developer.android.com/sdk/installing/index.html?pkg=studio to set up Android Studio on Linux.
  1. Unpack the downloaded ZIP file into an appropriate location for your applications.
  2. To launch Android Studio, navigate to the android-studio/bin/ directory in a terminal and execute studio.sh.
    You may want to add android-studio/bin/ to your PATH environmental variable so that you can start Android Studio from any directory.
  3. Follow the setup wizard to install any necessary SDK tools.
about JAVA_HOME:
Before run Android Studio, you have to set JAVA_HOME on your system, point to JDK. Otherwise, will have error of "'tools.jar' seems to be not in Android Studio classpath. Please ensure JAVA_HOME points to JDK rather than JRE."


To setup JAVA_HOME on Linux, refer last post "Setup JAVA_HOME and path to JDK on Linux".

Next:
Refer next post up Update Android Studio.


Updated@2015-10-25:
Install Android Studio on 64-bit Ubuntu 15.10 with Ubuntu Make

Setup JAVA_HOME and path to JDK on Linux


Setup JAVA_HOME and path to JDK on Linux (Ubuntu in this example), edit the file .bashrc
$ nano .bashrc

Add the line:
export JAVA_HOME=<path to jdk>
export PATH=$JAVA_HOME/bin:$PATH

Where <path to jdk> is the path to the installed JDK.


Save it and exit.

To reload .bashrc without log-out and log-in, enter the command:
$ source ~/.bashrc


Thursday, January 22, 2015

Wearable Fly Nixie Takes Flight on Stage at CES 2015


Fly Nixie Fly! The winner of the first ever Intel Make it Wearable
Challenge takes flight at CES 2015. More about Nixie: http://flynixie.com/

See more of Nixie at CES 2015: http://intel.ly/1xSmxiM

Learn more about Nixie in the Make it Wearable Challenge from Intel: http://intel.ly/17jn9p1


Win a BeagleBone Black from element14

Want to win a BeagleBone Black from element14? element14 are giving away BeagleBone Black boards every day from now until Feb 1 to new element14 members and the members who refer them. This means that your name will be entered again each time someone new lists you as a referral-- so tell all your friends and increase your odds of winning!

Visit http://www.element14.com/community/community/members/blog/2015/01/16/win-a-beaglebone-black-from-element14, register to enter.

Click "Enter to Win (Somebody Invited Me)" and BE SURE TO ENTER MY USERNAME "android-er" IN THE "INVITED BY" FIELD :)


Monday, January 19, 2015

Drag and Drop between ListView and GridView

Previous example show how to implement Drag-and-Drop between ListViews. As both ListView and Gridiew subclasses of AbsListView, it's easy to implement Drag-and-Drop between ListView and GridView.


Refactory LinearLayoutListView.java to LinearLayoutAbsListView.java, to implement our custom LinearLayout associate with AbsListView, for ListView or GridView.
package com.example.androidimageviewlist;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.AbsListView;
import android.widget.LinearLayout;

public class LinearLayoutAbsListView extends LinearLayout {
 
 AbsListView absListView;

 public LinearLayoutAbsListView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutAbsListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutAbsListView(Context context, AttributeSet attrs,
   int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  // TODO Auto-generated constructor stub
 }

 public void setAbsListView(AbsListView alv){
  absListView = alv;
 }

}

Create /res/layout/gridrow.xml to define row layout for GridView.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/gridrowImageView"
        android:layout_gravity="center"
        android:layout_width="48dp"
        android:layout_height="48dp" />

</LinearLayout>

Modify /res/layout/activity_main.xml to include three <com.example.androidimageviewlist.LinearLayoutAbsListView>
<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"
    android:padding="4dp"
    tools:context="com.example.androidimageviewlist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@android:color/background_dark"
        android:orientation="horizontal" >

        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>

        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>
        
        <com.example.androidimageviewlist.LinearLayoutAbsListView
            android:id="@+id/pane3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <GridView
                android:id="@+id/gridview3"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:numColumns="auto_fit"
                android:verticalSpacing="10dp"
                android:horizontalSpacing="10dp"
                android:columnWidth="60dp"
                android:stretchMode="columnWidth"
                android:gravity="center" />
        </com.example.androidimageviewlist.LinearLayoutAbsListView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/prompt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:gravity="bottom"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  List<Item> srcList;
  
  PassObject(View v, Item i, List<Item> s){
   view = v;
   item = i;
   srcList = s;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }
 
 static class GridViewHolder {
  ImageView icon; 
 }
 
 public class ItemBaseAdapter extends BaseAdapter {

  Context context;
  List<Item> list;

  ItemBaseAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }
  
  public List<Item> getList(){
   return list;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   // TODO Auto-generated method stub
   return null;
  }
  
 }
 

 public class ItemListAdapter extends ItemBaseAdapter {
  
  ItemListAdapter(Context c, List<Item> l) {
   super(c, l);
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);
      
      rowView.setOnDragListener(new ItemOnDragListener(list.get(position)));

      return rowView;
  }

 }
 
 public class ItemGridAdapter extends ItemBaseAdapter {

  ItemGridAdapter(Context c, List<Item> l) {
   super(c, l);
  }
  
  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View gridrowView = convertView;
   
      // reuse views
      if (gridrowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       gridrowView = inflater.inflate(R.layout.gridrow, null);

       GridViewHolder gridviewHolder = new GridViewHolder();
       gridviewHolder.icon = (ImageView) gridrowView.findViewById(R.id.gridrowImageView);
       gridrowView.setTag(gridviewHolder); 
      }

      GridViewHolder holder = (GridViewHolder) gridrowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);

      gridrowView.setOnDragListener(new ItemOnDragListener(list.get(position)));

      return gridrowView;
  }
  
 }

 List<Item> items1, items2, items3;
 ListView listView1, listView2;
 GridView gridView3;
 ItemListAdapter myItemListAdapter1, myItemListAdapter2;
 ItemGridAdapter myItemGridAdapter3;
 LinearLayoutAbsListView area1, area2, area3;
 TextView prompt;
 
 //Used to resume original color in drop ended/exited
 int resumeColor;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  gridView3 = (GridView)findViewById(R.id.gridview3);
  
  area1 = (LinearLayoutAbsListView)findViewById(R.id.pane1);
  area2 = (LinearLayoutAbsListView)findViewById(R.id.pane2);
  area3 = (LinearLayoutAbsListView)findViewById(R.id.pane3);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  area3.setOnDragListener(myOnDragListener);
  area1.setAbsListView(listView1);
  area2.setAbsListView(listView2);
  area3.setAbsListView(gridView3);
  
  initItems();
  myItemListAdapter1 = new ItemListAdapter(this, items1);
  myItemListAdapter2 = new ItemListAdapter(this, items2);
  myItemGridAdapter3 = new ItemGridAdapter(this, items3);
  listView1.setAdapter(myItemListAdapter1);
  listView2.setAdapter(myItemListAdapter2);
  gridView3.setAdapter(myItemGridAdapter3);
  
  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  gridView3.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  gridView3.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});
  
  resumeColor  = getResources().getColor(android.R.color.background_light);

 }
 
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   
   ItemBaseAdapter associatedAdapter = (ItemBaseAdapter)(parent.getAdapter());
      List<Item> associatedList = associatedAdapter.getList();
   
   PassObject passObj = new PassObject(view, selectedItem, associatedList);

   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else if(v == area3){
    area = "area3"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     List<Item> srcList = passObj.srcList;
     AbsListView oldParent = (AbsListView)view.getParent();
     ItemBaseAdapter srcAdapter = (ItemBaseAdapter)(oldParent.getAdapter());
     
     LinearLayoutAbsListView newParent = (LinearLayoutAbsListView)v;
     ItemBaseAdapter destAdapter = (ItemBaseAdapter)(newParent.absListView.getAdapter());
        List<Item> destList = destAdapter.getList();
     
     if(removeItemToList(srcList, passedItem)){
      addItemToList(destList, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
     
     //smooth scroll to bottom
     newParent.absListView.smoothScrollToPosition(destAdapter.getCount()-1);
     
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");  
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 class ItemOnDragListener implements OnDragListener{
  
  Item  me;
  
  ItemOnDragListener(Item i){
   me = i;
  }

  @Override
  public boolean onDrag(View v, DragEvent event) {
   switch (event.getAction()) {
   case DragEvent.ACTION_DRAG_STARTED:
    prompt.append("Item ACTION_DRAG_STARTED: " + "\n");
    break; 
   case DragEvent.ACTION_DRAG_ENTERED:
    prompt.append("Item ACTION_DRAG_ENTERED: " + "\n");
    v.setBackgroundColor(0x30000000);
    break; 
   case DragEvent.ACTION_DRAG_EXITED:
    prompt.append("Item ACTION_DRAG_EXITED: " + "\n");
    v.setBackgroundColor(resumeColor);
    break; 
   case DragEvent.ACTION_DROP:
    prompt.append("Item ACTION_DROP: " + "\n");

    PassObject passObj = (PassObject)event.getLocalState();
    View view = passObj.view;
    Item passedItem = passObj.item;
    List<Item> srcList = passObj.srcList;
    AbsListView oldParent = (AbsListView)view.getParent();
    ItemBaseAdapter srcAdapter = (ItemBaseAdapter)(oldParent.getAdapter());
    
    AbsListView newParent = (AbsListView)v.getParent();
    ItemBaseAdapter destAdapter = (ItemBaseAdapter)(newParent.getAdapter());
    List<Item> destList = destAdapter.getList();
    
    int removeLocation = srcList.indexOf(passedItem);
    int insertLocation = destList.indexOf(me);
    /*
     * If drag and drop on the same list, same position,
     * ignore
     */
    if(srcList != destList || removeLocation != insertLocation){
     if(removeItemToList(srcList, passedItem)){
      destList.add(insertLocation, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
    }

    v.setBackgroundColor(resumeColor);
    
    break;
     case DragEvent.ACTION_DRAG_ENDED:
      prompt.append("Item ACTION_DRAG_ENDED: "  + "\n");
      v.setBackgroundColor(resumeColor);
     default:
      break;    
  }
     
  return true;
  }
  
 }
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  items3 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

download filesDownload the files.

Wednesday, January 14, 2015

Easter Egg on Android 5.0.2 Lollipop, Flappy Bird game



To find the Easter Egg on Android 5.0.2 Lollipop, a build-in Flappy Bird game:
  • Access the Setting
  • Repeatedly tap on Android version to open Lollipop page
  • Tap on Lollipop logo to change color
  • Long tap on Lollipop logo to start the Flappy Bird game.

Nexus 7 (2012 version) WiFi received Android Lollipop, 5.0.2

My Nexus 7 (1st gen, 2012 version) WiFi received Android Lollipop, 5.0.2, OTA update.



Tuesday, January 13, 2015

Drag and Drop items between ListView, with OnDragListener on ListView cells

In last two post "Drag and Drop items between ListView" and "Improved Drag and Drop items between ListView", it drag a ListView item, drop on a LinearLayout, then add the item to the associated ListView, always on bottom. Not drop on ListView actually.

In this exercise, another OnDragListener (ItemOnDragListener) implemented for rowView of ListView items, such that we can actually drop on ListView items, and insert in the previous position.


MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  List<Item> srcList;
  
  PassObject(View v, Item i, List<Item> s){
   view = v;
   item = i;
   srcList = s;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }

 public class ItemsListAdapter extends BaseAdapter {
  
  private Context context;
  private List<Item> list;

  ItemsListAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);
      
      rowView.setOnDragListener(new ItemOnDragListener(list.get(position)));

      return rowView;
  }
  
  public List<Item> getList(){
   return list;
  }
 }

 List<Item> items1, items2, items3;
 ListView listView1, listView2, listView3;
 ItemsListAdapter myItemsListAdapter1, myItemsListAdapter2, myItemsListAdapter3;
 LinearLayoutListView area1, area2, area3;
 TextView prompt;
 
 //Used to resume original color in drop ended/exited
 int resumeColor;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  listView3 = (ListView)findViewById(R.id.listview3);
  
  area1 = (LinearLayoutListView)findViewById(R.id.pane1);
  area2 = (LinearLayoutListView)findViewById(R.id.pane2);
  area3 = (LinearLayoutListView)findViewById(R.id.pane3);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  area3.setOnDragListener(myOnDragListener);
  area1.setListView(listView1);
  area2.setListView(listView2);
  area3.setListView(listView3);
  
  initItems();
  myItemsListAdapter1 = new ItemsListAdapter(this, items1);
  myItemsListAdapter2 = new ItemsListAdapter(this, items2);
  myItemsListAdapter3 = new ItemsListAdapter(this, items3);
  listView1.setAdapter(myItemsListAdapter1);
  listView2.setAdapter(myItemsListAdapter2);
  listView3.setAdapter(myItemsListAdapter3);
  
  /*
  //Auto scroll to end of ListView
  listView1.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView2.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView3.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  */
  
  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  listView3.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  listView3.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});
  
  resumeColor  = getResources().getColor(android.R.color.background_light);

 }
 
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   
   ItemsListAdapter associatedAdapter = (ItemsListAdapter)(parent.getAdapter());
      List<Item> associatedList = associatedAdapter.getList();
   
   PassObject passObj = new PassObject(view, selectedItem, associatedList);

   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else if(v == area3){
    area = "area3"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     List<Item> srcList = passObj.srcList;
     ListView oldParent = (ListView)view.getParent();
     ItemsListAdapter srcAdapter = (ItemsListAdapter)(oldParent.getAdapter());
     
     LinearLayoutListView newParent = (LinearLayoutListView)v;
     ItemsListAdapter destAdapter = (ItemsListAdapter)(newParent.listView.getAdapter());
        List<Item> destList = destAdapter.getList();
     
     if(removeItemToList(srcList, passedItem)){
      addItemToList(destList, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
     
     //smooth scroll to bottom
     newParent.listView.smoothScrollToPosition(destAdapter.getCount()-1);
     
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");  
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 class ItemOnDragListener implements OnDragListener{
  
  Item  me;
  
  ItemOnDragListener(Item i){
   me = i;
  }

  @Override
  public boolean onDrag(View v, DragEvent event) {
   switch (event.getAction()) {
   case DragEvent.ACTION_DRAG_STARTED:
    prompt.append("Item ACTION_DRAG_STARTED: " + "\n");
    break; 
   case DragEvent.ACTION_DRAG_ENTERED:
    prompt.append("Item ACTION_DRAG_ENTERED: " + "\n");
    v.setBackgroundColor(0x30000000);
    break; 
   case DragEvent.ACTION_DRAG_EXITED:
    prompt.append("Item ACTION_DRAG_EXITED: " + "\n");
    v.setBackgroundColor(resumeColor);
    break; 
   case DragEvent.ACTION_DROP:
    prompt.append("Item ACTION_DROP: " + "\n");

    PassObject passObj = (PassObject)event.getLocalState();
    View view = passObj.view;
    Item passedItem = passObj.item;
    List<Item> srcList = passObj.srcList;
    ListView oldParent = (ListView)view.getParent();
    ItemsListAdapter srcAdapter = (ItemsListAdapter)(oldParent.getAdapter());
    
    ListView newParent = (ListView)v.getParent();
    ItemsListAdapter destAdapter = (ItemsListAdapter)(newParent.getAdapter());
    List<Item> destList = destAdapter.getList();
    
    int removeLocation = srcList.indexOf(passedItem);
    int insertLocation = destList.indexOf(me);
    /*
     * If drag and drop on the same list, same position,
     * ignore
     */
    if(srcList != destList || removeLocation != insertLocation){
     if(removeItemToList(srcList, passedItem)){
      destList.add(insertLocation, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
    }

    v.setBackgroundColor(resumeColor);
    
    break;
     case DragEvent.ACTION_DRAG_ENDED:
      prompt.append("Item ACTION_DRAG_ENDED: "  + "\n");
      v.setBackgroundColor(resumeColor);
     default:
      break;    
  }
     
  return true;
  }
  
 }
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  items3 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

LinearLayoutListView.java and the layout file, /res/layout/activity_main.xml, refer to last post.

/res/layout/row.xml and /res/values/arrays.xml, refer to previous post of "Custom ListView with ImageView".

To use Drag and Drop on your app, you have to modify AndroidManifest.xml to set android:minSdkVersion="11".



download filesDownload the files.

Next:
Drag and Drop between ListView and GridView

Sunday, January 11, 2015

Improved Drag and Drop items between ListView

Last post show my first try "Drag and Drop items between ListView". The source and destination of the Drag and Drop operation is hard coded and fixed. It's too un-flexible! It is improved here to provide more flexible approach.


In order to get the associated ListView from any LinearLayout to be drop, I create a new class LinearLayoutListView extends LinearLayout, associated with a ListView.

LinearLayoutListView.java
package com.example.androidimageviewlist;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.ListView;

public class LinearLayoutListView extends LinearLayout {
 
 ListView listView;

 public LinearLayoutListView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutListView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
 }

 public LinearLayoutListView(Context context, AttributeSet attrs,
   int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  // TODO Auto-generated constructor stub
 }

 public void setListView(ListView lv){
  listView = lv;
 }

}

Modify /res/layout/activity_main.xml, to use <com.example.androidimageviewlist.LinearLayoutListView>. And also display three to show the flexibility of the new approach.
<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"
    android:padding="4dp"
    tools:context="com.example.androidimageviewlist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@android:color/background_dark"
        android:orientation="horizontal" >

        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>

        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>
        
        <com.example.androidimageviewlist.LinearLayoutListView
            android:id="@+id/pane3"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview3"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </com.example.androidimageviewlist.LinearLayoutListView>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/prompt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:gravity="bottom"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  List<Item> srcList;
  
  PassObject(View v, Item i, List<Item> s){
   view = v;
   item = i;
   srcList = s;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }

 public class ItemsListAdapter extends BaseAdapter {
  
  private Context context;
  private List<Item> list;

  ItemsListAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);

      return rowView;
  }
  
  public List<Item> getList(){
   return list;
  }
 }

 List<Item> items1, items2, items3;
 ListView listView1, listView2, listView3;
 ItemsListAdapter myItemsListAdapter1, myItemsListAdapter2, myItemsListAdapter3;
 LinearLayoutListView area1, area2, area3;
 TextView prompt;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  listView3 = (ListView)findViewById(R.id.listview3);
  
  area1 = (LinearLayoutListView)findViewById(R.id.pane1);
  area2 = (LinearLayoutListView)findViewById(R.id.pane2);
  area3 = (LinearLayoutListView)findViewById(R.id.pane3);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  area3.setOnDragListener(myOnDragListener);
  area1.setListView(listView1);
  area2.setListView(listView2);
  area3.setListView(listView3);
  
  initItems();
  myItemsListAdapter1 = new ItemsListAdapter(this, items1);
  myItemsListAdapter2 = new ItemsListAdapter(this, items2);
  myItemsListAdapter3 = new ItemsListAdapter(this, items3);
  listView1.setAdapter(myItemsListAdapter1);
  listView2.setAdapter(myItemsListAdapter2);
  listView3.setAdapter(myItemsListAdapter3);
  
  //Auto scroll to end of ListView
  listView1.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView2.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView3.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  listView3.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  listView3.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});

 }
 
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   
   ItemsListAdapter associatedAdapter = (ItemsListAdapter)(parent.getAdapter());
      List<Item> associatedList = associatedAdapter.getList();
   
   PassObject passObj = new PassObject(view, selectedItem, associatedList);
   
   
   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else if(v == area3){
    area = "area3"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     List<Item> srcList = passObj.srcList;
     ListView oldParent = (ListView)view.getParent();
     ItemsListAdapter srcAdapter = (ItemsListAdapter)(oldParent.getAdapter());
     
     LinearLayoutListView newParent = (LinearLayoutListView)v;
     ItemsListAdapter destAdapter = (ItemsListAdapter)(newParent.listView.getAdapter());
        List<Item> destList = destAdapter.getList();
     
     if(removeItemToList(srcList, passedItem)){
      addItemToList(destList, passedItem);
     }
     
     srcAdapter.notifyDataSetChanged();
     destAdapter.notifyDataSetChanged();
     
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");   
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  items3 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

/res/layout/row.xml and /res/values/arrays.xml, refer to previous post of "Custom ListView with ImageView".

To use Drag and Drop on your app, you have to modify AndroidManifest.xml to set android:minSdkVersion="11".

download filesDownload the files.


In this exercise, it drag a ListView item, drop on a LinearLayout, then add the item to the associated ListView, always on bottom. Not drop on ListView actually.

In this exercise, "Drag and Drop items between ListView, with OnDragListener on ListView cells", another OnDragListener (ItemOnDragListener) implemented for rowView of ListView items, such that we can actually drop on ListView items, and insert in the previous position.

Drag and Drop items between ListView

This example show how to move items between ListView by Drag and Drop. (I don't know any standard method to achieve this, it's my own implementation)


MainActivity.java
package com.example.androidimageviewlist;

import java.util.ArrayList;
import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.text.method.ScrollingMovementMethod;
import android.app.Activity;
import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnDragListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {
 
 //items stored in ListView
 public class Item {
  Drawable ItemDrawable;
  String ItemString;
  Item(Drawable drawable, String t){
   ItemDrawable = drawable;
   ItemString = t;
  }
 }
 
 //objects passed in Drag and Drop operation
 class PassObject{
  View view;
  Item item;
  
  PassObject(View v, Item i){
   view = v;
   item = i;
  }
 }
 
 static class ViewHolder {
  ImageView icon;
  TextView text; 
 }

 public class ItemsListAdapter extends BaseAdapter {
  
  private Context context;
  private List<Item> list;

  ItemsListAdapter(Context c, List<Item> l){
   context = c;
   list = l;
  }

  @Override
  public int getCount() {
   return list.size();
  }

  @Override
  public Object getItem(int position) {
   return list.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   View rowView = convertView;
   
      // reuse views
      if (rowView == null) {
       LayoutInflater inflater = ((Activity) context).getLayoutInflater();
       rowView = inflater.inflate(R.layout.row, null);

       ViewHolder viewHolder = new ViewHolder();
       viewHolder.icon = (ImageView) rowView.findViewById(R.id.rowImageView);
       viewHolder.text = (TextView) rowView.findViewById(R.id.rowTextView);
       rowView.setTag(viewHolder); 
      }

      ViewHolder holder = (ViewHolder) rowView.getTag();
      holder.icon.setImageDrawable(list.get(position).ItemDrawable);
      holder.text.setText(list.get(position).ItemString);

      return rowView;
  }
  
  public List<Item> getList(){
   return list;
  }
 }

 List<Item> items1, items2;
 ListView listView1, listView2;
 ItemsListAdapter myItemsListAdapter1, myItemsListAdapter2;
 LinearLayout area1, area2;
 TextView prompt;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  listView1 = (ListView)findViewById(R.id.listview1);
  listView2 = (ListView)findViewById(R.id.listview2);
  
  area1 = (LinearLayout)findViewById(R.id.pane1);
  area2 = (LinearLayout)findViewById(R.id.pane2);
  area1.setOnDragListener(myOnDragListener);
  area2.setOnDragListener(myOnDragListener);
  
  initItems();
  myItemsListAdapter1 = new ItemsListAdapter(this, items1);
  myItemsListAdapter2 = new ItemsListAdapter(this, items2);
  listView1.setAdapter(myItemsListAdapter1);
  listView2.setAdapter(myItemsListAdapter2);
  
  //Auto scroll to end of ListView
  listView1.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
  listView2.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

  listView1.setOnItemClickListener(listOnItemClickListener);
  listView2.setOnItemClickListener(listOnItemClickListener);
  
  listView1.setOnItemLongClickListener(myOnItemLongClickListener);
  listView2.setOnItemLongClickListener(myOnItemLongClickListener);
  
  prompt = (TextView) findViewById(R.id.prompt);
  // make TextView scrollable
  prompt.setMovementMethod(new ScrollingMovementMethod());
  //clear prompt area if LongClick
  prompt.setOnLongClickListener(new OnLongClickListener(){
   
   @Override
   public boolean onLongClick(View v) {
    prompt.setText("");
    return true; 
   }});

 }
 OnItemLongClickListener myOnItemLongClickListener = new OnItemLongClickListener(){

  @Override
  public boolean onItemLongClick(AdapterView<?> parent, View view,
    int position, long id) {
   Item selectedItem = (Item)(parent.getItemAtPosition(position));
   PassObject passObj = new PassObject(view, selectedItem);
   
   
   ClipData data = ClipData.newPlainText("", "");
   DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
   view.startDrag(data, shadowBuilder, passObj, 0);
   
   return true;
  }
  
 };
 
 OnDragListener myOnDragListener = new OnDragListener() {

  @Override
  public boolean onDrag(View v, DragEvent event) {
   String area;
   if(v == area1){
    area = "area1"; 
   }else if(v == area2){
    area = "area2"; 
   }else{
    area = "unknown"; 
   }
   
   switch (event.getAction()) {
    case DragEvent.ACTION_DRAG_STARTED:
     prompt.append("ACTION_DRAG_STARTED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_ENTERED:
     prompt.append("ACTION_DRAG_ENTERED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DRAG_EXITED:
     prompt.append("ACTION_DRAG_EXITED: " + area  + "\n");
     break; 
    case DragEvent.ACTION_DROP:
     prompt.append("ACTION_DROP: " + area  + "\n");

     PassObject passObj = (PassObject)event.getLocalState();
     View view = passObj.view;
     Item passedItem = passObj.item;
     ListView oldParent = (ListView)view.getParent();
     LinearLayout newParent = (LinearLayout)v;
     
     if(oldParent.equals(listView1)){
      prompt.append("Drag source: listView1" + "\n");
      
      if(newParent.equals(area2)){
       prompt.append("Drop target: area2" + "\n");
       
       if(removeItemToList(items1, passedItem)){
        addItemToList(items2, passedItem); 
       }
       myItemsListAdapter1.notifyDataSetChanged();
       myItemsListAdapter2.notifyDataSetChanged();
       
      }else{
       prompt.append("Invalid Drop target" + "\n");
      }
      
     }else if(oldParent.equals(listView2)){
      prompt.append("Drag source: listView2" + "\n");
      
      if(newParent.equals(area1)){
       prompt.append("Drop target: area1" + "\n");
       
       if(removeItemToList(items2, passedItem)){
        addItemToList(items1, passedItem); 
       }
       myItemsListAdapter1.notifyDataSetChanged();
       myItemsListAdapter2.notifyDataSetChanged();
      }else{
       prompt.append("Invalid Drop target" + "\n");
      }
      
     }else{
      prompt.append("Invalid Drag source: unknown" + "\n");
     }
     break;
      case DragEvent.ACTION_DRAG_ENDED:
       prompt.append("ACTION_DRAG_ENDED: " + area  + "\n");   
      default:
       break;    
   }
      
   return true;
  }
  
 };
 
 OnItemClickListener listOnItemClickListener = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   Toast.makeText(MainActivity.this, 
     ((Item)(parent.getItemAtPosition(position))).ItemString, 
     Toast.LENGTH_SHORT).show();
  }
  
 };

 private void initItems(){
  items1 = new ArrayList<Item>();
  items2 = new ArrayList<Item>();
  
  TypedArray arrayDrawable = getResources().obtainTypedArray(R.array.resicon);
  TypedArray arrayText = getResources().obtainTypedArray(R.array.restext);
  
  for(int i=0; i<arrayDrawable.length(); i++){
   Drawable d = arrayDrawable.getDrawable(i);
   String s = arrayText.getString(i);
   Item item = new Item(d, s);
   items1.add(item);
  }
  
  arrayDrawable.recycle();
  arrayText.recycle();
 }
 
 private boolean removeItemToList(List<Item> l, Item it){
  boolean result = l.remove(it);
  return result;
 }
 
 private boolean addItemToList(List<Item> l, Item it){
  boolean result = l.add(it);
  return result;
 }

}

/res/layout/activity_main.xml
<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"
    android:padding="4dp"
    tools:context="com.example.androidimageviewlist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:background="@android:color/background_dark"
        android:orientation="horizontal" >

        <LinearLayout
            android:id="@+id/pane1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/pane2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_margin="4dp"
            android:layout_weight="1"
            android:background="@android:color/background_light"
            android:orientation="vertical" >

            <ListView
                android:id="@+id/listview2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal" >

        <TextView
            android:id="@+id/prompt"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/black"
            android:gravity="bottom"
            android:textColor="@android:color/white" />
    </LinearLayout>

</LinearLayout>

/res/layout/row.xml and /res/values/arrays.xml, refer to previous post of "Custom ListView with ImageView".

To use Drag and Drop on your app, you have to modify AndroidManifest.xml to set android:minSdkVersion="11".

download filesDownload the files.


Next:
- It's a too un-flexible approach, The source and destination of the Drag and Drop operation is hard coded and fixed. Next is a "Improved Drag and Drop items between ListView".

- In the exercise "Drag and Drop items between ListView, with OnDragListener on ListView cells", another OnDragListener (ItemOnDragListener) implemented for rowView of ListView items, such that we can actually drop on ListView items, and insert in the previous position.