Developer Tutorial - Android

Getting Started

Add RSOfflinePlayer.jar to your project and make sure it is linked in your IDE.

Authentication

Authentication for the SDK is handled by the host application and not the Rustici Engine. This is the most flexible solution for adding the SDK to existing applications as well as new projects. The host application will handle the user login and will provide any necessary authorization details to the SDK when required.

Offline Catalog

The Rustici Engine provides a simple offline catalog that will by default include any package that has been marked as available for offline use. Any courses that you wish to be used within the application will first need to be prepared using the Offline Exporter service. See the Preparing Content for Offline Playback document for more information on this process.

Using this offline catalog feature, however, will require some custom integration layer overrides. If you are already using an older custom integration, this may be the path you choose to take. If you are a newer integration only using Rustici Engine's REST API, then you should ignore Rustici Engine's catalog feature and create your own JSON for your mobile app to use for the offline catalog. The important part is to provide a way to download the exported packages and to have a proper ExternalRegistrationId that maps back to an existing registration on the Rustici Engine side.

In the method mentioned below for retrieving this default catalog, you will be required to pass along an authString value. This authString can be anything the host application needs it to be, from an encoded credentials string to a special token. This authString is one of the parameters available in the your integration layer’s GetOfflineCatalog or OfflineGetExternalRegistrationId overrides mentioned above. You will need to adjust the OfflineCatalog data based upon this authString. This will allow you to only display courses to a user which they should be allowed to access.

RSOfflinePlayer SDK provides a getOfflineCatalogWithConfiguration service that takes the authString and a few optional sorting and filtering options and returns a basic JSON object containing the data the host application needs to build the user’s catalog.

RSOfflineCatalog catalog = new RSOfflineCatalog(this,remoteServer,remotePathsForInit);
   final List<String> catalogList = new ArrayList<String>();
   catalog.getOfflineCatalogWithConfiguration("&",userName,10,0,null,new RSOfflineCatalog.getOfflineInterface() {
       @Override
       public void completionBlock(String s) {
           try{
               JSONObject jObject = new JSONObject(s);
               JSONArray statementArray = jObject.getJSONArray("offlineCatalog");
               for (int i=0; i < statementArray.length(); i++)
               {
                   try {
                       JSONObject catalogItem = statementArray.getJSONObject(i);
                       // Pulling items from the array
                       String catalogItemTitle = catalogItem.getString("title");
                       catalogList.add(catalogItemTitle);
                   } catch (JSONException e) {
                       // handle error
                   }
               }
               // update the listview here using catalogList for the adaptor
           }catch (Exception ex) {
             //handle exception
           }
       }

       @Override
       public void errorBlock(String s) {
           Log.d("error", "error : " + s);
       }
   });

Playing Content

To play the offline package, a RSOfflinePlayer object needs to be created by supplying a valid Rustici Engine ExternalPackageId, ExternalRegistrationId and ExternalConfiguration to the service along with the remote Rustici Engine URL and paths.

rsop.initWithPackageId(packageItem.packageId,"RegistrationApiId|1006!InstanceId|0", "", "12", kRemoteServerUrl, remotePathsForInit, remoteCredentialsForInit, isOnline,new RSOfflinePlayer.initPackageInterface(){
       @Override
       public void completionBlock() {
           Log.d("completion Block", "the url = " + rsop.packageLaunchUrl);
           WebView webView = (WebView) findViewById(R.id.myWebView);
           webView.getSettings().setJavaScriptEnabled(true);
           webView.getSettings().setSupportMultipleWindows(true);
           webView.getSettings().setDomStorageEnabled(true);

           MyJavaScriptInterface javaInterface = new MyJavaScriptInterface();
           webView.addJavascriptInterface(javaInterface, "injectedObject");
           webView.setWebChromeClient(new WebChromeClient() {
               public void onProgressChanged(WebView view, int progress) {
               }
               @Override
               public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, android.os.Message resultMsg)
               {
                   RelativeLayout contentContainer = (RelativeLayout) findViewById(R.id.contentContainer);
                   contentContainer.removeAllViews();

                   WebView childView = new WebView(mContext);
                   childView.getSettings().setJavaScriptEnabled(true);
                   childView.setWebChromeClient(this);
                   childView.setWebViewClient(new WebViewClient());
                   childView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));

                   contentContainer.addView(childView);
                   WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
                   transport.setWebView(childView);
                   resultMsg.sendToTarget();
                   return true;
               }
           });

           webView.setWebViewClient(new WebViewClient() {
               public void onPageStarted(WebView view, String url, Bitmap favicon) {
                   super.onPageStarted(view, url, favicon);
                   Log.d("onPageStarted","url is = " + url);
               }
               public void onPageFinished(WebView view, String url) {
                   super.onPageFinished(view, url);
                   Log.d("onPageFinished","url is = " + url);
                   if(url.contains("results.html"))
                   {
                       view.loadUrl("javascript:( function () { var resultSrc = getRuntimeXml(); window.injectedObject.getRuntimeXml(resultSrc); } ) ()");
                       view.loadUrl("javascript:( function () { var resultSrc = getRegistrationJson(); window.injectedObject.getRegistrationJson(resultSrc); } ) ()");
                   }
               }
               public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                   super.onReceivedError(view, errorCode, description, failingUrl);
                   // Do something here
               }
           });
           // load the package start page into the WebView
           webView.loadUrl("file:///" + rsop.packageLaunchUrl);
       }

       @Override
       public void errorBlock()
       {
           Log.d("failure Block", "error");
       }
   });

Syncing Results

The SDK relies on the host application to create a UIWebView and navigate to the rsop.packageLaunchUrl as the start page. When the host application wants to sync the course data, it grabs the current values from the UIWebView’s content and sends it to the SDK for syncing. The host application supplies the isOnline value to allow various definitions of ‘online’ to be determined by the host application.

 class MyJavaScriptInterface {
     public void getRuntimeXml(String jsResult) {
        runtimeXml = jsResult;
     }
     public void getRegistrationJson(String jsResult) {
         attemptInfo = jsResult;
         rsop.syncCurrentSessionWithAttemptInfo(attemptInfo,runtimeXml,null,isOnline,new RSOfflinePlayer.syncCurrentSessionInterface(){
             @Override
             public void completionBlock()
             {
                 Log.d("syncCurrentSessionWithAttemptInfo completion Block", "we're done");
             }
             @Override
             public void errorBlock()
             {
                 Log.d("syncCurrentSessionWithAttemptInfo errorBlock", "sad face");
             }
         });
     }
 }

Getting Current Status on Device

To get the current SCORM status for a registration, you can use the RSOfflinePlayer’s getCurrentStatus service. If you are offline, the most recent results from the local datastore are returned. If you are online, the data will be pulled from the Rustici Engine server and then checked against the latest local results to ensure the most recent or completed attempt is used.

rsop.getCurrentStatusWithCompletionBlock(new RSOfflinePlayer.getCurrentStatusInterface() {
     @Override
     public void completionBlock(Map<String, Object> stringObjectMap) {
         //display status
     }

     @Override
     public void errorBlock() {
         //handle error
     }
});

Deleting Local Content

The host application can use the SDK to delete content from the device when the user is finished with it and synced the results back to the Rustici Engine. Using RSOfflinePlayer’s deletePackage service will delete the package folder from the local filesystem as well as all of the session information in the datastore.

Special measures should be taken by the host application to not allow this for multi-user setups where one user could delete the package while another is still using it.

catalog.deletePackageWithCompletionBlock(itemToDelete, new RSOfflineCatalog.deletePackageInterface() {
       @Override
       public void completionBlock() {
           Log.d("deletePackage", "completionBlock");
       }
   });

results matching ""

    No results matching ""