Click here to Skip to main content
Click here to Skip to main content

Tagged as

Utilize the Accessory File Transfer on Samsung Gear

, 3 Jul 2014 Public Domain
Rate this:
Please Sign up or sign in to vote.
This tutorial analyzes how a Samsung Gear File Transfer application is created.

The tutorial found below analyzes how a Samsung Gear File Transfer application is created. This session is a four-part series. Parts 1 and 2 show the development on the Android part of the application. The first deals with the Service part of the application, while the second deals with the creation of the Activity part. Part 3 shows the development of the Tizen part of the application. Finally, Part 4 demonstrates the complete Gear File Transfer application, which is a package composed of the combined Tizen part and the Android part.

Service

As you can see, this is the architecture of the Samsung Accessory File Transfer SDK. Your application basically interacts the SAP Framework through the accessory file transfer JAR. Let's get started. Here's what we need in order to create the Android side of the file transfer application for Samsung Gear 2. First, make sure to have Eclipse with Android Development tools installed. Make sure it is updated to include Jelly Bean packages. Jelly Bean is the minimum requirement to use the Samsung Accessory.

We also need the three JAR files that are used to introduce SAP, and SAP File Transfer to the device.

  • accessory-v1.0.0.jar
  • remotesensor-v1.0.0.jar
  • sdk-v1.0.0.jar

These can be found in the Samsung Mobile SDK 1.5 beta archive file. Also, make sure to have a Samsung Android Smart Device that has Jelly Bean. The other materials we will need will be discussed as we proceed.

Before we use our IDE, let's brief ourselves with the basics of Samsung Accessory File Transfer.

The SAft class that provides the Samsung Accessory File Transfer package. It provides methods for initializing and for checking if it is supported. This can be done by declaring an object of SAft instance and initializing it in a try catch block.

SAft accessoryfiletransfer = new SAft();

try {
    accessoryfiletransfer.initialize(this);
} catch (SsdkUnsupportedException e) {
        // Error handling
        e.printStackTrace();
}

Next is the SAFileTransfer class that provides the File Transfer JAR implementation. You must also define a broadcast receiver for action, SAP file transfer requested on file receiver side for files to be received. It contains four methods. Mainly, send, receive, reject, and cancel as seen the code below.

mSAFileTransfer = new SAFileTransfer(FTServiceProvider.this, mCallback);

...

mSAFileTransfer.send(peerAgent, filename)

...

mSAFileTransfer.receive(transID, path)

...

mSAFileTransfer.reject(transID);

...

mSAFileTransfer.cancel(transID);

Finally, the SAFileTransfer.EventListener. This listener is the interface that receives file transfer update events. It contains three methods to assist in the progress of transferring files. These are: onTransferRequested, onProgressChanged, and onTransferCompleted.

mCallback = new EventListener() {

... 

@Override
public void onTransferRequested(int id, String fileName) {}

...

@Override
public void onProgressChanged(int transId, int progress) {}

...

@Override
public void onTransferCompleted(int transId, String fileName, int errorCode) {}

To understand how Samsung Accessory File Transfer works, you can download the file transfer sample under http://developer.samsung.com/samsung-gear.

Let's break down the application. As stated earlier, we'll focus on the service part of the application. First off, check if the library is complete.

Now let's begin analzying the code. Inside FTSampleProviderImp.java you can see we created and initialized an instance of SAft in the onCreate method as follows.

        SAft SAftPkg = new SAft();
        try {
            SAftPkg.initialize(this);
            Log.d("[USER SERVICE] FT SERVICE SAFT INITIALIZE", "VERSION: " + SAftPkg.getVersionName() + " || " + "CODE: "+ SAftPkg.getVersion
        } catch (SsdkUnsupportedException e) {
            if (e.getType() == SsdkUnsupportedException.DEVICE_NOT_SUPPORTED) {
                Toast.makeText(getBaseContext(), "Cannot initialize, DEVICE_NOT_SUPPORTED", Toast.LENGTH_SHORT).show();
                Log.d("USER SERVICE] SAFT INITIALIZE SDK UNSUPPORTED", "DEVICE NOT SUPPORTED");
            } else if (e.getType() == SsdkUnsupportedException.LIBRARY_NOT_INSTALLED) {
                Toast.makeText(getBaseContext(), "Cannot initialize, LIBRARY_NOT_INSTALLED.", Toast.LENGTH_SHORT).show();
                Log.d("USER SERVICE] SAFT INITIALIZE SDK UNSUPPORTED", "LIBRARY NOT INSTALLED");
            } else {
                Toast.makeText(getBaseContext(), "Cannot initialize, unknown.", Toast.LENGTH_SHORT).show();
                Log.d("USER SERVICE] SAFT INITIALIZE UNKNOWN ERROR", e.getType() + " || " + e.getMessage();
            }

            e.printStackTrace();                
            return;
        } catch (Exception e1) {
            Toast.makeText(getBaseContext(), "Cannot initialize, SAFileTransfer.", Toast.LENGTH_SHORT).show();
            e1.printStackTrace();
            Log.d("[USER SERVICE] FT SERVICE SAFT CATCH", e1.getMessage());
            return;
        }

We also initialized an instance of an EventListener in the onCreate method.

        mCallback = new EventListener() {                
            @Override
            public void onProgressChanged(int transId, int progress) {
                Log.d("[USER SERVICE]" + TAG, "onTransferProgress : " + progress + " transId : " + transId);

                if (mFileAction != null) {
                    mFileAction.onProgress(progress);
                }
            }
                
            @Override
            public void onTransferCompleted(int transId, String fileName, int errorCode) {
                Log.d( "[USER SERVICE]" + TAG, "onTransferComplete,  tr id : " + transId +  " file name : " + fileName + " error code : " + errorCode);
                if (errorCode == 0) {
                    mFileAction.onTransferComplete(fileName);
                } else {
                    mFileAction.onError("Error", errorCode);
                }
            }                

            @Override
            public void onTransferRequested(int id, String fileName) {
                Log.d( "[USER SERVICE]" + TAG, "onTransferRequested,  tr id : " + id +  " file name : " + fileName);
                if (FTSampleProviderActivity.isUp)
                    mFileAction.onTransferRequested(id, fileName);
                else
                    mContext.startActivity(new Intent().setClass(mContext, FTSampleProviderActivity.class)
                                                       .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                                                       .setAction("incomingFT")
                                                       .putExtra("tx", id)
                                                       .putExtra("fileName", fileName));
            }

        };

Mainly, the EventListener is the listener interface for receiving file transfer update events, such as onProgressChanged, onTransferCompleted, and onTransferRequested. We will be using this once we initiate an instance of the SAFileTransfer, which can be located at the bottom of the onCreate method.

        mSAFileTransfer = new SAFileTransfer(FTSampleProviderImpl.this, mCallback);

Another thing to take note of is that we created a nested class, that extended the SASocket.

    public class FTSampleProviderConnection extends  {
        public static final String TAG = "FTSampleProviderConnection";
        int mConnectionId;

        public FTSampleProviderConnection() {
            super(FTSampleProviderConnection.class.getName());
            Log.d("[USER SERVICE SOCKET] FTSAMPLEPROVIDERCONNECTION CONSTRUCTOR", FTSampleProviderConnection.class.getName());
        }

        @Override
        protected void onServiceConnectionLost(int errorCode) {
            Log.e("[USER SERVICE SOCKET]" + TAG, "onServiceConectionLost  for peer = " + mConnectionId + "error code =" + errorCode);
            mConnection = null;
        }

        @Override
        public void onReceive(int channelId, byte[] data) {
            try {
                onDataAvailableonChannel(mConnectionId, channelId, new String(data, "UTF-8"));
                Log.d("[USER SERVICE SOCKET]FTSAMPLEPROVIDERCONNECTION ONRECEIVE", "onDataAvailableonChannel " + " Connectio ID " + + mConnectionId + "error code =" + errorCode);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onError(int channelId, String errorMessage, int errorCode) {
            mFileAction.onError(errorMessage, errorCode);
            Log.e("[USER SERVICE SOCKET]" + TAG, "Connection is not alive ERROR: " + errorMessage + "  " + errorCode);
        }
    }

And we created an instance of the FTSampleProviderConnection class which was a class that extended SASocket. This represents an instance of a service connection between a service provider and a consumer.

    private FTSampleProviderConnection mConnection = null;

The mConnection is used in the overidden function of SA Agent named onServiceConnectionResponse. Basically, what this does is if the error code is 0, FTSampleProviderConnection is initialized to uSocket. It is casted as FTSampleProviderConnection. If uSocket is not null, mConnection is initialized to FTSampleProviderConnection and a Toast message is shown notifying that a connection has been established.

    protected void onServiceConnectionResponse(SASocket uSocket, int error) {
        if (error == 0) {
        	FTSampleProviderConnection localConnection = (FTSampleProviderConnection) uSocket;
            Log.d("[USER SERVICE] FT ONSERVICE CONNECTION RESPONSE", "ERROR: " + error + " || " + "LOCAL CONNECTION: " + localConnection.toString());
            Log.d("[USER SERVICE] FT ONSERVICE CONNECTION RESPONSE", "USOCKET : " + uSocket.toString());
            if (uSocket != null) {
                mConnection = localConnection;
                Toast.makeText(getBaseContext(), "Connection established for FT", Toast.LENGTH_SHORT).show();
                Log.d("[USER SERVICE] FT ONSERVICE CONNECTION RESPONSE", "USOCKET NOT NULL " + " MCONNECTION: " + mConnection.toString());
            }
        }
    }

You should also have the FileAction interface. This part will be given functionality a little later in the article when we create the Activity part of the project.

    public interface FileAction {
        void onError(String errorMsg, int errorCode);
        void onProgress(long progress);
        void onTransferComplete(String path);
        void onTransferRequested(int id, String path);
    }

Now let's open AndroidManifest.xml. Don't forget to add these:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.samsung.accessory.permission.ACCESSORY_FRAMEWORK"/>
<uses-permission android:name="com.samsung.wmanager.APP"/>

<application 
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" >
    <activity 
        android:name="com.samsung.accessory.FTSampleProvider.ui.FTSampleProviderActivity"
        android:label="FTSampleProvider"
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <service android:name="com.samsung.accessory.FTSampleProvider.backend.FTSampleProviderImpl"/>
    
    <receiver android:name="com.samsung.android.sdk.accessory.RegisterUponInstallReceiver">
        <intent-filter>
            <action android:name="android.accessory.device.action.REGISTER_AFTER_INSTALL"/>
        </intent-filter>
    </receiver>
    <receiver android:name="com.samsung.android.sdk.accessory.ServiceConnectionIndicationBroadcastReceiver">
        <intent-filter>
            <action android:name="android.accessory.service.action.ACCESSORY_SERVICE_CONNECTION_IND"/>
        </intent-filter>
    </receiver>
    
    <receiver android:name="com.samsung.android.sdk.accessoryfiletransfer.SAFileTransferIncomingRequestReceiver">
        <intent-filter>
            <action android:name="com.samsung.accessory.ftconnection"/>
        </intent-filter>
    </receiver>

Activity

The Samsung Accessory framework facilitates the interconnectivity among accessory devices and Samsung smart devices. This is efficient and convenient to use since it is compatible with various network set-ups. Samsung smart devices equipped with Samsung Accessory framework support a variety of services for accessory devices mainly for file transfer.

If you open ft_provider_activity.xml you will see that there is a progress bar. We will be using this as the UI that will inform the user of the current progress of the transfer.

<ProgressBar 
    android:layout_height="wrap_content" 
    android:layout_width="fill_parent" 
    android:text="Recv Progress" 
    android:id="@+id/RecvProgress" 
    android:layout_below="@+id/RecvStatus" 
    style="@android:style/Widget.ProgressBar.Horizontal"/>

Let's look at FTSampleProviderActivity.java. The TAG variable will be used for logging purposes and the destination path will be used to locate where we will put the file transfered from the wearable.

public class FTSampleProviderActivity extends Activity {	
    private static final String TAG = "FT PROVIDER";
    private static final String DEST_PATH  = Environment.getExternalStorageDirectory() + File.separator + "FTSampleProvider/";

Let's check out the showQuitDialog(). Here the method shows an alert profile dialogue transfer with the negative button to cancel the file transfer.

    private void showQuitDialog() {
        Log.d("[USER] FT ACTIVITY SHOWQUITDIALOG", "showing quit dialog for file transfer");
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                AlertDialog.Builder alertbox = new AlertDialog.Builder(FTSampleProviderActivity.this);
                alertbox = new AlertDialog.Builder(FTSampleProviderActivity.this);
                alertbox.setMessage("Receiving file : [" + mFilePath + "] QUIT?");
                alertbox.setNegativeButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface arg0, int arg1) {
                        try {
                            mFTService.cancelFileTransfer(mTransId);
                        } catch (Exception e) {
                            e.printStackTrace();
                            Toast.makeText(mCtxt, "IllegalArgumentException", Toast.LENGTH_SHORT).show();
                        }
                        mAlert.dismiss();
                        mRecvProgressBar.setProgress(0);
                    }
                });

                mAlert = alertbox.create();
                mAlert.show();
            }
        });
    }

Let's look into the getFileAction method. onError it allows us to dismiss an alert dialogue for transfer and shows a toast saying "Transfer cancelled" plus and error message. And the onProgress method allows us to update the UI progress bar by setting its progress. The onTransferComplete method notifies the transfer, resets the progress to 0, dismisses the transfer alert dialogue, and provides a toast saying "receive Completed!"

The onTransferRequested method is called by the wearable and runs on a UI thread. It shows an alert box to notify if the file transfer has been received. It has two interactive buttons, the PositiveButton to accept the file by calling FTService.receiveFile, and the NegativeButton to reject the file. The main difference between these two values are the boolean provided, a true and false flag.

    private FileAction getFileAction() {
        return new FileAction() {
            @Override
            public void onError(final String errorMsg,final int errorCode) {
                Log.d("[USER] FILEACTION ONERROR", errorCode + "||" + errorMsg);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (mAlert != null && mAlert.isShowing()) {
                            mAlert.dismiss();
                        }
                        Toast.makeText(mCtxt, "Transfer cancelled "+errorMsg, Toast.LENGTH_SHORT).show();
                        mRecvProgressBar.setProgress(0);
                    }
                });
            }

            @Override
            public void onProgress(final long progress) {
                Log.d("[USER] FILEACTION ONPROGRESS", "FILE PROGRESS: " + progress);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mRecvProgressBar.setProgress((int) progress);
                    }
                });
            }

            @Override
            public void onTransferComplete(String path) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mRecvProgressBar.setProgress(0);
                        mAlert.dismiss();
                        Toast.makeText(getBaseContext(), "receive Completed!", Toast.LENGTH_SHORT).show();
                    }
                });
            }

            @Override
            public void onTransferRequested(int id, String path) {
                mFilePath = path;
                mTransId = id;
                Log.d("[USER] FILEACTION ONTRASFERREQUESTED", "TRANSFER ID: " + " || " + "PATH: " + path);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        AlertDialog.Builder alertbox = new AlertDialog.Builder(FTSampleProviderActivity.this);
                        alertbox.setMessage("Do you want to receive file: " + mFilePath + " ?");
                        alertbox.setPositiveButton("Accept",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface arg0, int arg1) {
                                        mAlert.dismiss();
                                        try {
                                        
                                            fileName = mFilePath.substring(nFilePath.lastIndexOf('/')+1);
                                            
                                            Log.d("[USER] FILENAME: ", fileName);
                                            
                                            mFTService.receiveFile(mTransId, DEST_PATH + fileName, true);//DEST_PATH
                                            
                                            Log.i("[USER]" + TAG, "sending accepted");
                                            showQuitDialog();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                            Toast.makeText(mCtxt, "IllegalArgumentException", Toast.LENGTH_SHORT).show();
                                        }
                                }
                        });
                        
                        alertbox.setNegativeButton("Reject",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface arg0, int arg1) {
                                        mAlert.dismiss();

                                        try {
                                            mFTService.receiveFile(mTransId, DEST_PATH, false);//DEST_PATH
                                            Log.i("[USER]" + TAG, "sending rejected");
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                            Toast.makeText(mCtxt, "IllegalArgumentException", Toast.LENGTH_SHORT).show();
                                        }
                                    }
                        });

Here's the instance of the ServiceConnection class that we initialized. Basically, these are just listeners for the onServiceDisconnected and onServiceConnected methods. On the onServiceDisconnected method it allows us to set our mFTService object to null. The onServiceConnected is called when the connection to the service has been established.

    private ServiceConnection mFTConnection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            //Log.d("[USER] mFTConnection", "FT CONNECTION LOST");
            mFTService = null;
            Log.d("[USER] mFTCONNECTION DISCONNECTED", mFTService.toString());
        }

        @Override
        public void onServiceConnected(ComponentName arg0, IBinder service) {
            Log.d(TAG, "FT CONNECTED");
            mFTService = ((LocalBinder) service).getService();
            Log.d("[USER] mFTCONNECTION CONNECTED", mFTService.toString() + " mFTService initialized as LocalBinder service "););
            mFTService.registerFileAction(getFileAction());
            Log.d("[USER] mFTCONNECTION CONNECTED", " mFTService register in Service File Action "););
        }
    };

Here we can see an if statement that checks if storage space is available. If no storage space is detected, the application simple finishes. Otherwise if storage space is available then we create a directory.

        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Toast.makeText(mCtxt, " No SDCARD Present", Toast.LENGTH_SHORT).show();
            finish();
            Log.d("[USER] FT ACTIVITY onCREATE", "FAILED TO DETECT EXTERNAL STORAGE";
        } else {
            mDirPath = Environment.getExternalStorageDirectory() + File.separator + "FTSampleProvider";
            Log.d("[USER] FT STORAGE DETECTED", mDirPath);
            
            File file = new File(mDirPath);
            
            if (!file.exists()) 
            {
                file.mkdirs();
                Log.d("[USER] FT DIRECTORY CREATED", "created " + file.getName());
            }
            
            Toast.makeText(mCtxt, " Stored in " + mDirPath, Toast.LENGTH_LONG).show();
        }

Let's check out the accessoryservices.xml file. The following allows us to communicate with the Tizen end:

<resources>
    <application name="FTSampleProvider">
        <serviceProfile 
            role="provider" 
            name="FTSampleProvider" 
            serviceTimeout="10" 
            serviceLimit="ANY" 
            version="1.0" 
            serviceImpl="com.samsung.accessory.FTSampleProvider.backend.FTSampleProviderImpl" 
            id="/System/FTSample">
            <supportedTransports>
                <transport type="TRANSPORT_BT"/>
            </supportedTransports>
            
            <serviceChannel 
                id="107" 
                reliability="enable" 
                priority="low" 
                dataRate="low"/>
    </serviceProfile>
    </application>
</resources>

Tizen

File Transfer is an efficient way to send and share files among interconnected devices. Using the Accessory File Transfer package, developers can create an application, such as voice recording, for the accessory device and share it to the host device. Any file size may be sent over all connectivity types supported by the Samsung Accessory framework.

Let's open our Tizen IDE. If you notice, there's a jquery-1.9.1.js in the js folder. That file is auto-generated when you create a jQuery template. Let's start off with the UI in index.html. If you analyze the code, it has created a button and a list:

<input type="button" class="ui-btn ui-inline" value="Connect" onclick="initialize();" style="width:100%;"/>
<div style="overflow-y:scroll; -webkit-overfolw-scrolling:touch; height:70%;">
    <ul class="ui-listview">
	</ul>
</div>

Next let's analyze sap.js. This file is responsible for the file transfer. Let's anazle the variable that have been declared. gSocket is initialized when SAP is initialized. gPeerAgent is initialized when PeerAgent is found. gChannel defined channel ID as the same as the android side. gAgent is analyzed with the first element that returns an agent's list. gUnavailable is simply a boolean flag, while gFileTransfer is initialized with the result of gAgent.getSAFileTransfer.

var gSocket = null;
var gPeerAgent = null;
var gChannel = 107;
var gAgent = null;
var gUnavailable = false;
var gFileTransfer = null;

PROVIDER_APP_NAME is the name of the android SAP provider application.

var PROVIDER_APP_NAME = 'FTSampleProvider';

There is a method named sapRequest it checks if the gSocket is connected. If it is, it sends data to the given channel.

function sapRequest(reqData, successCb, errorCb) {
	if (gSocket == null || !gSocket.isConnected()) {
		throw {
		    name : 'NotConnectedError',
		    message : 'SAP is not connected'
		};
	}

	gSocket.sendData(gChannel, JSON.stringify(reqData));

	gCurrentRequest = {
	    data : reqData,
	    successCb : successCb,
	    errorCb : errorCb
	}
}

Next we have ftCancel. This checks if gAgent, gFileTransfer, or gPeerAgent is null. If any of them are null, the method throws an error exception. The method also allows cancelling of the file being transferred.

function ftCancel(id, successCb, errorCb) {
	if (gAgent == null || gFileTransfer == null || gPeerAgent == null) {
		errorCb({
			name : 'NotConnectedError',
		    message : 'SAP is not connected'
		});
		return;
	}

	try {
		gFileTransfer.cancelFile(id);
		successCb();
	} catch (err) {
		console.log('cancelFile exception <' + err.name + '> : ' + err.message);
		window.setTimeout(function() {
			errorCb({
			    name : 'RequestFailedError',
			    message : 'cancel request failed'
			});
		}, 0);
	}
	
}

Next we have ftSend. It has the same checking mechanism as ftCancel for gAgent, gFileTransfer, and gPeerAgent. This is charge of initializing the transferId with the result of gFileTransfer.

function ftSend(path, successCb, errorCb) {
	if (gAgent == null || gFileTransfer == null || gPeerAgent == null) {
		errorCb({
			name : 'NotConnectedError',
		    message : 'SAP is not connected'
		});
		return;
	}
	
	try {
		var transferId = gFileTransfer.sendFile(gPeerAgent, path);
		successCb(transferId);
	} catch (err) {
		console.log('sendFile exception <' + err.name + '> : ' + err.message);
		window.setTimeout(function() {
			errorCb({
			    name : 'RequestFailedError',
			    message : 'send request failed'
			});
		}, 0);
	}
}

Finally we have ftInit. Basically, the whole chuck of this is for initialization of network communication elements and call backs.

function ftInit(successCb, errorCb) {
	if (gAgent == null) {
		errorCb({
		    name : 'NetworkError',
		    message : 'Connection failed'
		});
		return;
	}

	var filesendcallback = {
		onprogress : successCb.onsendprogress,
		oncomplete : successCb.onsendcomplete,
		onerror : successCb.onsenderror
	};
	
	try {
		gFileTransfer = gAgent.getSAFileTransfer();
		gFileTransfer.setFileSendListener(filesendcallback);
		successCb.onsuccess();
	} catch (err) {
		console.log('getSAFileTransfer exception <' + err.name + '> : ' + err.message);
		window.setTimeout(function() {
			errorCb({
			    name : 'NetworkError',
			    message : 'Connection failed'
			});
		}, 0);
	}
}

Now let's analyze the code of the main.js file. We temporarily commented out SOURCE_PATH but this helps us locate the file location of the data we want to send.

//var SOURCE_PATH = "/opt/usr/media/Sounds/Over the horizon.mp3"

Let's analyze the methods. Here we have toastAlert. It accepts a msg parameter, it clears the popupToastMsg div, and appends the msg. gear.ui.openPopup is used to display div as popup.

function toastAlert(msg) {
	$('#popupToastMsg').empty();
	$('#popupToastMsg').append(msg);
	gear.ui.openPopup($('#popupToast'));
	console.log(msg);
}

The showSendPage hides the main page and shows the send page. This sets the sendprogress to 0.

function showSendPage() {
	$('#main').hide();
	$('#sendPage').show();
	$('#sendprogress').attr('value', 0);
	console.log("SHOWSENDPAGE");
}

We now have showMain. This accepts a message parameter, hides the sendPage, shows the main page. If message is not defined a toast alert is shown displaying the content of the message. The value of gTransferId is set to 0.

function showMain(message) {
	$('#sendPage').hide();
	$('#main').show();
	if (message != undefined) {
		toastAlert(message);
	}
	gTransferId = 0;
	console.log("SHOWMAINPAGE" + ":Message: " + message);
}

Proceeding to the next method, updateContents finds the contents given a filter.

function updateContents() {
	try {
		tizen.content.find(function(contents) {
			$('#main ul').empty();
			if (contents.length > 0) {
				for(var i = 0; i < contents.length ; i++) {
					console.log('name : ' + contents[i].title + ' URI : ' + contents[i].contentURI);
					var nameStr = (contents[i].title.length > 15) ? (contents[i].title.substring(0, 11) + '...') : contents[i].title;
					$('#main ul').append(
					        '<li><a onclick="sendFile(\'' + contents[i].contentURI + '\');">' + nameStr
					                + '</a></li>');
				}
				$('#main ul').append('<li><a onclick="updateContents();">Update contents...</a></li>');
			} else {
				$('#main ul').append('<li><a onclick="updateContents();">No items. Update contents</a></li>');
			}
		}, function(err) {
			console.log('Failed to find contents');
		});
	} catch(err) {
		console.log('content.find exception <' + err.name + '> : ' + err.message);
	}
}

The initialize function calls sapInit and passes the result of ftInit.

function initialize() {
	var successCb = {
			onsuccess : function () {
				toastAlert('Succeed to connect');
			},
			onsendprogress : function (id, progress) {
				console.log('onprogress id : ' + id + ' progress : ' + progress);
				$('#sendprogress').attr('value', progress);
			},
			onsendcomplete : function (id, localPath) {
				$('#sendprogress').attr('value', 100);
				showMain('send Completed!! id : ' + id + ' localPath :' + localPath);
			},
			onsenderror : function (errCode, id) {
				showMain('Failed to send File. id : ' + id + ' errorCode :' + errCode);
			}
	};
	
	sapInit(function() {
		console.log('Succeed to connect');
		ftInit(successCb, function(err) {
			toastAlert('Failed to get File Transfer');
		});
	}, function(err) {
		toastAlert('Failed to connect to service');
	});
}

cancelFile() calls ftCancel passing the gTransferId. It is the inner function for success in file cancel, or error, or failed file cancel.

function cancelFile() {
	ftCancel(gTransferId,function() {
		console.log('Succeed to cancel file');
		showMain();
	}, function(err) {
		toastAlert('Failed to cancel File');
		console.log('CANCEL FILE ERROR CALLBACK : ' + gTransferId);
	});	
}

With sendFile it accepts a path parameter and calls ftSend placing the path as a parameter. A callback function handles file transfer success that initializes gTransferId for the past id parameter.

function sendFile(path) {
	ftSend(path, function(id) {
		console.log('Succeed to send file');
		gTransferId = id;
		showSendPage();
	}, function(err) {
		showMain('Failed to send File');
     	console.log('FT SEND FILE ERROR CALLBACK send failed: ' + err);
	});	
}

Look at the following two static functions. The first is a back EventListener to exit the application and load the main page and update the contents upon starting the application. The second binds the popup toast to automatically close after three seconds. This the window.gear.ui library.

(function() {
	window.addEventListener('tizenhwkey', function(ev) {
		if (ev.keyName == 'back') {
		    console.log("EXIT : " + ev.keyName);
			tizen.application.getCurrentApplication().exit();
		}
	});
	window.addEventListener('load', function(ev) {
		$('#sendPage').hide();
		$('#main').show();
		updateContents();
		console.log('ONPAGE LOAD FUNCTION');
	});
}());

(function(ui) {
    console.log('TOAST STATIC FUNCTION');
	var closePopup = ui.closePopup.bind(ui);
	var toastPopup = document.getElementById('popupToast');
	toastPopup.addEventListener('popupshow', function(ev){
		setTimeout(closePopup, 3000);
	}, false);
})(window.gear.ui);

Let's go back to index.html. If you notice the JavaScript files are loaded using the following code:

<!-- load javascript file for your application -->
<script type="text/javascript" src="js/jquery-1.9.1.js"></script>
<script type="text/javascript" src="js/sap.js"></script>
<script type="text/javascript" src="js/main.js"></script>
</html>

Then let's analyze serviceprofile.xml. Make sure that this file is in sync with AndroidManifest.xml.

<resources>
    <application name="FTSampleConsumer">
        <serviceProfile 
            role="consumer" 
            name="FTSampleConsumer" 
            serviceTimeout="30" 
            serviceLimit="ONE_PEERAGENT" 
            version="2.0" 
            id="/System/FTSample">
            <supportedTransports>
                <transport type="TRANSPORT_BT"/>
            </supportedTransports>
            <serviceChannel 
                id="107" 
                reliability="enable" 
                priority="low" 
                dataRate="low"> 
            </serviceChannel>
        </serviceProfile>
    </application>

</resources>

Open config.xml and you'll see the permissions needed.

<?xml version="1.0" encoding="UTF-8"?>
<widget viewmodes="maximized" version="1.0.0" id="http://yourdomain/FTSampleConsumer" xmlns:tizen="http://tizen.org/ns/widgets" xmlns="http://www.w3.org/ns/widgets">
    <tizen:application id="XIQbjfoxch.FTSampleConsumer" required_version="1.0" package="XIQbjfoxch"/>
    <content src="index.html"/>
    <feature name="http://tizen.org/feature/screen.size.normal.320.320"/>
    <icon src="icon.png"/>
    <tizen:metadata value="res/xml/serviceprofile.xml" key="AccessoryServicesLocation"/>
    <name>FTSampleConsumer</name>
    <tizen:privilege name="http://tizen.org/privilege/content.read"/>
<tizen:privilege name="http://developer.samsung.com/privilege/accessoryprotocol"/><tizen:setting hwkey-event="enable"/>
</widget>

Packing the File Transfer Application

File Transfer is an efficient way to send and share files among interconnected devices. Using the Accessory File Transfer package, developers can create an application, such as voice recording, for the accessory device and share it to the host device. Any file size may be sent over all connectivity types supported by the Samsung Accessory framework.

In this part we'll be testing the file transfer project and analyze the Tizen part of our application. Now let's package it.

Now that we have our applications, Android and Tizen respectively, we can start packaging our application together. In your Tizen application there will be a file name with a .wgt filename extension. Simply copy and paste it in the assets folder of your Android project. The reason for this is that the application we develop is an integrated type application which means that the application will be simultaneously installed in the Galaxy Gear device upon installing SAP service to the Android device.

Install the android application on your phone if you wish to test it on an actual gear device.

If you want to run it on an emulator, have your Samsung Android Smart Device connected via a USB cable and prepare the testing environment.

Then open the terminal and key in:

adb -d forward tcp:8230 tcp:8230

Remember to run FTSampleProvider first before launching the consumer and application.

I placed a hello.txt file instead of an MP4 file to tweak the application a little bit.

As you can see, both ends receive a toast.

For inquiries please post your questions on the official forum at http://developer.samsung.com.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

Developer.Samsung.com

United States United States
No Biography provided

Comments and Discussions

 
QuestionSamsung Accessory Protocol - Transfer files with incorrectly permission from phone to gear 2 device Pinmemberkeviner20041-Oct-14 22:33 
QuestionAbout methods PinmemberLMendoza1sv5-Jul-14 23:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150327.1 | Last Updated 3 Jul 2014
Article Copyright 2014 by Developer.Samsung.com
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid