NAV

Introduction

Gfycat Android SDK is a modular SDK that provides convenient access to the Gfycat API. As of November 2017 we’ve released the following modules:

The SDK provides access to upload and creation API of Gfycat. In addition, it provides simple access to the Gfycat content accessible through search and tags.

Developers that want to test the SDK can use the sample app on the Github page Please use the issue tracker on Github to file and track any issues.

Gfycat Core SDK

This module is an API wrapper over Gfycat API, targeted to simplify Gfycat API integration into your Android application.

The SDK is dependent on the following Android libraries:

You may want to familiarize yourself with the basics of these libraries in order to get started with Gfycat Core SDK.

Core integration steps

1. Add dependency

repositories {
    jcenter()
}

Gfycat distributes the SDK through the JCenter Maven repository. Make sure your build script points to jcenter() for dependencies.

dependencies {
    compile 'com.gfycat.android:gfycat-core:0.5.+' // for Gfycat Core compile dependency
}

Add the compile dependency with the latest version of the Gfycat SDK in the build.gradle file

The latest Gfycat Core SDK version is:

Latest

2. Set up SDK credentials

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        GfyCoreInitializer.initialize(
               new GfyCoreInitializationBuilder(
                       this,
                       new GfycatApplicationInfo("CLIENT_ID", "CLIENT_SECRET")));
    }
}

The Gfycat API is accessible only when you provide clientId and clientSecret. Get them for free on the API Keys Management page. You need to have or create a Gfycat account and log in with those credentials to manage your keys.

Once you get those, initialize GfyCore with GfyCoreInitializer and GfyCoreInitializationBuilder. The most suitable place to do that is in the onCreate method of your Application class.

3. Use GfyCore

class GfyCore {

    /**
     * Responsible for downloading Gfycat items.
     */
    FeedManager getFeedManager();

    /**
     * User account management. Sign up, sign in and sign out actions. Update user data.
     */
    UserAccountManager getUserAccountManager();

    /**
     * Provides access to Gfycat media cache files.
     */
    MediaFilesManager getMediaFilesManager();

    /**
     * Gfycat content creation and upload API wrapper.
     */
    UploadManager getUploadManager();

    /**
     * Restricted content report manager
     */
    NSFWContentManager getNSFWContentManager();

    /**
     * Allows management of user-owned Gfycats.
     */
    UserOwnedContentManager getUserOwnedContentManager();
}

Gfycat API access is provided through a set of Managers in GfyCore class.

Get GIFs

interface FeedManager {

    /**
     * Request categories list from cache. If none found, network request will be performed.
     */
    Observable<GfycatCategoriesList> getCategories();

    /**
     * Subscribe for Gfycat changes in local DB.
     * !!! Important: You should NOT forget to unsubscribe, otherwise memory leak will occur.
     */
    Observable<FeedData> observeGfycats(Context context, FeedIdentifier feedIdentifier);

    /**
     * Perform a network request of Gfycat list for a specified {@param feedIdentifier}.
     * {@link FeedData} containing this request result will be returned via {@link #observeGfycats(Context, FeedIdentifier)}.
     */
    Completable getGfycats(FeedIdentifier feedIdentifier);

    /**
     * Perform a network request of new Gfycats for a specified {@param feedDescription}.
     * {@link FeedData} containing this request result will be returned via {@link #observeGfycats(Context, FeedIdentifier)}.
     */
    Completable getNewGfycats(FeedDescription feedDescription);

    /**
     * Perform a network request of more Gfycats for corresponding feed. Will download Gfycats and save to local DB if needed.
     * {@link FeedData} containing this request result will be returned via {@link #observeGfycats(Context, FeedIdentifier)}.
     */
    Completable getMoreGfycats(FeedDescription feedDescription);

    /**
     * Look for a Gfycat in cache. Request one from network, if not found.
     */
    Single<Gfycat> getGfycat(String gfyId);
}

Every Gfycat GIF is wrapped by a Gfycat object containing various metadata and media files mp4, webp, gif, etc…

FeedManager is responsible for accessing Gfycat feeds. FeedManager caches all the feeds into a device database.

There are the following feed types:

You need to subscribe to an Observable returned by FeedManager::getGfycats(feedIdentifier) in order to receive a FeedData with a list of Gfycats. Provide FeedIdentifier to pull the correct feed of Gfycats. You can get that FeedIdentifier using the PublicFeedIdentifier class.

Feed lists are delivered in parts (or pages). In order to get the next part (page) call FeedManager::getMoreGfycats(feedDescription). It will trigger your FeedManager::getGfycats(feedIdentifier) subscription when more Gfycats are loaded.

See our Javadoc for more details on FeedManager.

Get media files

interface MediaFilesManager {

    /**
     * Allocates Gfycat media file as byte array into memory.
     */
    Observable<byte[]> loadAsByteArray(Gfycat gfycat, MediaType mediaType);

    /**
     * Get Gfycat media file.
     */
    Observable<File> loadAsFile(Gfycat gfycat, MediaType mediaType);
}

Available media types supported by our SDK are listed in MediaType class.

The media types that you will find useful are:

You can obtain the Url you need with one of the getters in the Gfycat object or use the MediaType::getUrl(Gfycat gfycat) method. For example: MediaType.WEBP.getUrl(gfycat).

Use MediaFilesManager to get a File object or byte array of a media file returned by DiskLruCache.

Upload GIFs

/**
 * Manages Gfycat uploading to Gfycat.com
 * Upload flow consists of 3 separate steps: request creation key, upload content and server processing.
 */
public interface UploadManager {

    /**
     * Creation key request. This is the first creation flow step.
     * Call #upload(String, InputStream) method next, by providing returned key.
     *
     * @param creationParams creation parameters.
     * @return Returns a unique creation key. Needed for upload and processing status check.
     * @throws CanNotCreateKeyException in case of request failure.
     */
    String requestCreationKey(CreateGfycatRequest creationParams) throws CanNotCreateKeyException;

    /**
     * Upload video file with creation key provided by #requestCreationKey(CreateGfycatRequest). This is a second creation flow step.
     *
     * @param creationKey unique key for this upload.
     * @param inputStream gfycat content from stream.
     * @throws CanNotUploadGfycatException thrown error happens during uploading.
     */
    void upload(String creationKey, InputStream inputStream) throws CanNotUploadGfycatException;

    /**
     * Same as #upload(String, InputStream) but with progress reporting.
     */
    void upload(String creationKey, InputStream inputStream, UploadListener uploadListener) throws CanNotUploadGfycatException;

    /**
     * Synchronously wait for server processing result. This is the third creation flow step.
     * Gfycat will be returned once it becomes available on the server.
     *
     * @param creationKey unique key for this upload provided by #requestCreationKey(CreateGfycatRequest)
     * @param timeout     wait timeout.
     * @return Returns Gfycat available on server.
     * @throws CanNotGetGfycatStatusException if Gfycat was not returned by server within a specified timeout.
     */
    Gfycat waitUntilReady(String creationKey, long timeout) throws CanNotGetGfycatStatusException, FailedToCreateGfycatException, GfycatWasDeletedBeforeCompletionException;

    /**
     * Same as #waitUntilReady(String) but with default timeout
     */
    Gfycat waitUntilReady(String creationKey) throws CanNotGetGfycatStatusException, FailedToCreateGfycatException, GfycatWasDeletedBeforeCompletionException;

    ...

    /**
     * Encapsulates all three steps of creation flow.
     * Calls the following methods in the corresponding order: #requestCreationKey(CreateGfycatRequest), #upload(String, InputStream), #waitUntilReady(String)
     *
     * @param creationParams creation parameters.
     * @param inputStream gfycat content from stream.
     * @return Returns Gfycat available on server.
     * @throws CanNotGetGfycatStatusException if Gfycat was not returned by server within a default timeout.
     */
    Gfycat createGfycat(CreateGfycatRequest creationParams, InputStream inputStream) throws CanNotCreateKeyException, CanNotGetGfycatStatusException, CanNotUploadGfycatException, FailedToCreateGfycatException, GfycatWasDeletedBeforeCompletionException;

    /**
     * Same as #createGfycat(CreateGfycatRequest, InputStream) but with progress reporting.
     */
    Gfycat createGfycat(CreateGfycatRequest creationParams, InputStream inputStream, UploadListener uploadListener) throws CanNotCreateKeyException, CanNotGetGfycatStatusException, CanNotUploadGfycatException, FailedToCreateGfycatException, GfycatWasDeletedBeforeCompletionException;
}

Create Gfycats and upload them to Gfycat.com using UploadManager class.

Call UploadManager::createGfycat(creationParams, inputStream) method to upload a video file or an InputStream of almost any moving picture format. CreateGfycatRequest parameter is a Gfycat metadata used to properly initialize Gfycat on our server. Call GfyCore.getUploadManager() to retrieve an UploadManager instance.

You can also subscribe for Gfycat creation status updates by providing UploadListener as a third optional parameter in UploadManager::createGfycat(creationParams, inputStream, uploadListener)

Technical details

Upload flow consists of 3 steps:

Please note that the entire upload flow might take some time to go through all of the steps and will mostly depend on user’s internet connection quality. It may take less then 30 seconds or it can last for minutes. Consider using UploadManager in a Service working in foreground. This will help to provide the best user experience and keep the flow running until its completion.

Access user info

/**
 * Sign in existing user.
 *
 * @param accountOrEmail user's email or account.
 * @param password       user's password.
 */
Single<String> signIn(String accountOrEmail, String password);

/**
 * UserInfo changes Observable.
 * This is an infinite Observable - onComplete() and onError() will never be called.
 * Note: This call will not perform any network requests.
 *
 * @return UserInfo observable.
 */
Observable<UserInfo> observe();

Using the Gfycat SDK you can authenticate a user into his or her Gfycat account. See UserAccountManager for details.

User authentication is performed in the background by subscribing for a Single. Use onSuccess callback to subscribe for successful Sign in, or oError - for sign in failure.

Subscribe for UserAccountManager::observe() observable to observe UserInfo changes.

See our Javadoc for more details on UserAccountManager.

Manage user content

interface UserOwnedContentManager {

    /**
     * Delete gfycat.
     */
    void delete(Gfycat gfycat);

    /**
     * Make gfycat private.
     */
    Runnable makePrivate(Gfycat gfycat, long undoDuration);

    /**
     * Make gfycat public.
     */
    Runnable makePublic(Gfycat gfycat, long undoDuration);

    /**
     * Mark content as being suitable for all ages.
     */
    Runnable suitableForAllAges(Gfycat gfycat, long undoDuration);

    /**
     * Mark own content as being suitable for 18+ only.
     */
    Runnable markAs18Only(Gfycat gfycat, long undoDuration);
}

UserOwnedContentManager has basic capabilities for user content management.

Not safe for work content

interface NSFWContentManager {

    /**
     * Report Gfycat as not safe for work. Gfycat will be reported to Gfycat.com and hidden locally.
     */
    Runnable reportItem(Gfycat gfycat);

    /**
     * Report owner of Gfycat.
     * Application user will not see any content from Gfycat owner anymore.
     */
    Runnable reportUser(Gfycat gfycat, long undoPossibilityTimeMs);
}

Content that does not abide by our Terms of Service can be reported via NSFWContentManager

Gfycat Picker Fragment

This module provides the ability to integrate Gfycat Picker into your Android application.

Picker integration steps

1. Add dependency

repositories {
    jcenter()
}

Gfycat distributes the SDK through the JCenter Maven repository. Make sure your build script is pointing to jcenter() for dependencies.

dependencies {
    compile 'com.gfycat.android:gfycat-picker:0.5.+' // for Gfycat Picker compile dependency
}

Add the compile dependency with the latest version of the Gfycat SDK in the build.gradle file

The latest Gfycat Picker version is:

Latest

2. Set up SDK credentials

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        GfyCoreInitializer.initialize(
               new GfyCoreInitializationBuilder(
                       this,
                       new GfycatApplicationInfo("CLIENT_ID", "CLIENT_SECRET")));
    }
}

The Gfycat API is accessible only when providing clientId and clientSecret. Get them for free on the API Keys Management page. You need to have or make a Gfycat account and log in with those credentials to manage your keys.

Once you get those, initialize GfyCore with GfyCoreInitializer and GfyCoreInitializationBuilder. The most suitable place to do that is in the onCreate method of your Application class.

3. Add a Picker fragment

<fragment
    android:name="com.gfycat.picker.GfycatPickerFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:recentCategoryEnabled="true"/>

Just add a fragment into your XML layout and you are ready to search and look through Gfycat content. It’s that easy!

4. Handle GIF selection.

<fragment
    android:name="com.gfycat.picker.GfycatPickerFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:recentCategoryEnabled="true"
    app:onGfycatSelected="gfycatSelected"/>
public class MyActivity extends Activity {
    ...
    public boolean gfycatSelected(FeedIdentifier identifier, Gfycat gfycat, int position) {
        return false;
    }
    ...
}

Subscribe to user selection by adding an XML onGfycatSelected attribute along with a callback method in your activity hosting a picker fragment.

You can find two additional ways to handle a callback below.

In a callback method you get a user-selected Gfycat object. It includes metadata and web links to the real media files that are hosted on Gfycat servers. For example, these methods provide the following links:

5. Pitfalls

android {
    ...
    packagingOptions {
        exclude 'META-INF/LICENSE'
    }
}

The Gfycat SDK uses additional dependencies that may be duplicates of ones in your current project. You might get DuplicateFileException: Duplicate files copied in APK META-INF/LICENSE.. This means that there are some libraries using the same LICENSE file path. Nothing critical, just exclude it in the packagingOptions clause of your Gradle file.

6. Check our sample app

We have created a sample app with basic integration as described in this document on our Github page.

Handling Gfycat selection

There are 3 different options for Gfycat selection handling:

Option one

<fragment
    android:id="@+id/pickerFragment"
    android:name="com.gfycat.picker.GfycatPickerFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:recentCategoryEnabled="true"
    app:onGfycatSelected="gfycatSelected"/>

1 . Specify an XML attribute onGfycatSelected, as described in Picker integration steps.

Option two

public class SampleGfycatPickerFragment extends GfycatPickerFragment {
    /**
     * Called when user clicks on Gfycat in identifier.getName() category.
     *
     * @return true if GfycatPickerFragment should go back to categories, false otherwise.
     */
    @Override
    public void onGfycatSelected(FeedIdentifier identifier, Gfycat gfycat, int position) {
        /**
         * You've got a Gfycat object, use it to get links to the actual files you need:
         *
         * gfycat.getMobileMp4Url() - mobile optimized Mp4 file.
         * gfycat.getWebPUrl()      - webp file.
         * gfycat.getGif1mbUrl()    - GIF not bigger than 1 MB.
         * gfycat.getGif2mbUrl()    - GIF not bigger than 2 MB.
         * gfycat.getGif5mbUrl()    - GIF not bigger than 5 MB.
         */
    }
}

2 . Extend from GfycatPickerFragment and override onGfycatSelected(FeedIdentifier, Gfycat, int) method.

Option three

GfycatPickerFragment.OnGfycatSelectedListener listener = new GfycatPickerFragment.OnGfycatSelectedListener() {
    @Override
    public void onGfycatSelected(FeedIdentifier identifier, Gfycat gfycat, int position) {
      // Gfycat selected logic here
    }
};

GfycatPickerFragment pickerFragment = (GfycatPickerFragment) getFragmentManager().findFragmentById(R.id.pickerFragment);
pickerFragment.addOnGfycatSelectedListener(listener);

...

pickerFragment.removeOnGfycatSelectedListener(listener);

3 . Call addOnGfycatSelectedListener(OnGfycatSelectedListener) to subscribe to the onGfycatSelected event from the code. Call removeOnGfycatSelectedListener(OnGfycatSelectedListener) to unsubscribe.

Picker properties

<fragment
    android:id="@+id/pickerFragment"
    android:name="com.gfycat.picker.GfycatPickerFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:accentTint="@color/colorAccent"
    app:closeOnGfycatClick="true"
    app:columnCountCategories="4"
    app:columnCountGfycats="3"
    app:onGfycatSelected="gfycatSelected"
    app:recentCategoryEnabled="true"
    app:categoryAspectRatio="1"/>

Override these GfycatPickerFragment methods

@Override
public int getCategoriesColumnCount() {
    return 3;
}

@Override
public int getGfycatsColumnCount() {
    return 2;
}

@Override
public int getSearchViewTopPadding() {
    return 0;
}

@Override
public int getContentBottomPadding() {
    return 0;
}

@Override
public int getContentTopPadding() {
    return 0;
}

@Override
public int getAccentTintColor() {
    return ContextCompat.getColor(getContext(), R.color.colorAccent);
}

@Override
public float getCategoryAspectRatio() {
    return 1f;
}

List of properties that may be customized in XML:

Or by overriding GfycatPickerFragment class methods:

Search View customization

Default Search View implementation.

/**
 * Called to have the fragment instantiate its Search View. This is optional,
 * and if not overridden default search view will be used.
 *
 * This will be called in onCreateView(LayoutInflater, ViewGroup, Bundle).
 *
 * @param container Search View should be added to this ViewGroup object.
 *                  
 * @return Return a SearchController instance, cannot be null.
 */
public SearchController onCreateSearchView(ViewGroup container) {
    DefaultSearchCategoryView localSearchController = new DefaultSearchCategoryView(container.getContext());
    container.addView(localSearchController);
    return localSearchController;
}

You can customize a Search View in order to make its appearance match your project’s design.

/**
 * Search View controller to be used by GfycatPickerFragment.
 */
public interface SearchController {

    /**
     * @param query A query string to show in Search View.
     */
    void setSearchQuery(String query);

    /**
     * @return Returns current search query.
     */
    String getSearchQuery();

    /**
     * @param searchControllerListener Search View events listener.
     */
    void setSearchControllerListener(SearchControllerListener searchControllerListener);

    /**
     * Used as part of contentTopPadding distance.
     *
     * @return Returns a height of search view.
     */
    int getSearchHeight();

    /**
     * Set accent tint color called by GfycatPickerFragment when accent tinting is being applied.
     */
    void setAccentTintColor(int color);

    /**
     * Set search view visibility.
     */
    void setSearchViewVisible(boolean isVisible);

    /**
     * Check if search view is visible.
     */
    boolean isSearchViewVisible();
}
/**
 * Listener used by GfycatPickerFragment to listen for Search View behavior.
 */
public interface SearchControllerListener {

    /**
     * Called when text changed in SearchController.
     *
     * @param newText
     */
    void onQueryTextChange(String newText);

    /**
     * Called when user clicked search button.
     *
     * @param text search query
     */
    void onSearchClicked(String text);

    /**
     * Called when user clicked clear search query button.
     */
    void onClearClicked();
}

Use SearchController and SearchControllerListener interfaces when implementing a custom Search View for GfycatPickerFragment.

WebP and GIF support

dependencies {
    ...
    compile ('com.gfycat.android:gfycat-picker:0.5.+') {
        exclude module: 'gfycat-webp-player'
    }
    ...
}
public class YourApplication extends Application {
    ...
    @Override
    public void onCreate() {
        ...
        MainPlayerFactory.setup(new GfycatGifPlayerFactory());
        ...
    }
    ...
}

Gfycat Picker has a built-in GIF and WebP support. It uses WebP file format by default. If, for any reason, you need to switch to GIF file format, here are a couple of ways to achieve this:
1) exclude gfycat-webp-player module from gfycat-picker dependencies (this will remove all the WebP sources and binaries and reduce the .apk size by ~900Kb)
2) setup MainPlayerFactory with GfycatGifPlayerFactory in runtime

Gfycat Players

  compile 'com.gfycat.android:gfycat-webp-player:0.5.+' // Gfycat WebP player dependency
  compile 'com.gfycat.android:gfycat-gif-player:0.5.+'  // Gfycat GIF player dependency

You can find two Gfycat Players in our SDK playing either WebP or GIF formats.

The latest Gfycat WebP Player module version is:

Latest

The latest Gfycat GIF Player module version is:

Latest

Player setup steps

Add to XML layout

    <com.gfycat.webp.view.GfycatWebpView
        android:id="@+id/gfycat_player_view"
        android:layout_width="200dp"
        android:layout_height="200dp"
        app:autoplay="true"
        app:shouldLoadPreview="true" />

Both GfycatWebpView and GfycatGifView implement GfycatPlayer interface, so setting up either of them is done in the same way.

Start by adding GfycatWebpView or GfycatGifView to your layout as you would do with any other view.

Then you might want to set a couple of boolean parameters:

Set the Gfycat and initiate playback

GfycatPlayer playerView = (GfycatPlayer) view.findViewById(R.id.gfycat_player_view);

// The following call should happen before setupGfycat(gfycat) call. Otherwise it won't have any effect
playerView.setShouldLoadPreview(true);

playerView.setOnStartAnimationListener(() -> {
        // observe animation start event
    }
});
playerView.setupGfycat(gfycatObject);
playerView.play();

...

// Release playerView resources.
// Call this method when you are done with this view. For example in onDestroy
playerView.release();

Public methods in GfycatPlayer you might find useful when developing your application:

class GfycatPlayer {
    /**
     * Set a Gfycat object to display by GfycatPlayer.
     */
    void setupGfycat(@NonNull Gfycat gfycat);

    /**
     * If set to true Gfycat preview image will be loaded into Gfycat cache and displayed while larger file with animation is being loaded.
     * Default value is false.
     * IMPORTANT: Call this method before #setupGfycat(Gfycat), otherwise preview image will not be loaded.
     * Note:
     * Use #setupPreview(Drawable, boolean) instead if you need to display your own preview.
     * You can also download Gfycat preview image manually by using Gfycat#getPosterThumbnailUrl() link
     * and set it as a preview with #setupPreview(Drawable, boolean) to avoid Gfycat cache, if there is a need.
     */
    void setShouldLoadPreview(boolean shouldLoadPreview);

    /**
     * Subscribe for animation start. Will be called when the file has loaded and animation starts.
     */
    void setOnStartAnimationListener(OnStartAnimationListener onStartAnimationListener);

    /**
     * Set Gfycat preview manually by calling this method.
     * Note: If #setShouldLoadPreview(boolean) is set to true then preview image will be replaced by the one loaded by Gfycat SDK.
     *
     * @param drawable to use as preview, while the file is being loaded.
     * @param animate set to true if transition animation needed.
     */
    void setupPreview(Drawable drawable, boolean animate);

    /**
     * Start animation playback.
     * Will download and play an animation, or play it instantly if it is already downloaded.
     */
    void play();

    /**
     * Pause animation playback.
     */
    void pause();

    /**
     * You MUST call this method when GfycatPlayer is no longer needed in order to release resources and avoid memory leak.
     * Note: We are working on automatic resource release encapsulation. This method will be marked as Deprecated when done.
     */
    void release();
}

Dependencies

We bring the following dependencies into your application.

  // Google support library
  compile 'com.android.support:cardview-v7'
  compile 'com.android.support:appcompat-v7'
  compile 'com.android.support:recyclerview-v7'
  compile 'com.android.support:design'

  // RxJava and RxAndroid
  compile 'io.reactivex:rxjava'
  compile 'io.reactivex:rxandroid'

  // Retrofit2 and Gson for Networking
  compile 'com.squareup.retrofit2:retrofit'
  compile 'com.squareup.retrofit2:converter-gson'
  compile com.squareup.retrofit2:adapter-rxjava'

  // Utils
  compile 'commons-io:commons-io' // Apache IO library

Gradle provides transitive dependency management system. It automatically resolves and adds all dependencies needed for the project. If you are already using some of these libraries, Gradle will pick the newest.

Library exclusion example

compile("com.gfycat.android:gfycat-picker:0.5.+") {
    exclude module: 'support-v4'
    exclude module: 'appcompat-v7'
    exclude module: 'recyclerview-v7'
    exclude module: 'design'
    exclude module: 'cardview-v7'
    ...
    exclude module: 'another-lib-you-may-need-to-exclude'
}

If you need to use specific versions of your library, exclude those from the Gfycat compile dependencies.

Tips

Proguard rules

We have included ProGuard files for the SDK and its dependencies, inside the SDK.







Have fun, enjoy and don’t forget to drop us a line at partners@gfycat.com to let us know how you’re using the SDK and what you’d like to see on our feature list next.

Let us know of any issues.