Updating Aviary’s System Permissions for Marshmallow

Posted on Friday, January 29th, 2016 by Zak Reik
Category: Android

With the release of Android Marshmallow (API 23), system permissions underwent a significant set of changes. Users now gain runtime control over individual permission requests, allowing them to deny access to important system capabilities that apps rely on.

Imagine an app that needs permission to use your calendar, contacts, and location. Now, what if a user denies access to a single one of these? What if they deny access to two of them?

Developers now have to handle all of these possible combinations if they want to properly support phones running Marshmallow. In this post, I will show a simpler case of how we have chosen to address this problem for the Adobe Aviary app and the Creative SDK Image Editor.

Aviary for Android gallery

Aviary for Android

Contents

“Normal” vs “Dangerous” Permissions

Prior to the Marshmallow release, Aviary was only requesting three permissions that required user approval:

<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

With the update to Android 6.0, there were changes that affected these specific permissions. In the screenshots below, you can see our permissions details both before and after our upgrade to support Marshmallow.

Android Marshmallow permission details comparison

Pre-Marshmallow and Marshmallow permission details

Let’s have a look at how these permissions have changed for Android 6.0.

The billing permission

The billing permission is used in Aviary for in-app purchases and is needed for buying items from the Aviary Supply Shop.

As of Marshmallow, in-app billing no longer requires user-granted permission. It is now classed as protectionLevel="normal".

All normal permissions do not require explicit action from the user, due to being relatively low risk with regards to privacy and security. Aviary uses a number of these permissions like android.permission.INTERNET for accessing the supply shop and the discover tab, or android.permission.NFC for sending an image to a nearby device.

The in-app purchase permission happens to fall outside of these groups because it is not a system permission. It is a custom permission declared in package com.android.vending (the Google Play Store). However, all custom permissions must declare a protectionLevel attribute, classifying them as either dangerous or normal. The com.android.vending.BILLING permission is declared normal, so it behaves exactly like a normal system permission would.

The change to billing required no work on our end. Permission will always be granted, with no prompt to the user.

The storage permissions

The reading from and writing to external storage, however, are another story. Both of these permissions are classified as protectionLevel="dangerous" and require checking at runtime anywhere they are invoked.

External storage permissions are absolutely essential to the image editor. Without storage access, photos from a user’s image gallery cannot be loaded and any image edits cannot be saved outside of the app’s private folder.

Handling Permissions at Runtime on Marshmallow

The ability to read from and write to storage are both essential for basic use of the Aviary app. When the editor is opened, read permission is required to get access to user photos. When an edit is finished, write permission is needed to save the file.

We also have to account for the Image Editor being launched via an Intent from another app like Google Photos. In addition, the Aviary Supply Shop requires write access to save any downloaded content.

It would be great if this meant we could just request storage permission when the app first opens. But since this new system grants the user control over permissions at any time, requesting when the app first opens would not be enough. Users might grant permission, then while the app is still open go into Settings, turn the permission off and return to the app.

Because of this, a single request point cannot work. Checks must be made more strategically.

Checking for storage permissions

Our first step in handling this was to make a utility class to cut down on redundant code.

Here is the PermissionUtils class we built to facilitate checking for storage permission throughout our code base:

public final class PermissionUtils {
    private PermissionUtils() { }

    public static boolean hasRequiredStoragePermissions(final Context context) {
        return ContextCompat.checkSelfPermission(context, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED;
    }

    public static void requestStoragePermissions(final Fragment fragment, final int requestCode) {
        fragment.requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, requestCode);
    }

    public static void requestStoragePermissions(final AppCompatActivity activity, final int requestCode) {
        activity.requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, requestCode);
    }

    public static boolean storagePermissionGranted(@NonNull final String[] permissions, @NonNull final int[] grantResults) {
        return permissionGranted(permissions, grantResults, new String[]{READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE});
    }

    public static boolean permissionGranted(
        @NonNull final String[] permissions,
        @NonNull final int[] grantResults,
        @NonNull String[] requestedPermissions) {

        for (int i = 0; i &lt; permissions.length; i++) {
            String permission = permissions[i];

            for (String requested : requestedPermissions) {
                if (requested.equals(permission)) {
                    return grantResults[i] == PERMISSION_GRANTED;
                }
            }
        }
        return false;
    }
}

This class encompasses many of the steps needed when checking and requesting permissions (as described in the official docs). So now when we need to check for storage permission, the code is simplified.

One of the places where we decided to request permission is the onStart() method in the GridFragment, the first fragment that the user sees when opening Aviary:

    @Override
    public void onStart() {
        super.onStart();

        // check if we have access to external storage already
        if (!PermissionUtils.hasRequiredStoragePermissions(getContext())) {
            // we don&#039;t have permission, so check if it has been previously denied
            if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
                // we were previously denied access, so time to update the UI to reflect this
                showNoPermissionsOverlay();
            } else {
                // we haven&#039;t been denied permission yet, so let&#039;s make the request.
                hideNoPermissionsOverlay();
                PermissionUtils.requestStoragePermissions(this, STORAGE_PERMISSIONS_REQUEST_MAIN);
            }
            return;
        } else {
            // we already have permission so just proceed normally
            hideNoPermissionsOverlay();

            // ...
        }

        // ...
    }

The sequence here is straightforward:

  1. First we check if permission was already granted, and if so we proceed with loading the fragment. If not, we have more work to do.
  2. Next, we check if the user has previously denied permission. If we've never been denied before, we can just ask for permission. If we've previously been denied, instead of just asking again, we're going to update the UI to tell the user why we're asking:

Android Marshmallow, no-permission overlay

The “Update Permissions” text here is clickable and will launch the permission request dialog:

Android Marshmallow, ask for permission dialog

It seems like we’ve covered our bases for requesting permission. However, things are not quite so simple.

Taking a user directly to the Settings app

In the permission, dialog there is a checkbox allowing the user to never be prompted again for this request. If this is selected, we can no longer launch the dialog with a call to requestPermissions().

But we definitely don’t want this to make the app unusable forever. Once “Never ask again” has been selected, the only way to get permission back is through the Settings app.

So with this in mind, here is the code for the showNoPermissionsOverlay() method which includes the onClickListener for the “Update Permissions” TextView:

private void showNoPermissionsOverlay() {
        if (mNoPermissionOverlay.getVisibility() != View.VISIBLE) {
            mNoPermissionOverlay.setVisibility(View.VISIBLE);

            TextView updatePermissionsView = (TextView) mNoPermissionOverlay.findViewById(R.id.update_permissions_textview);
            updatePermissionsView
                .setOnClickListener(v -&gt; {
                        // If “never ask again” has been clicked, our user seems to have changed their mind, so send them to Settings where they can toggle the permission back on
                        if (!shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
                            final Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
                            intent.setData(uri);
                            getActivity().startActivity(intent);
                        } else {
                            // We can request permission normally
                            PermissionUtils.requestStoragePermissions(this, STORAGE_PERMISSIONS_REQUEST_MAIN);
                        }
                    }
                );

            // ...
        }
    }

If “Never ask again” has been selected, and the user changes their mind and clicks the “Update Permission” text, they will be brought directly to the permission settings for the app:

Aviary for Android, settings permissions

Now we can make sure that the user always has an easy way to grant permissions, even if they have previously denied them.

Handling the results of a permissions request

The last piece of the puzzle is how to handle the result from requestPermissions(). A callback will be fired whenever the user selects either “Allow” or “Deny” from the request dialog.

To do this, you override the onRequestPermissionsResult() method in your fragment or activity.

Here is how we handle the result:

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
        switch (requestCode) {
            case STORAGE_PERMISSIONS_REQUEST_MAIN: {
                if (PermissionUtils.storagePermissionGranted(permissions, grantResults)) {
                    hideNoPermissionsOverlay();
                    // ... Reload UI Here
                } else {
                    showNoPermissionsOverlay();
                }
                break;
            }
        }
    }

Very simple. If permission was granted, we hide the overlay and reload the UI. If not, we show the overlay or keep it visible.

Final Thoughts

The new Marshmallow Permission system is a great step forward. Giving users control over each individual dangerous permission allows them to have the experience that they want with an app.

But for developers, properly addressing this new system means a lot of thought and work. Hopefully, this post has given you some ideas of how you can start approaching the problem as you update your own apps.

Try the Creative SDK Image Editor

You can add the power of our image editor to your Android, iOS, or web app with the Creative SDK Image Editor. With a minimal amount of code, you can be up and running with an image editor that offers effects, stickers, cropping, and more.

Visit the Creative SDK developer portal to learn more.

Adventures with Google Play Store Listing Experiments

Posted on Thursday, January 21st, 2016 by Blake Darling
Category: Android

The Creative SDK’s Image Editor is based on the Aviary app for Android and iOS. For the Aviary app, we’ve recently been experimenting with how Google Play screenshots affect download rates.

I’d like to share some things we’ve learned from our experiments with A/B testing.

Background

Our Google Play representative suggested we try updating our app screenshots to improve our app download numbers. His first recommendation was to change the style of our screenshots.

We were using full screen screenshots (on the left in the image below). Instead, he recommended we use in-device screenshots annotated with text (on the right), which is more commonly seen across the Play Store.

Google Play Store A/B testing

He also suggested we experiment with the order in which we tout Aviary’s features. Currently, we call out our most popular feature first (the number of free tools in the editor). He mentioned other apps have had success highlighting the feature that differentiates them from similar apps (for Aviary, this is our content: effects, stickers, etc.). Another suggestion was to try featuring awards and press mentions first.

We’ve been itching to try out the new Google Play Store Listing Experiments, which allows A/B testing for variations of store listings, and this was the perfect opportunity! So I set up a test with four versions against a small subset of users.

The Tests

Below are the images we are currently using in the Google Play Store.

Current Version

This set of images was our control for the tests. As you can see, the individual images are simple screenshots of the app.

We then tried three different tests:

  1. screenshots in the existing order
  2. screenshots with content featured first
  3. screenshots with accolades (awards and/or press mentions) first

Test 1: Screenshots with Existing Order

In the first test, we showed features of the app in the same order that we did before. The difference was that now each screenshot was annotated with an explanation of the feature or benefit to the user.

Test 2: Screenshots with Content First

In the second test, we front-loaded a couple of screenshots that show off our most popular content features: effects, stickers, and frames.

Test 3: Screenshots with Accolades First

For the final test, the first screenshot contained media accolades from Mashable, USA Today, and Lifehacker.

The results

“Text with Existing Order” outperformed “Current Version”, but the others came up short. I was happy to see an improvement, however to be honest I was hoping it would be more significant.

Google Play A/B testing

Then, I remembered that ~70% of our Android userbase is outside the US. This experiment was global, so maybe the screenshots with text annotations didn’t perform as well as they could have because they were not localized.

The Localized Test

Luckily, Google Play allows you to experiment with Store Listings by language. Localizing screenshots is a lot of work, so before doing it for every language, I set up a test in one of our more popular countries (Russia) with two versions.

Current Version

This set of images is the same as our first test above, where the screenshots are annotated with an explanation of the feature or benefit to the user.

Russian Text

For this test, we localized the screenshot annotations from the Current Version test into Russian.

Our hope was that the localized screenshots would significantly improve our download numbers. If not, at least we would save time by only testing on one language.

The Localized Text Results

Success! Combined with the results of the previous experiment, we might be able to expect a ~5% increase in installs just by updating and localizing our screenshots. That is significant.

Google Play A/B testing

This is just a start. Every language might be different, but this confirms it is worth spending the time for further experimentation.

Going forward, I’m eager to continue this experiment with different languages. Also, I’m curious whether using different images or highlighting different use cases for the editor might resonate better or worse on a per country basis.

The Creative SDK Image Editor

You can add the power of our image editor to your Android, iOS, or web app with the Creative SDK Image Editor. With a minimal amount of code, you can be up and running with an image editor that offers effects, stickers, cropping, and more.

Start by integrating the Creative SDK Image Editor into your Android app, then conduct your own experiments with A/B testing on the Google Play Store.

Visit the Creative SDK developer portal to learn more.

Checking Network Status on Android

Posted on Wednesday, January 20th, 2016 by Ash Ryan
Category: Android
Tags: Asset Browser UI, User Auth UI

Hi Android developers! The Creative SDK offers a range of components designed to let you offer your users creative tools and connectivity to the Creative Cloud ecosystem.

Because of this deep integration with the Creative Cloud, all Creative SDK components require a network connection for full functionality. In some cases, Creative SDK components require an internet connection to launch successfully.

For example, the User Auth UI and Asset Browser UI components talk directly to the Creative Cloud. If your user isn’t connected to the internet, these components won’t do much other than tell the user that a connection is required.

If you want to save the user a step, you can have your app check for network status before launching a component that requires an internet connection.

Let’s take a quick look at a way to do that on Android.

Contents

An example UI

Let’s say we’re just starting to build an app that will let the user log in to the Creative Cloud, then view their Creative Cloud Assets via the Creative SDK’s Asset Browser UI (see our Asset Browser UI guide if you’re interested in making such an app).

In this fictional example, we’ve just started building the app and we’re working on the Creative Cloud login. The user will click a button to launch the Creative SDK’s User Auth UI component:

Android network status helper method

In this example, when a user clicks this button, we want to:

  1. Check the network status
  2. Show a Toast to users who are not connected
  3. Launch the Creative SDK’s User Auth UI component for users who are connected

A network status helper method

To check network status, we’ll make a helper method called isConnectedToNetwork():

private boolean isConnectedToNetwork() {
    ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    return networkInfo != null && networkInfo.isConnected();
}

This method will return true if the device is connected to a network and false if it is not.

The click listener

We’ll handle the logic for when to launch the Creative SDK’s User Auth UI component in a View.OnClickListener:

View.OnClickListener launchAdobeAuthUIListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {

        if (isConnectedToNetwork()) {
            showAdobeLoginUI(); // An example helper method you would write elsewhere in the Activity class
        }
        else {
            Toast.makeText(MainActivity.this, "Please connect to a network", Toast.LENGTH_SHORT).show();
        }

    }
};
mLaunchAdobeAuthUIButton.setOnClickListener(launchAdobeAuthUIListener);

In the code above, when the button is clicked, onClick() is called. The onClick() method will use our isConnectedToNetwork() helper method in an if statement that:

  • calls showAdobeLoginUI() if the device is connected
  • shows a Toast to the user if the device is not connected

Android network status Toast

Checking the device’s network status this way, we have saved the user a step by letting them know immediately that there is no network connection.

Learn more

Want to learn more about the Creative SDK components mentioned above? Check out our User Auth UI guide and Asset Browser UI guide.

If you want to know more about what the Creative SDK for Android can do for you, visit our developer portal!

Fixing Layout Issues For Android API 22

Posted on Tuesday, January 12th, 2016 by Ash Ryan
Category: Android

Note: this post applies to version 0.7.329 and below. From version 0.9.7, the Creative SDK supports Android API 23. Get the latest info at creativesdk.com.

Hi Android developers! These days when you create a new project in Android Studio, you will have two layout files by default: activity_main.xml and content_main.xml.

Android Studio makes the assumption that you are writing for API 23. Since the Creative SDK currently requires API 21 or 22, you may find that the new breakup of XML files doesn’t work as expected, with your UI elements all stacking up on top of each other:

Android API 22 layout issues

Luckily, there is a simple fix for this. Let’s have a look at it.

Note: for this post, we will assume you are creating a new project in Android Studio with a Blank Activity.

Contents

The layout

In a new Android Studio project, you will have two layout files by default: activity_main.xml and content_main.xml.

In content_main.xml, add a Button and an ImageView within the RelativeLayout:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/open_gallery_button"
    android:id="@+id/openGalleryButton"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" />

<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/selectedImageView"
    android:layout_centerVertical="true"
    android:layout_centerHorizontal="true"
    android:adjustViewBounds="true" />

The Gradle file

In your Module build.gradle file, build for API 22 (see comments #1-3 in the code below):

apply plugin: 'com.android.application'

android {

    /* 1) Use API 22 */
    compileSdkVersion 22
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.adobe.mycsdkapp"
        minSdkVersion 14

        /* 2) Use API 22 */        
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'

    /* 3) Use API 22 */  
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'com.android.support:design:22.2.1'
}

(To see a complete Module build.gradle file for the Creative SDK, visit our Getting Started guide for Android).

At this point, if you have given your ImageView a URI within your Activity, the UI elements from your content_main.xml will stack on top of one another, just under the Toolbar:

Android API 22 layout issues

Let’s fix this.

The fix

We can fix this easily in the activity_main.xml layout file.

By default in activity_main.xml, you will have an AppBarLayout and an include for your content_main.xml file. Both of these elements need to be wrapped in a LinearLayout.

Adding the LinearLayout, your code will look something like this:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.design.widget.AppBarLayout android:layout_height="wrap_content"
            android:layout_width="match_parent" android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
                android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/content_main" />

    </LinearLayout>

    <android.support.design.widget.FloatingActionButton android:id="@+id/fab"
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

Now, your app’s layout looks as expected:

Android API 22 layout issues fixed

No more stacking!

Big thanks to our Android Image Editor engineers Alessandro Crugnola and Zak Reik for their help with this issue.


To learn about integrating the Creative SDK in your Android app, visit our developer portal or have a look at our other blog posts on Android.

Configuring the Asset Browser

Posted on Friday, December 18th, 2015 by Ash Ryan
Category: Android
Tag: Asset Browser UI

Note: this post applies to version 0.7.329 and below. Get the latest info at creativesdk.com.

Hi Android developers! We recently published two new guides for User Auth UI and Asset Browser UI. As you might imagine, User Auth lets your users log in to the Creative Cloud from within your app, and the Asset Browser serves as a file picker, letting your users see the creative assets they have stored in the Creative Cloud.

Your creative users may have a number of asset types stored in the Creative Cloud, ranging from PSDs and image files to Lightroom Photos and mobile creations from Illustrator Draw, Photoshop Mix and more.

But if your app is only intended to work with, say, PSDs, it’s better to focus the Asset Browser experience by only allowing interaction with PSDs.

We can acheive this by configuring the Asset Browser for an experience customized for your app.

As an example, let’s have a look at how to customize the Asset Browser so that it allows interaction only with assets that are both:

  1. stored in the Creative Cloud Files data source
  2. either JPEGs or PNGs

Contents

Getting started

This post will assume you have completed the steps in the following resources:

Also, be sure you have the following Creative SDK dependencies in your Module build.gradle file:

/* Add the CSDK framework dependencies (Make sure these version numbers are correct) */
compile 'com.adobe.creativesdk.foundation:auth:0.7.329'
compile 'com.adobe.creativesdk.foundation:assetux:0.7.329'

If you’ve completed these steps, you’re ready to go!

The default Asset Browser

First, let’s take a quick look at what the default Asset Browser looks like.

We will continue with the launchAssetBrowser() method that we created in our post on Launching the Asset Browser UI component:

private void launchAssetBrowser() {
    AdobeUXAssetBrowser assetBrowser = AdobeUXAssetBrowser.getSharedInstance();

    try {
        assetBrowser.popupFileBrowser(this, LAUNCH_BROWSER_REQUEST_CODE);
    }
    catch (AdobeCSDKException e) {
        Log.e(MainActivity.class.getSimpleName(), "Error: " + e.getMessage());
    }
}

The result will be an Asset Browser like the one in the image below. Note the highlights that are irrelevant for our app’s use case:

  1. Red: All available data sources are listed, but we only want to allow access to Creative Cloud Files.
  2. Blue: All file types are available for interaction, but we only want to allow interaction with JPEGs and PNGs.

The Creative SDK Default Asset Browser

Let’s use filters to focus the Asset Browser experience.

Filtering by data source

We can restrict available data sources by using the dataSourceFilter property of AdobeUXAssetBrowserConfiguration.

Change your launchAssetBrowser() method to match the following:

private void launchAssetBrowser() {
    AdobeUXAssetBrowser assetBrowser = AdobeUXAssetBrowser.getSharedInstance();
    AdobeUXAssetBrowserConfiguration configuration = new AdobeUXAssetBrowserConfiguration();

    /* 1) Set the dataSourceFilter property */
    configuration.dataSourceFilter = AdobeAssetDataSourceFilter.createFromDataSources(
            /* 1.a) Provide a set of source types */
            EnumSet.of(AdobeAssetDataSourceType.AdobeAssetDataSourceFiles),
            /* 1.b) Select the type of filter, INCLUSION or EXCLUSION */
            AdobeAssetDataSourceFilterType.ADOBE_ASSET_DATASOUCE_FILTER_INCLUSION
    );

    try {
        assetBrowser.popupFileBrowser(this, ASSET_BROWSER_REQUEST_CODE, configuration);
    }
    catch (AdobeCSDKException e) {
        Log.e(MainActivity.class.getSimpleName(), "Error: " + e.getMessage());
    }
}

Note that for the Asset Browser’s popupFileBrowser() method, we now pass in 3 arguments instead of 2.

Since we restricted the data sources to a single source, the Asset Browser will no longer display a list of sources at the top of the screen:
The Creative SDK Asset Browser, filtered by data source

Now let’s restrict which assets can be interacted with by mime type.

Filtering by mime type

In this example, we only want our users to interact with JPEGs and PNGs. We can filter by mime type with the mimeTypeFilter property of AdobeUXAssetBrowserConfiguration.

Add the following to your launchAssetBrowser() method:

/* 1) Set the mimeTypeFilter property */
configuration.mimeTypeFilter = AdobeAssetMIMETypeFilter.createFromMimeTypes(
        /* 1.a) Provide a set of mime types */
        EnumSet.of(AdobeAssetMimeTypes.MIMETYPE_JPEG, AdobeAssetMimeTypes.MIMETYPE_PNG),
        /* 1.b) Select the type of filter, INCLUSION or EXCLUSION */
        AdobeAssetMIMETypeFilterType.ADOBE_ASSET_MIMETYPE_FILTERTYPE_INCLUSION
);

The Asset Browser will now look like this:
The Creative SDK Asset Browser, filtered by mime type

Notice how the MOV and SVG are now grayed out. Your users will not be able to pick these files, nor any others that are not JPEGs or PNGs.

Learn more

Customizing the Asset Browser is a powerful way to add focus to the user experience and prevent errors caused by unexpected file types.

In this example, we restricted the data source and mime types that users can interact with. But there are still more options available!

Have a look at our brand-new Asset Browser UI guide for details on further customization and more.

New Android Guides for User Auth and Asset Browser

Posted on Friday, December 11th, 2015 by Ash Ryan
Category: Android
Tags: Asset Browser UI, User Auth UI

Hi Android developers! Following our recent refresh of the Getting Started and Image Editor guides, we have two new guides for you on our developer portal!

Contents

An all-new User Auth guide

We’ve got a brand new guide for you that explains the Creative SDK’s User Auth UI Component. Follow the steps in this guide to learn how to:

  • provide for your users the Creative Cloud login UI
  • check for auth status during all parts of the Android Activity Lifecycle
  • allow your users to log out.

The User Auth guide features enough plug-and-play code that you can expect to get up and running in a very short amount of time.

Adobe Creative SDK User Auth UI login screen

A refreshed Asset Browser guide

After you complete the User Auth UI guide, head over to our completely refreshed Asset Browser UI guide. This guide will show you how to offer your users access to their Creative Cloud assets via the Creative SDK’s Asset Browser.

This guide will help you get started with the Asset Browser and even show you the various configuration options so you can customize the Asset Browser experience for users of your app.

Adobe Creative SDK Asset Browser UI screen

Stay tuned

We remain hard at work preparing new Creative SDK guides for Android developers. We’ll have more resources to help you explore the power of the Creative SDK soon!

If you’re looking for more Android-related information, check out our developer portal, or read more about the Creative SDK for Android right here on this blog.

Launching the Asset Browser UI component

Posted on Tuesday, December 1st, 2015 by Ash Ryan
Category: Android
Tag: Asset Browser UI

Note: this post was written for version 0.7.329 and below. Get the latest info at creativesdk.com.

Hi Android developers! If you’re developing for creative users, there’s a good chance that they already have an Adobe Creative Cloud account full of creative assets like Photoshop files, Lightroom photos, and mobile creations made in one of Adobe’s many mobile apps like Photoshop Mix or Illustrator Draw.

The Creative SDK’s Asset Browser UI component is one way for your app to become part of the Creative Cloud ecosystem. With the Asset Browser, you can launch a familiar Creative Cloud UI right within your app, and allow your users to browse and download their Creative Cloud assets for further work inside of your own app.

Let’s have a basic look at the very first step to integrating the Asset Browser: launching the UI component. We will make a quick demo where, when the user taps a button, the Asset Browser UI component launches:
The Creative SDK Asset Browser

Contents

Getting started

This post will assume you have completed the steps in the following resources:

Also, be sure you have the following Creative SDK dependencies in your Module build.gradle file:

/* Add the CSDK framework dependencies (Make sure these version numbers are correct) */
compile 'com.adobe.creativesdk.foundation:auth:0.7.329'
compile 'com.adobe.creativesdk.foundation:assetux:0.7.329'

Make a launch button

Add a button to your activity’s layout:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Browse assets"
    android:id="@+id/launchBrowserButton"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true" />

For this demo, this is the only element you will need in your layout.

Make a launch method

Next we need to add the code to our Main Activity that will be used to launch the Asset Browser. Let’s make ourselves a helper method for that purpose:

private void launchAssetBrowser() {
    AdobeUXAssetBrowser assetBrowser = AdobeUXAssetBrowser.getSharedInstance();

    try {
        assetBrowser.popupFileBrowser(this, LAUNCH_BROWSER_REQUEST_CODE);
    }
    catch (AdobeCSDKException e) {
        Log.e(MainActivity.class.getSimpleName(), "Error: " + e.getMessage());
    }
}

Note that at the top of your Main Activity, you’ll need to declare a constant for the second argument for popupFileBrowser():

private static final int LAUNCH_BROWSER_REQUEST_CODE = 301;

Now you just need to call launchAssetBrowser() when the button is clicked.

Add an on-click listener

If you followed along with our post on User Auth, then you will have a method called showAuthenticatedUI(). We will add our button and on-click listener in this method:

private void showAuthenticatedUI() {
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    Log.i(MainActivity.class.getSimpleName(), "User is logged in!");


    mLaunchAssetBrowserButton = (Button)findViewById(R.id.launchBrowserButton);

    View.OnClickListener buttonListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            launchAssetBrowser();
        }
    };
    mLaunchAssetBrowserButton.setOnClickListener(buttonListener);
}

Be sure to declare mLaunchAssetBrowserButton at the top of your Main Activity:

private Button mLaunchAssetBrowserButton;

Launch the app and try it out. You’ll need to log into your Creative Cloud account first. Once you do, just click the button we made above and you should see the Creative SDK Asset Browser UI component:
The Creative SDK Asset Browser

Next steps

Of course, allowing the user to browse is just the first step. Next, you’ll likely want to explore the process of downloading user-selected assets for use within your app. This will all happen in the onActivityResult() method of your Main Activity.

If you want to continue exploring, AdobeUXAssetBrowser.ResultProvider is a good place to start.

Be sure to stay tuned for more info on the Creative SDK for Android!

How to Avoid Three Common Image Editor Integration Issues

Posted on Tuesday, November 24th, 2015 by Ash Ryan
Category: Android
Tag: Image Editor UI

Note: this post applies to version 0.7.329 and below. From version 0.9.7, the Creative SDK supports Android API 23. Get the latest info at creativesdk.com.

Hi Android developers! Integrating the Creative SDK’s Image Editor UI component can be a very quick process. If you follow the steps in our new Getting Started and Image Editor UI guides, you can get up and running in no time:
The Creative SDK Image Editor UI component

But there are a few common issues that come up from time to time due to missed steps. Let’s have a look at those issues and how you can avoid them!

Content

Forgetting to add the CDS provider

The problem

If you forget to add the Content Delivery Service (CDS) provider in your Android Manifest, your app will crash and you’ll get a runtime error that looks like this:

E/Aviary-SDK: ==================================================
E/Aviary-SDK: ==================================================
E/Aviary-SDK: 'AviaryCdsProvider' not found. Did you forget to include it in your AndroidManifest.xml file?
E/Aviary-SDK: ==================================================
E/Aviary-SDK: ==================================================
E/AndroidRuntime: FATAL EXCEPTION: IntentService[AviaryCdsService]

The error gives us a pretty good hint about where to start troubleshooting: AndroidManifest.xml.

The fix

The fix for this error is quite simple. Open up your Android Manifest and add the CDS provider:

<application
    // ...
    <activity>
        // ...
    </activity>

    <provider
        android:name="com.aviary.android.feather.sdk.internal.cds.AviaryCdsProvider"
        android:authorities="${applicationId}.AviaryCdsProvider"
        android:exported="false" />
</application>

Note: you shouldn’t hard code the ${applicationId} in android:authorities. Leave it as-is.

Omissions in configuring the Application class extension

The problem

Before integrating any component of the Creative SDK, you will need to implement Client Auth. To do so, make a new class that extends Application and implements the appropriate Client Auth interface.

The process for doing this is covered in our Getting Started guide.

There is, however, a particular Client Auth interface required for the Image Editor UI component, as noted in our Image Editor UI guide.

If you don’t implement the correct Client Auth interface for the Image Editor, you will get a runtime error that looks like this:

E/AndroidRuntime: FATAL EXCEPTION: main
E/AndroidRuntime: Process: com.adobe.myapp:standalone, PID: 17614
E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.adobe.myapp/com.aviary.android.feather.sdk.FeatherActivity}: java.lang.ClassCastException: com.adobe.myapp.MainApplication cannot be cast to com.aviary.android.feather.sdk.IAviaryClientCredentials

The fix

We just need to impletment IAviaryClientCredentials instead of IAdobeAuthClientCredentials.

We have a blog post that compares the two Client Auth interfaces. Check it out to see the code!

Building for Android API 23

Note: this post applies to version 0.7.329 and below. From version 0.9.7, the Creative SDK supports Android API 23. Get the latest info at creativesdk.com.

The problem

The Creative SDK doesn’t currently support Android API 23. If you try to build for API 23, depending on your setup, you may get either compile time errors related to a values.xml file, or crashes when you try to save an image in the Image Editor.

Here’s one resulting error you might see:

Error:(87, 44) No resource found that matches the given name (at 'android:textColorLink' with value '@color/link_text_material_light').

The fix

The best solution for this issue is to build for Android API 22. In our Getting Started guide, we have an example build.gradle file that you can reference (see the “Configure your Module build.gradle file” section).


Avoiding these common issues will help you get your Creative SDK Image Editor integration up and running quickly and smoothly. We look forward to seeing what you make!

Displaying Basic Creative Cloud User Profiles

Posted on Tuesday, November 17th, 2015 by Ash Ryan
Category: Android
Tag: User Auth UI

Hi Android developers! We previously talked about the User Auth UI component in the Creative SDK. The User Auth UI component is what lets your users log in to the Creative Cloud right from within your app.

We’ve covered logging a user in, but there are other things you can explore within User Auth. Let’s have a quick look at a simple way to display the user’s first name in a Toast popup.

While you may find that you want to expand on the core concept here, it only takes two lines of code to get started!

Note: If you haven’t already, be sure to check out our post on logging a user in. We’ll be building upon the code there.

Content

Where we left off

When we concluded last time, we had made a method called showAuthenticatedUI(), which determines what the user sees after they log in:

private void showAuthenticatedUI() {
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    Log.i(MainActivity.class.getSimpleName(), "User is logged in!");
}

Currently this method logs “User is logged in!” to the console, which is fine for us. But what about letting the user know that something happened?

We could, for example, use a Toast that displays “You are logged in”, but we can do something a little more dynamic and informative using the Creative SDK.

Getting the User Profile data

Inside of our showAuthenticatedUI() method, let’s use the User Profile from the AdobeUXAuthManager.

Under the Log.i(), add a new line:

String firstName = mUXAuthManager.getUserProfile().getFirstName();

We’ve just assigned the first name from the user’s Adobe ID profile to the firstName variable. Specifically, AdobeUXAuthManager.getUserProfile() returns an AdobeAuthUserProfile, which in turn has the method getFirstName().

As you may expect, there are more methods on AdobeAuthUserProfile than just getFirstName(). Feel free to read the docs and experiment, but for this post, we will stick with the first name only.

Displaying a Toast

The last step here is to simply make a Toast and show it:

Toast.makeText(MainActivity.this, "Logged in to Creative Cloud as " + firstName, Toast.LENGTH_LONG).show();

Now when a user logs in or re-enters the app, they will see something like this:
Creative SDK and Creative Cloud user profiles


This is a fairly simple example, but it’s easy to imagine using this data in other ways as well: displaying User Profile data in the layout, or creating smart default file names.

As a further step for the example above, imagine how you might display the Toast we made only right after the user logs in, but not when the user re-enters the app.

User Auth UI for Android

Posted on Tuesday, November 10th, 2015 by Ash Ryan
Category: Android
Tag: User Auth UI

Hi Android developers! With the Creative SDK, you can allow your users to interact with their assets in the Creative Cloud, like Lightroom Photos, Photoshop Mixes, Sketches, and more.

Before a user can view their Creative Cloud assets, they will need to log in to their Creative Cloud account through your app. The Creative SDK makes this easy by providing the developer with a UI component that is quick to implement and familiar to Creative Cloud users:
The Adobe Creative SDK login screen

Let’s have a quick look at how this works.

Note: Before you continue on, you’ll want to follow our all-new Getting Started guide for Android to include the SDK, configure your Gradle build, and implement Client Auth.

Contents

Configuration

Add the following Creative SDK dependency to your Module build.gradle file:

/* Add the CSDK framework dependency (Make sure the version number is correct) */
compile 'com.adobe.creativesdk.foundation:auth:0.7.329'

Allowing the user to log in

In the example below, user login involves the following flow in the Main Activity.

  1. The AdobeUXAuthManager is initialized with the Client ID and Secret. Note: this must be done before auth operations such as login and logout.
  2. The AdobeAuthSessionHelper retrieves the user’s current auth status with a callback.
  3. If the user is not logged in, they will see the Adobe ID login screen.
  4. If the user is logged in (or if they logged in during the previous step), they will see the Main Activity layout.
  5. Your app continues from there.

These steps are numbered #1-5 in the code comments below:

public class MainActivity extends AppCompatActivity {

    private static int DEFAULT_SIGN_IN_REQUEST_CODE = 202;

    private AdobeAuthSessionHelper mAuthSessionHelper;
    private AdobeUXAuthManager mUXAuthManager;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        /* 1 */
        mUXAuthManager = AdobeUXAuthManager.getSharedAuthManager();

        /* 2 */
        mAuthSessionHelper = new AdobeAuthSessionHelper(mStatusCallback);
        mAuthSessionHelper.onCreate(savedInstanceState);
    }

    private AdobeAuthSessionHelper.IAdobeAuthStatusCallback mStatusCallback;
    {
        mStatusCallback = new AdobeAuthSessionHelper.IAdobeAuthStatusCallback() {
            @Override
            public void call(AdobeAuthSessionHelper.AdobeAuthStatus adobeAuthStatus, AdobeAuthException e) {
                if (AdobeAuthSessionHelper.AdobeAuthStatus.AdobeAuthLoggedIn == adobeAuthStatus) {
                /* 3 */
                    showAuthenticatedUI();
                } else {
                /* 4 */
                    showAdobeLoginUI();
                }
            }
        };
    }

    private void showAdobeLoginUI() {
        mUXAuthManager.login(new AdobeAuthSessionLauncher.Builder()
                        .withActivity(this)
                        .withRequestCode(DEFAULT_SIGN_IN_REQUEST_CODE)
                        .build()
        );
    }

    private void showAuthenticatedUI() {
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        /* 5 */
        Log.i(MainActivity.class.getSimpleName(), "User is logged in!");
    }

    // ...

Checking user auth status

The Adobe Auth Session Helper contains methods for checking the user’s auth status during all parts of the Activity lifecycle.

Be sure to override the following Activity methods, calling the related AdobeAuthSessionHelper methods within:

    // ...

    @Override
    protected void onResume() {
        super.onResume();
        mAuthSessionHelper.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mAuthSessionHelper.onPause();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mAuthSessionHelper.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
        mAuthSessionHelper.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mAuthSessionHelper.onDestroy();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        mAuthSessionHelper.onActivityResult(requestCode, resultCode, data);
    }

    // ...

Explore

There’s still plenty more you can do with User Auth, like logging a user out, or accessing basic profile information.

Read more about the AdobeAuthSessionHelper and AdobeUXAuthManager classes on the Creative SDK Android developer portal.

Also, be sure to stayed tuned for more info on the Creative SDK’s User Auth UI component and APIs. We’ll have a new guide for you on the developer portal soon!