Resources

MY CLOUD HOME

Build your First Device App

This section will guide you to build your first My Cloud Home App using Device SDK with step-by-step instructions.

Requirements

Here are some requirements that must be met when using My Cloud Home SDK:

  1. You must create a class that implements MyCloudUIServer.java (UI part of device app) and overrides its mandatory methods.
  2. You must create a service named as StartupService.java that will extend BaseStartupService.java and should be located in the root source package.

Creating Sample App

This sample app asks for a folder name from the user and creates a folder on the Device with the same name. This app also downloads few pictures from the Internet to the newly created folder. 

You can find the complete source code of this sample app in the Downloads section.

Before you begin

  1. Execute the Hello World app to get a brief experience of My Cloud Home device application. Go through the source code of the Hello World app from the Downloads page to get the basic understanding of the application structure.
  2. Disable Instant Run in Android studio.

    To prevent Android Studio from automatically starting the current activity on code changes, you have to disable some default settings. Follow the steps below to disable "Instant Run" and "Automatic Activity Restart" in Android studio:

    1. Open the Settings or Preferences dialog.
    2. Navigate to Build, Execution, Deployment → Instant Run.
    3. Uncheck the box next to Enable Instant Run.
    4. Uncheck the box next to Restart activity on code changes.

    Refer https://developer.android.com/studio/run/index.html to get more information.

Step 1: Create an Android project 

Create an Android project with the desired app name and package. Click Next.

Step 2: Select API Level

Select the appropriate minimum version of Android as API level 23. This is a crucial step as the Device is built on a specific Android version. You should also make your app for the same android version to avoid any API compatibility issues. Click Next.

Step 3: Create an empty activity

Select an empty activity to add to the emulator. A class will be created based on the Activity Name entered. Click on the Finish button. The project gets created.

Note  This activity screen is to emulate the My Cloud Home Device App in an emulator. You are not required to create an activity in the actual device app.

Step 4: Add the .aar file to the project

Get the latest My Cloud Home SDK .aar file from the Downloads page and add it to your project. Follow the steps below to add a .aar file:

  1. Navigate to File → New → New Module.

Select "Import .JAR/.AAR Package" and click Next.

  1. Enter the path to the .aar file and click Finish.
  2. Go to File → Project Structure.
  3. Under Modules, in the left menu, select app.
  4. Click on the Dependencies tab and then click the "+" in the lower left corner.
  5. Select Module Dependency.

Click OK.

Select the new module from the list.

Step 5: Compile and Sync your project

Follow the steps below to compile and sync your project:

  1. Add nanohttp dependency in app's build.gradle.

    nanohttpd-webserver can serve any local directory as a webserver using nanohttpd. nanohttpd is a light-weight HTTP server designed for embedding in other applications.

    compile'org.nanohttpd:nanohttpd-webserver:2.3.1'

  2. Click Sync Project with Gradle Files  in the menu bar to sync your project.

Step 6: Create a new class

When you created your project, Android studio may have created few files like MainActivity.java and main_layout.xml. Leave all these default classes and layout resources untouched for now. We will come to that later

Create a new class in the same package as your MainActivity.java. Extend MyCloudUIServer.javaand implement its abstract methods and its constructor. This class will act as the UI interface for your application.

public class SampleDeviceAppUI extends MyCloudUIServer {
    private static String TAG = SampleDeviceAppUI.class.getName();
    private Context mContext;    
    public SampleDeviceAppUI(Context context) {
        super(context);
        mContext = context;
    }    
    @Override
    public Response get(IHTTPSession session) {
        return null;
    }    
    @Override
    public Response post(IHTTPSession session) {
        return null;
    }
}

Step 7: Get the landing page

SampleDeviceAppUI your application's UI. Override get() method of class SampleDeviceAppUIto get the landing page of your app. When a user connects to your app from the browser, My Cloud Home SDK will call this method for obtaining the landing page of your app. Refer to the Interactive UI using GET and POST section for more information on how to make interactive UI using HTTP GET and POST.

private String getViewSource() {
    try {
        StringBuilder builder = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(mContext.getAssets().open("index.html")));
        String line = "";
        while ((line = reader.readLine()) != null) {
            builder.append(line);
        }
        reader.close();
        return builder.toString();
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        return "<h1>Sorry index.html cannot be loaded</h1>
";
    }
}

Follow the steps below to create an HTML landing page:

  1. Create an HTML file index.html in asset folder.

Update the index.html with the following content.

<html>
<head></head>
<body><h1>Welcome to My Cloud Home Device SDK App</h1>
<h2>Please enter a folder name and the app will automatically download nice pictures from internet
    to that folder</h2>
<form action="/" method="post">
    <input type="text" name="folder_name"><br/><br/>
   <input type="submit" value="Create">
</form>
</body>
</html>
  1. Create a utility method to read the HTML content from asset folder.

  2. Update your get() method to return the HTML content.

@Override
public Response get(IHTTPSession session) {
    String htmlSource = getViewSource();
    Log.d(TAG, "HTML source:" + htmlSource);
    return newFixedLengthResponse(htmlSource); 
}

The First Device sample app shows only one HTML page for the app, but typically you may have multiple HTML pages for different screens such as signup, user management, and settings. Refer Useful Tips for information on how the multi-page navigation works.

The HTML communication between the browser and app requires access_token for authentication and authorization. Hence, when you test your app on the device, you will need an access_token in every HTTP communication between the browser and the device. Refer Working with Real Device for more information. 

Step 8:  Using get() and post() method

To build an interactive UI for this application, we have to use both the get() and post() methods to complete the communication cycle. Override the post() method of SampleDeviceAppUI.java.

Since this sample app is using HTML form POST action to submit the folder name, you need to handle it in post() method. Refer to the HTTP Post Handling section of Useful Tips.

In this app, the folder_name is passed in form post body (refer step #7 above). The code below retrieves the folder name from post body.

@Override
public Response post(IHTTPSession session) {
    try{
        Map<String, String> files = new HashMap<String, String>();
        session.parseBody(files);
        String postBody = session.getQueryParameterString();
        Log.d(TAG, "##### Post Body:" + postBody);
        String folderName = null;
        String[] keyValues = postBody.split("=");
        if (keyValues != null){
            folderName = keyValues[1]; // Since there is only one key value pair.
            Log.d(TAG, "##### Folder Name:" + folderName);
            return newFixedLengthResponse("<h1>Folder name successfully retrieved.</h1>");
        }
    }catch (Exception e) {
        Log.d(TAG, "##### POST handling exception " + e.getMessage());
    }
    return newFixedLengthResponse("<h1>Sorry, folder name cannot be retrieved.</h1>");
}

Step 9: Create StartupService

As mentioned in the Conventions section, app's starting point is StartupService. To implement this service in your app, the service name MUST be StartupService.

Create a new class StartupService that extends the BaseStartupService. The StartupService class MUST be located at the application package root folder.

Observe that in the example code, app package name is com.wdc.mycloud.firstdeviceapp and the StartupService is located at /app/src/main/java/com/wdc/mycloud/firstdeviceapp/.

import com.mycloud.devicelib.BaseStartupService;
import com.mycloud.devicelib.MyCloudUIServer;
 
public class StartupService extends BaseStartupService {
    @Override
    public MyCloudUIServer createMyCloudUIServer() {
        return null;
    }
}

Step 10: Update StartupService

To complete the StartupService, you need to do the following:

  • Initialize UI service SampleDeviceAppUI() built in step #7 and #8.

  • Implement onStartCommand() method of the service. When My Cloud Home platform starts the application, it calls onStartCommand() with "MyCloudId" in the intent as an extra.

public class StartupService extends BaseStartupService {
    /**
     * App must override this method and return the UI server instance.
     * @return
     */
    @Override
    public MyCloudUIServer createMyCloudUIServer() {
        return new SampleDeviceAppUI(getApplicationContext());
    }
    /**
     * This onStartCommand() method will be called for each user in the My Cloud Home device with different mycloud user-id.
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "StartupService onStartCommand.... ");
        if(intent.getAction() != null) {
            Log.d(TAG, "Action-->"+ intent.getAction());
        }
        if(intent.getExtras() != null) {
            Log.d(TAG, "StartupService extras-- >"+ intent.getExtras().getString("MyCloudId"));
        }
        return super.onStartCommand(intent, flags, startId);
    }
}

ENSURE to add this new service's entry in application manifest file. You must add action android:name="START" in the manifest to make it a startup service.

<service
android:name=".StartupService"
android:exported="true">
    <intent-filter>
        <action android:name="START"/>
    </intent-filter>
</service>

Step 11: Invoke StartupService

To start StartupService for testing purpose, you can call this service from our MainActivity. In the actual device, this service is automatically started by My Cloud Home platform. 

Modify your MainActivity.java to invoke StartupService.

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
 
      Intent intent =new Intent(this, StartupService.class);
      intent.putExtra("MyCloudId","1234"); // dummy mycloud user id
      MainActivity.this.startService(intent);
    }
}

Step 12: Run the app on a simulator or your android phone

When you run the app in the simulator, you will see your activity on the phone's screen (whatever you had written in your layout.activity_main). Verify whether StartupService is started by checking adb logs.

D/com.wdc.mycloud.firstdeviceapp.StartupService: StartupService....
E/MyCloudUIServer: Full url::: ->http://localhost/sdk/v1/apps/com.wdc.mycloud.firstdeviceapp
D/com.wdc.mycloud.firstdeviceapp.StartupService: StartupService onStartCommand.... 
D/com.wdc.mycloud.firstdeviceapp.StartupService: StartupService Action-->START 
D/com.wdc.mycloud.firstdeviceapp.StartupService: StartupService extras My Cloud ID -- >1234

Open the browser on your phone/tablet and type http://localhost:9090. You will see the UI of your first app.

Enter folder name. Click Create. The following screen is displayed.

Observe adb logcat, you will find the folder name printed in logcat.

D/com.wdc.mycloud.firstdeviceapp.SampleDeviceAppUI: ##### Folder Name sample_folder

Step 13: Understand the business logic

As explained earlier, First Device app will take a folder name from the user, create a folder with that name on My Cloud Home device, and download few random pictures from the internet to the newly created folder.

To separate the UI and Business logic, follow the steps:

  1. Make a business logic class as service. Create a new service ‘DownloaderService’ (you can give any name) that extends Service or IntentService.
public class DownloaderService extends IntentService {
    public DownloaderService() {
        super("name");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        final String action = intent.getAction();
        return Service.START_STICKY;
    }
    @Override
    protected void onHandleIntent(Intent intent) {
    }
}

Do not forget to add this new service entry in Android manifest file.

<service
 android:name=".DownloaderService"
 android:exported="false"></service>

Now that you have the business logic service (DownloaderService) in place, you have to call it from the UI. Recall that you retrieved the folder name in SampleDeviceAppUI.post()method (refer step #8 above). Create a simple method to invoke the business logic from UI service. 

private void startBusinessLogic(String folderName, String myCloudUser){
    Intent i = new Intent(mContext, DownloaderService.class);
    i.setAction(ACTION_TASK);
    i.putExtra(KEY_FOLDER_NAME, folderName); // key string for KEY_FOLDER_NAME
    i.putExtra(KEY_MYCLOUD_USER, myCloudUser); // key string for KEY_FOLDER_NAME
    mContext.startService(i);
}

Call the new startBusinessLogic() method from your post() method of SampleDeviceAppUI.java.

@Override
public Response post(IHTTPSession session) {
    try{
        Map<String, String> files = new HashMap<String, String>();
        session.parseBody(files);
        String postBody = session.getQueryParameterString();
        Log.d(TAG, "##### Post Body:" + postBody);
        String folderName = null;
 
        String[] keyValues = postBody.split("=");
        if (keyValues != null){
            folderName = keyValues[1]; // Since there is only one key value pair.
            Log.d(TAG, "##### Folder Name:" + folderName);
            startBusinessLogic(folderName, getMyCloudUserId(session));
            return newFixedLengthResponse("<h1>Folder name successfully retrieved.</h1>");
        }
    }catch (Exception e) {
        Log.d(TAG, "##### POST handling exception " + e.getMessage());
    }
    return newFixedLengthResponse("<h1>Sorry, folder name cannot be retrieved.</h1>");
}

Since the app is running only one instance for all users, you need to find which user actually made the call. My Cloud Home SDK provides a convenient utility API,getMyCloudUserId() to find the user id of the invoking user.

Update onStartCommand() of DownloaderService, which you created in step #10, to take folder name and user-id being passed from the UI service.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    final String action = intent.getAction();
    if (ACTION_TASK.equals(action)) {
        final String folderName = intent.getStringExtra(KEY_FOLDER_NAME);
        final String userName = intent.getStringExtra(KEY_MYCLOUD_USER);
    }
    return Service.START_STICKY;
}

Step 14: Create a folder on the device

Now that the business logic class got the folder name, you can create a new folder on the My Cloud Home device. 

As mentioned in the Regular Android App vs. Device App section, the device will be shared by different users. For example, family members use different My Cloud accounts to share the same My Cloud Home device. Ensure that the folder is created in the right user's space.

Instead of using Environment.getExternalStorage() to locate the root folder, useMyCloudUIServer.getRootFolder(context, userName).

Create a new method createFolder() and call it from onStartCommand() of DownloaderService.

private File createFolder(String folderName, String userName) {
    File newFolder = null;
    try {
        newFolder = new File(MyCloudUIServer.getRootFolder(DownloaderService.this, userName) + File.separator + folderName);
        if (!newFolder.exists()) {
            newFolder.mkdir();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return newFolder;
}

On creating the folder, you can download the pictures from the internet and write them into the folder, as you do in a normal android application.

 

Handling Permissions

  • External Storage Permission: If you are running your app on a simulator or android phone (Not on the My Cloud Home Device), then you have to add the write external storage permission to the app. When the app is running on simulator/phone, the SDK will use phone's external storage folder as My Cloud Home storage.

  • private <manifest xmlns:android=
    "http://schemas.android.com/apk/res/android" package=
    "com.wdc.mycloud.firstdeviceapp"> <application .... </application> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
    </uses-permission> </manifest>{
    
  • Explicit permission: With the latest change in Android, permissions have to be explicitly granted by the user. Adding <uses-permission> in the manifest will not grant the permission by default, but you have to make a permission request. The user can grant permission by going to Settings → Apps → Permissions.

Running on Device

This section will guide you on running your device app on the Emulator, Android phone/tablet, and on My Cloud Home device.

You can use an emulator to quickly run and test your apps and make changes while developing. My Cloud Device SDK is tested and verified on the emulator and ensures the testing of all the APIs.

Running on Android Emulator

You can run standard Android apps in the emulator without any issues. My Cloud Device SDK library internally handles both the real My Cloud Home Device and Emulator.

  • Ensure that you have created an AVD configured for API level 23.
  • For running in the emulator, create a starting activity StartupService (as explained in Step #9 above) that can be triggered by Android studio itself. Refer to the Hello World and Creating Sample App section to get a complete understanding of running an app from an Android studio.
  • You can see your application logs in adb logcat similar to any other Android application.
  • You can set breakpoints anywhere in your program similar to any other Android application.
  • The MyCloudUIServer.getRootFolder() API call in the emulator will give a mounted data folder for the app. Test your application for file read/write with the simulator.

 

Running on Android Phone/Tablet

Running the application on Android phone/tablet is similar to running on the emulator. It will enhance your testing especially when your app needs to do a number of network calls.


Running on My Cloud Home Device

Running on a real My Cloud Home device require few additional steps than a normal android app.

  • Download ‘Device App Installation Utility Script’ from the Downloads section. 
  • Make sure that your computer and My Cloud Home are on the same local network.
  • Run the script from any folder. 
  • root$ python install_script.py
    =========================================================
    My Cloud Home Device App Installation/Uninstallation Tool
    =========================================================
    
  • Enter username and password of your My Cloud Home account.
  • Please enter email: developer@mycloud.com
    Password: *******
    Successfully authenticated
    
  • Select Install or Uninstall option.
  • Please enter 1 for install, 2 for uninstall: 1
    
  • Provide application information.
  • Please enter package name: com.mycompany.app
    Please enter apk path: /Users/root/Projects/SampleApps/myapp/myapp.apk
    Please wait, app is being installed
    
  • Copy the app URL to any browser and you can see your application's UI.
  • You can now launch the app with the following URL: 
    http://<ip_address>/sdk/v1/apps/com.mycompany.myapp/proxy/?redirect-ip=
    http://<ip_address>/sdk/v1/apps/com.mycompany.myapp/proxy/&access_token=eyJ0eXA...
    

WORKING WITH REAL DEVICE

This section covers some essential topics, which will help you in testing the app on the My Cloud Home device and later with the My Cloud App catalog. Most of these topics may not be required when testing the app on Emulator or their Android phones.

 

App Entry Point

A typical Android app's entry point is an 'Activity' with intent-filter "android:name=android.intent.category.LAUNCHER", but My Cloud Home device app's entry point is the ‘StartupService’ with intent-filter android:name="START".

<service android:name=".StartupService" android:exported="true">
    <intent-filter>
        <action android:name="START">
    </action></intent-filter></service>

App Initiation

When an app is installed on the My Cloud Home device by any user, it invokes StartupService of your application and calls the onStartcommand(). For example, If user A installs Dropbox device app on his/her My Cloud Home device, onStartcommand() will be called with “myCloudId” in the Intent. You can get the mycloudId and initialize the settings such as setting up configuration table, creating an entry for a user, etc.

String myCloudUserId = intent.getExtras().getString("MyCloudId")

Restarting My Cloud Home Device

When the My Cloud Home device restarts, it invokes StartupService of your application and calls onStartcommand() for each user who has installed your app. For example, if User A and User B have installed Dropbox app on their My Cloud Home device, the onStartcommand() will be called twice with different MyCloudId in the Intent.

String myCloudUserId = intent.getExtras().getString("MyCloudId")


Access Token and Base URL

The HTML communication between the browser and My Cloud Home app requires Access Token to ensure the communication is authenticated. If your app expects some sort of configuration information from the user through the web UI, then you must include access_token as a request parameter in all your HTTP calls to My Cloud Home device. You will get access_token and base_url in every get() and post() method of your extended MyCloudUIServer class.

Refer Interactive UI using GET and POST section to get more information on attaching Access Tokens.

@Override
public Response get(IHTTPSession ihttpSession) {
String token = getAccessToken(ihttpSession);
String baseUrl = getBaseUrl(ihttpSession);
...
}

MyCloud User ID

At times, your app may need to know which My Cloud Home user initiated the specified request. My Cloud Home SDK provides a convenient utility method getMyCloudUserId that can be used in get() and post() methods of your MyCloudUIServer extended class.

@Override
public Response get(IHTTPSession ihttpSession) {
String myCloudID = getMyCloudUserId(ihttpSession);
...
}

Read Write Permission

You MUST NOT use write/read permissions in the manifest. Usually, this permission is required to read/write content to external SD card. However, with My Cloud Home device, this permission is implicit for all apps. All the apps will have the permission to read/write content to the space allocated to the given user.

"android.permission.WRITE_EXTERNAL_STORAGE" and
"android.permission.READ_EXTERNAL_STORAGE"

Note  The app catalog will reject apps if it contains android.permission.WRITE_EXTERNAL_STORAGE.

 

 

< < Previous   Next >>

Do more with the
My Cloud Home On-Device SDK

Click the buttons below to visit the My Cloud Home On-Device Developer Home & Workflow pages

 

Developer
Home

Get started, access the SDK, build your apps, & learn more


View Home

Developer
Workflow

Register & submit your app for review and contact support


View Workflow