Sender-side Implementation

 

On the object sender side, three things should be implemented in response to the user's drag gesture, such as a long press.

1. Creating the ClipData

2. Building the drag shadow

3. Starting the drag

 

When the Dual window application sends the object except for text data, the Android permission error can be occurred since SAF (Storage Access Frameworks) of Android KitKat does not recommend it. Therefore, this document and sample applications does NOT also recommend sending the object except of the text data. Regarding the text data, Android framework supports text drag & drop between applications, so developers do not need to implement it.
For this reason, please use the following contents just as secondary information for helping your understanding, and note that it is NOT recommended for 3rd party applications to send the object except for the text data without the permission.

Android SAF (Storage Access Framework) on Kitkat
SAF provides the common Documents UI to access content. Android KitKat recommends that the application use this Documents UI for accessing the other application’s content. To implement drag and drop using Documents UI, the application need the permission, android.permission.MANAGE_DOCUMENTS. However, this permission is NOT allowed for third-party applications. Therefore, Dual window applications may not allow you to send some content, such as: images, video, and so on from one app to the other.

Creating the ClipData

ClipData Strucutre

Android provides the ClipData object, allowing you to use it to copy the data from drag target. ClipData consists of the ClipDescription and the ClipData.Item. ClipData contains one ClipDescription object, but also contains a ClipData.Item object.

 

ClipDescription

Contains the metadata which describes the contents of the ClipData, but not the data itself. In particular, it contains an array of available mimeTypes for the clip data.

ClipDescription can have the following values:

  • If you create the ClipData with the static method newPlainText(label, text),
    - the ClipDescription sets MIMETYPE_TEXT_PLAIN
  • If you create the ClipData with the static method newUri(contentResolver, label, uri), - the content URI: the ClipDescription sets available mimeTypes retrieved using ContentResolver. - the file URI: the ClipDescription sets MIMETYPE_TEXT_URILIST.

 

For more information on ClipDescription, please refer to the following link: http://developer.android.com/reference/android/content/ClipDescription.html

 

ClipData.Item

Describes a single item in the ClipData. It can contain text, URI, or intent data:

  • Text: Contains an actual charSequence
  • URI: Contains the URI of the data, but not the actual data. The receiver-side application gets the URI of the data from it and retrieves the data. It can either be the content URI or the file URI.
  • Intent: Contains an application shortcut.

 

Creating Text ClipData

You can create a ClipData from a text string using the following static method of ClipData.

static ClipData newPlainText(CharSequence label, CharSequence text)
ClipData data = ClipData.newPlainText(“com.lge.example.interactionSenderActivity”, 
                    “this is a sentence to drag”);

 

Creating URI ClipData

You can create a ClipData from a URI in two ways:

1. Using the ClipData's constructor, new ClipData()

This approach manually sets the mimeTypes and URI as follows. You can use it for both the content URI and file URI.

ClipData(CharSequence label, String[] mimeTypes, ClipData.Item item)
ClipData.Item item = new Item( Uri.parse(“file://mnt/sdcard/Pictures/vdeo_1.3gp”) );
String[] mimeTypes= new String[] { “video/3gpp”, ClipDescription.MIMETYPE_TEXT_URILIST};
ClipData data = new ClipData(“com.lge.example.SenderActivity”, mimeTypes, item);

 

2. Using the ClipData's static method, newUri()

This approach retrieves the mimeTypes from URI using ContentResolver. You can only use it for content URI; it should not be used for the file URI. It is also possible to get the wrong mimeType, for example, the actual MIME data is audio/3GP, but is incorrectly set as video/3GP.

static ClipData newUri(ContentResolver resolver, CharSequence label, Uri uri)
Uri contentUri= Uri.parse(“content://media/external/video/media/1”);
ContentResolver cr= getContentResolover();
ClipData data = ClipData.newUri(cr, “com.lge.example.SenderActivity”, contentUri);

 

1. You should NOT use newUri() for the file URI.
2. If you can get the explicit mimeType, using new ClipData() is recommended rather than newUri().

You should NOT use the newRawUri() method because it cannot set the actual mimeType to ClipDescription.

 

Creating Intent ClipData

Intent ClipData is not used for Dual window mode, so this document does not explain it.

 

Building the Drag Shadow

During a drag-and-drop operation, the system displays an image that represents the data being dragged. This image is called a drag shadow. You can create it by overriding methods of View.DragShadowBuilder object and pass it to the system when you start a drag using startDrag(). As part of its response to startDrag(), the system invokes the callback methods you’ve defined in View.DragShadowBuilder to obtain a drag shadow.

 

For example, you can simply create a gray box for a drag shadow as follows.

private class GreyBoxDragShadowBuilder extends View.DragShadowBuilder {
ColorDrawable greyBox;
public GreyBoxDragShadowBuilder(View v) {
super(v);
greyBox = new ColorDrawable(Color.LTGRAY);
}
@Override
public void onProvideShadowMetrics(Point shadowSize,
Point shadowTouchPoint) {
View v = getView();
int width = (int) v.getWidth() / 2;
int height = (int) v.getHeight() / 2;
greyBox.setBounds(0, 0, width, height);
shadowSize.set(width, height);
shadowTouchPoint.set((int) width / 2, (int) height / 2);
}
Override
public void onDrawShadow(Canvas canvas) {
greyBox.draw(canvas);
}
}

 

DragShadowBuilder Constructor

You can pass any of your application’s View objects when you create the DragShadowBuilder instance.

public void onProvideShadowMetrics(Point dimension, Point shadowTouchPoint)

You can send the dimension and touch point of the drag shadow to the system. The dimension is the Point object that represents drag shadow width and height. The shadowTouchPoint is the Point object that represents the location within the drag shadow that should be under the user’s finger during the drag.

 

public void onDrawShadow(Canvas canvas)

The system calls this method to draw the drag shadow on the canvas.

 

Starting the Drag

To start the drag, call the startDrag() in response to a user’s drag gesture, usually a long press. As arguments of the startDrag(), you should pass the ClipData, drag shadow, and the specific flag for Dual window.

 

public final boolean startDrag (ClipData data, View.DragShadowBuilder shadowBuilder, Object myLocalState, int flags)

public class SenderActivityextends Activity {
private static final int DUAL_WINDOW_DRAG_AND_DROP_ON = 1;
private static final int DUAL_WINDOW_DRAG_AND_DROP_OFF = 0;
...
View.OnLongClickListener onLongClickListener= new View.OnLongClickListener( ){
public boolean onLongClick( View v ) {
ContentResolver cr= getContentResolver();
ClipData data = ClipData.newUri(cr, getLocalClassName(), contentUri);
GreyBoxDagShadowBuilder dragShadowBuilder=
new GreyBoxDragShadowBuilder (v);
v.startDrag( data, // clip data that you created
dragShadowBuilder, //drag shadow that you created
null, 
DUAL_WINDOW_DRAG_AND_DROP_ON); //It must have the value, 1
}
}
...
}

 

When you call the startDrag(), you should pass the flag value for allowing drag and drop between Dual window applications on the same screen. If this integer value is set to 1, drag and drop to the other Split window is allowed. If this integer value is set to 0, drag and drop is NOT allowed. However, according to the SAF(Storage Access Framework) recommendation, the application should have the permission for accessing the other application’s contents.

 

Navigation