65.9K
CodeProject 正在变化。 阅读更多。
Home

在三星 Gear 上使用配件文件传输

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.54/5 (4投票s)

2014年7月3日

公共领域

9分钟阅读

viewsIcon

22380

downloadIcon

249

本教程将分析三星Gear文件传输应用程序的创建过程。

下面的教程分析了三星Gear文件传输应用程序的创建过程。本课程共分为四部分。第一和第二部分展示了应用程序Android部分的开发。第一部分涉及应用程序的服务部分,而第二部分则涉及Activity部分的创建。第三部分展示了应用程序Tizen部分的开发。最后,第四部分演示了完整的三星Gear文件传输应用程序,这是一个由Tizen部分和Android部分组合而成的包。

Service

正如您所见,这就是三星附件文件传输SDK的架构。您的应用程序基本上通过附件文件传输JAR与SAP框架进行交互。让我们开始吧。为了创建三星Gear 2文件传输应用程序的Android端,我们需要以下内容:首先,请确保已安装带有Android开发工具的Eclipse。确保它已更新,包括Jelly Bean软件包。Jelly Bean是使用三星附件的最低要求。

我们还需要三个JAR文件,用于将SAP和SAP文件传输引入设备。

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

这些文件可以在三星移动SDK 1.5 beta归档文件中找到。另外,请确保您拥有运行Jelly Bean的三星Android智能设备。我们将使用的其他材料将在后续过程中讨论。

在使用我们的IDE之前,让我们简要了解三星附件文件传输的基础知识。

SAft类提供了三星附件文件传输包。它提供了初始化和检查支持情况的方法。这可以通过声明一个SAft实例对象并在try catch块中进行初始化来完成。

SAft accessoryfiletransfer = new SAft();

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

接下来是SAFileTransfer类,它提供了文件传输JAR实现。您还必须为接收方定义一个广播接收器,用于处理SAP文件传输请求,以便接收文件。它包含四个主要方法:sendreceiverejectcancel,如下面的代码所示。

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

...

mSAFileTransfer.send(peerAgent, filename)

...

mSAFileTransfer.receive(transID, path)

...

mSAFileTransfer.reject(transID);

...

mSAFileTransfer.cancel(transID);

最后是SAFileTransfer.EventListener。这个监听器是接收文件传输更新事件的接口。它包含三个方法,用于协助文件传输进度。它们是:onTransferRequestedonProgressChangedonTransferCompleted

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) {}

要了解三星附件文件传输的工作原理,您可以下载三星Gear下的文件传输示例:http://developer.samsung.com/samsung-gear

让我们分解一下这个应用程序。如前所述,我们将重点关注应用程序的服务部分。首先,检查库是否完整。

现在让我们开始分析代码。在FTSampleProviderImp.java文件中,您可以看到我们在onCreate方法中创建并初始化了SAft的一个实例,如下所示。

        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;
        }

我们还在onCreate方法中初始化了一个EventListener的实例。

        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));
            }

        };

主要地,EventListener是用于接收文件传输更新事件(如onProgressChangedonTransferCompletedonTransferRequested)的监听器接口。一旦我们初始化了SAFileTransfer的实例,我们就会使用它,该实例位于onCreate方法的底部。

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

另一件需要注意的事情是,我们创建了一个嵌套类,它扩展了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);
        }
    }

我们还创建了一个扩展了SASocket的类FTSampleProviderConnection的实例。这代表了服务提供者和使用者之间服务连接的一个实例。

    private FTSampleProviderConnection mConnection = null;

mConnection在重写的SA Agent函数onServiceConnectionResponse中使用。基本上,如果错误代码为0,FTSampleProviderConnection将初始化为uSocket。它被转换为FTSampleProviderConnection。如果uSocket不为null,则mConnection被初始化为FTSampleProviderConnection,并显示一个Toast消息,通知已建立连接。

    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());
            }
        }
    }

您还应该拥有FileAction接口。当我们在项目中创建Activity部分时,这部分将在稍后提供功能。

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

现在让我们打开AndroidManifest.xml。不要忘记添加这些。

<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

三星附件框架促进了附件设备与三星智能设备之间的互联。它效率高且易于使用,因为它与各种网络设置兼容。配备三星附件框架的三星智能设备支持多种服务,主要用于附件设备的文件传输。

如果您打开ft_provider_activity.xml,您会看到一个进度条。我们将使用它作为UI,用于向用户显示传输的当前进度。

<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"/>

让我们看一下FTSampleProviderActivity.javaTAG变量将用于日志记录,而目标路径将用于定位我们将要放置从可穿戴设备传输过来的文件的位置。

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

让我们检查一下showQuitDialog()。在此,该方法显示一个带有取消文件传输的否定按钮的警报配置文件对话框。

    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();
            }
        });
    }

让我们看一下getFileAction方法。在onError中,它允许我们关闭传输警报对话框,并显示一条Toast消息,说“传输已取消”,并附带错误消息。onProgress方法允许我们通过设置进度来更新UI进度条。onTransferComplete方法通知传输完成,将进度重置为0,关闭传输警报对话框,并提供一条Toast消息,说“接收完成!”

onTransferRequested方法由可穿戴设备调用,并在UI线程上运行。它显示一个警报框,通知用户文件传输是否已被接收。它有两个交互式按钮:PositiveButton用于通过调用FTService.receiveFile来接受文件,NegativeButton用于拒绝文件。这两个值的主要区别在于提供的布尔值,即true和false标志。

    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();
                                        }
                                    }
                        });

这是我们初始化的ServiceConnection类的一个实例。基本上,这些只是onServiceDisconnectedonServiceConnected方法的监听器。在onServiceDisconnected方法中,它允许我们将mFTService对象设置为null。onServiceConnected在与服务建立连接时被调用。

    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 "););
        }
    };

在这里,我们看到一个if语句,它检查存储空间是否可用。如果没有检测到存储空间,应用程序将简单地结束。否则,如果存储空间可用,我们就会创建一个目录。

        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();
        }

让我们看一下accessoryservices.xml文件。以下配置允许我们与Tizen端进行通信。

<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

文件传输是一种在互连设备之间发送和共享文件的有效方式。使用附件文件传输包,开发人员可以为附件设备创建应用程序(例如语音录音),并将其共享给主机设备。任何文件大小都可以通过三星附件框架支持的所有连接类型进行发送。

让我们打开我们的Tizen IDE。如果您注意到,在js文件夹中有一个jquery-1.9.1.js文件。该文件在创建jQuery模板时是自动生成的。让我们从index.html中的UI开始。如果您分析代码,它创建了一个按钮和一个列表。

<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>

接下来,让我们分析sap.js。此文件负责文件传输。让我们分析声明的变量。gSocket在SAP初始化时初始化。gPeerAgent在找到PeerAgent时初始化。gChannel定义了与Android端相同的通道ID。gAgent通过返回代理列表的第一个元素进行分析。gUnavailable只是一个布尔标志,而gFileTransfer则通过gAgent.getSAFileTransfer的结果进行初始化。

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

PROVIDER_APP_NAME是Android SAP提供者应用程序的名称。

var PROVIDER_APP_NAME = 'FTSampleProvider';

有一个名为sapRequest的方法,它检查gSocket是否已连接。如果已连接,它会将数据发送到指定的通道。

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
	}
}

接下来是ftCancel。它会像ftCancel一样检查gAgentgFileTransfergPeerAgent是否为null。如果其中任何一个为null,该方法将抛出错误异常。该方法还允许取消正在传输的文件。

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);
	}
	
}

接下来是ftSend。它具有与ftCancel相同的检查机制,用于gAgentgFileTransfergPeerAgent。它负责使用gFileTransfer的结果初始化transferId

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);
	}
}

最后是ftInit。基本上,这整个部分是用于初始化网络通信元素和回调的。

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);
	}
}

现在让我们分析main.js文件的代码。我们暂时注释掉了SOURCE_PATH,但这有助于我们定位要发送数据的文件的位置。

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

让我们分析这些方法。这里我们有toastAlert。它接受一个msg参数,清除popupToastMsg div,并追加msggear.ui.openPopup用于将div显示为弹出窗口。

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

showSendPage隐藏主页并显示发送页。这会将sendprogress设置为0。

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

现在我们有了showMain。它接受一个message参数,隐藏sendPage,显示主页。如果未定义message,则显示一个Toast警报,显示消息的内容。gTransferId的值设置为0。

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

继续下一个方法,updateContents根据过滤器查找内容。

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);
	}
}

initialize函数调用sapInit并将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()调用ftCancel并将gTransferId作为参数传递。这是用于文件取消成功、错误或文件取消失败的内部函数。

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);
	});	
}

使用sendFile,它接受一个path参数并调用ftSend,将路径作为参数。回调函数处理文件传输成功,该函数使用过去的id参数初始化gTransferId

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);
	});	
}

查看以下两个静态函数。第一个是用于退出应用程序并加载主页以及在应用程序启动时更新内容的返回EventListener。第二个是将弹出Toast绑定为在三秒后自动关闭。这是window.gear.ui库。

(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);

让我们回到index.html。如果您注意到JavaScript文件是使用以下代码加载的。

<!-- 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>

然后,让我们分析serviceprofile.xml。确保此文件与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>

打开config.xml,您将看到所需的权限。

<?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>

打包文件传输应用程序

文件传输是一种在互连设备之间发送和共享文件的有效方式。使用附件文件传输包,开发人员可以为附件设备创建应用程序(例如语音录音),并将其共享给主机设备。任何文件大小都可以通过三星附件框架支持的所有连接类型进行发送。

在这一部分,我们将测试文件传输项目并分析我们应用程序的Tizen部分。现在让我们进行打包。

现在我们有了Android和Tizen应用程序,我们可以开始打包我们的应用程序了。在您的Tizen应用程序中,将有一个扩展名为.wgt的文件。只需将其复制并粘贴到Android项目的assets文件夹中。这样做的原因是,我们开发的应用程序是一种集成类型应用程序,这意味着在Android设备上安装SAP服务时,该应用程序将同时安装在Galaxy Gear设备上。

如果您希望在实际的Gear设备上测试,请将Android应用程序安装到您的手机上。

如果您想在模拟器上运行它,请通过USB线连接您的三星Android智能设备,并准备测试环境。

然后打开终端并输入:

adb -d forward tcp:8230 tcp:8230

请记住,在启动消费者应用程序之前,先运行FTSampleProvider。

我放了一个hello.txt文件而不是MP4文件,以便稍微调整一下应用程序。

正如您所见,双方都收到了Toast消息。

如有疑问,请在官方论坛提问:http://developer.samsung.com

© . All rights reserved.