Docs
API Docs

Background Export

This document guides you through the process of enabling the VideoEditor SDK background exporting.

Implement background export capability

If you want to export in the background the onActivityResult() method will not longer work. Instead you have to use a BroadcastReceiver to receive your exported data.

Create a BroadcastReceiver

In order to enable the editor to pass the resulting file to you in an asynchronous way, you have to create a BroadcastReceiver class like in this example.

public class EditorResultReceiverJava extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        EditorSDKResult data = new EditorSDKResult(intent);

        switch (data.getResultStatus()) {
            case CANCELED: {
                Toast.makeText(context, "Editor is closed because the User clicks the cancel button.", Toast.LENGTH_LONG).show();
            }
            break;
            case CANCELED_BY_SYSTEM: {
                /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
                 * The editor is canceled by the System, because of unknown reason.  *
                 * The User has maybe force closed the App                           *
                 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
            }
            break;
            case DONE_WITHOUT_EXPORT: {
                Toast.makeText(context, "Editor result accepted but not changed.", Toast.LENGTH_LONG).show();
            }
            break;
            case EXPORT_STARTED: {
                // The editor has started the export, we can now save the serialization in the meantime.
                File file = new File(Environment.getExternalStorageDirectory(), "SavedEditorState.json");
                if (file.delete()) try {
                    new IMGLYFileWriter(data.getSettingsList()).writeJson(file);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            break;
            case EXPORT_DONE: {
                data.notifyGallery(EditorSDKResult.UPDATE_RESULT & EditorSDKResult.UPDATE_SOURCE);
                Toast.makeText(PESDK.getAppContext(), "Editor Export is done. File is saved at: " + data.getResultUri() + ".", Toast.LENGTH_LONG).show();
            }
            break;
        }
    }
}
class EditorResultReceiverKotlin : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val data = EditorSDKResult(intent)

        when (data.resultStatus) {
            EditorSDKResult.Status.CANCELED -> {
                Toast.makeText(context, "Editor is closed because the User clicks the cancel button.", Toast.LENGTH_LONG).show()
            }
            EditorSDKResult.Status.CANCELED_BY_SYSTEM -> {
                /********************************************************************
                 * The editor is canceled by the System, because of unknown reason. *
                 * The User has maybe force closed the App                          *
                 *******************************************************************/
            }
            EditorSDKResult.Status.DONE_WITHOUT_EXPORT -> {
                Toast.makeText(context, "Editor result accepted but not changed.", Toast.LENGTH_LONG).show()
            }
            EditorSDKResult.Status.EXPORT_STARTED -> {
                // The editor has started the export, we can now save the serialization in the meantime.
                File(Environment.getExternalStorageDirectory(), "SavedEditorState.json").also {
                    it.delete()
                }.outputStream().use {
                    IMGLYFileWriter(data.settingsList).writeJson(it)
                }
            }
            EditorSDKResult.Status.EXPORT_DONE -> {
                // The editor export is done, we can now save the image and notify the gallery to update the image.
                data.notifyGallery(to = EditorSDKResult.UPDATE_RESULT and EditorSDKResult.UPDATE_SOURCE)
                Toast.makeText(PESDK.getAppContext(), "Editor Export is done. File is saved at: ${data.resultUri}.", Toast.LENGTH_LONG).show()
            }
        }
    }
}

Update your AndroidManifest.xml

Open your Application Manifest and add the BroadcastReceiver

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="..." >

    <application your_paramters="...">
        <receiver
            android:name=".EditorResultReceiverKotlin"
            android:permission="ly.img.sdk.RECEIVE_RESULT_PERMISSION">
            <intent-filter>
                <!-- It is important to have your own action name here-->
                <action android:name="my.fancy.BroadcastReceiverAction" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

Update your build.gradle and include the headless module.

This will add the RenderService and some app permission we need for the background export.

imglyConfig {
    modules {
        include 'backend:headless'
    }
}

Start the editor with startActivityForBroadcast instead of startActivityForResult

Now instead of starting with startActivityForResult we use startActivityForBroadcast and pass the BroadcastReceiver action name as the second parameter.

public class VideoEditorDemoActivityAsync extends Activity implements PermissionRequest.Response {

    public static final int GALLERY_RESULT = 2;
    public static final String VESDK_RESULT_BROADCAST_RECEIVER = "my.fancy.BroadcastReceiverAction";

    // Important permission request for Android 6.0 and above, don't forget to add this!
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        PermissionRequest.onRequestPermissionsResult(requestCode, permissions, grantResults);
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    @Override
    public void permissionGranted() {}

    @Override
    public void permissionDenied() {
        /* TODO: The Permission was rejected by the user. The Editor was not opened,
         * Show a hint to the user and try again. */
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    private VideoEditorSettingsList createVesdkSettingsList() {
        // Create an empty new SettingsList and apply the changes on this reference.
        VideoEditorSettingsList settingsList = new VideoEditorSettingsList();

        // If you include our asset Packs and you use our UI you also need to add them to the UI,
        // otherwise they are only available for the backend
        // See the specific feature sections of our guides if you want to know how to add your own Assets.

        settingsList.getSettingsModel(UiConfigFilter.class).setFilterList(
            FilterPackBasic.getFilterPack()
        );

        settingsList.getSettingsModel(UiConfigText.class).setFontList(
          FontPackBasic.getFontPack()
        );

        settingsList.getSettingsModel(UiConfigFrame.class).setFrameList(
          FramePackBasic.getFramePack()
        );

        settingsList.getSettingsModel(UiConfigOverlay.class).setOverlayList(
          OverlayPackBasic.getOverlayPack()
        );

        settingsList.getSettingsModel(UiConfigSticker.class).setStickerLists(
          StickerPackEmoticons.getStickerCategory(),
          StickerPackShapes.getStickerCategory()
        );

        // Set custom editor image export settings
        settingsList.getSettingsModel(SaveSettings.class)
          .setExportDir(Directory.DCIM, "SomeFolderName")
          .setExportPrefix("result_")
          .setSavePolicy(SaveSettings.SavePolicy.RETURN_ALWAYS_ONLY_OUTPUT);

        return settingsList;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        openSystemGalleryToSelectAnVideo();
    }

    private void openSystemGalleryToSelectAnVideo() {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,"video/*");
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, GALLERY_RESULT);
        } else {
            Toast.makeText(
              this,
              "No Gallery app installed",
              Toast.LENGTH_LONG
            ).show();
        }
    }

    private void openEditor(Uri inputImage) {
        VideoEditorSettingsList settingsList;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            settingsList = createVesdkSettingsList();
        } else {
            Toast.makeText(this, "Video support needs Android 4.3", Toast.LENGTH_LONG).show();
            return;
        }

        // Set input video
        settingsList.getSettingsModel(LoadSettings.class).setSource(inputImage);

        new VideoEditorBuilder(this)
          .setSettingsList(settingsList)
          .startActivityForBroadcast(this, VESDK_RESULT_BROADCAST_RECEIVER);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK && requestCode == GALLERY_RESULT) {
            // Open Editor with some uri in this case with an image selected from the system gallery.
            Uri selectedImage = data.getData();
            openEditor(selectedImage);
        }
    }
}
class KVideoEditorDemoActivityAsync : Activity(), PermissionRequest.Response {

    companion object {
        const val VESDK_RESULT_BROADCAST_RECEIVER = "my.fancy.BroadcastReceiverAction"
        const val GALLERY_RESULT = 2
    }

    // Important permission request for Android 6.0 and above, don't forget to add this!
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        PermissionRequest.onRequestPermissionsResult(requestCode, permissions, grantResults)
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }

    override fun permissionGranted() {}

    override fun permissionDenied() {
        /* TODO: The Permission was rejected by the user. The Editor was not opened,
         * Show a hint to the user and try again. */
    }

    // Create an empty new SettingsList and apply the changes on this reference.
    // If you include our asset Packs and use our UI you also need to add them to the UI,
    // otherwise they are only available for the backend (like Serialization)
    // See the specific feature sections of our guides if you want to know how to add your own Assets.
    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private fun createVesdkSettingsList() =
      VideoEditorSettingsList()
        .configure<UiConfigFilter> {
            it.setFilterList(FilterPackBasic.getFilterPack())
        }
        .configure<UiConfigText> {
            it.setFontList(FontPackBasic.getFontPack())
        }
        .configure<UiConfigFrame> {
            it.setFrameList(FramePackBasic.getFramePack())
        }
        .configure<UiConfigOverlay> {
            it.setOverlayList(OverlayPackBasic.getOverlayPack())
        }
        .configure<UiConfigSticker> {
          it.setStickerLists(
            StickerPackEmoticons.getStickerCategory(),
            StickerPackShapes.getStickerCategory()
          )
        }
        .configure<SaveSettings> {
          // Set custom editor image export settings
          it.setExportDir(Directory.DCIM, "SomeFolderName")
          it.setExportPrefix("result_")
          it.savePolicy = SaveSettings.SavePolicy.RETURN_ALWAYS_ONLY_OUTPUT
        }


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        openSystemGalleryToSelectAnVideo()
    }

    fun openSystemGalleryToSelectAnVideo() {
        val intent = Intent(Intent.ACTION_PICK)
        intent.setDataAndType(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,"video/*")

        if (intent.resolveActivity(packageManager) != null) {
            startActivityForResult(intent, GALLERY_RESULT)
        } else {
            Toast.makeText(
              this,
              "No Gallery app installed",
              Toast.LENGTH_LONG
            ).show()
        }
    }

    fun openEditor(inputImage: Uri?) {
        val settingsList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            createVesdkSettingsList()
        } else {
            Toast.makeText(this, "Video support needs Android 4.3", Toast.LENGTH_LONG).show()
            return
        }

        settingsList.configure<LoadSettings> {
            it.source = inputImage
        }

        settingsList[LoadSettings::class].source = inputImage

        VideoEditorBuilder(this)
          .setSettingsList(settingsList)
          .startActivityForBroadcast(this, VESDK_RESULT_BROADCAST_RECEIVER)
    }
    
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == RESULT_OK && requestCode == GALLERY_RESULT) {
            // Open Editor with some uri in this case with an video selected from the system gallery.
            openEditor(data?.data)

        }
    }

}